post queries: add methods for normalizing queries.

This commit is contained in:
evazion
2022-04-04 03:48:15 -05:00
parent 1957cb354e
commit 7fe717506d
2 changed files with 101 additions and 15 deletions

View File

@@ -3,14 +3,40 @@
class PostQuery
extend Memoist
attr_reader :search, :parser, :builder, :ast
delegate :tag?, :metatag?, :wildcard?, :metatags, :wildcards, :tag_names, :metatags, to: :ast
private attr_reader :current_user, :tag_limit, :safe_mode, :hide_deleted_posts, :builder
def initialize(search, current_user: User.anonymous, tag_limit: nil, safe_mode: false, hide_deleted_posts: false)
@search = search
@parser = Parser.new(search)
@builder = PostQueryBuilder.new(search, current_user, tag_limit: tag_limit, safe_mode: safe_mode, hide_deleted_posts: hide_deleted_posts)
@ast = parser.parse.simplify
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(...)
@@ -47,15 +73,47 @@ class PostQuery
select_metatags(*names).first&.value
end
# Return a new query AST, with aliased tags replaced with real tags.
def replace_aliases
ast.replace_tags(aliases)
# 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
# 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
TagAlias.aliases_for(tag_names)
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

View File

@@ -54,9 +54,37 @@ class PostQuery
@args = args
end
# Create an AST node.
def node(type, *args)
AST.new(type, args)
concerning :ConstructorMethods do
class_methods do
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
concerning :SimplificationMethods do