post queries: add methods for normalizing queries.
This commit is contained in:
@@ -3,14 +3,40 @@
|
|||||||
class PostQuery
|
class PostQuery
|
||||||
extend Memoist
|
extend Memoist
|
||||||
|
|
||||||
attr_reader :search, :parser, :builder, :ast
|
private attr_reader :current_user, :tag_limit, :safe_mode, :hide_deleted_posts, :builder
|
||||||
delegate :tag?, :metatag?, :wildcard?, :metatags, :wildcards, :tag_names, :metatags, to: :ast
|
|
||||||
|
|
||||||
def initialize(search, current_user: User.anonymous, tag_limit: nil, safe_mode: false, hide_deleted_posts: false)
|
delegate :tag?, :metatag?, :wildcard?, :metatags, :wildcards, :tag_names, :metatags, to: :ast
|
||||||
@search = search
|
alias_method :safe_mode?, :safe_mode
|
||||||
@parser = Parser.new(search)
|
alias_method :hide_deleted_posts?, :hide_deleted_posts
|
||||||
@builder = PostQueryBuilder.new(search, current_user, tag_limit: tag_limit, safe_mode: safe_mode, hide_deleted_posts: hide_deleted_posts)
|
|
||||||
@ast = parser.parse.simplify
|
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
|
end
|
||||||
|
|
||||||
def fast_count(...)
|
def fast_count(...)
|
||||||
@@ -47,15 +73,47 @@ class PostQuery
|
|||||||
select_metatags(*names).first&.value
|
select_metatags(*names).first&.value
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return a new query AST, with aliased tags replaced with real tags.
|
# Return a new PostQuery with aliases replaced, implicit metatags added, and the query converted to conjunctive normal form.
|
||||||
def replace_aliases
|
def normalize
|
||||||
ast.replace_tags(aliases)
|
replace_aliases.with_implicit_metatags.to_cnf
|
||||||
end
|
end
|
||||||
|
|
||||||
# A hash mapping aliased tag names to real tag names.
|
# 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
|
def aliases
|
||||||
TagAlias.aliases_for(tag_names)
|
TagAlias.aliases_for(tag_names)
|
||||||
end
|
end
|
||||||
|
|
||||||
memoize :tags, :aliases
|
# 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
|
end
|
||||||
|
|||||||
@@ -54,9 +54,37 @@ class PostQuery
|
|||||||
@args = args
|
@args = args
|
||||||
end
|
end
|
||||||
|
|
||||||
# Create an AST node.
|
concerning :ConstructorMethods do
|
||||||
def node(type, *args)
|
class_methods do
|
||||||
AST.new(type, args)
|
def tag(name)
|
||||||
|
AST.new(:tag, [name])
|
||||||
|
end
|
||||||
|
|
||||||
|
def metatag(name, value)
|
||||||
|
AST.new(:metatag, [name, value])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def &(other)
|
||||||
|
AST.new(:and, [self, other])
|
||||||
|
end
|
||||||
|
|
||||||
|
def |(other)
|
||||||
|
AST.new(:or, [self, other])
|
||||||
|
end
|
||||||
|
|
||||||
|
def ~
|
||||||
|
AST.new(:opt, [self])
|
||||||
|
end
|
||||||
|
|
||||||
|
def -@
|
||||||
|
AST.new(:not, [self])
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create an AST node.
|
||||||
|
def node(type, *args)
|
||||||
|
AST.new(type, args)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
concerning :SimplificationMethods do
|
concerning :SimplificationMethods do
|
||||||
|
|||||||
Reference in New Issue
Block a user