chewy 5.0.0 → 7.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +240 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.github/PULL_REQUEST_TEMPLATE.md +16 -0
  6. data/.github/workflows/ruby.yml +94 -0
  7. data/Appraisals +1 -17
  8. data/CHANGELOG.md +308 -356
  9. data/CODE_OF_CONDUCT.md +14 -0
  10. data/CONTRIBUTING.md +63 -0
  11. data/Gemfile +2 -0
  12. data/LICENSE.txt +1 -1
  13. data/README.md +71 -55
  14. data/chewy.gemspec +5 -5
  15. data/gemfiles/rails.5.2.activerecord.gemfile +4 -3
  16. data/gemfiles/{rails.5.0.activerecord.gemfile → rails.5.2.mongoid.6.4.gemfile} +4 -3
  17. data/gemfiles/{rails.5.0.mongoid.6.1.gemfile → rails.6.0.activerecord.gemfile} +4 -3
  18. data/gemfiles/{rails.5.1.activerecord.gemfile → rails.6.1.activerecord.gemfile} +6 -3
  19. data/lib/chewy.rb +1 -1
  20. data/lib/chewy/backports/duplicable.rb +1 -1
  21. data/lib/chewy/config.rb +2 -20
  22. data/lib/chewy/fields/base.rb +1 -7
  23. data/lib/chewy/fields/root.rb +2 -2
  24. data/lib/chewy/index.rb +2 -0
  25. data/lib/chewy/index/actions.rb +15 -5
  26. data/lib/chewy/index/aliases.rb +16 -5
  27. data/lib/chewy/multi_search.rb +62 -0
  28. data/lib/chewy/railtie.rb +1 -1
  29. data/lib/chewy/search.rb +2 -9
  30. data/lib/chewy/search/loader.rb +1 -1
  31. data/lib/chewy/search/pagination/will_paginate.rb +1 -1
  32. data/lib/chewy/search/parameters.rb +24 -6
  33. data/lib/chewy/search/parameters/allow_partial_search_results.rb +27 -0
  34. data/lib/chewy/search/parameters/indices.rb +123 -0
  35. data/lib/chewy/search/parameters/none.rb +1 -3
  36. data/lib/chewy/search/request.rb +101 -74
  37. data/lib/chewy/search/scrolling.rb +7 -6
  38. data/lib/chewy/stash.rb +1 -1
  39. data/lib/chewy/strategy/active_job.rb +1 -1
  40. data/lib/chewy/strategy/sidekiq.rb +1 -1
  41. data/lib/chewy/type.rb +4 -1
  42. data/lib/chewy/type/adapter/active_record.rb +1 -1
  43. data/lib/chewy/type/adapter/mongoid.rb +1 -1
  44. data/lib/chewy/type/adapter/orm.rb +7 -4
  45. data/lib/chewy/type/adapter/sequel.rb +1 -1
  46. data/lib/chewy/type/import.rb +14 -4
  47. data/lib/chewy/type/import/bulk_request.rb +4 -2
  48. data/lib/chewy/type/import/journal_builder.rb +1 -1
  49. data/lib/chewy/type/import/routine.rb +1 -1
  50. data/lib/chewy/type/mapping.rb +4 -4
  51. data/lib/chewy/type/observe.rb +3 -3
  52. data/lib/chewy/type/syncer.rb +4 -5
  53. data/lib/chewy/type/witchcraft.rb +4 -2
  54. data/lib/chewy/type/wrapper.rb +12 -2
  55. data/lib/chewy/version.rb +1 -1
  56. data/migration_guide.md +56 -0
  57. data/spec/chewy/config_spec.rb +1 -22
  58. data/spec/chewy/fields/base_spec.rb +11 -9
  59. data/spec/chewy/index/actions_spec.rb +120 -33
  60. data/spec/chewy/index/aliases_spec.rb +3 -3
  61. data/spec/chewy/index_spec.rb +16 -39
  62. data/spec/chewy/journal_spec.rb +22 -18
  63. data/spec/chewy/minitest/search_index_receiver_spec.rb +11 -9
  64. data/spec/chewy/multi_search_spec.rb +85 -0
  65. data/spec/chewy/rake_helper_spec.rb +102 -87
  66. data/spec/chewy/rspec/update_index_spec.rb +47 -46
  67. data/spec/chewy/runtime_spec.rb +2 -2
  68. data/spec/chewy/search/parameters/indices_spec.rb +190 -0
  69. data/spec/chewy/search/parameters/none_spec.rb +1 -1
  70. data/spec/chewy/search/parameters_spec.rb +21 -4
  71. data/spec/chewy/search/request_spec.rb +103 -70
  72. data/spec/chewy/search/response_spec.rb +27 -17
  73. data/spec/chewy/search/scrolling_spec.rb +27 -17
  74. data/spec/chewy/search_spec.rb +45 -41
  75. data/spec/chewy/stash_spec.rb +14 -12
  76. data/spec/chewy/strategy/active_job_spec.rb +15 -2
  77. data/spec/chewy/strategy/shoryuken_spec.rb +6 -2
  78. data/spec/chewy/strategy/sidekiq_spec.rb +6 -2
  79. data/spec/chewy/type/adapter/active_record_spec.rb +16 -4
  80. data/spec/chewy/type/import/bulk_builder_spec.rb +9 -94
  81. data/spec/chewy/type/import/journal_builder_spec.rb +9 -7
  82. data/spec/chewy/type/import_spec.rb +9 -0
  83. data/spec/chewy/type/mapping_spec.rb +3 -1
  84. data/spec/chewy/type/observe_spec.rb +4 -4
  85. data/spec/chewy/type/witchcraft_spec.rb +15 -0
  86. data/spec/chewy/type/wrapper_spec.rb +3 -1
  87. data/spec/chewy_spec.rb +0 -7
  88. data/spec/spec_helper.rb +4 -8
  89. data/spec/support/active_record.rb +21 -0
  90. metadata +32 -97
  91. data/.travis.yml +0 -45
  92. data/LEGACY_DSL.md +0 -497
  93. data/gemfiles/rails.4.0.activerecord.gemfile +0 -15
  94. data/gemfiles/rails.4.1.activerecord.gemfile +0 -15
  95. data/gemfiles/rails.4.2.activerecord.gemfile +0 -16
  96. data/gemfiles/rails.4.2.mongoid.5.2.gemfile +0 -16
  97. data/gemfiles/rails.5.1.mongoid.6.3.gemfile +0 -16
  98. data/lib/chewy/query.rb +0 -1137
  99. data/lib/chewy/query/compose.rb +0 -68
  100. data/lib/chewy/query/criteria.rb +0 -191
  101. data/lib/chewy/query/filters.rb +0 -244
  102. data/lib/chewy/query/loading.rb +0 -110
  103. data/lib/chewy/query/nodes/and.rb +0 -25
  104. data/lib/chewy/query/nodes/base.rb +0 -17
  105. data/lib/chewy/query/nodes/bool.rb +0 -34
  106. data/lib/chewy/query/nodes/equal.rb +0 -34
  107. data/lib/chewy/query/nodes/exists.rb +0 -20
  108. data/lib/chewy/query/nodes/expr.rb +0 -28
  109. data/lib/chewy/query/nodes/field.rb +0 -110
  110. data/lib/chewy/query/nodes/has_child.rb +0 -15
  111. data/lib/chewy/query/nodes/has_parent.rb +0 -15
  112. data/lib/chewy/query/nodes/has_relation.rb +0 -59
  113. data/lib/chewy/query/nodes/match_all.rb +0 -11
  114. data/lib/chewy/query/nodes/missing.rb +0 -20
  115. data/lib/chewy/query/nodes/not.rb +0 -25
  116. data/lib/chewy/query/nodes/or.rb +0 -25
  117. data/lib/chewy/query/nodes/prefix.rb +0 -19
  118. data/lib/chewy/query/nodes/query.rb +0 -20
  119. data/lib/chewy/query/nodes/range.rb +0 -63
  120. data/lib/chewy/query/nodes/raw.rb +0 -15
  121. data/lib/chewy/query/nodes/regexp.rb +0 -35
  122. data/lib/chewy/query/nodes/script.rb +0 -20
  123. data/lib/chewy/query/pagination.rb +0 -25
  124. data/spec/chewy/query/criteria_spec.rb +0 -700
  125. data/spec/chewy/query/filters_spec.rb +0 -201
  126. data/spec/chewy/query/loading_spec.rb +0 -124
  127. data/spec/chewy/query/nodes/and_spec.rb +0 -12
  128. data/spec/chewy/query/nodes/bool_spec.rb +0 -14
  129. data/spec/chewy/query/nodes/equal_spec.rb +0 -32
  130. data/spec/chewy/query/nodes/exists_spec.rb +0 -18
  131. data/spec/chewy/query/nodes/has_child_spec.rb +0 -59
  132. data/spec/chewy/query/nodes/has_parent_spec.rb +0 -59
  133. data/spec/chewy/query/nodes/match_all_spec.rb +0 -11
  134. data/spec/chewy/query/nodes/missing_spec.rb +0 -16
  135. data/spec/chewy/query/nodes/not_spec.rb +0 -14
  136. data/spec/chewy/query/nodes/or_spec.rb +0 -12
  137. data/spec/chewy/query/nodes/prefix_spec.rb +0 -16
  138. data/spec/chewy/query/nodes/query_spec.rb +0 -12
  139. data/spec/chewy/query/nodes/range_spec.rb +0 -32
  140. data/spec/chewy/query/nodes/raw_spec.rb +0 -11
  141. data/spec/chewy/query/nodes/regexp_spec.rb +0 -43
  142. data/spec/chewy/query/nodes/script_spec.rb +0 -15
  143. data/spec/chewy/query/pagination/kaminari_spec.rb +0 -5
  144. data/spec/chewy/query/pagination/will_paginate_spec.rb +0 -5
  145. data/spec/chewy/query/pagination_spec.rb +0 -39
  146. data/spec/chewy/query_spec.rb +0 -637
  147. data/spec/chewy/search/parameters/indices_boost_spec.rb +0 -83
