search: split tag_match into user_tag_match / system_tag_match.

When doing a tag search, we have to be careful about which user we're
running the search as because the results depend on the current user.
Specifically, things like private favorites, private favorite groups,
post votes, saved searches, and flagger names depend on the user's
permissions, and whether non-safe or deleted posts are filtered out
depend on whether the user has safe mode on or the hide deleted posts
setting enabled.

* Refactor internal searches to explicitly state whether they're
  running as the system user (DanbooruBot) or as the current user.
* Explicitly pass in the current user to PostQueryBuilder instead of
  implicitly relying on the CurrentUser global.
* Get rid of CurrentUser.admin_mode? (used to ignore the hide deleted
  post setting) and CurrentUser.without_safe_mode (used to ignore safe
  mode).
* Change the /counts/posts.json endpoint to ignore safe mode and the
  hide deleted posts settings when counting posts.
* Fix searches not correctly overriding the hide deleted posts setting
  when multiple status: metatags were used (e.g. `status:banned status:active`)
* Fix fast_count not respecting the hide deleted posts setting when the
  status:banned metatag was used.
This commit is contained in:
evazion
2020-05-06 22:00:47 -05:00
parent a753ebbea9
commit f38c38f26e
24 changed files with 120 additions and 147 deletions

View File

@@ -134,10 +134,8 @@ class Artist < ApplicationRecord
source = params.delete(:source)
if source.blank? && params[:name].present?
CurrentUser.without_safe_mode do
post = Post.tag_match("source:http* #{params[:name]}").first
source = post.try(:source)
end
post = Post.system_tag_match("source:http* #{params[:name]}").first
source = post.try(:source)
end
if source.present?
@@ -168,36 +166,32 @@ class Artist < ApplicationRecord
module BanMethods
def unban!
Post.transaction do
CurrentUser.without_safe_mode do
ti = TagImplication.find_by(antecedent_name: name, consequent_name: "banned_artist")
ti&.destroy
ti = TagImplication.find_by(antecedent_name: name, consequent_name: "banned_artist")
ti&.destroy
Post.tag_match(name).find_each do |post|
post.unban!
fixed_tags = post.tag_string.sub(/(?:\A| )banned_artist(?:\Z| )/, " ").strip
post.update(tag_string: fixed_tags)
end
update!(is_banned: false)
ModAction.log("unbanned artist ##{id}", :artist_unban)
Post.raw_tag_match(name).find_each do |post|
post.unban!
fixed_tags = post.tag_string.sub(/(?:\A| )banned_artist(?:\Z| )/, " ").strip
post.update(tag_string: fixed_tags)
end
update!(is_banned: false)
ModAction.log("unbanned artist ##{id}", :artist_unban)
end
end
def ban!(banner: CurrentUser.user)
Post.transaction do
CurrentUser.without_safe_mode do
Post.tag_match(name).each(&:ban!)
Post.raw_tag_match(name).each(&:ban!)
# potential race condition but unlikely
unless TagImplication.where(:antecedent_name => name, :consequent_name => "banned_artist").exists?
tag_implication = TagImplication.create!(antecedent_name: name, consequent_name: "banned_artist", skip_secondary_validations: true, creator: banner)
tag_implication.approve!(approver: banner)
end
update!(is_banned: true)
ModAction.log("banned artist ##{id}", :artist_ban)
# potential race condition but unlikely
unless TagImplication.where(:antecedent_name => name, :consequent_name => "banned_artist").exists?
tag_implication = TagImplication.create!(antecedent_name: name, consequent_name: "banned_artist", skip_secondary_validations: true, creator: banner)
tag_implication.approve!(approver: banner)
end
update!(is_banned: true)
ModAction.log("banned artist ##{id}", :artist_ban)
end
end
end

View File

@@ -27,7 +27,7 @@ class Pool < ApplicationRecord
end
def post_tags_match(query)
posts = Post.tag_match(query).select(:id).reorder(nil)
posts = Post.user_tag_match(query).select(:id).reorder(nil)
pools = Pool.joins("CROSS JOIN unnest(post_ids) AS post_id").group(:id).where("post_id IN (?)", posts)
where(id: pools)
end

View File

@@ -604,7 +604,7 @@ class Post < ApplicationRecord
# If someone else committed changes to this post before we did,
# then try to merge the tag changes together.
current_tags = tag_string_was.split
new_tags = PostQueryBuilder.new(tag_string).parse_tag_edit
new_tags = PostQueryBuilder.new(tag_string).split_query
old_tags = old_tag_string.split
kept_tags = current_tags & new_tags
@@ -642,7 +642,7 @@ class Post < ApplicationRecord
end
def normalize_tags
normalized_tags = PostQueryBuilder.new(tag_string).parse_tag_edit
normalized_tags = PostQueryBuilder.new(tag_string).split_query
normalized_tags = apply_casesensitive_metatags(normalized_tags)
normalized_tags = normalized_tags.map(&:downcase)
normalized_tags = filter_metatags(normalized_tags)
@@ -1372,9 +1372,7 @@ class Post < ApplicationRecord
end
def sample(query, sample_size)
CurrentUser.without_safe_mode do
tag_match(query).reorder(:md5).limit(sample_size)
end
user_tag_match(query, safe_mode: false, hide_deleted_posts: false).reorder(:md5).limit(sample_size)
end
# unflattens the tag_string into one tag per row.
@@ -1472,8 +1470,12 @@ class Post < ApplicationRecord
where("posts.tag_index @@ to_tsquery('danbooru', E?)", tag.to_escaped_for_tsquery)
end
def tag_match(query)
PostQueryBuilder.new(query).build
def system_tag_match(query)
user_tag_match(query, User.system, safe_mode: false, hide_deleted_posts: false)
end
def user_tag_match(query, user = CurrentUser.user, safe_mode: CurrentUser.safe_mode?, hide_deleted_posts: user.hide_deleted_posts?)
PostQueryBuilder.new(query, user, safe_mode: safe_mode, hide_deleted_posts: hide_deleted_posts).build
end
def search(params)
@@ -1488,7 +1490,7 @@ class Post < ApplicationRecord
)
if params[:tags].present?
q = q.tag_match(params[:tags])
q = q.user_tag_match(params[:tags])
end
if params[:order].present?

View File

@@ -114,18 +114,16 @@ class SavedSearch < ApplicationRecord
end
def populate(query, timeout: 10_000)
CurrentUser.as_system do
redis_key = "search:#{query}"
return if redis.exists(redis_key)
redis_key = "search:#{query}"
return if redis.exists(redis_key)
post_ids = Post.with_timeout(timeout, [], query: query) do
Post.tag_match(query).limit(QUERY_LIMIT).pluck(:id)
end
post_ids = Post.with_timeout(timeout, [], query: query) do
Post.system_tag_match(query).limit(QUERY_LIMIT).pluck(:id)
end
if post_ids.present?
redis.sadd(redis_key, post_ids)
redis.expire(redis_key, REDIS_EXPIRY.to_i)
end
if post_ids.present?
redis.sadd(redis_key, post_ids)
redis.expire(redis_key, REDIS_EXPIRY.to_i)
end
end
end

View File

@@ -349,7 +349,7 @@ class Tag < ApplicationRecord
end
def posts
Post.tag_match(name)
Post.system_tag_match(name)
end
def self.available_includes