Class: SearchFlip::Criteria

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Aggregatable, Customable, Explainable, Filterable, Highlightable, Paginatable, PostFilterable, Sortable, Sourceable
Defined in:
lib/search_flip/criteria.rb

Overview

The SearchFlip::Criteria class serves the purpose of chaining various filtering and aggregation methods. Each chainable method creates a new criteria object until a method is called that finally sends the respective request to Elasticsearch and returns the result.

Examples:

CommentIndex.where(public: true).sort(id: "desc").limit(1_000).records
CommentIndex.range(:created_at, lt: Time.parse("2014-01-01").delete
CommentIndex.search("hello world").total_entries
CommentIndex.query(more_like_this: { "...", fields: ["description"] })]
CommentIndex.exists(:user_id).paginate(page: 1, per_page: 100)
CommentIndex.sort("_doc").find_each { |comment| "..." }

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Aggregatable

#aggregate, included

Methods included from PostFilterable

included, #post_exists, #post_exists_not, #post_filter, #post_must, #post_must_not, #post_range, #post_search, #post_should, #post_where, #post_where_not

Methods included from Filterable

#exists, #exists_not, #filter, included, #match_all, #must, #must_not, #range, #search, #should, #to_query, #where, #where_not

Methods included from Customable

#custom, included

Methods included from Paginatable

included, #limit, #limit_value_with_default, #offset, #offset_value_with_default, #page, #paginate, #per

Methods included from Explainable

#explain, included

Methods included from Highlightable

#highlight, included

Methods included from Sourceable

included, #source

Methods included from Sortable

included, #resort, #sort

Constructor Details

#initialize(attributes = {}) ⇒ Criteria

Creates a new SearchFlip::Criteria.

Parameters:

  • attributes (Hash) (defaults to: {})

    Attributes to initialize the Criteria with



193
194
195
196
197
# File 'lib/search_flip/criteria.rb', line 193

def initialize(attributes = {})
  attributes.each do |key, value|
    send "#{key}=", value
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object



582
583
584
585
586
587
588
# File 'lib/search_flip/criteria.rb', line 582

def method_missing(name, *args, &block)
  if target.respond_to?(name)
    merge(target.send(name, *args, &block))
  else
    super
  end
end

Instance Attribute Details

#eager_load_valuesObject

Returns the value of attribute eager_load_values.



27
28
29
# File 'lib/search_flip/criteria.rb', line 27

def eager_load_values
  @eager_load_values
end

#failsafe_valueObject

Returns the value of attribute failsafe_value.



27
28
29
# File 'lib/search_flip/criteria.rb', line 27

def failsafe_value
  @failsafe_value
end

#includes_valuesObject

Returns the value of attribute includes_values.



27
28
29
# File 'lib/search_flip/criteria.rb', line 27

def includes_values
  @includes_values
end

#preference_valueObject

Returns the value of attribute preference_value.



27
28
29
# File 'lib/search_flip/criteria.rb', line 27

def preference_value
  @preference_value
end

#preload_valuesObject

Returns the value of attribute preload_values.



27
28
29
# File 'lib/search_flip/criteria.rb', line 27

def preload_values
  @preload_values
end

#profile_valueObject

Returns the value of attribute profile_value.



27
28
29
# File 'lib/search_flip/criteria.rb', line 27

def profile_value
  @profile_value
end

#routing_valueObject

Returns the value of attribute routing_value.



27
28
29
# File 'lib/search_flip/criteria.rb', line 27

def routing_value
  @routing_value
end

#scroll_argsObject

Returns the value of attribute scroll_args.



27
28
29
# File 'lib/search_flip/criteria.rb', line 27

def scroll_args
  @scroll_args
end

#search_type_valueObject

Returns the value of attribute search_type_value.



27
28
29
# File 'lib/search_flip/criteria.rb', line 27

def search_type_value
  @search_type_value
end

#source_valueObject

Returns the value of attribute source_value.



