about summary refs log tree commit diff
path: root/app/services/search_service.rb
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2019-02-26 15:21:36 +0100
committerGitHub <noreply@github.com>2019-02-26 15:21:36 +0100
commite7f20cc43ff21afa229da40ee4e5755495948772 (patch)
tree4b39ece2b4ed3ecb6b4aa876a6fdf304a0cb9e1e /app/services/search_service.rb
parentea58e31822d07ca2286f971bf6b0275954dd2726 (diff)
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
Diffstat (limited to 'app/services/search_service.rb')
-rw-r--r--app/services/search_service.rb84
1 files changed, 65 insertions, 19 deletions
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