@@ -1,5 +1,5 @@
1
1
  require 'chewy/search/parameters/bool_storage_examples'
2
2
 
3
3
  describe Chewy::Search::Parameters::None do
4
- it_behaves_like :bool_storage, query: {bool: {filter: {bool: {must_not: {match_all: {}}}}}}
4
+ it_behaves_like :bool_storage, query: {match_none: {}}
5
5
  end
@@ -72,18 +72,20 @@ describe Chewy::Search::Parameters do
72
72
  describe '#merge!' do
73
73
  let(:first) { described_class.new(offset: 10, order: 'foo') }
74
74
  let(:second) { described_class.new(limit: 20, offset: 20, order: 'bar') }
75
+ let(:first_clone) { first.clone }
76
+ let(:second_clone) { second.clone }
75
77
 
76
78
  specify do
77
79
  expect { first.merge!(second) }.to change { first.clone }
78
80
  .to(described_class.new(limit: 20, offset: 20, order: %w[foo bar]))
79
81
  end
80
- specify { expect { first.merge!(second) }.not_to change { second.clone } }
82
+ specify { expect { first.merge!(second) }.not_to change { second_clone } }
81
83
 
82
84
  specify do
83
85
  expect { second.merge!(first) }.to change { second.clone }
84
86
  .to(described_class.new(limit: 20, offset: 10, order: %w[bar foo]))
