class SearchFlip::Response
The SearchFlip::Response
class wraps a raw SearchFlip
response and decorates it with methods for aggregations, hits, records, pagination, etc.
Attributes
Public Class Methods
@api private
Initializes a new response object for the provided criteria and raw Elasticsearch response.
# File lib/search_flip/response.rb, line 15 def initialize(criteria, response) self.criteria = criteria self.response = response end
Public Instance Methods
Returns a single or all aggregations returned by Elasticsearch, depending on whether or not a name is specified. If no name is specified, the raw aggregation hash is simply returned. Contrary, if a name is specified, only this aggregation is returned. Moreover, if a name is specified and the aggregation includes a buckets section, a post-processed aggregation hash is returned.
@example All aggregations
CommentIndex.aggregate(:user_id).aggregations # => {"user_id"=>{..., "buckets"=>[{"key"=>4922, "doc_count"=>1129}, ...]}
@example Specific and post-processed aggregations
CommentIndex.aggregate(:user_id).aggregations(:user_id) # => {4922=>1129, ...}
@return [Hash] Specific or all aggregations returned by Elasticsearch
# File lib/search_flip/response.rb, line 296 def aggregations(name = nil) return response["aggregations"] || {} unless name @aggregations ||= {} key = name.to_s return @aggregations[key] if @aggregations.key?(key) @aggregations[key] = if response["aggregations"].nil? || response["aggregations"][key].nil? Result.new elsif response["aggregations"][key]["buckets"].is_a?(Array) response["aggregations"][key]["buckets"].each_with_object({}) { |bucket, hash| hash[bucket["key"]] = Result.new(bucket) } elsif response["aggregations"][key]["buckets"].is_a?(Hash) Result.new response["aggregations"][key]["buckets"] else Result.new response["aggregations"][key] end end
Returns the current page number, useful for pagination.
@example
CommentIndex.search("hello world").paginate(page: 10).current_page # => 10
@return [Fixnum] The current page number
# File lib/search_flip/response.rb, line 104 def current_page 1 + (criteria.offset_value_with_default / criteria.limit_value_with_default) end
Returns whether or not the current page is the first page.
@example
CommentIndex.paginate(page: 1).first_page? # => true CommentIndex.paginate(page: 2).first_page? # => false
@return [Boolean] Returns true if the current page is the
first page or false otherwise
# File lib/search_flip/response.rb, line 59 def first_page? current_page == 1 end
Returns the hits returned by Elasticsearch.
@example
CommentIndex.search("hello world").hits # => {"total"=>3, "max_score"=>2.34, "hits"=>[{...}, ...]}
@return [Hash] The hits returned by Elasticsearch
# File lib/search_flip/response.rb, line 200 def hits response["hits"] end
Returns the array of ids returned by Elasticsearch for the current result set, ie the ids listed in the hits section of the response.
@example
CommentIndex.match_all.ids # => [20341, 12942, ...]
@return The array of ids in the current result set
# File lib/search_flip/response.rb, line 261 def ids @ids ||= hits["hits"].map { |hit| hit["_id"] } end
Returns whether or not the current page is the last page.
@example
CommentIndex.paginate(page: 100).last_page? # => true CommentIndex.paginate(page: 1).last_page? # => false
@return [Boolean] Returns true if the current page is the
last page or false otherwise
# File lib/search_flip/response.rb, line 75 def last_page? current_page == total_pages end
Returns the next page number or nil if there is no next page, ie the current page is the last page.
@example
CommentIndex.search("hello world").paginate(page: 2).next_page # => 3
@return [Fixnum, nil] The next page number
# File lib/search_flip/response.rb, line 151 def next_page return nil if current_page >= total_pages return 1 if current_page < 1 current_page + 1 end
Returns whether or not the current page is out of range, ie. smaller than 1 or larger than total_pages
@example
CommentIndex.paginate(page: 1_000_000).out_of_range? # => true CommentIndex.paginate(page: 1).out_of_range? # => false
@return [Boolean] Returns true if the current page is out
of range
# File lib/search_flip/response.rb, line 92 def out_of_range? current_page < 1 || current_page > total_pages end
Returns the previous page number or nil if no previous page exists, ie if the current page is the first page.
@example
CommentIndex.search("hello world").paginate(page: 2).previous_page # => 1 CommentIndex.search("hello world").paginate(page: 1).previous_page # => nil
@return [Fixnum, nil] The previous page number
# File lib/search_flip/response.rb, line 133 def previous_page return nil if current_page <= 1 return total_pages if current_page > total_pages current_page - 1 end
Returns the raw response, ie a hash derived from the Elasticsearch JSON
response.
@example
CommentIndex.search("hello world").raw_response # => {"took"=>3, "timed_out"=>false, "_shards"=>"..."}
@return [Hash] The raw response hash
# File lib/search_flip/response.rb, line 29 def raw_response response end
Returns the database records, usually ActiveRecord objects, depending on the ORM you're using. The records are sorted using the order returned by Elasticsearch.
@example
CommentIndex.search("hello world").records # => [#<Comment ...>, ...]
@return [Array] An array of database records
# File lib/search_flip/response.rb, line 225 def records @records ||= begin sort_map = ids.each_with_index.each_with_object({}) { |(id, index), hash| hash[id.to_s] = index } scope.to_a.sort_by { |record| sort_map[criteria.target.record_id(record).to_s] } end end
Returns the results, ie hits, wrapped in a SearchFlip::Result
object which basically is a Hashie::Mash. Check out the Hashie docs for further details.
@example
CommentIndex.search("hello world").results # => [#<SearchFlip::Result ...>, ...]
@return [Array] An array of results
# File lib/search_flip/response.rb, line 168 def results @results ||= hits["hits"].map { |hit| Result.from_hit(hit) } end
Builds and returns a scope for the array of ids in the current result set returned by Elasticsearch, including the eager load, preload and includes associations, if specified. A scope is eg an ActiveRecord::Relation, depending on the ORM you're using.
@example
CommentIndex.preload(:user).scope # => #<Comment::ActiveRecord_Criteria:0x0...>
@return The scope for the array of ids in the current result set
# File lib/search_flip/response.rb, line 243 def scope res = criteria.target.fetch_records(ids) res = res.includes(*criteria.includes_values) if criteria.includes_values res = res.eager_load(*criteria.eager_load_values) if criteria.eager_load_values res = res.preload(*criteria.preload_values) if criteria.preload_values res end
Returns the scroll id returned by Elasticsearch, that can be used in the following request to fetch the next batch of records.
@example
CommentIndex.scroll(timeout: "1m").scroll_id #=> "cXVlcnlUaGVuRmV0Y2..."
@return [String] The scroll id returned by Elasticsearch
# File lib/search_flip/response.rb, line 212 def scroll_id response["_scroll_id"] end
Returns the named sugggetion, if a name is specified or alle suggestions.
@example
query = CommentIndex.suggest(:suggestion, text: "helo", term: { field: "message" }) query.suggestions # => {"suggestion"=>[{"text"=>...}, ...]}
@example Named suggestions
query = CommentIndex.suggest(:suggestion, text: "helo", term: { field: "message" }) query.suggestions(:sugestion).first["text"] # => "hello"
@return [Hash, Array] The named suggestion or all suggestions
# File lib/search_flip/response.rb, line 184 def suggestions(name = nil) if name response["suggest"][name.to_s].first["options"] else response["suggest"] end end
Returns the response time in milliseconds of Elasticsearch specified in the took info of the response.
@example
CommentIndex.match_all.took # => 6
@return [Fixnum] The Elasticsearch response time in milliseconds
# File lib/search_flip/response.rb, line 275 def took response["took"] end
Returns the total number of results.
@example
CommentIndex.search("hello world").total_count # => 13
@return [Fixnum] The total number of results
# File lib/search_flip/response.rb, line 41 def total_count hits["total"].is_a?(Hash) ? hits["total"]["value"] : hits["total"] end
Returns the number of total pages for the current pagination settings, ie per page/limit settings.
@example
CommentIndex.search("hello world").paginate(per_page: 60).total_pages # => 5
@return [Fixnum] The total number of pages
# File lib/search_flip/response.rb, line 117 def total_pages [(total_count.to_f / criteria.limit_value_with_default).ceil, 1].max end