diff --git a/app/logical/post_query_builder.rb b/app/logical/post_query_builder.rb index 7ae704637..0e7eba29c 100644 --- a/app/logical/post_query_builder.rb +++ b/app/logical/post_query_builder.rb @@ -14,7 +14,7 @@ class PostQueryBuilder METATAGS = %w[ -user user -approver approver -commenter commenter comm -noter noter -noteupdater noteupdater artcomm -pool pool ordpool -favgroup favgroup -fav - fav ordfav md5 -rating rating -locked locked width height mpixels ratio + fav -ordfav ordfav md5 -rating rating -locked locked width height mpixels ratio score favcount filesize source -source id -id date age order limit -status status tagcount parent -parent child pixiv_id pixiv search -upvote upvote -downvote downvote filetype -filetype flagger -flagger appealer -appealer @@ -475,8 +475,6 @@ class PostQueryBuilder relation = relation.bit_flags_match(:has_embedded_notes, false) end - relation = add_tag_string_search_relation(q[:tags], relation) - if q[:ordpool].present? pool_name = q[:ordpool] @@ -511,12 +509,40 @@ class PostQueryBuilder relation = add_user_subquery_relation(PostVote.negative.visible(CurrentUser.user), downvoter, relation, field: :user).negate end - if q[:ordfav].present? - user_id = q[:ordfav].to_i - relation = relation.joins("INNER JOIN favorites ON favorites.post_id = posts.id") - relation = relation.where("favorites.user_id % 100 = ? and favorites.user_id = ?", user_id % 100, user_id).order("favorites.id DESC") + q[:fav_neg].to_a.each do |username| + favuser = User.find_by_name(username) + + if favuser.present? && Pundit.policy!([CurrentUser.user, nil], favuser).can_see_favorites? + q[:tags][:exclude] << "fav:#{favuser.id}" + else + relation = relation.all # no-op; excluding a nonexistent user returns everything + end end + q[:fav].to_a.each do |username| + favuser = User.find_by_name(username) + + if favuser.present? && Pundit.policy!([CurrentUser.user, nil], favuser).can_see_favorites? + q[:tags][:related] << "fav:#{favuser.id}" + else + relation = relation.none + end + end + + q[:ordfav].to_a.each do |username| + favuser = User.find_by_name(username) + + if favuser.present? && Pundit.policy!([CurrentUser.user, nil], favuser).can_see_favorites? + q[:tags][:related] << "fav:#{favuser.id}" + relation = relation.joins("INNER JOIN favorites ON favorites.post_id = posts.id") + relation = relation.where("favorites.user_id % 100 = ? and favorites.user_id = ?", favuser.id % 100, favuser.id).order("favorites.id DESC") + else + relation = relation.none + end + end + + relation = add_tag_string_search_relation(q[:tags], relation) + # HACK: if we're using a date: or age: metatag, default to ordering by # created_at instead of id so that the query will use the created_at index. if q[:date].present? || q[:age].present? @@ -791,33 +817,17 @@ class PostQueryBuilder q[:favgroup] ||= [] q[:favgroup] << g2 - when "-fav" - favuser = User.find_by_name(g2) - - if favuser.nil? || !Pundit.policy!([CurrentUser.user, nil], favuser).can_see_favorites? - raise User::PrivilegeError - end - - q[:tags][:exclude] << "fav:#{favuser.id}" + when "-fav", "-ordfav" + q[:fav_neg] ||= [] + q[:fav_neg] << g2 when "fav" - favuser = User.find_by_name(g2) - - if favuser.nil? || !Pundit.policy!([CurrentUser.user, nil], favuser).can_see_favorites? - raise User::PrivilegeError - end - - q[:tags][:related] << "fav:#{favuser.id}" + q[:fav] ||= [] + q[:fav] << g2 when "ordfav" - favuser = User.find_by_name(g2) - - if favuser.nil? || !Pundit.policy!([CurrentUser.user, nil], favuser).can_see_favorites? - raise User::PrivilegeError.new - end - - q[:tags][:related] << "fav:#{favuser.id}" - q[:ordfav] = favuser.id + q[:ordfav] ||= [] + q[:ordfav] << g2 when "search" q[:saved_searches] ||= [] diff --git a/test/unit/post_test.rb b/test/unit/post_test.rb index 90929ae73..a45388097 100644 --- a/test/unit/post_test.rb +++ b/test/unit/post_test.rb @@ -2016,13 +2016,25 @@ class PostTest < ActiveSupport::TestCase end should "return posts for the fav: metatag" do - users = FactoryBot.create_list(:user, 2) - posts = users.map do |u| - CurrentUser.scoped(u) { FactoryBot.create(:post, tag_string: "fav:#{u.name}") } - end + user1 = create(:user) + user2 = create(:user) + user3 = create(:user, enable_private_favorites: true) + post1 = as(user1) { create(:post, tag_string: "fav:true") } + post2 = as(user2) { create(:post, tag_string: "fav:true") } + post3 = as(user3) { create(:post, tag_string: "fav:true") } - assert_tag_match([posts[0]], "fav:#{users[0].name}") - assert_tag_match([posts[1]], "-fav:#{users[0].name}") + assert_tag_match([post1], "fav:#{user1.name}") + assert_tag_match([post2], "fav:#{user2.name}") + assert_tag_match([], "fav:#{user3.name}") + assert_tag_match([], "fav:dne") + + assert_tag_match([post3, post2], "-fav:#{user1.name}") + assert_tag_match([post3, post2, post1], "-fav:dne") + + as(user3) do + assert_tag_match([post3], "fav:#{user3.name}") + assert_tag_match([post2, post1], "-fav:#{user3.name}") + end end should "return posts for the ordfav: metatag" do