27
28
29
# File 'lib/search_flip/criteria.rb', line 27

def source_value
  @source_value
end

#suggest_valuesObject

Returns the value of attribute suggest_values.



27
28
29
# File 'lib/search_flip/criteria.rb', line 27

def suggest_values
  @suggest_values
end

#targetObject

Returns the value of attribute target.



27
28
29
# File 'lib/search_flip/criteria.rb', line 27

def target
  @target
end

#terminate_after_valueObject

Returns the value of attribute terminate_after_value.



27
28
29
# File 'lib/search_flip/criteria.rb', line 27

def terminate_after_value
  @terminate_after_value
end

#timeout_valueObject

Returns the value of attribute timeout_value.



27
28
29
# File 'lib/search_flip/criteria.rb', line 27

def timeout_value
  @timeout_value
end

#track_total_hits_valueObject

Returns the value of attribute track_total_hits_value.



27
28
29
# File 'lib/search_flip/criteria.rb', line 27

def track_total_hits_value
  @track_total_hits_value
end

Instance Method Details

#criteriaSearchFlip::Criteria Also known as: all

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.

Convenience method to have a unified conversion api.

Returns:



183
184
185
# File 'lib/search_flip/criteria.rb', line 183

def criteria
  self
end

#delete(params = {}) ⇒ Object

Sends a delete by query request to Elasticsearch, such that all documents matching the query get deleted. Please note, for certain Elasticsearch versions you need to install the delete-by-query plugin to get support for this feature. Refreshes the index if the auto_refresh is enabled. Raises SearchFlip::ResponseError in case any errors occur.

Examples:

CommentIndex.range(lt: Time.parse("2014-01-01")).delete
CommentIndex.where(public: false).delete

See Also:



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/search_flip/criteria.rb', line 338

def delete(params = {})
  dupped_request = request.dup
  dupped_request.delete(:from)
  dupped_request.delete(:size)

  if connection.version.to_i >= 5
    connection.http_client.post("#{target.type_url}/_delete_by_query", params: request_params.merge(params), json: dupped_request)
  else
    connection.http_client.delete("#{target.type_url}/_query", params: request_params.merge(params), json: dupped_request)
  end

  target.refresh if SearchFlip::Config[:auto_refresh]

  true
end

#eager_load(*args) ⇒ SearchFlip::Criteria

Specify associations of the target model you want to eager load via ActiveRecord’s or other ORM’s mechanisms when records get fetched from the database.

Examples:

CommentIndex.eager_load(:user, :post).records
PostIndex.eager_load(:comments => :user).records

Parameters:

  • args

    The args that get passed to the eager load method of ActiveRecord or other ORMs

Returns:



386
387
388
389
390
# File 'lib/search_flip/criteria.rb', line 386

def eager_load(*args)
  fresh.tap do |criteria|
    criteria.eager_load_values = (eager_load_values || []) + args
  end
end

#executeSearchFlip::Response Also known as: response

Executes the search request for the current criteria, ie sends the request to Elasticsearch and returns the response. Connection and response errors will be rescued if you specify the criteria to be #failsafe, such that an empty response is returned instead.

Examples:

response = CommentIndex.search("hello world").execute

Returns:



523
524
525
526
527
528
529
530
531
532
533
# File 'lib/search_flip/criteria.rb', line 523

def execute
  @response ||= begin
    Config[:instrumenter].instrument("request.search_flip", index: target, request: request) do |payload|
      response = execute!

      payload[:response] = response

      response
    end
  end
end

#failsafe(value) ⇒ SearchFlip::Response

Marks the criteria to be failsafe, ie certain exceptions raised due to invalid queries, inavailability of Elasticsearch, etc get rescued and an empty criteria is returned instead.

Examples:

CommentIndex.search("invalid/request").execute
# raises SearchFlip::ResponseError

# ...

CommentIndex.search("invalid/request").failsafe(true).execute
# => #<SearchFlip::Response ...>

Parameters:

  • value (Boolean)

    Whether or not the criteria should be failsafe

