120 lines
3.4 KiB
Ruby
120 lines
3.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class PostQuery
|
|
extend Memoist
|
|
|
|
private attr_reader :current_user, :tag_limit, :safe_mode, :hide_deleted_posts, :builder
|
|
|
|
delegate :tag?, :metatag?, :wildcard?, :metatags, :wildcards, :tag_names, :metatags, to: :ast
|
|
alias_method :safe_mode?, :safe_mode
|
|
alias_method :hide_deleted_posts?, :hide_deleted_posts
|
|
|
|
def initialize(search_or_ast, current_user: User.anonymous, tag_limit: nil, safe_mode: false, hide_deleted_posts: false)
|
|
if search_or_ast.is_a?(AST)
|
|
@ast = search_or_ast
|
|
else
|
|
@search = search_or_ast.to_s
|
|
end
|
|
|
|
@current_user = current_user
|
|
@tag_limit = tag_limit
|
|
@safe_mode = safe_mode
|
|
@hide_deleted_posts = hide_deleted_posts
|
|
end
|
|
|
|
# Build a new PostQuery from the given AST and the current settings.
|
|
def build(ast)
|
|
PostQuery.new(ast, current_user: current_user, tag_limit: tag_limit, safe_mode: safe_mode, hide_deleted_posts: hide_deleted_posts)
|
|
end
|
|
|
|
def builder
|
|
@builder ||= PostQueryBuilder.new(search, current_user, tag_limit: tag_limit, safe_mode: safe_mode, hide_deleted_posts: hide_deleted_posts)
|
|
end
|
|
|
|
def search
|
|
@search ||= ast.to_infix
|
|
end
|
|
|
|
def ast
|
|
@ast ||= Parser.parse(search)
|
|
end
|
|
|
|
def fast_count(...)
|
|
builder.normalized_query.fast_count(...)
|
|
end
|
|
|
|
def tags
|
|
Tag.where(name: tag_names)
|
|
end
|
|
|
|
# True if this search would return all posts (normally because the search is the empty string).
|
|
def is_empty_search?
|
|
ast.all?
|
|
end
|
|
|
|
# True if this search would return nothing (normally because there was a syntax error).
|
|
def is_null_search?
|
|
ast.none?
|
|
end
|
|
|
|
def is_single_tag?
|
|
ast.tag?
|
|
end
|
|
|
|
def select_metatags(*names)
|
|
metatags.select { |metatag| metatag.name.in?(names.map(&:to_s).map(&:downcase)) }
|
|
end
|
|
|
|
def has_metatag?(*names)
|
|
select_metatags(*names).present?
|
|
end
|
|
|
|
def find_metatag(*names)
|
|
select_metatags(*names).first&.value
|
|
end
|
|
|
|
# Return a new PostQuery with aliases replaced, implicit metatags added, and the query converted to conjunctive normal form.
|
|
def normalize
|
|
replace_aliases.with_implicit_metatags.to_cnf
|
|
end
|
|
|
|
# Return a new PostQuery with aliases replaced.
|
|
def replace_aliases
|
|
return self if aliases.empty?
|
|
build(ast.replace_tags(aliases))
|
|
end
|
|
|
|
# Return a new PostQuery with implicit metatags (rating:safe and -status:deleted) added.
|
|
def with_implicit_metatags
|
|
return self if implicit_metatags.empty?
|
|
build(AST.new(:and, [ast, *implicit_metatags]))
|
|
end
|
|
|
|
# Return a new PostQuery converted to conjunctive normal form.
|
|
def to_cnf
|
|
build(ast.to_cnf)
|
|
end
|
|
|
|
# Return a hash mapping aliased tag names to real tag names.
|
|
def aliases
|
|
TagAlias.aliases_for(tag_names)
|
|
end
|
|
|
|
# Implicit metatags are metatags added by the user's account settings. rating:s is implicit
|
|
# under safe mode. -status:deleted is implicit when the "hide deleted posts" setting is on.
|
|
def implicit_metatags
|
|
metatags = []
|
|
metatags << AST.metatag("rating", "s") if safe_mode?
|
|
metatags << -AST.metatag("status", "deleted") if hide_deleted?
|
|
metatags
|
|
end
|
|
|
|
# XXX unify with PostSets::Post#show_deleted?
|
|
def hide_deleted?
|
|
has_status_metatag = select_metatags(:status).any? { |metatag| metatag.value.downcase.in?(%w[deleted active any all unmoderated modqueue appealed]) }
|
|
hide_deleted_posts? && !has_status_metatag
|
|
end
|
|
|
|
memoize :tags, :normalize, :replace_aliases, :with_implicit_metatags, :to_cnf, :aliases, :implicit_metatags, :hide_deleted?
|
|
end
|