85
87
  end
86
- specify { expect { second.merge!(first) }.not_to change { first.clone } }
88
+ specify { expect { second.merge!(first) }.not_to change { first_clone } }
87
89
 
88
90
  context 'spawns new storages for the merge' do
89
91
  let(:names) { %i[limit offset order] }
@@ -100,7 +102,22 @@ describe Chewy::Search::Parameters do
100
102
  subject { described_class.new(offset: 10, order: 'foo') }
101
103
 
102
104
  specify { expect(subject.render).to eq(body: {from: 10, sort: ['foo']}) }
103
- specify { expect(described_class.new.render).to eq({}) }
105
+ specify { expect(described_class.new.render).to eq(body: {}) }
106
+
107
+ context do
108
+ subject { described_class.new(request_cache: true) }
109
+ specify { expect(subject.render).to eq(body: {}, request_cache: true) }
110
+ end
111
+
112
+ context do
113
+ subject { described_class.new(search_type: 'query_then_fetch') }
114
+ specify { expect(subject.render).to eq(body: {}, search_type: 'query_then_fetch') }
115
+ end
116
+
117
+ context do
118
+ subject { described_class.new(allow_partial_search_results: true) }
119
+ specify { expect(subject.render).to eq(body: {}, allow_partial_search_results: true) }
120
+ end
104
121
 
105
122
  context do
106
123
  subject { described_class.new(query: {foo: 'bar'}, filter: {moo: 'baz'}) }
@@ -124,7 +141,7 @@ describe Chewy::Search::Parameters do
124
141
 
125
142
  context do
126
143
  subject { described_class.new(filter: {moo: 'baz'}, none: true) }
127
- specify { expect(subject.render).to eq(body: {query: {bool: {filter: {bool: {must_not: {match_all: {}}}}}}}) }
144
+ specify { expect(subject.render).to eq(body: {query: {match_none: {}}}) }
128
145
  end
129
146
  end
130
147
  end
@@ -10,17 +10,19 @@ describe Chewy::Search::Request do
10
10
  field :name
11
11
  field :age, type: :integer
12
12
  end
13
+ end
14
+
15
+ stub_index(:cities) do
13
16
  define_type :city do
14
17
  field :id, type: :integer
15
18
  end
19
+ end
20
+
21
+ stub_index(:countries) do
16
22
  define_type :country do
17
23
  field :id, type: :integer
18
24
  end
19
25
  end
20
-
21
- stub_index(:cities) do
22
- define_type :city
23
- end
24
26
  end
25
27
 
26
28
  subject { described_class.new(ProductsIndex) }
@@ -30,11 +32,7 @@ describe Chewy::Search::Request do
30
32
  specify { expect(described_class.new(ProductsIndex)).not_to eq(described_class.new(CitiesIndex)) }
31
33
  specify { expect(described_class.new(ProductsIndex)).not_to eq(described_class.new(ProductsIndex, CitiesIndex)) }
32
34
  specify { expect(described_class.new(CitiesIndex, ProductsIndex)).to eq(described_class.new(ProductsIndex, CitiesIndex)) }
33
- specify { expect(described_class.new(ProductsIndex::Product)).to eq(described_class.new(ProductsIndex::Product)) }
34
- specify { expect(described_class.new(ProductsIndex::Product)).not_to eq(described_class.new(ProductsIndex::City)) }
35
- specify { expect(described_class.new(ProductsIndex::Product)).not_to eq(described_class.new(ProductsIndex::Product, ProductsIndex::City)) }
36
- specify { expect(described_class.new(ProductsIndex::City, ProductsIndex::Product)).to eq(described_class.new(ProductsIndex::Product, ProductsIndex::City)) }
37
- specify { expect(described_class.new(ProductsIndex::City, CitiesIndex::City)).to eq(described_class.new(CitiesIndex::City, ProductsIndex::City)) }
35
+ specify { expect(described_class.new(ProductsIndex, CitiesIndex)).to eq(described_class.new(CitiesIndex, ProductsIndex)) }
38
36
 
39
37
  specify { expect(described_class.new(ProductsIndex).limit(10)).to eq(described_class.new(ProductsIndex).limit(10)) }