Returns:

See Also:



556
557
558
559
560
# File 'lib/search_flip/criteria.rb', line 556

def failsafe(value)
  fresh.tap do |criteria|
    criteria.failsafe_value = value
  end
end

#find_each(options = {}) ⇒ Object Also known as: each

Fetches the records specified by the relatin in batches using the Elasticsearch scroll API and yields each record. The batch size and scroll API timeout can be specified. Check out the Elasticsearch docs for further details.

Examples:

CommentIndex.search("hello world").find_each(batch_size: 100) do |record|
  # ...
end

Parameters:

  • options (Hash) (defaults to: {})

    The options to control the fetching of batches

Options Hash (options):

  • batch_size (Fixnum)

    The number of records to fetch per batch. Uses #limit to control the batch size.

  • timeout (String)

    The timeout per scroll request, ie how long Elasticsearch will keep the request handle open.



475
476
477
478
479
480
481
482
483
# File 'lib/search_flip/criteria.rb', line 475

def find_each(options = {})
  return enum_for(:find_each, options) unless block_given?

  find_in_batches options do |batch|
    batch.each do |record|
      yield record
    end
  end
end

#find_each_result(options = {}) ⇒ Object

Fetches the results specified by the criteria in batches using the Elasticsearch scroll API and yields each result. The batch size and scroll API timeout can be specified. Checkout out the Elasticsearch docs for further details.

Examples:

CommentIndex.search("hello world").find_each_result(batch_size: 100) do |result|
  # ...
end

Parameters:

  • options (Hash) (defaults to: {})

    The options to control the fetching of batches

Options Hash (options):

  • batch_size (Fixnum)

    The number of records to fetch per batch. Uses #limit to control the batch size.

  • timeout (String)

    The timeout per scroll request, ie how long Elasticsearch will keep the request handle open.



503
504
505
506
507
508
509
510
511
# File 'lib/search_flip/criteria.rb', line 503

def find_each_result(options = {})
  return enum_for(:find_each_result, options) unless block_given?

  find_results_in_batches options do |batch|
    batch.each do |result|
      yield result
    end
  end
end

#find_in_batches(options = {}) ⇒ Object

Fetches the records specified by the criteria in batches using the ElasicSearch scroll API and yields each batch. The batch size and scroll API timeout can be specified. Check out the Elasticsearch docs for further details.

Examples:

CommentIndex.search("hello world").find_in_batches(batch_size: 100) do |batch|
  # ...
end

Parameters:

  • options (Hash) (defaults to: {})

    The options to control the fetching of batches

Options Hash (options):

  • batch_size (Fixnum)

    The number of records to fetch per batch. Uses #limit to control the batch size.

  • timeout (String)

    The timeout per scroll request, ie how long Elasticsearch will keep the request handle open.



427
428
429
430
431
432
433
# File 'lib/search_flip/criteria.rb', line 427

def find_in_batches(options = {})
  return enum_for(:find_in_batches, options) unless block_given?

  yield_in_batches(options) do |criteria|
    yield(criteria.records) if criteria.records.size > 0
  end
end

#find_results_in_batches(options = {}) ⇒ Object

Fetches the results specified by the criteria in batches using the Elasticsearch scroll API and yields each batch. The batch size and scroll API timeout can be specified. Checkout out the Elasticsearch docs for further details.

Examples:

CommentIndex.search("hello world").find_results_in_batches(batch_size: 100) do |batch|
  # ...
end

Parameters:

  • options (Hash) (defaults to: {})

    The options to control the fetching of batches

Options Hash (options):

  • batch_size (Fixnum)

    The number of records to fetch per batch. Uses #limit to control the batch size.

  • timeout (String)

    The timeout per scroll request, ie how long Elasticsearch will keep the request handle open.



451
452
453
454
455
456
457
# File 'lib/search_flip/criteria.rb', line 451

