chewy 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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