40
38
  specify { expect(described_class.new(ProductsIndex).limit(10)).not_to eq(described_class.new(ProductsIndex).limit(20)) }
@@ -48,7 +46,7 @@ describe Chewy::Search::Request do
48
46
  expect(subject.render)
49
47
  .to match(
50
48
  index: %w[products],
51
- type: array_including(%w[product city country]),
49
+ type: array_including(%w[product]),
52
50
  body: {}
53
51
  )
54
52
  end
@@ -57,11 +55,11 @@ describe Chewy::Search::Request do
57
55
  describe '#inspect' do
58
56
  specify do
59
57
  expect(described_class.new(ProductsIndex).inspect)
60
- .to eq('<Chewy::Search::Request {:index=>["products"], :type=>["product", "city", "country"], :body=>{}}>')
58
+ .to eq('<Chewy::Search::Request {:index=>["products"], :type=>["product"], :body=>{}}>')
61
59
  end
62
60
  specify do
63
61
  expect(ProductsIndex.limit(10).inspect)
64
- .to eq('<ProductsIndex::Query {:index=>["products"], :type=>["product", "city", "country"], :body=>{:size=>10}}>')
62
+ .to eq('<ProductsIndex::Query {:index=>["products"], :type=>["product"], :body=>{:size=>10}}>')
65
63
  end
66
64
  end
67
65
 
@@ -151,13 +149,20 @@ describe Chewy::Search::Request do
151
149
  end
152
150
 
153
151
  describe '#request_cache' do
154
- specify { expect(subject.request_cache(true).render[:body]).to include(request_cache: true) }
155
- specify { expect(subject.request_cache(true).request_cache(false).render[:body]).to include(request_cache: false) }
156
- specify { expect(subject.request_cache(true).request_cache(nil).render[:body]).to be_blank }
152
+ specify { expect(subject.request_cache(true).render).to include(request_cache: true) }
153
+ specify { expect(subject.request_cache(true).request_cache(false).render).to include(request_cache: false) }
154
+ specify { expect(subject.request_cache(true).request_cache(nil).render[:request_cache]).to be_blank }
157
155
  specify { expect { subject.request_cache(true) }.not_to change { subject.render } }
158
156
  end
159
157
 
160
- %i[search_type preference timeout].each do |name|
158
+ describe '#search_type' do
159
+ specify { expect(subject.search_type('foo').render).to include(search_type: 'foo') }
160
+ specify { expect(subject.search_type('foo').search_type('bar').render).to include(search_type: 'bar') }
161
+ specify { expect(subject.search_type('foo').search_type(nil).render[:search_type]).to be_blank }
162
+ specify { expect { subject.search_type('foo') }.not_to change { subject.render } }
163
+ end
164
+
165
+ %i[preference timeout].each do |name|
161
166
  describe "##{name}" do
162
167
  specify { expect(subject.send(name, :foo).render[:body]).to include(name => 'foo') }
163
168
  specify { expect(subject.send(name, :foo).send(name, :bar).render[:body]).to include(name => 'bar') }
@@ -218,10 +223,18 @@ describe Chewy::Search::Request do
218
223
  specify { expect { subject.docvalue_fields(:foo) }.not_to change { subject.render } }
219
224
  end
220
225
 
226
+ describe '#indices' do
227
+ specify { expect(described_class.new(:products).render[:index]).to eq(%w[products]) }
228
+ specify { expect(subject.indices(:cities).render[:index]).to eq(%w[cities products]) }
229
+ specify { expect(subject.indices(CitiesIndex, :whatever).render[:index]).to eq(%w[cities products whatever]) }
230
+ specify { expect(subject.indices([CitiesIndex, :products]).render[:index]).to eq(%w[cities products]) }
231
+ specify { expect { subject.indices(:cities) }.not_to change { subject.render } }
232
+ end
233
+
221
234
  describe '#types' do
222
235
  specify { expect(subject.types(:product).render[:type]).to contain_exactly('product') }
223
- specify { expect(subject.types(%i[product city]).types(nil).render[:type]).to match_array(%w[product city]) }
224
- specify { expect(subject.types(:product).types(:product, :city, :something).render[:type]).to match_array(%w[product city]) }
236
+ specify { expect(subject.types(%i[product city]).types(nil).render[:type]).to match_array(%w[product]) }
237
+ specify { expect(subject.types(:product).types(:product, :city, :something).render[:type]).to match_array(%w[product]) }
225
238
  specify { expect(subject.types(nil).render[:body]).to be_blank }
226
239
  specify { expect { subject.types(:product) }.not_to change { subject.render } }
227
240
  end
@@ -263,14 +276,16 @@ describe Chewy::Search::Request do
263
276
  define_type City do
264
277
  field :rating, type: 'integer'
265
278
  end
279
+ end
266
280
 
281
+ stub_index(:countries) do
267
282
  define_type Country do
268
283
  field :rating, type: 'integer'
269
284
  end
270
285
  end
271
286
  end
272
287
 
273
- before { PlacesIndex.import!(cities: cities, countries: countries) }
288
+ before { PlacesIndex.import!(cities) }
274
289
 
275
290
  let(:cities) { Array.new(2) { |i| City.create!(rating: i) } }
