chewy 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -3
  3. data/CHANGELOG.md +43 -1
  4. data/Gemfile +3 -0
  5. data/README.md +49 -11
  6. data/chewy.gemspec +1 -2
  7. data/gemfiles/Gemfile.rails-3.2 +1 -0
  8. data/gemfiles/Gemfile.rails-4.0 +1 -0
  9. data/lib/chewy.rb +8 -2
  10. data/lib/chewy/backports/deep_dup.rb +46 -0
  11. data/lib/chewy/backports/duplicable.rb +90 -0
  12. data/lib/chewy/config.rb +33 -6
  13. data/lib/chewy/errors.rb +1 -1
  14. data/lib/chewy/fields/base.rb +19 -7
  15. data/lib/chewy/fields/root.rb +13 -0
  16. data/lib/chewy/index/actions.rb +14 -6
  17. data/lib/chewy/index/search.rb +3 -2
  18. data/lib/chewy/query.rb +131 -17
  19. data/lib/chewy/query/compose.rb +27 -17
  20. data/lib/chewy/query/criteria.rb +34 -22
  21. data/lib/chewy/query/loading.rb +94 -10
  22. data/lib/chewy/query/nodes/exists.rb +1 -1
  23. data/lib/chewy/query/nodes/has_relation.rb +1 -1
  24. data/lib/chewy/query/nodes/missing.rb +1 -1
  25. data/lib/chewy/query/pagination.rb +8 -38
  26. data/lib/chewy/query/pagination/kaminari.rb +37 -0
  27. data/lib/chewy/runtime.rb +9 -0
  28. data/lib/chewy/runtime/version.rb +25 -0
  29. data/lib/chewy/type/adapter/active_record.rb +21 -7
  30. data/lib/chewy/type/adapter/base.rb +1 -1
  31. data/lib/chewy/type/adapter/object.rb +9 -6
  32. data/lib/chewy/type/import.rb +7 -4
  33. data/lib/chewy/type/mapping.rb +9 -9
  34. data/lib/chewy/type/wrapper.rb +1 -1
  35. data/lib/chewy/version.rb +1 -1
  36. data/lib/tasks/chewy.rake +40 -21
  37. data/spec/chewy/config_spec.rb +1 -1
  38. data/spec/chewy/fields/base_spec.rb +273 -8
  39. data/spec/chewy/index/actions_spec.rb +1 -2
  40. data/spec/chewy/index/aliases_spec.rb +0 -1
  41. data/spec/chewy/index/search_spec.rb +0 -8
  42. data/spec/chewy/index/settings_spec.rb +0 -2
  43. data/spec/chewy/index_spec.rb +0 -2
  44. data/spec/chewy/query/criteria_spec.rb +85 -18
  45. data/spec/chewy/query/loading_spec.rb +26 -9
  46. data/spec/chewy/query/nodes/and_spec.rb +2 -2
  47. data/spec/chewy/query/nodes/exists_spec.rb +6 -6
  48. data/spec/chewy/query/nodes/missing_spec.rb +4 -4
  49. data/spec/chewy/query/nodes/or_spec.rb +2 -2
  50. data/spec/chewy/query/pagination/kaminari_spec.rb +55 -0
  51. data/spec/chewy/query/pagination_spec.rb +15 -22
  52. data/spec/chewy/query_spec.rb +121 -52
  53. data/spec/chewy/rspec/update_index_spec.rb +0 -1
  54. data/spec/chewy/runtime/version_spec.rb +48 -0
  55. data/spec/chewy/runtime_spec.rb +9 -0
  56. data/spec/chewy/type/adapter/active_record_spec.rb +52 -0
  57. data/spec/chewy/type/adapter/object_spec.rb +33 -0
  58. data/spec/chewy/type/import_spec.rb +1 -3
  59. data/spec/chewy/type/mapping_spec.rb +4 -6
  60. data/spec/chewy/type/observe_spec.rb +0 -2
  61. data/spec/chewy/type/wrapper_spec.rb +0 -2
  62. data/spec/chewy/type_spec.rb +26 -5
  63. data/spec/chewy_spec.rb +0 -2
  64. data/spec/spec_helper.rb +2 -2
  65. metadata +15 -21
  66. data/lib/chewy/fields/default.rb +0 -10
  67. data/spec/chewy/fields/default_spec.rb +0 -6
data/lib/chewy/errors.rb CHANGED
@@ -11,7 +11,7 @@ module Chewy
11
11
  class UnderivableType < Error
