Merge branch 'master' into glitch-soc/merge-upstream

Conflicts:
- app/controllers/home_controller.rb
- app/controllers/shares_controller.rb
- app/javascript/packs/public.js
- app/models/status.rb
- app/serializers/initial_state_serializer.rb
- app/views/home/index.html.haml
- app/views/layouts/public.html.haml
- app/views/public_timelines/show.html.haml
- app/views/shares/show.html.haml
- app/views/tags/show.html.haml
- config/initializers/content_security_policy.rb
- config/locales/en.yml
- config/webpack/shared.js
- package.json
This commit is contained in:
Thibaut Girka
2019-08-19 19:31:32 +02:00
234 changed files with 6726 additions and 1414 deletions

View File

@@ -4,47 +4,150 @@ class AccountSearchService < BaseService
attr_reader :query, :limit, :offset, :options, :account
def call(query, account = nil, options = {})
@query = query.strip
@limit = options[:limit].to_i
@offset = options[:offset].to_i
@options = options
@account = account
@acct_hint = query.start_with?('@')
@query = query.strip.gsub(/\A@/, '')
@limit = options[:limit].to_i
@offset = options[:offset].to_i
@options = options
@account = account
search_service_results
search_service_results.compact.uniq
end
private
def search_service_results
return [] if query_blank_or_hashtag? || limit < 1
return [] if query.blank? || limit < 1
if resolving_non_matching_remote_account?
[ResolveAccountService.new.call("#{query_username}@#{query_domain}")].compact
else
search_results_and_exact_match.compact.uniq
[exact_match] + search_results
end
def exact_match
return unless offset.zero? && username_complete?
return @exact_match if defined?(@exact_match)
@exact_match = begin
if options[:resolve]
ResolveAccountService.new.call(query)
elsif domain_is_local?
Account.find_local(query_username)
else
Account.find_remote(query_username, query_domain)
end
end
end
def resolving_non_matching_remote_account?
offset.zero? && options[:resolve] && !exact_match? && !domain_is_local?
def search_results
return [] if limit_for_non_exact_results.zero?
@search_results ||= begin
if Chewy.enabled?
from_elasticsearch
else
from_database
end
end
end
def search_results_and_exact_match
return search_results.to_a unless offset.zero?
results = [exact_match]
return results if exact_match? && limit == 1
results + search_results.to_a
def from_database
if account
advanced_search_results
else
simple_search_results
end
end
def query_blank_or_hashtag?
query.blank? || query.start_with?('#')
def advanced_search_results
Account.advanced_search_for(terms_for_query, account, limit_for_non_exact_results, options[:following], offset)
end
def simple_search_results
Account.search_for(terms_for_query, limit_for_non_exact_results, offset)
end
def from_elasticsearch
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' } }]
should_clauses = []
if account
return [] if options[:following] && following_ids.empty?
if options[:following]
must_clauses << { terms: { id: following_ids } }
elsif following_ids.any?
should_clauses << { terms: { id: following_ids, boost: 100 } }
end
end
query = { bool: { must: must_clauses, should: should_clauses } }
functions = [reputation_score_function, followers_score_function, time_distance_function]
records = AccountsIndex.query(function_score: { query: query, functions: functions, boost_mode: 'multiply', score_mode: 'avg' })
.limit(limit_for_non_exact_results)
.offset(offset)
.objects
.compact
ActiveRecord::Associations::Preloader.new.preload(records, :account_stat)
records
end
def reputation_score_function
{
script_score: {
script: {
source: "(doc['followers_count'].value + 0.0) / (doc['followers_count'].value + doc['following_count'].value + 1)",
},
},
}
end
def followers_score_function
{
field_value_factor: {
field: 'followers_count',
modifier: 'log2p',
missing: 0,
},
}
end
def time_distance_function
{
gauss: {
last_status_at: {
scale: '30d',
offset: '30d',
decay: 0.3,
},
},
}
end
def following_ids
@following_ids ||= account.active_relationships.pluck(:target_account_id)
end
def limit_for_non_exact_results
if exact_match?
limit - 1
else
limit
end
end
def terms_for_query
if domain_is_local?
query_username
else
query
end
end
def split_query_string
@split_query_string ||= query.gsub(/\A@/, '').split('@')
@split_query_string ||= query.split('@')
end
def query_username
@@ -63,57 +166,15 @@ class AccountSearchService < BaseService
@domain_is_local ||= TagManager.instance.local_domain?(query_domain)
end
def search_from
options[:following] && account ? account.following : Account
end
def exact_match?
exact_match.present?
end
def exact_match
return @exact_match if defined?(@exact_match)
@exact_match = begin
if domain_is_local?
search_from.without_suspended.find_local(query_username)
else
search_from.without_suspended.find_remote(query_username, query_domain)
end
end
def username_complete?
query.include?('@') && "@#{query}" =~ Account::MENTION_RE
end
def search_results
@search_results ||= begin
if account
advanced_search_results
else
simple_search_results
end
end
end
def advanced_search_results
Account.advanced_search_for(terms_for_query, account, limit_for_non_exact_results, options[:following], offset)
end
def simple_search_results
Account.search_for(terms_for_query, limit_for_non_exact_results, offset)
end
def limit_for_non_exact_results
if offset.zero? && exact_match?
limit - 1
else
limit
end
end
def terms_for_query
if domain_is_local?
query_username
else
"#{query_username} #{query_domain}"
end
def likely_acct?
@acct_hint || username_complete?
end
end

View File

@@ -52,15 +52,15 @@ class SearchService < BaseService
preloaded_relations = relations_map_for_account(@account, account_ids, account_domains)
results.reject { |status| StatusFilter.new(status, @account, preloaded_relations).filtered? }
rescue Faraday::ConnectionFailed
rescue Faraday::ConnectionFailed, Parslet::ParseFailed
[]
end
def perform_hashtags_search!
Tag.search_for(
@query.gsub(/\A#/, ''),
@limit,
@offset
TagSearchService.new.call(
@query,
limit: @limit,
offset: @offset
)
end

View File

@@ -0,0 +1,82 @@
# frozen_string_literal: true
class TagSearchService < BaseService
def call(query, options = {})
@query = query.strip.gsub(/\A#/, '')
@offset = options[:offset].to_i
@limit = options[:limit].to_i
if Chewy.enabled?
from_elasticsearch
else
from_database
end
end
private
def from_elasticsearch
query = {
function_score: {
query: {
multi_match: {
query: @query,
fields: %w(name.edge_ngram name),
type: 'most_fields',
operator: 'and',
},
},
functions: [
{
field_value_factor: {
field: 'usage',
modifier: 'log2p',
missing: 0,
},
},
{
gauss: {
last_status_at: {
scale: '7d',
offset: '14d',
decay: 0.5,
},
},
},
],
boost_mode: 'multiply',
},
}
filter = {
bool: {
should: [
{
term: {
reviewed: {
value: true,
},
},
},
{
term: {
name: {
value: @query,
},
},
},
],
},
}
TagsIndex.query(query).filter(filter).limit(@limit).offset(@offset).objects.compact
end
def from_database
Tag.search_for(@query, @limit, @offset)
end
end