276
291
  let(:countries) { Array.new(2) { |i| Country.create!(rating: i + 2) } }
@@ -278,14 +293,14 @@ describe Chewy::Search::Request do
278
293
  subject { described_class.new(PlacesIndex).order(:rating) }
279
294
 
280
295
  describe '#objects' do
281
- specify { expect(subject.objects).to eq([*cities, *countries]) }
296
+ specify { expect(subject.objects).to eq([*cities]) }
282
297
  specify { expect(subject.objects.class).to eq(Array) }
283
298
  end
284
299
 
285
300
  describe '#load' do
286
- specify { expect(subject.load(only: 'city')).to eq([*cities, *countries]) }
287
- specify { expect(subject.load(only: 'city').map(&:class).uniq).to eq([PlacesIndex::City, PlacesIndex::Country]) }
288
- specify { expect(subject.load(only: 'city').objects).to eq([*cities, nil, nil]) }
301
+ specify { expect(subject.load(only: 'city')).to eq([*cities]) }
302
+ specify { expect(subject.load(only: 'city').map(&:class).uniq).to eq([PlacesIndex::City]) }
303
+ specify { expect(subject.load(only: 'city').objects).to eq([*cities]) }
289
304
  end
290
305
  end
291
306
 
@@ -372,13 +387,13 @@ describe Chewy::Search::Request do
372
387
  end
373
388
 
374
389
  context 'integration' do
375
- let(:products) { Array.new(3) { |i| {id: i.next.to_i, name: "Name#{i.next}", age: 10 * i.next}.stringify_keys! } }
376
- let(:cities) { Array.new(3) { |i| {id: (i.next + 3).to_i}.stringify_keys! } }
377
- let(:countries) { Array.new(3) { |i| {id: (i.next + 6).to_i}.stringify_keys! } }
390
+ let(:products_count) { 9 }
391
+ let(:products) { Array.new(products_count) { |i| {id: i.next.to_i, name: "Name#{i.next}", age: 10 * i.next}.stringify_keys! } }
392
+ let(:cities) { Array.new(3) { |i| {id: (i.next + 9).to_i}.stringify_keys! } }
393
+ let(:countries) { Array.new(3) { |i| {id: (i.next + 12).to_i}.stringify_keys! } }
378
394
  before do
379
395
  ProductsIndex::Product.import!(products.map { |h| double(h) })
380
- ProductsIndex::City.import!(cities.map { |h| double(h) })
381
- ProductsIndex::Country.import!(countries.map { |h| double(h) })
396
+ CountriesIndex::Country.import!(countries.map { |h| double(h) })
382
397
  CitiesIndex::City.import!(cities.map { |h| double(h) })
383
398
  end
384
399
 
@@ -391,18 +406,11 @@ describe Chewy::Search::Request do
391
406
  specify { expect(subject.size).to eq(3) }
392
407
  end
393
408
 
394
- context 'limited types' do
395
- subject { described_class.new(ProductsIndex::City, ProductsIndex::Country) }
396
-
397
- specify { expect(subject.count).to eq(6) }
398
- specify { expect(subject.size).to eq(6) }
399
- end
400
-
401
- context 'mixed types' do
402
- subject { described_class.new(CitiesIndex, ProductsIndex::Product) }
409
+ context 'mixed indexes' do
410
+ subject { described_class.new(CitiesIndex, ProductsIndex) }
403
411
 
404
- specify { expect(subject.count).to eq(9) }
405
- specify { expect(subject.size).to eq(9) }
412
+ specify { expect(subject.count).to eq(12) }
413
+ specify { expect(subject.size).to eq(10) } # pagination limit
406
414
  end
407
415
 
408
416
  context 'instrumentation' do
@@ -412,12 +420,14 @@ describe Chewy::Search::Request do
412
420
  outer_payload = payload
413
421
  end
414
422
  subject.query(match: {name: 'name3'}).to_a
423
+ request = {index: ['products'], type: %w[product], body: {query: {match: {name: 'name3'}}}}
424
+ request[:rest_total_hits_as_int] = true if Chewy::Runtime.version >= '7.0.0'
415
425
  expect(outer_payload).to eq(
416
426
  index: ProductsIndex,
417
427
  indexes: [ProductsIndex],
418
- request: {index: ['products'], type: %w[product city country], body: {query: {match: {name: 'name3'}}}},
419
- type: [ProductsIndex::Product, ProductsIndex::City, ProductsIndex::Country],
420
- types: [ProductsIndex::Product, ProductsIndex::City, ProductsIndex::Country]
428
+ request: request,
429
+ type: ProductsIndex::Product,
430
+ types: [ProductsIndex::Product]
421
431
  )
422
432
  end
423
433
  end
@@ -429,10 +439,13 @@ describe Chewy::Search::Request do
429
439
  describe '#highlight' do
430
440
  specify { expect(subject.query(match: {name: 'name3'}).highlight(fields: {name: {}}).first.name).to eq('Name3') }
431
441
  specify { expect(subject.query(match: {name: 'name3'}).highlight(fields: {name: {}}).first.name_highlight).to eq('<em>Name3</em>') }