12
12
  end
13
13
 
14
- class FailedImport < Error
14
+ class ImportFailed < Error
15
15
  def initialize type, errors
16
16
  output = "Import failed for `#{type}` with:\n"
17
17
  errors.each do |action, errors|
@@ -4,23 +4,33 @@ module Chewy
4
4
  attr_reader :name, :options, :value
5
5
 
6
6
  def initialize(name, options = {})
7
- @name, @options, @nested = name.to_sym, options, {}
7
+ @name, @options, @nested = name.to_sym, options.deep_symbolize_keys, {}
8
8
  @value = @options.delete(:value)
9
9
  end
10
10
 
11
11
  def multi_field?
12
- @options[:type] == 'multi_field'
12
+ nested.any? && !object_field?
13
+ end
14
+
15
+ def object_field?
16
+ (nested.any? && options[:type].blank?) || options[:type].to_s == 'object'
17
+ end
18
+
19
+ def root_field?
20
+ false
13
21
  end
14
22
 
15
23
  def compose(object)
16
24
  result = if value && value.is_a?(Proc)
17
25
  value.arity == 0 ? object.instance_exec(&value) : value.call(object)
26
+ elsif object.is_a?(Hash)
27
+ object[name] || object[name.to_s]
18
28
  else
19
29
  object.send(name)
20
30
  end
21
31
 
22
- result = if result.is_a?(Enumerable) && !result.is_a?(Hash)
23
- result.map { |object| nested_compose(object) }
32
+ result = if result.respond_to?(:to_ary)
33
+ result.to_ary.map { |object| nested_compose(object) }
24
34
  else
25
35
  nested_compose(result)
26
36
  end if nested.any? && !multi_field?
@@ -37,16 +47,18 @@ module Chewy
37
47
  end
38
48
 
39
49
  def mappings_hash
