0
0
Fork 0

Make account search blazing fast and rank followers/followees higher in the results

This commit is contained in:
Eugen Rochko 2017-03-17 20:47:38 +01:00
parent 22f9399cc3
commit ad0d82d3ce
6 changed files with 50 additions and 15 deletions

View file

@ -2,7 +2,6 @@
class Account < ApplicationRecord
include Targetable
include PgSearch
MENTION_RE = /(?:^|[^\/\w])@([a-z0-9_]+(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i
IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif'].freeze
@ -56,9 +55,6 @@ class Account < ApplicationRecord
# PuSH subscriptions
has_many :subscriptions, dependent: :destroy
pg_search_scope :search_for, against: { display_name: 'A', username: 'B', domain: 'C' },
using: { tsearch: { prefix: true } }
scope :remote, -> { where.not(domain: nil) }
scope :local, -> { where(domain: nil) }
scope :without_followers, -> { where('(select count(f.id) from follows as f where f.target_account_id = accounts.id) = 0') }
@ -212,6 +208,42 @@ SQL
Account.find_by_sql([sql, account.id, account.id, limit])
end
def search_for(terms, limit = 10)
textsearch = '(setweight(to_tsvector(\'simple\', accounts.display_name), \'A\') || setweight(to_tsvector(\'simple\', accounts.username), \'B\') || setweight(to_tsvector(\'simple\', coalesce(accounts.domain, \'\')), \'C\'))'
query = 'to_tsquery(\'simple\', \'\'\' \' || ? || \' \'\'\' || \':*\')'
sql = <<SQL
SELECT
accounts.*,
ts_rank_cd(#{textsearch}, #{query}, 32) AS rank
FROM accounts
WHERE #{query} @@ #{textsearch}
ORDER BY rank DESC
LIMIT ?
SQL
Account.find_by_sql([sql, terms, terms, limit])
end
def advanced_search_for(terms, account, limit = 10)
textsearch = '(setweight(to_tsvector(\'simple\', accounts.display_name), \'A\') || setweight(to_tsvector(\'simple\', accounts.username), \'B\') || setweight(to_tsvector(\'simple\', coalesce(accounts.domain, \'\')), \'C\'))'
query = 'to_tsquery(\'simple\', \'\'\' \' || ? || \' \'\'\' || \':*\')'
sql = <<SQL
SELECT
accounts.*,
(count(f.id) + 1) * ts_rank_cd(#{textsearch}, #{query}, 32) AS rank
FROM accounts
LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = ?) OR (accounts.id = f.target_account_id AND f.account_id = ?)
WHERE #{query} @@ #{textsearch}
GROUP BY accounts.id
ORDER BY rank DESC
LIMIT ?
SQL
Account.find_by_sql([sql, terms, account.id, account.id, terms, limit])
end
def following_map(target_account_ids, account_id)
follow_mapping(Follow.where(target_account_id: target_account_ids, account_id: account_id), :target_account_id)
end