442
+ specify { expect(subject.query(match: {name: 'name3'}).highlight(fields: {name: {}}).first.name_highlights).to eq(['<em>Name3</em>']) }
432
443
  specify { expect(subject.query(match: {name: 'name3'}).highlight(fields: {name: {}}).first._data['_source']['name']).to eq('Name3') }
433
444
  end
434
445
 
435
446
  describe '#suggest' do
447
+ let(:products_count) { 3 }
448
+
436
449
  specify do
437
450
  expect(subject.suggest(
438
451
  foo: {
@@ -454,7 +467,7 @@ describe Chewy::Search::Request do
454
467
  describe '#aggs' do
455
468
  specify do
456
469
  expect(subject.aggs(avg_age: {avg: {field: :age}}).aggs)
457
- .to eq('avg_age' => {'value' => 20.0})
470
+ .to eq('avg_age' => {'value' => 50.0})
458
471
  end
459
472
  end
460
473
 
@@ -474,8 +487,7 @@ describe Chewy::Search::Request do
474
487
  specify { expect(subject.count).to eq(9) }
475
488
  specify { expect(subject.limit(6).count).to eq(9) }
476
489
  specify { expect(subject.offset(6).count).to eq(9) }
477
- specify { expect(subject.types(:product, :something).count).to eq(3) }
478
- specify { expect(subject.types(:product, :country).count).to eq(6) }
490
+ specify { expect(subject.indices(:products, CountriesIndex).count).to eq(12) }
479
491
  specify { expect(subject.filter(term: {age: 10}).count).to eq(1) }
480
492
  specify { expect(subject.query(term: {age: 10}).count).to eq(1) }
481
493
  specify { expect(subject.order(nil).count).to eq(9) }
@@ -514,7 +526,7 @@ describe Chewy::Search::Request do
514
526
  context do
515
527
  before { expect(Chewy.client).to receive(:search).once.and_call_original }
516
528
 
517
- specify { expect(subject.first).to be_a(ProductsIndex::Country).and have_attributes(id: 9) }
529
+ specify { expect(subject.first).to be_a(ProductsIndex::Product).and have_attributes(id: 9) }
518
530
  specify { expect(subject.first(3).map(&:id)).to eq([9, 8, 7]) }
519
531
  specify { expect(subject.first(10).map(&:id)).to have(9).items }
520
532
  specify { expect(subject.limit(5).first(10).map(&:id)).to have(9).items }
@@ -527,7 +539,7 @@ describe Chewy::Search::Request do
527
539
  expect(Chewy.client).not_to receive(:search)
528
540
  end
529
541
 
530
- specify { expect(subject.first).to be_a(ProductsIndex::Country).and have_attributes(id: 9) }
542
+ specify { expect(subject.first).to be_a(ProductsIndex::Product).and have_attributes(id: 9) }
531
543
  specify { expect(subject.first(3).map(&:id)).to eq([9, 8, 7]) }
532
544
  specify { expect(subject.first(10).map(&:id)).to have(9).items }
533
545
 
@@ -560,7 +572,7 @@ describe Chewy::Search::Request do
560
572
  specify { expect { subject.post_filter(match: {name: 'name2'}).find('1', '3') }.to raise_error Chewy::DocumentNotFound, 'Could not find documents for ids: 1 and 3' }
561
573
 
562
574
  context 'make sure it returns everything' do
563
- let(:countries) { Array.new(6) { |i| {id: (i.next + 6).to_i}.stringify_keys! } }
575
+ let(:products_count) { 12 }
564
576
  before { expect(Chewy.client).not_to receive(:scroll) }
565
577
 
566
578
  specify { expect(subject.find((1..12).to_a)).to have(12).items }
@@ -577,15 +589,15 @@ describe Chewy::Search::Request do
577
589
 
578
590
  describe '#pluck' do
579
591
  specify { expect(subject.limit(5).pluck(:_id)).to eq(%w[1 2 3 4 5]) }
580
- specify { expect(subject.limit(5).pluck(:_id, :age)).to eq([['1', 10], ['2', 20], ['3', 30], ['4', nil], ['5', nil]]) }
581
- specify { expect(subject.limit(5).source(:name).pluck(:id, :age)).to eq([[1, 10], [2, 20], [3, 30], [4, nil], [5, nil]]) }
592
+ specify { expect(subject.limit(5).pluck(:_id, :age)).to eq([['1', 10], ['2', 20], ['3', 30], ['4', 40], ['5', 50]]) }
593
+ specify { expect(subject.limit(5).source(:name).pluck(:id, :age)).to eq([[1, 10], [2, 20], [3, 30], [4, 40], [5, 50]]) }
582
594
  specify do
583
595
  expect(subject.limit(5).pluck(:_index, :_type, :name)).to eq([
584
596
  %w[products product Name1],
585
597
  %w[products product Name2],
586
598
  %w[products product Name3],
587
- ['products', 'city', nil],
588
- ['products', 'city', nil]
599
+ %w[products product Name4],
600
+ %w[products product Name5]
589
601
  ])
590
602
  end
591
603
 
@@ -616,24 +628,12 @@ describe Chewy::Search::Request do
616
628
  Chewy.client.indices.refresh(index: 'products')
617
629
  end.to change { described_class.new(ProductsIndex).total_count }.from(9).to(7)
618
630
  end
619
- specify do
620
- expect do
621
- subject.types(:product).delete_all
622
- Chewy.client.indices.refresh(index: 'products')
623
- end.to change { described_class.new(ProductsIndex::Product).total_entries }.from(3).to(0)
624
- end
625
631
  specify do
626
632
  expect do
627
633
  subject.delete_all
628
634
  Chewy.client.indices.refresh(index: 'products')
629
635
  end.to change { described_class.new(ProductsIndex).total }.from(9).to(0)
630
636
  end
631
- specify do
632
- expect do
633
- described_class.new(ProductsIndex::City).delete_all
634
- Chewy.client.indices.refresh(index: 'products')
635
- end.to change { described_class.new(ProductsIndex).total }.from(9).to(6)
636
- end
637
637
 
638
638
  specify do
639
639
  outer_payload = nil
@@ -644,9 +644,9 @@ describe Chewy::Search::Request do
644
644
  expect(outer_payload).to eq(
645
645
  index: ProductsIndex,
646
646
  indexes: [ProductsIndex],
647
- request: {index: ['products'], type: %w[product city country], body: {query: {match: {name: 'name3'}}}, refresh: true},
648
- type: [ProductsIndex::Product, ProductsIndex::City, ProductsIndex::Country],
649
- types: [ProductsIndex::Product, ProductsIndex::City, ProductsIndex::Country]
647
+ request: {index: ['products'], type: %w[product], body: {query: {match: {name: 'name3'}}}, refresh: true},
648
+ type: ProductsIndex::Product,
649
+ types: [ProductsIndex::Product]
650
650
  )
651
651
  end
652
652
 
@@ -659,11 +659,44 @@ describe Chewy::Search::Request do
659
659
  expect(outer_payload).to eq(
660
660
  index: ProductsIndex,
661
661
  indexes: [ProductsIndex],
662
- request: {index: ['products'], type: %w[product city country], body: {query: {match: {name: 'name3'}}}, refresh: false},
663
- type: [ProductsIndex::Product, ProductsIndex::City, ProductsIndex::Country],
664
- types: [ProductsIndex::Product, ProductsIndex::City, ProductsIndex::Country]
662
+ request: {index: ['products'], type: %w[product], body: {query: {match: {name: 'name3'}}}, refresh: false},
663
+ type: ProductsIndex::Product,
664
+ types: [ProductsIndex::Product]
665
665
  )
666
666
  end
667
667
  end
668
+
669
+ describe '#response=' do
670
+ let(:query) { ProductsIndex.limit(0) }
671
+ let(:raw_response) { Chewy.client.search(query.render) }
672
+
673
+ it 'wraps and assigns the raw response' do
674
+ query.response = raw_response
675
+ expect(query.response).to be_a(Chewy::Search::Response)
676
+ end
677
+ end
678
+
679
+ describe '#performed?' do
680
+ let(:query) { ProductsIndex.limit(0) }
681
+ let(:raw_response) { Chewy.client.search(query.render) }
682
+
683
+ it 'is false on a new query' do
684
+ expect(query.performed?).to eq(false)
685
+ end
686
+
687
+ it 'is true after the search request was issued' do
688
+ expect do
689
+ # The `response` method has a side effect of performing unperformed
690
+ # queries.
691
+ query.response
692
+ end.to change(query, :performed?).from(false).to(true)
693
+ end
694
+
695
+ it 'is true after assigning a raw response' do
696
+ expect do
697
+ query.response = raw_response
698
+ end.to change(query, :performed?).from(false).to(true)
699
+ end
700
+ end
668
701
  end
669
702
  end
@@ -7,12 +7,13 @@ describe Chewy::Search::Response, :orm do
7
7
  stub_model(:city)
8
8
  stub_model(:country)
9
9
 
10
- stub_index(:places) do
10
+ stub_index(:cities) do
11
11
  define_type City do
12
12
  field :name
13
13
  field :rating, type: 'integer'
14
14
  end
15
-
15
+ end
16
+ stub_index(:countries) do
16
17
  define_type Country do
17
18
  field :name
18
19
  field :rating, type: 'integer'
@@ -20,15 +21,18 @@ describe Chewy::Search::Response, :orm do
20
21
  end
21
22
  end
22
23
 
23
- before { PlacesIndex.import!(cities: cities, countries: countries) }
24
+ before do
25
+ CitiesIndex.import!(cities: cities)
26
+ CountriesIndex.import!(countries)
27
+ end
24
28
 
25
29
  let(:cities) { Array.new(2) { |i| City.create!(rating: i, name: "city #{i}") } }
26
30
  let(:countries) { Array.new(2) { |i| Country.create!(rating: i + 2, name: "country #{i}") } }
27
31
 
28
- let(:request) { Chewy::Search::Request.new(PlacesIndex).order(:rating) }
32
+ let(:request) { Chewy::Search::Request.new(CitiesIndex, CountriesIndex).order(:rating) }
29
33
  let(:raw_response) { request.send(:perform) }
30
34
  let(:load_options) { {} }
31
- let(:loader) { Chewy::Search::Loader.new(indexes: [PlacesIndex], **load_options) }
35
+ let(:loader) { Chewy::Search::Loader.new(indexes: [CitiesIndex, CountriesIndex], **load_options) }
32
36
  subject { described_class.new(raw_response, loader) }
33
37
 
34
38
  describe '#hits' do
@@ -59,7 +63,7 @@ describe Chewy::Search::Response, :orm do
59
63
  specify { expect(subject.max_score).to be_nil }
60
64
 
61
65
  context do
62
- let(:request) { Chewy::Search::Request.new(PlacesIndex).query(range: {rating: {lte: 42}}) }
66
+ let(:request) { Chewy::Search::Request.new(CitiesIndex).query(range: {rating: {lte: 42}}) }
63
67
  specify { expect(subject.max_score).to eq(1.0) }
64
68
  end
65
69
  end
@@ -69,10 +73,13 @@ describe Chewy::Search::Response, :orm do
69
73
 
70
74
  context do
71
75
  let(:request) do
72
- Chewy::Search::Request.new(PlacesIndex)
76
+ Chewy::Search::Request.new(CitiesIndex)
73
77
  .query(script: {script: {inline: 'sleep(100); true', lang: 'groovy'}})
74
78
  end
75
- specify { expect(subject.took).to be > 100 }
79
+ specify do
80
+ pending
81
+ expect(subject.took).to be > 100
82
+ end
76
83
  end
77
84
  end
78
85
 
@@ -81,10 +88,13 @@ describe Chewy::Search::Response, :orm do
81
88
 
82
89
  context do
83
90
  let(:request) do
84
- Chewy::Search::Request.new(PlacesIndex)
91
+ Chewy::Search::Request.new(CitiesIndex)
85
92
  .query(script: {script: {inline: 'sleep(100); true', lang: 'groovy'}}).timeout('10ms')
86
93
  end
87
- specify { expect(subject.timed_out?).to eq(true) }
94
+ specify do
95
+ pending
96
+ expect(subject.timed_out?).to eq(true)
97
+ end
88
98
  end
89
99
  end
90
100
 
@@ -93,7 +103,7 @@ describe Chewy::Search::Response, :orm do
93
103
 
94
104
  context do
95
105
  let(:request) do
96
- Chewy::Search::Request.new(PlacesIndex).suggest(
106
+ Chewy::Search::Request.new(CitiesIndex).suggest(
97
107
  my_suggestion: {
98
108
  text: 'city country',
99
109
  term: {
@@ -117,7 +127,7 @@ describe Chewy::Search::Response, :orm do
117
127
  specify { expect(subject.aggs).to eq({}) }
118
128
 
119
129
  context do
120
- let(:request) { Chewy::Search::Request.new(PlacesIndex).aggs(avg_rating: {avg: {field: :rating}}) }
130
+ let(:request) { Chewy::Search::Request.new(CitiesIndex, CountriesIndex).aggs(avg_rating: {avg: {field: :rating}}) }
121
131
  specify { expect(subject.aggs).to eq('avg_rating' => {'value' => 1.5}) }
122
132
  end
123
133
  end
@@ -127,7 +137,7 @@ describe Chewy::Search::Response, :orm do
127
137
  specify { expect(subject.wrappers).to have(4).items }
128
138
  specify do
129
139
  expect(subject.wrappers.map(&:class).uniq)
130
- .to contain_exactly(PlacesIndex::City, PlacesIndex::Country)
140
+ .to contain_exactly(CitiesIndex::City, CountriesIndex::Country)
131
141
  end
132
142
  specify { expect(subject.wrappers.map(&:_data)).to eq(subject.hits) }
133
143
 
@@ -149,14 +159,14 @@ describe Chewy::Search::Response, :orm do
149
159
  context do
150
160
  let(:raw_response) do
151
161
  {'hits' => {'hits' => [
152
- {'_index' => 'places',
162
+ {'_index' => 'cities',
153
163
  '_type' => 'city',
154
164
  '_id' => '1',
155
165
  '_score' => 1.3,
156
166
  '_source' => {'id' => 2, 'rating' => 0}}
157
167
  ]}}
158
168
  end
159
- specify { expect(subject.wrappers.first).to be_a(PlacesIndex::City) }
169
+ specify { expect(subject.wrappers.first).to be_a(CitiesIndex::City) }
160
170
  specify { expect(subject.wrappers.first.id).to eq(2) }
161
171
  specify { expect(subject.wrappers.first.rating).to eq(0) }
162
172
  specify { expect(subject.wrappers.first._score).to eq(1.3) }
@@ -166,14 +176,14 @@ describe Chewy::Search::Response, :orm do
166
176
  context do
167
177
  let(:raw_response) do
168
178
  {'hits' => {'hits' => [
169
- {'_index' => 'places',
179
+ {'_index' => 'countries',
170
180
  '_type' => 'country',
171
181
  '_id' => '2',
172
182
  '_score' => 1.2,
173
183
  '_explanation' => {foo: 'bar'}}
174
184
  ]}}
175
185
  end
176
- specify { expect(subject.wrappers.first).to be_a(PlacesIndex::Country) }
186
+ specify { expect(subject.wrappers.first).to be_a(CountriesIndex::Country) }
177
187
  specify { expect(subject.wrappers.first.id).to eq('2') }
178
188
  specify { expect(subject.wrappers.first.rating).to be_nil }
179
189
  specify { expect(subject.wrappers.first._score).to eq(1.2) }