40
- subfields = nested.any? ? {
50
+ mapping = nested.any? ? {
41
51
  (multi_field? ? :fields : :properties) => nested.values.map(&:mappings_hash).inject(:merge)
42
52
  } : {}
43
- {name => options.deep_symbolize_keys.merge(subfields)}
53
+ mapping.reverse_merge!(options)
54
+ mapping.reverse_merge!(type: (nested.any? ? 'object' : 'string')) unless root_field?
55
+ {name => mapping}
44
56
  end
45
57
 
46
58
  private
47
59
 
48
60
  def nested_compose(value)
49
- nested.values.map { |field| field.compose(value) }.inject(:merge)
61
+ nested.values.map { |field| field.compose(value) if value }.compact.inject(:merge)
50
62
  end
51
63
  end
52
64
  end
@@ -6,9 +6,22 @@ module Chewy
6
6
  def initialize(name, options = {})
7
7
  options.reverse_merge!(value: ->(_){_})
8
8
  super(name, options)
9
+ options.delete(:type)
9
10
  @dynamic_templates = []
10
11
  end
11
12
 
13
+ def multi_field?
14
+ false
15
+ end
16
+
17
+ def object_field?
18
+ true
19
+ end
20
+
21
+ def root_field?
22
+ true
23
+ end
24
+
12
25
  def mappings_hash
13
26
  mappings = super
14
27
  if dynamic_templates.any?
@@ -56,10 +56,16 @@ module Chewy
56
56
  options = args.extract_options!.reverse_merge!(alias: true)
57
57
  name = build_index_name(suffix: args.first)
58
58
 
59
- Chewy.wait_for_status
59
+ if Chewy::Runtime.version >= 1.1
60
+ body = index_params
61
+ body.merge!(aliases: {index_name => {}}) if options[:alias] && name != index_name
62
+ result = client.indices.create(index: name, body: body)
63
+ else
64
+ result = client.indices.create(index: name, body: index_params)
65
+ result &&= client.indices.put_alias(index: name, name: index_name) if options[:alias] && name != index_name
66
+ end
60
67
 
61
- result = client.indices.create(index: name, body: index_params)
62
- result &&= client.indices.put_alias(index: name, name: index_name) if options[:alias] && name != index_name
68
+ Chewy.wait_for_status if result
63
69
  result
64
70
  end
65
71
 
@@ -87,9 +93,9 @@ module Chewy
87
93
  # UsersIndex.delete '01-2014' # deletes `users_01-2014` index
88
94
  #
89
95
  def delete! suffix = nil
90
- Chewy.wait_for_status
91
-
92
- client.indices.delete index: build_index_name(suffix: suffix)
96
+ result = client.indices.delete index: build_index_name(suffix: suffix)
97
+ Chewy.wait_for_status if result
98
+ result
93
99
  end
94
100
 
95
101
  # Deletes and recreates index. Supports suffixes.
@@ -128,6 +134,8 @@ module Chewy
128
134
  # UsersIndex.import suffix: Time.now.to_i # imports data to index with specified suffix if such is exists
129
135
  # UsersIndex.import batch_size: 300 # import batch size
130
136
  #
137
+ # See [import.rb](lib/chewy/type/import.rb) for more details.
138
+ #
131
139
  [:import, :import!].each do |method|
132
140
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
133
141
  def #{method} options = {}
@@ -4,8 +4,9 @@ module Chewy
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- singleton_class.delegate :explain, :limit, :offset, :facets, :aggregations, :query,
8
- :filter, :order, :reorder, :only, :types, :none, to: :all
7
+ singleton_class.delegate :explain, :limit, :offset, :highlight, :rescore,
8
+ :facets, :aggregations, :none, :strategy, :query, :filter, :post_filter,
9
+ :order, :reorder, :only, :types, :suggest, to: :all
9
10
  end
10
11
 
11
12
  module ClassMethods
data/lib/chewy/query.rb CHANGED
@@ -1,8 +1,3 @@
1
- begin
2
- require 'kaminari'
3
- rescue LoadError
4
- end
5
-
6
1
  require 'chewy/query/criteria'
7
2
  require 'chewy/query/filters'
8
3
  require 'chewy/query/loading'
@@ -21,7 +16,17 @@ module Chewy
21
16
  include Loading
22
17
  include Pagination
23
18
 
24
- delegate :each, :count, :size, to: :_results
19
+ RESULT_MERGER = lambda do |key, old_value, new_value|
20
+ if old_value.is_a?(Hash) && new_value.is_a?(Hash)
21
+ old_value.merge(new_value, &RESULT_MERGER)
22
+ elsif new_value.is_a?(Array) && new_value.count > 1
23
+ new_value
24
+ else
25
+ old_value.is_a?(Array) ? new_value : new_value.first
26
+ end
27
+ end
28
+
29
+ delegate :each, :count, :size, to: :_collection
25
30
  alias_method :to_ary, :to_a
26
31
 
27
32
  attr_reader :index, :options, :criteria
@@ -62,7 +67,7 @@ module Chewy
62
67
  # UsersIndex::User.filter(term: {name: 'Johny'}).explain.first._explanation # => {...}
63
68
  #
64
69
  def explain value = nil
65
- chain { criteria.update_options explain: (value.nil? ? true : value) }
70
+ chain { criteria.update_request_options explain: (value.nil? ? true : value) }
66
71
  end
67
72
 
68
73
  # Sets query compilation mode for search request.
@@ -207,6 +212,18 @@ module Chewy
207
212
  chain { criteria.update_options filter_mode: value }
208
213
  end
209
214
 
215
+ # Acts the same way as `filter_mode`, but used for `post_filter`.
216
+ # Note that it fallbacks by default to `Chewy.filter_mode` if
217
+ # `Chewy.post_filter_mode` is nil.
218
+ #
219
+ # UsersIndex.post_filter{ name == 'Johny' }.post_filter{ age <= 42 }.post_filter_mode(:and)
220
+ # UsersIndex.post_filter{ name == 'Johny' }.post_filter{ age <= 42 }.post_filter_mode(:should)
221
+ # UsersIndex.post_filter{ name == 'Johny' }.post_filter{ age <= 42 }.post_filter_mode('50%')
222
+ #
223
+ def post_filter_mode value
224
+ chain { criteria.update_options post_filter_mode: value }
225
+ end
226
+
210
227
  # Sets elasticsearch <tt>size</tt> search request param
211
228
  # Default value is set in the elasticsearch and is 10.
212
229
  #
@@ -217,7 +234,7 @@ module Chewy
217
234
  # }}
218
235
  #
219
236
  def limit value
220
- chain { criteria.update_options size: Integer(value) }
237
+ chain { criteria.update_request_options size: Integer(value) }
221
238
  end
222
239
 
223
240
  # Sets elasticsearch <tt>from</tt> search request param
@@ -229,7 +246,23 @@ module Chewy
229
246
  # }}
230
247
  #
231
248
  def offset value
232
- chain { criteria.update_options from: Integer(value) }
249
+ chain { criteria.update_request_options from: Integer(value) }
250
+ end
251
+
252
+ # Elasticsearch highlight query option support
253
+ #
254
+ # UsersIndex.query(...).highlight(fields: { ... })
255
+ #
256
+ def highlight value
257
+ chain { criteria.update_request_options highlight: value }
258
+ end
259
+
260
+ # Elasticsearch rescore query option support
261
+ #
262
+ # UsersIndex.query(...).rescore(query: { ... })
263
+ #
264
+ def rescore value
265
+ chain { criteria.update_request_options rescore: value }
233
266
  end
234
267
 
235
268
  # Adds facets section to the search request.
@@ -274,6 +307,27 @@ module Chewy
274
307
  end
275
308
  alias :aggs :aggregations
276
309
 
310
+ # Sets elasticsearch <tt>suggest</tt> search request param
311
+ #
312
+ # UsersIndex.suggest(name: {text: 'Joh', term: {field: 'name'}})
313
+ # # => {body: {
314
+ # query: {...},
315
+ # suggest: {
316
+ # text: 'Joh',
317
+ # term: {
318
+ # field: 'name'
319
+ # }
320
+ # }
321
+ # }}
322
+ #
323
+ def suggest params = nil
324
+ if params
325
+ chain { criteria.update_suggest params }
326
+ else
327
+ _response['suggest'] || {}
328
+ end
329
+ end
330
+
277
331
  # Marks the criteria as having zero records. This scope always returns empty array
278
332
  # without touching the elasticsearch server.
279
333
  # All the chained calls of methods don't affect the result
@@ -288,6 +342,20 @@ module Chewy
288
342
  chain { criteria.update_options none: true }
289
343
  end
290
344
 
345
+ # Setups strategy for top-level filtered query
346
+ #
347
+ # UsersIndex.filter { name == 'Johny'}.strategy(:leap_frog)
348
+ # # => {body: {
349
+ # query: { filtered: {
350
+ # filter: { term: { name: 'Johny' } },
351
+ # strategy: 'leap_frog'
352
+ # } }
353
+ # }}
354
+ #
355
+ def strategy value = nil
356
+ chain { criteria.update_options strategy: value }
357
+ end
358
+
291
359
  # Adds one or more query to the search request
292
360
  # Internally queries are stored as an array
293
361
  # While the full query compilation this array compiles
@@ -319,7 +387,7 @@ module Chewy
319
387
  # While the full query compilation this array compiles
320
388
  # according to <tt>:filter_mode</tt> option value
321
389
  #
322
- # By default it joines inside <tt>and</tt> filter
390
+ # By default it joins inside <tt>and</tt> filter
323
391
  # See <tt>#filter_mode</tt> chainable method for more info.
324
392
  #
325
393
  # Also this method supports block DSL.
@@ -347,6 +415,37 @@ module Chewy
347
415
  chain { criteria.update_filters params }
348
416
  end
349
417
 
418
+ # Adds one or more post_filter to the search request
419
+ # Internally post_filters are stored as an array
420
+ # While the full query compilation this array compiles
421
+ # according to <tt>:post_filter_mode</tt> option value
422
+ #
423
+ # By default it joins inside <tt>and</tt> filter
424
+ # See <tt>#post_filter_mode</tt> chainable method for more info.
425
+ #
426
+ # Also this method supports block DSL.
427
+ # See <tt>Chewy::Query::Filters</tt> for more info.
428
+ #
429
+ # UsersIndex.post_filter(term: {name: 'Johny'}).post_filter(range: {age: {lte: 42}})
430
+ # UsersIndex::User.post_filter(term: {name: 'Johny'}).post_filter(range: {age: {lte: 42}})
431
+ # UsersIndex.post_filter{ name == 'Johny' }.post_filter{ age <= 42 }
432
+ # # => {body: {
433
+ # post_filter: {and: [{term: {name: 'Johny'}}, {range: {age: {lte: 42}}}]}
434
+ # }}
435
+ #
436
+ # If only one post_filter was specified, it will become a result
437
+ # post_filter as is, without joining.
438
+ #
439
+ # UsersIndex.post_filter(term: {name: 'Johny'})
440
+ # # => {body: {
441
+ # post_filter: {term: {name: 'Johny'}}
442
+ # }}
443
+ #
444
+ def post_filter params = nil, &block
445
+ params = Filters.new(&block).__render__ if block
446
+ chain { criteria.update_post_filters params }
447
+ end
448
+
350
449
  # Sets search request sorting
351
450
  #
352
451
  # UsersIndex.order(:first_name, :last_name).order(age: :desc).order(price: {order: :asc, mode: :avg})
@@ -474,7 +573,7 @@ module Chewy
474
573
  end
475
574
 
476
575
  def reset
477
- @_request, @_response, @_results = nil
576
+ @_request, @_response, @_results, @_collection = nil
478
577
  end
479
578
 
480
579
  def _request
@@ -484,17 +583,32 @@ module Chewy
484
583
  def _response
485
584
  @_response ||= begin
486
585
  ActiveSupport::Notifications.instrument 'search_query.chewy', request: _request, index: index do
487
- index.client.search(_request)
586
+ begin
587
+ index.client.search(_request)
588
+ rescue Elasticsearch::Transport::Transport::Errors::NotFound => e
589
+ raise e if e.message !~ /IndexMissingException/
590
+ {}
591
+ end
488
592
  end
489
593
  end
490
594
  end
491
595
 
492
596
  def _results
493
- @_results ||= (criteria.none? ? [] : _response['hits']['hits']).map do |hit|
494
- attributes = hit['_source'] || {}
495
- attributes.reverse_merge!(id: hit['_id'])
496
- .merge!(_score: hit['_score'], _explanation: hit['_explanation'])
497
- index.type_hash[hit['_type']].new attributes
597
+ @_results ||= (criteria.none? || _response == {} ? [] : _response['hits']['hits']).map do |hit|
598
+ attributes = (hit['_source'] || {}).merge(hit['highlight'] || {}, &RESULT_MERGER)
599
+ attributes.reverse_merge!(id: hit['_id']).merge!(_score: hit['_score'])
600
+
601
+ wrapper = index.type_hash[hit['_type']].new attributes
602
+ wrapper._data = hit
603
+ wrapper
604
+ end
605
+ end
606
+
607
+ def _collection
608
+ @_collection ||= begin
609
+ _load_objects! if criteria.options[:preload]
610
+ criteria.options[:preload] && criteria.options[:loaded_objects] ?
611
+ _results.map(&:_object) : _results
498
612
  end
499
613
  end
500
614
  end
@@ -4,16 +4,26 @@ module Chewy
4
4
 
5
5
  protected
6
6
 
7
- def _composed_query queries, filters
8
- if filters
9
- {query: {
10
- filtered: {
11
- query: queries ? queries : {match_all: {}},
12
- filter: filters
13
- }
14
- }}
15
- elsif queries
16
- {query: queries}
7
+ def _filtered_query query, filter, options = {}
8
+ query = { match_all: {} } if !query.present? && filter.present?
9
+
10
+ if filter.present?
11
+ filtered = if query.present?
12
+ { query: { filtered: {
13
+ query: query,
14
+ filter: filter
15
+ } } }
16
+ else
17
+ { query: { filtered: {
18
+ filter: filter
19
+ } } }
20
+ end
21
+ filtered[:query][:filtered].merge!(strategy: options[:strategy].to_s) if options[:strategy].present?
22
+ filtered
23
+ elsif query.present?
24
+ { query: query }
25
+ else
26
+ { }
17
27
  end
18
28
  end
19
29
 
@@ -23,14 +33,14 @@ module Chewy
23
33
  if queries.many?
24
34
  case logic
25
35
  when :dis_max
26
- {dis_max: {queries: queries}}
36
+ { dis_max: { queries: queries } }
27
37
  when :must, :should
28
- {bool: {logic => queries}}
38
+ { bool: { logic => queries } }
29
39
  else
30
40
  if logic.is_a?(Float)
31
- {dis_max: {queries: queries, tie_breaker: logic}}
41
+ { dis_max: { queries: queries, tie_breaker: logic } }
32
42
  else
33
- {bool: {should: queries, minimum_should_match: logic}}
43
+ { bool: { should: queries, minimum_should_match: logic } }
34
44
  end
35
45
  end
36
46
  else
@@ -44,11 +54,11 @@ module Chewy
44
54
  if filters.many?
45
55
  case logic
46
56
  when :and, :or
47
- {logic => filters}
57
+ { logic => filters }
48
58
  when :must, :should
49
- {bool: {logic => filters}}
59
+ { bool: { logic => filters } }
50
60
  else
51
- {bool: {should: filters, minimum_should_match: logic}}
61
+ { bool: { should: filters, minimum_should_match: logic } }
52
62
  end
53
63
  else
54
64
  filters.first
@@ -4,12 +4,16 @@ module Chewy
4
4
  class Query
5
5
  class Criteria
6
6
  include Compose
7
- ARRAY_STORAGES = [:queries, :filters, :sort, :fields, :types]
8
- HASH_STORAGES = [:options, :facets, :aggregations]
7
+ ARRAY_STORAGES = [:queries, :filters, :post_filters, :sort, :fields, :types]
8
+ HASH_STORAGES = [:options, :request_options, :facets, :aggregations, :suggest]
9
9
  STORAGES = ARRAY_STORAGES + HASH_STORAGES
10
10
 
11
11
  def initialize options = {}
12
- @options = options.merge(query_mode: Chewy.query_mode, filter_mode: Chewy.filter_mode)
12
+ @options = options.merge(
13
+ query_mode: Chewy.query_mode,
14
+ filter_mode: Chewy.filter_mode,
15
+ post_filter_mode: Chewy.filter_mode || Chewy.post_filter_mode
16
+ )
13
17
  end
14
18
 
15
19
  def == other
@@ -40,6 +44,10 @@ module Chewy
40
44
  options.merge!(modifer)
41
45
  end
42
46
 
47
+ def update_request_options(modifer)
48
+ request_options.merge!(modifer)
49
+ end
50
+
43
51
  def update_facets(modifer)
44
52
  facets.merge!(modifer)
45
53
  end
@@ -48,12 +56,16 @@ module Chewy
48
56
  aggregations.merge!(modifer)
49
57
  end
50
58
 
51
- def update_queries(modifer)
52
- @queries = queries + Array.wrap(modifer).reject(&:blank?)
59
+ def update_suggest(modifier)
60
+ suggest.merge!(modifier)
53
61
  end
54
62
 
55
- def update_filters(modifer)
56
- @filters = filters + Array.wrap(modifer).reject(&:blank?)
63
+ [:filters, :queries, :post_filters].each do |storage|
64
+ class_eval <<-RUBY
65
+ def update_#{storage}(modifer)
66
+ @#{storage} = #{storage} + Array.wrap(modifer).reject(&:blank?)
67
+ end
68
+ RUBY
57
69
  end
58
70
 
59
71
  def update_sort(modifer, options = {})
@@ -85,14 +97,17 @@ module Chewy
85
97
  end
86
98
 
87
99
  def request_body
88
- body = (_composed_query(_request_query, _request_filter) || {}).tap do |body|
89
- body.merge!(facets: facets) if facets?
90
- body.merge!(aggregations: aggregations) if aggregations?
91
- body.merge!(sort: sort) if sort?
92
- body.merge!(_source: fields) if fields?
93
- end
100
+ body = {}
101
+
102
+ body.merge!(_filtered_query(_request_query, _request_filter, options.slice(:strategy)))
103
+ body.merge!(post_filter: _request_post_filter) if post_filters?
104
+ body.merge!(facets: facets) if facets?
105
+ body.merge!(aggregations: aggregations) if aggregations?
106
+ body.merge!(suggest: suggest) if suggest?
107
+ body.merge!(sort: sort) if sort?
108
+ body.merge!(_source: fields) if fields?
94
109
 
95
- {body: body.merge!(_request_options)}
110
+ { body: body.merge!(request_options) }
96
111
  end
97
112
 
98
113
  protected
@@ -104,17 +119,10 @@ module Chewy
104
119
  def initialize_clone(other)
105
120
  STORAGES.each do |storage|
106
121
  value = other.send(storage)
107
- if value
108
- value = Marshal.load(Marshal.dump(value))
109
- instance_variable_set("@#{storage}", value)
110
- end
122
+ instance_variable_set("@#{storage}", value.deep_dup)
111
123
  end
112
124
  end
113
125
 
114
- def _request_options
115
- options.slice(:size, :from, :explain, :aggregations)
116
- end
117
-
118
126
  def _request_query
119
127
  _queries_join(queries, options[:query_mode])
120
128
  end
@@ -133,6 +141,10 @@ module Chewy
133
141
  def _request_types
134
142
  _filters_join(types.map { |type| {type: {value: type}} }, :or)
135
143
  end
144
+
145
+ def _request_post_filter
146
+ _filters_join(post_filters, options[:post_filter_mode])
147
+ end
136
148
  end
137
149
  end
138
150
  end