def find_results_in_batches(options = {})
  return enum_for(:find_results_in_batches, options) unless block_given?

  yield_in_batches(options) do |criteria|
    yield criteria.results
  end
end

#freshSearchFlip::Response

Returns a fresh, ie dupped, criteria with the response cache being cleared.

Examples:

CommentIndex.search("hello world").fresh

Returns:



571
572
573
574
575
576
# File 'lib/search_flip/criteria.rb', line 571

def fresh
  dup.tap do |criteria|
    criteria.instance_variable_set(:@request, nil)
    criteria.instance_variable_set(:@response, nil)
  end
end

#includes(*args) ⇒ SearchFlip::Criteria

Specify associations of the target model you want to include via ActiveRecord’s or other ORM’s mechanisms when records get fetched from the database.

Examples:

CommentIndex.includes(:user, :post).records
PostIndex.includes(:comments => :user).records

Parameters:

  • args

    The args that get passed to the includes method of ActiveRecord or other ORMs

Returns:



367
368
369
370
371
# File 'lib/search_flip/criteria.rb', line 367

def includes(*args)
  fresh.tap do |criteria|
    criteria.includes_values = (includes_values || []) + args
  end
end

#merge(other) ⇒ SearchFlip::Criteria

Creates a new criteria while merging the attributes (constraints, settings, etc) of the current criteria with the attributes of another one passed as argument. For multi-value contstraints the resulting criteria will include constraints of both criterias. For single-value constraints, the values of the criteria passed as an argument are used.

Examples:

CommentIndex.where(approved: true).merge(CommentIndex.range(:created_at, gt: Time.parse("2015-01-01")))
CommentIndex.aggregate(:user_id).merge(CommentIndex.where(admin: true))

Returns:



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/search_flip/criteria.rb', line 43

def merge(other)
  other = other.criteria

  fresh.tap do |criteria|
    criteria.profile_value = other.profile_value unless other.profile_value.nil?
    criteria.failsafe_value = other.failsafe_value unless other.failsafe_value.nil?
    criteria.terminate_after_value = other.terminate_after_value unless other.terminate_after_value.nil?
    criteria.timeout_value = other.timeout_value unless other.timeout_value.nil?
    criteria.offset_value = other.offset_value if other.offset_value
    criteria.limit_value = other.limit_value if other.limit_value
    criteria.scroll_args = other.scroll_args if other.scroll_args
    criteria.source_value = other.source_value if other.source_value
    criteria.preference_value = other.preference_value if other.preference_value
    criteria.search_type_value = other.search_type_value if other.search_type_value
    criteria.routing_value = other.routing_value if other.routing_value
    criteria.track_total_hits_value = other.track_total_hits_value unless other.track_total_hits_value.nil?
    criteria.explain_value = other.explain_value unless other.explain_value.nil?

    criteria.sort_values = (criteria.sort_values || []) + other.sort_values if other.sort_values
    criteria.includes_values = (criteria.includes_values || []) + other.includes_values if other.includes_values
    criteria.preload_values = (criteria.preload_values || []) + other.preload_values if other.preload_values
    criteria.eager_load_values = (criteria.eager_load_values || []) + other.eager_load_values if other.eager_load_values
    criteria.must_values = (criteria.must_values || []) + other.must_values if other.must_values
    criteria.must_not_values = (criteria.must_not_values || []) + other.must_not_values if other.must_not_values
    criteria.filter_values = (criteria.filter_values || []) + other.filter_values if other.filter_values
    criteria.post_must_values = (criteria.post_must_values || []) + other.post_must_values if other.post_must_values
    criteria.post_must_not_values = (criteria.post_must_not_values || []) + other.post_must_not_values if other.post_must_not_values
    criteria.post_filter_values = (criteria.post_filter_values || []) + other.post_filter_values if other.post_filter_values

    criteria.highlight_values = (criteria.highlight_values || {}).merge(other.highlight_values) if other.highlight_values
    criteria.suggest_values = (criteria.suggest_values || {}).merge(other.suggest_values) if other.suggest_values
    criteria.custom_value = (criteria.custom_value || {}).merge(other.custom_value) if other.custom_value
    criteria.aggregation_values = (criteria.aggregation_values || {}).merge(other.aggregation_values) if other.aggregation_values
  end
