Class: SearchFlip::Response

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/search_flip/response.rb

Overview

The SearchFlip::Response class wraps a raw SearchFlip response and decorates it with methods for aggregations, hits, records, pagination, etc.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(criteria, response) ⇒ Response

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Initializes a new response object for the provided criteria and raw Elasticsearch response.



15
16
17
18
# File 'lib/search_flip/response.rb', line 15

def initialize(criteria, response)
  self.criteria = criteria
  self.response = response
end

Instance Attribute Details

#criteriaObject

Returns the value of attribute criteria.



8
9
10
# File 'lib/search_flip/response.rb', line 8

def criteria
  @criteria
end

#responseObject

Returns the value of attribute response.



8
9
10
# File 'lib/search_flip/response.rb', line 8

def response
  @response
end

Instance Method Details

#aggregations(name = nil) ⇒ Hash

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.

Examples:

All aggregations

CommentIndex.aggregate(:user_id).aggregations
# => {"user_id"=>{..., "buckets"=>[{"key"=>4922, "doc_count"=>1129}, ...]}

Specific and post-processed aggregations

CommentIndex.aggregate(:user_id).aggregations(:user_id)
# => {4922=>1129, ...}

Returns:

  • (Hash)

    Specific or all aggregations returned by Elasticsearch



296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# 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

#current_pageFixnum

Returns the current page number, useful for pagination.

Examples:

CommentIndex.search("hello world").paginate(page: 10).current_page
# => 10

Returns:

  • (Fixnum)

    The current page number



104
105
106
# File 'lib/search_flip/response.rb', line 104

def current_page
  1 + (criteria.offset_value_with_default / criteria.limit_value_with_default)
end

#first_page?Boolean

Returns whether or not the current page is the first page.

Examples:

CommentIndex.paginate(page: 1).first_page?
# => true

CommentIndex.paginate(page: 2).first_page?
# => false

Returns:

  • (Boolean)

    Returns true if the current page is the first page or false otherwise



59
60
61
# File 'lib/search_flip/response.rb', line 59

def first_page?
  current_page == 1
end

#hitsHash

Returns the hits returned by Elasticsearch.

Examples:

CommentIndex.search("hello world").hits
# => {"total"=>3, "max_score"=>2.34, "hits"=>[{...}, ...]}

Returns:

  • (Hash)

    The hits returned by Elasticsearch



200
201
202
# File 'lib/search_flip/response.rb', line 200

def hits
  response["hits"]
end

#idsObject

Returns the array of ids returned by Elasticsearch for the current result set, ie the ids listed in the hits section of the response.

Examples:

CommentIndex.match_all.ids # => [20341, 12942, ...]

Returns:

  • The array of ids in the current result set



261
262
263
# File 'lib/search_flip/response.rb', line 261

def ids
  @ids ||= hits["hits"].map { |hit| hit["_id"] }
end

#last_page?Boolean

Returns whether or not the current page is the last page.

Examples:

CommentIndex.paginate(page: 100).last_page?
# => true

CommentIndex.paginate(page: 1).last_page?
# => false

Returns:

  • (Boolean)

    Returns true if the current page is the last page or false otherwise



75
76
77
# File 'lib/search_flip/response.rb', line 75

def last_page?
  current_page == total_pages
end

#next_pageFixnum?

Returns the next page number or nil if there is no next page, ie the current page is the last page.

Examples:

CommentIndex.search("hello world").paginate(page: 2).next_page
# => 3

Returns:

  • (Fixnum, nil)

    The next page number



151
152
153
154
155
156
# 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

#out_of_range?Boolean

Returns whether or not the current page is out of range, ie. smaller than 1 or larger than #total_pages

Examples:

CommentIndex.paginate(page: 1_000_000).out_of_range?
# => true

CommentIndex.paginate(page: 1).out_of_range?
# => false

Returns:

  • (Boolean)

    Returns true if the current page is out of range



92
93
94
# File 'lib/search_flip/response.rb', line 92

def out_of_range?
  current_page < 1 || current_page > total_pages
