favorites: refactor fav:/ordfav: searches to not use fav_string.

Refactor fav:<name> and ordfav:<name> searches to use the favorites
table instead of the posts.fav_string.

This may be slower for fav:<name> searches. The fav_string effectively
treats favorites like secret tags on the post, so fav:<name> searches
were effectively the same as tag searches. Now they do a subquery on the
favorites table, which may not perform as well for things like multiple
fav:<name> metatags or negated fav:<name> metatags.

For ordfav:<name> searches, this may be faster. ordfav: searches had a
tag match clause (`tag_index @@ 'fav:123'`) in addition to a join on the
favs table. This was redundant, and in some cases it inhibited the query
planner from choosing a more optimal plan.

Partially addresses #4652 by eliminating another place where we depended
on the fav_string.
This commit is contained in:
evazion
2021-01-03 19:01:44 -06:00
parent 11a8c2877b
commit 98ee6c31c1
2 changed files with 10 additions and 3 deletions

View File

@@ -393,7 +393,8 @@ class PostQueryBuilder
favuser = User.find_by_name(username)
if favuser.present? && Pundit.policy!([current_user, nil], favuser).can_see_favorites?
tags_include("fav:#{favuser.id}")
favorites = Favorite.from("favorites_#{favuser.id % 100} AS favorites").where(user: favuser)
Post.where(id: favorites.select(:post_id))
else
Post.none
end
@@ -402,8 +403,8 @@ class PostQueryBuilder
def ordfav_matches(username)
user = User.find_by_name(username)
if user.present?
favorites_include(username).joins(:favorites).merge(Favorite.for_user(user.id)).order("favorites.id DESC")
if user.present? && Pundit.policy!([current_user, nil], user).can_see_favorites?
Post.joins(:favorites).merge(Favorite.for_user(user.id)).order("favorites.id DESC")
else
Post.none
end

View File

@@ -191,9 +191,15 @@ class PostQueryBuilderTest < ActiveSupport::TestCase
assert_tag_match([post1], "fav:#{user1.name}")
assert_tag_match([post2], "fav:#{user2.name}")
assert_tag_match([], "fav:#{user3.name}")
assert_tag_match([], "fav:#{user1.name} fav:#{user2.name}")
assert_tag_match([post1], "fav:#{user1.name} -fav:#{user2.name}")
assert_tag_match([post3], "-fav:#{user1.name} -fav:#{user2.name}")
assert_tag_match([], "fav:dne")
assert_tag_match([post3, post2], "-fav:#{user1.name}")
assert_tag_match([post3], "-fav:#{user1.name} -fav:#{user2.name}")
assert_tag_match([post3, post2, post1], "-fav:dne")
as(user3) do