end

#preference(value) ⇒ SearchFlip::Criteria

Specifies a preference value for the request. Check out the elasticsearch docs for further details.

Examples:

CommentIndex.preference("_primary")

Parameters:

  • value

    The preference value

Returns:



106
107
108
109
110
# File 'lib/search_flip/criteria.rb', line 106

def preference(value)
  fresh.tap do |criteria|
    criteria.preference_value = value
  end
end

#preload(*args) ⇒ SearchFlip::Criteria

Specify associations of the target model you want to preload via ActiveRecord’s or other ORM’s mechanisms when records get fetched from the database.

Examples:

CommentIndex.preload(:user, :post).records
PostIndex.includes(:comments => :user).records

Parameters:

  • args

    The args that get passed to the preload method of ActiveRecord or other ORMs

Returns:



405
406
407
408
409
# File 'lib/search_flip/criteria.rb', line 405

def preload(*args)
  fresh.tap do |criteria|
    criteria.preload_values = (preload_values || []) + args
  end
end

#profile(value) ⇒ SearchFlip::Criteria

Sets whether or not query profiling should be enabled.

Examples:

query = CommentIndex.profile(true)
query.raw_response["profile"] # => { "shards" => ... }

Parameters:

  • value (Boolean)

    Whether query profiling should be enabled or not

Returns:



294
295
296
297
298
# File 'lib/search_flip/criteria.rb', line 294

def profile(value)
  fresh.tap do |criteria|
    criteria.profile_value = value
  end
end

#requestHash

Generates the request object from the attributes specified via chaining, like eg offset, limit, query, filters, aggregations, etc and returns a Hash that later gets serialized as JSON.

Returns:

  • (Hash)

    The generated request object



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/search_flip/criteria.rb', line 221

def request
  @request ||= begin
    res = {}

    if must_values || must_not_values || filter_values
      res[:query] = {
        bool: {
          must: must_values.to_a,
          must_not: must_not_values.to_a,
          filter: filter_values.to_a
        }.reject { |_, value| value.empty? }
      }
    end

    res.update(from: offset_value_with_default, size: limit_value_with_default)

    res[:track_total_hits] = track_total_hits_value unless track_total_hits_value.nil?
    res[:explain] = explain_value unless explain_value.nil?
    res[:timeout] = timeout_value if timeout_value
    res[:terminate_after] = terminate_after_value if terminate_after_value
    res[:highlight] = highlight_values if highlight_values
    res[:suggest] = suggest_values if suggest_values
    res[:sort] = sort_values if sort_values
    res[:aggregations] = aggregation_values if aggregation_values

    if post_must_values || post_must_not_values || post_filter_values
      res[:post_filter] = {
        bool: {
          must: post_must_values.to_a,
          must_not: post_must_not_values.to_a,
          filter: post_filter_values.to_a
        }.reject { |_, value| value.empty? }
      }
    end

    res[:_source] = source_value unless source_value.nil?
    res[:profile] = true if profile_value

    res.update(custom_value) if custom_value

    res
  end
end

#respond_to_missing?(name, *args) ⇒ Boolean

Returns:

  • (Boolean)


578
579
580
# File 'lib/search_flip/criteria.rb', line 578

def respond_to_missing?(name, *args)
  target.respond_to?(name, *args) || super
end

#routing(value) ⇒ SearchFlip::Criteria

Specifies the routing value for the request. Check out the elasticsearch docs for further details.

Examples:

CommentIndex.routing("user_id")

Parameters:

  • value

    The search type value

Returns:



138
139
140
141
142
# File 'lib/search_flip/criteria.rb', line 138

def routing(value)
  fresh.tap do |criteria|
    criteria.routing_value = value
  end
