From e7f20cc43ff21afa229da40ee4e5755495948772 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 26 Feb 2019 15:21:36 +0100 Subject: Add type, limit, offset, min_id, max_id, account_id to search API (#10091) * Add type, limit, offset, min_id, max_id, account_id to search API Fix #8939 * Make the offset work on accounts and hashtags search as well * Assure brakeman we are not doing mass assignment here * Do not allow paginating unless a type is chosen * Fix search query and index id field on statuses instead of created_at --- app/services/account_search_service.rb | 11 +++-- app/services/search_service.rb | 84 ++++++++++++++++++++++++++-------- 2 files changed, 71 insertions(+), 24 deletions(-) (limited to 'app/services') diff --git a/app/services/account_search_service.rb b/app/services/account_search_service.rb index 7edbd9b47..7bdffbbd2 100644 --- a/app/services/account_search_service.rb +++ b/app/services/account_search_service.rb @@ -1,11 +1,12 @@ # frozen_string_literal: true class AccountSearchService < BaseService - attr_reader :query, :limit, :options, :account + attr_reader :query, :limit, :offset, :options, :account - def call(query, limit, account = nil, options = {}) + def call(query, account = nil, options = {}) @query = query.strip - @limit = limit + @limit = options[:limit].to_i + @offset = options[:offset].to_i @options = options @account = account @@ -83,11 +84,11 @@ class AccountSearchService < BaseService end def advanced_search_results - Account.advanced_search_for(terms_for_query, account, limit, options[:following]) + Account.advanced_search_for(terms_for_query, account, limit, options[:following], offset) end def simple_search_results - Account.search_for(terms_for_query, limit) + Account.search_for(terms_for_query, limit, offset) end def terms_for_query diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 1c31e0509..e0da61dac 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -1,18 +1,18 @@ # frozen_string_literal: true class SearchService < BaseService - attr_accessor :query, :account, :limit, :resolve - - def call(query, limit, resolve = false, account = nil) + def call(query, account, limit, options = {}) @query = query.strip @account = account - @limit = limit - @resolve = resolve + @options = options + @limit = limit.to_i + @offset = options[:type].blank? ? 0 : options[:offset].to_i + @resolve = options[:resolve] || false default_results.tap do |results| if url_query? results.merge!(url_resource_results) unless url_resource.nil? - elsif query.present? + elsif @query.present? results[:accounts] = perform_accounts_search! if account_searchable? results[:statuses] = perform_statuses_search! if full_text_searchable? results[:hashtags] = perform_hashtags_search! if hashtag_searchable? @@ -23,23 +23,46 @@ class SearchService < BaseService private def perform_accounts_search! - AccountSearchService.new.call(query, limit, account, resolve: resolve) + AccountSearchService.new.call( + @query, + @account, + limit: @limit, + resolve: @resolve, + offset: @offset + ) end def perform_statuses_search! - statuses = StatusesIndex.filter(term: { searchable_by: account.id }) - .query(multi_match: { type: 'most_fields', query: query, operator: 'and', fields: %w(text text.stemmed) }) - .limit(limit) - .objects - .compact + definition = StatusesIndex.filter(term: { searchable_by: @account.id }) + .query(multi_match: { type: 'most_fields', query: @query, operator: 'and', fields: %w(text text.stemmed) }) + + if @options[:account_id].present? + definition = definition.filter(term: { account_id: @options[:account_id] }) + end + + if @options[:min_id].present? || @options[:max_id].present? + range = {} + range[:gt] = @options[:min_id].to_i if @options[:min_id].present? + range[:lt] = @options[:max_id].to_i if @options[:max_id].present? + definition = definition.filter(range: { id: range }) + end + + results = definition.limit(@limit).offset(@offset).objects.compact + account_ids = results.map(&:account_id) + account_domains = results.map(&:account_domain) + preloaded_relations = relations_map_for_account(@account, account_ids, account_domains) - statuses.reject { |status| StatusFilter.new(status, account).filtered? } + results.reject { |status| StatusFilter.new(status, @account, preloaded_relations).filtered? } rescue Faraday::ConnectionFailed [] end def perform_hashtags_search! - Tag.search_for(query.gsub(/\A#/, ''), limit) + Tag.search_for( + @query.gsub(/\A#/, ''), + @limit, + @offset + ) end def default_results @@ -47,7 +70,7 @@ class SearchService < BaseService end def url_query? - query =~ /\Ahttps?:\/\// + @options[:type].blank? && @query =~ /\Ahttps?:\/\// end def url_resource_results @@ -55,7 +78,7 @@ class SearchService < BaseService end def url_resource - @_url_resource ||= ResolveURLService.new.call(query, on_behalf_of: @account) + @_url_resource ||= ResolveURLService.new.call(@query, on_behalf_of: @account) end def url_resource_symbol @@ -64,14 +87,37 @@ class SearchService < BaseService def full_text_searchable? return false unless Chewy.enabled? - !account.nil? && !((query.start_with?('#') || query.include?('@')) && !query.include?(' ')) + + statuses_search? && !@account.nil? && !((@query.start_with?('#') || @query.include?('@')) && !@query.include?(' ')) end def account_searchable? - !(query.include?('@') && query.include?(' ')) + account_search? && !(@query.include?('@') && @query.include?(' ')) end def hashtag_searchable? - !query.include?('@') + hashtag_search? && !@query.include?('@') + end + + def account_search? + @options[:type].blank? || @options[:type] == 'accounts' + end + + def hashtag_search? + @options[:type].blank? || @options[:type] == 'hashtags' + end + + def statuses_search? + @options[:type].blank? || @options[:type] == 'statuses' + end + + def relations_map_for_account(account, account_ids, domains) + { + blocking: Account.blocking_map(account_ids, account.id), + blocked_by: Account.blocked_by_map(account_ids, account.id), + muting: Account.muting_map(account_ids, account.id), + following: Account.following_map(account_ids, account.id), + domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id), + } end end -- cgit