1 # frozen_string_literal: true
3 class AccountSearchService
< BaseService
4 attr_reader
:query, :limit, :offset, :options, :account
6 def call(query
, account
= nil, options
= {})
7 @acct_hint = query
.start_with
?('@')
8 @query = query
.strip
.gsub(/\A@/, '')
9 @limit = options
[:limit].to_i
10 @offset = options
[:offset].to_i
14 search_service_results
.compact
.uniq
19 def search_service_results
20 return [] if query
.blank
? || limit
< 1
22 [exact_match
] + search_results
26 return unless offset
.zero
? && username_complete
?
28 return @exact_match if defined?(@exact_match)
32 ResolveAccountService
.new
.call(query
)
33 elsif domain_is_local
?
34 Account
.find_local(query_username
)
36 Account
.find_remote(query_username
, query_domain
)
42 return [] if limit_for_non_exact_results
.zero
?
44 @search_results ||= begin
45 results
= from_elasticsearch
if Chewy
.enabled
?
46 results
||= from_database
53 advanced_search_results
59 def advanced_search_results
60 Account
.advanced_search_for(terms_for_query
, account
, limit_for_non_exact_results
, options
[:following], offset
)
63 def simple_search_results
64 Account
.search_for(terms_for_query
, limit_for_non_exact_results
, offset
)
67 def from_elasticsearch
68 must_clauses
= [{ multi_match
: { query
: terms_for_query
, fields
: likely_acct
? ? %w(acct
.edge_ngram acct
) : %w(acct
.edge_ngram acct display_name
.edge_ngram display_name
), type
: 'most_fields', operator
: 'and' } }]
72 return [] if options
[:following] && following_ids
.empty
?
74 if options
[:following]
75 must_clauses
<< { terms
: { id
: following_ids
} }
76 elsif following_ids
.any
?
77 should_clauses
<< { terms
: { id
: following_ids
, boost
: 100 } }
81 query
= { bool
: { must
: must_clauses
, should
: should_clauses
} }
82 functions
= [reputation_score_function
, followers_score_function
, time_distance_function
]
84 records
= AccountsIndex
.query(function_score
: { query
: query
, functions
: functions
, boost_mode
: 'multiply', score_mode
: 'avg' })
85 .limit(limit_for_non_exact_results
)
90 ActiveRecord
::Associations::Preloader.new
.preload(records
, :account_stat)
93 rescue Faraday
::ConnectionFailed, Parslet
::ParseFailed
97 def reputation_score_function
101 source
: "(doc['followers_count'].value + 0.0) / (doc
['followers_count'].value + doc
['following_count'].value +
1)",
107 def followers_score_function
109 field_value_factor: {
110 field: 'followers_count',
117 def time_distance_function
130 @following_ids ||= account.active_relationships.pluck(:target_account_id)
133 def limit_for_non_exact_results
149 def split_query_string
150 @split_query_string ||= query.split('@')
154 @query_username ||= split_query_string.first || ''
158 @query_domain ||= query_without_split? ? nil : split_query_string.last
161 def query_without_split?
162 split_query_string.size == 1
166 @domain_is_local ||= TagManager.instance.local_domain?(query_domain)
173 def username_complete?
174 query.include?('@') && "@
#{query}" =~ Account::MENTION_RE
178 @acct_hint || username_complete?