end

#scroll(id: nil, timeout: "1m") ⇒ SearchFlip::Criteria

Adds scrolling to the request with or without an already existing scroll id and using the specified timeout.

Examples:

query = CommentIndex.scroll(timeout: "5m")

until query.records.empty?
  # ...

  query = query.scroll(id: query.scroll_id, timeout: "5m")
end

Parameters:

  • id (String, nil) (defaults to: nil)

    The scroll id of the last request returned by SearchFlip or nil

  • timeout (String) (defaults to: "1m")

    The timeout of the scroll request, ie. how long SearchFlip should keep the scroll handle open

Returns:



320
321
322
323
324
# File 'lib/search_flip/criteria.rb', line 320

def scroll(id: nil, timeout: "1m")
  fresh.tap do |criteria|
    criteria.scroll_args = { id: id, timeout: timeout }
  end
end

#search_type(value) ⇒ SearchFlip::Criteria

Specifies the search type value for the request. Check out the elasticsearch docs for further details.

Examples:

CommentIndex.search_type("dfs_query_then_fetch")

Parameters:

  • value

    The search type value

Returns:



122
123
124
125
126
# File 'lib/search_flip/criteria.rb', line 122

def search_type(value)
  fresh.tap do |criteria|
    criteria.search_type_value = value
  end
end

#suggest(name, options = {}) ⇒ SearchFlip::Criteria

Adds a suggestion section with the given name to the request.

Examples:

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

Parameters:

  • name (String, Symbol)

    The name of the suggestion section

  • options (Hash) (defaults to: {})

    Additional suggestion options. Check out the Elasticsearch docs for further details.

Returns:



278
279
280
281
282
# File 'lib/search_flip/criteria.rb', line 278

def suggest(name, options = {})
  fresh.tap do |criteria|
    criteria.suggest_values = (criteria.suggest_values || {}).merge(name => options)
  end
end

#terminate_after(value) ⇒ SearchFlip::Criteria

Specifies early query termination, such that the processing will be stopped after the specified number of results has been accumulated.

Examples:

ProductIndex.terminate_after(10_000).search("hello world")

Parameters:

  • value (Fixnum)

    The number of records to terminate after

Returns:



171
172
173
174
175
# File 'lib/search_flip/criteria.rb', line 171

def terminate_after(value)
  fresh.tap do |criteria|
    criteria.terminate_after_value = value
  end
end

#timeout(value) ⇒ SearchFlip::Criteria

Specifies a query timeout, such that the processing will be stopped after that timeout and only the results calculated up to that point will be processed and returned.

Examples:

ProductIndex.timeout("3s").search("hello world")

Parameters:

  • value (String)

    The timeout value

Returns:



155
156
157
158
159
# File 'lib/search_flip/criteria.rb', line 155

def timeout(value)
  fresh.tap do |criteria|
    criteria.timeout_value = value
  end
end

#track_total_hits(value) ⇒ SearchFlip::Criteria

Specifies if or how many hits should be counted/tracked. Check out the elasticsearch docs for futher details.

Examples:

CommentIndex.track_total_hits(true)
CommentIndex.track_total_hits(10_000)

Parameters:

  • value

    The value for track_total_hits

Returns:



90
91
92
93
94
# File 'lib/search_flip/criteria.rb', line 90

def track_total_hits(value)
  fresh.tap do |criteria|
    criteria.track_total_hits_value = value
  end
end

#with_settingsSearchFlip::Criteria

Allows to set query specific settings like e.g. connection and index name. Please note, however, that this should only be used for special cases and the subsequent query can not be serialized. Checkout SearchFlip::Index.with_settings for more details.

Examples:

UserIndex.where("...").with_settings(connection: ProxyConnection)

Returns:



209
210
211
212
213
# File 'lib/search_flip/criteria.rb', line 209

ruby2_keywords def with_settings(*args)
  fresh.tap do |criteria|
    criteria.target = target.with_settings(*args)
  end
end