end

#previous_pageFixnum? Also known as: prev_page

Returns the previous page number or nil if no previous page exists, ie if the current page is the first page.

Examples:

CommentIndex.search("hello world").paginate(page: 2).previous_page
# => 1

CommentIndex.search("hello world").paginate(page: 1).previous_page
# => nil

Returns:

  • (Fixnum, nil)

    The previous page number



133
134
135
136
137
138
# 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

#raw_responseHash

Returns the raw response, ie a hash derived from the Elasticsearch JSON response.

Examples:

CommentIndex.search("hello world").raw_response
# => {"took"=>3, "timed_out"=>false, "_shards"=>"..."}

Returns:

  • (Hash)

    The raw response hash



29
30
31
# File 'lib/search_flip/response.rb', line 29

def raw_response
  response
end

#recordsArray

Returns the database records, usually ActiveRecord objects, depending on the ORM you’re using. The records are sorted using the order returned by Elasticsearch.

Examples:

CommentIndex.search("hello world").records # => [#<Comment ...>, ...]

Returns:

  • (Array)

    An array of database records



225
226
227
228
229
230
231
# File 'lib/search_flip/response.rb', line 225

def records
  @records ||= begin
    sort_map = ids.each_with_index.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

#resultsArray

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.

Examples:

CommentIndex.search("hello world").results
# => [#<SearchFlip::Result ...>, ...]

Returns:

  • (Array)

    An array of results



168
169
170
# File 'lib/search_flip/response.rb', line 168

def results
  @results ||= hits["hits"].map { |hit| Result.from_hit(hit) }
end

#scopeObject

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.

Examples:

CommentIndex.preload(:user).scope # => #<Comment::ActiveRecord_Criteria:0x0...>

Returns:

  • The scope for the array of ids in the current result set



243
244
245
246
247
248
249
250
251
# 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

#scroll_idString

Returns the scroll id returned by Elasticsearch, that can be used in the following request to fetch the next batch of records.

Examples:

CommentIndex.scroll(timeout: "1m").scroll_id #=> "cXVlcnlUaGVuRmV0Y2..."

Returns:

  • (String)

    The scroll id returned by Elasticsearch



212
213
214
# File 'lib/search_flip/response.rb', line 212

def scroll_id
  response["_scroll_id"]
end

#suggestions(name = nil) ⇒ Hash, Array

Returns the named sugggetion, if a name is specified or alle suggestions.

Examples:

query = CommentIndex.suggest(:suggestion, text: "helo", term: { field: "message" })
query.suggestions # => {"suggestion"=>[{"text"=>...}, ...]}

Named suggestions

query = CommentIndex.suggest(:suggestion, text: "helo", term: { field: "message" })
query.suggestions(:sugestion).first["text"] # => "hello"

Returns:

  • (Hash, Array)

    The named suggestion or all suggestions



184
185
186
187
188
189
190
# 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

#tookFixnum

Returns the response time in milliseconds of Elasticsearch specified in the took info of the response.

Examples:

CommentIndex.match_all.took # => 6

Returns:

  • (Fixnum)

    The Elasticsearch response time in milliseconds



275
276
277
# File 'lib/search_flip/response.rb', line 275

def took
  response["took"]
end

#total_countFixnum Also known as: total_entries

Returns the total number of results.

Examples:

CommentIndex.search("hello world").total_count
# => 13

Returns:

  • (Fixnum)

    The total number of results



41
42
43
# File 'lib/search_flip/response.rb', line 41

def total_count
  hits["total"].is_a?(Hash) ? hits["total"]["value"] : hits["total"]
end

#total_pagesFixnum

Returns the number of total pages for the current pagination settings, ie per page/limit settings.

Examples:

CommentIndex.search("hello world").paginate(per_page: 60).total_pages
# => 5

Returns:

  • (Fixnum)

    The total number of pages



117
118
119
# File 'lib/search_flip/response.rb', line 117

def total_pages
  [(total_count.to_f / criteria.limit_value_with_default).ceil, 1].max
end