chewy 5.0.0 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +214 -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/Appraisals +1 -17
  7. data/CHANGELOG.md +312 -356
  8. data/CODE_OF_CONDUCT.md +14 -0
  9. data/CONTRIBUTING.md +63 -0
  10. data/Gemfile +2 -0
  11. data/LICENSE.txt +1 -1
  12. data/README.md +97 -92
  13. data/chewy.gemspec +5 -5
  14. data/gemfiles/rails.5.2.activerecord.gemfile +4 -3
  15. data/gemfiles/{rails.5.0.activerecord.gemfile → rails.5.2.mongoid.6.4.gemfile} +4 -3
  16. data/gemfiles/{rails.5.0.mongoid.6.1.gemfile → rails.6.0.activerecord.gemfile} +4 -3
  17. data/gemfiles/{rails.5.1.activerecord.gemfile → rails.6.1.activerecord.gemfile} +6 -3
  18. data/lib/chewy.rb +1 -1
  19. data/lib/chewy/backports/duplicable.rb +1 -1
  20. data/lib/chewy/config.rb +2 -20
  21. data/lib/chewy/fields/base.rb +1 -7
  22. data/lib/chewy/fields/root.rb +3 -4
  23. data/lib/chewy/index.rb +6 -15
  24. data/lib/chewy/index/actions.rb +12 -4
  25. data/lib/chewy/index/aliases.rb +14 -5
  26. data/lib/chewy/multi_search.rb +62 -0
  27. data/lib/chewy/railtie.rb +3 -19
  28. data/lib/chewy/search.rb +2 -9
  29. data/lib/chewy/search/loader.rb +3 -13
  30. data/lib/chewy/search/pagination/will_paginate.rb +1 -1
  31. data/lib/chewy/search/parameters.rb +24 -6
  32. data/lib/chewy/search/parameters/allow_partial_search_results.rb +27 -0
  33. data/lib/chewy/search/parameters/indices.rb +78 -0
  34. data/lib/chewy/search/parameters/none.rb +1 -3
  35. data/lib/chewy/search/request.rb +92 -85
  36. data/lib/chewy/search/response.rb +1 -1
  37. data/lib/chewy/search/scrolling.rb +8 -7
  38. data/lib/chewy/stash.rb +3 -6
  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 +5 -4
  48. data/lib/chewy/type/import/journal_builder.rb +2 -3
  49. data/lib/chewy/type/import/routine.rb +3 -3
  50. data/lib/chewy/type/mapping.rb +5 -5
  51. data/lib/chewy/type/observe.rb +3 -3
  52. data/lib/chewy/type/syncer.rb +2 -6
  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/lib/tasks/chewy.rake +10 -10
  57. data/migration_guide.md +37 -0
  58. data/spec/chewy/config_spec.rb +1 -22
  59. data/spec/chewy/fields/base_spec.rb +15 -13
  60. data/spec/chewy/fields/root_spec.rb +4 -4
  61. data/spec/chewy/index/actions_spec.rb +120 -33
  62. data/spec/chewy/index/aliases_spec.rb +3 -3
  63. data/spec/chewy/index/specification_spec.rb +13 -13
  64. data/spec/chewy/index_spec.rb +17 -42
  65. data/spec/chewy/journal_spec.rb +25 -21
  66. data/spec/chewy/minitest/search_index_receiver_spec.rb +11 -9
  67. data/spec/chewy/multi_search_spec.rb +85 -0
  68. data/spec/chewy/rake_helper_spec.rb +102 -87
  69. data/spec/chewy/rspec/update_index_spec.rb +47 -46
  70. data/spec/chewy/runtime_spec.rb +2 -2
  71. data/spec/chewy/search/loader_spec.rb +0 -16
  72. data/spec/chewy/search/parameters/indices_spec.rb +105 -0
  73. data/spec/chewy/search/parameters/none_spec.rb +1 -1
  74. data/spec/chewy/search/parameters_spec.rb +21 -4
  75. data/spec/chewy/search/request_spec.rb +94 -78
  76. data/spec/chewy/search/response_spec.rb +27 -17
  77. data/spec/chewy/search/scrolling_spec.rb +22 -18
  78. data/spec/chewy/search_spec.rb +45 -41
  79. data/spec/chewy/stash_spec.rb +14 -12
  80. data/spec/chewy/strategy/active_job_spec.rb +15 -2
  81. data/spec/chewy/strategy/shoryuken_spec.rb +6 -2
  82. data/spec/chewy/strategy/sidekiq_spec.rb +6 -2
  83. data/spec/chewy/type/adapter/active_record_spec.rb +16 -4
  84. data/spec/chewy/type/import/bulk_builder_spec.rb +9 -94
  85. data/spec/chewy/type/import/bulk_request_spec.rb +0 -6
  86. data/spec/chewy/type/import/journal_builder_spec.rb +9 -11
  87. data/spec/chewy/type/import_spec.rb +11 -2
  88. data/spec/chewy/type/mapping_spec.rb +8 -38
  89. data/spec/chewy/type/observe_spec.rb +4 -4
  90. data/spec/chewy/type/witchcraft_spec.rb +15 -0
  91. data/spec/chewy/type/wrapper_spec.rb +3 -1
  92. data/spec/chewy_spec.rb +0 -7
  93. data/spec/spec_helper.rb +4 -8
  94. data/spec/support/active_record.rb +21 -0
  95. metadata +31 -100
  96. data/.travis.yml +0 -45
  97. data/LEGACY_DSL.md +0 -497
  98. data/gemfiles/rails.4.0.activerecord.gemfile +0 -15
  99. data/gemfiles/rails.4.1.activerecord.gemfile +0 -15
  100. data/gemfiles/rails.4.2.activerecord.gemfile +0 -16
  101. data/gemfiles/rails.4.2.mongoid.5.2.gemfile +0 -16
  102. data/gemfiles/rails.5.1.mongoid.6.3.gemfile +0 -16
  103. data/lib/chewy/query.rb +0 -1137
  104. data/lib/chewy/query/compose.rb +0 -68
  105. data/lib/chewy/query/criteria.rb +0 -191
  106. data/lib/chewy/query/filters.rb +0 -244
  107. data/lib/chewy/query/loading.rb +0 -110
  108. data/lib/chewy/query/nodes/and.rb +0 -25
  109. data/lib/chewy/query/nodes/base.rb +0 -17
  110. data/lib/chewy/query/nodes/bool.rb +0 -34
  111. data/lib/chewy/query/nodes/equal.rb +0 -34
  112. data/lib/chewy/query/nodes/exists.rb +0 -20
  113. data/lib/chewy/query/nodes/expr.rb +0 -28
  114. data/lib/chewy/query/nodes/field.rb +0 -110
  115. data/lib/chewy/query/nodes/has_child.rb +0 -15
  116. data/lib/chewy/query/nodes/has_parent.rb +0 -15
  117. data/lib/chewy/query/nodes/has_relation.rb +0 -59
  118. data/lib/chewy/query/nodes/match_all.rb +0 -11
  119. data/lib/chewy/query/nodes/missing.rb +0 -20
  120. data/lib/chewy/query/nodes/not.rb +0 -25
  121. data/lib/chewy/query/nodes/or.rb +0 -25
  122. data/lib/chewy/query/nodes/prefix.rb +0 -19
  123. data/lib/chewy/query/nodes/query.rb +0 -20
  124. data/lib/chewy/query/nodes/range.rb +0 -63
  125. data/lib/chewy/query/nodes/raw.rb +0 -15
  126. data/lib/chewy/query/nodes/regexp.rb +0 -35
  127. data/lib/chewy/query/nodes/script.rb +0 -20
  128. data/lib/chewy/query/pagination.rb +0 -25
  129. data/lib/chewy/search/parameters/types.rb +0 -20
  130. data/spec/chewy/query/criteria_spec.rb +0 -700
  131. data/spec/chewy/query/filters_spec.rb +0 -201
  132. data/spec/chewy/query/loading_spec.rb +0 -124
  133. data/spec/chewy/query/nodes/and_spec.rb +0 -12
  134. data/spec/chewy/query/nodes/bool_spec.rb +0 -14
  135. data/spec/chewy/query/nodes/equal_spec.rb +0 -32
  136. data/spec/chewy/query/nodes/exists_spec.rb +0 -18
  137. data/spec/chewy/query/nodes/has_child_spec.rb +0 -59
  138. data/spec/chewy/query/nodes/has_parent_spec.rb +0 -59
  139. data/spec/chewy/query/nodes/match_all_spec.rb +0 -11
  140. data/spec/chewy/query/nodes/missing_spec.rb +0 -16
  141. data/spec/chewy/query/nodes/not_spec.rb +0 -14
  142. data/spec/chewy/query/nodes/or_spec.rb +0 -12
  143. data/spec/chewy/query/nodes/prefix_spec.rb +0 -16
  144. data/spec/chewy/query/nodes/query_spec.rb +0 -12
  145. data/spec/chewy/query/nodes/range_spec.rb +0 -32
  146. data/spec/chewy/query/nodes/raw_spec.rb +0 -11
  147. data/spec/chewy/query/nodes/regexp_spec.rb +0 -43
  148. data/spec/chewy/query/nodes/script_spec.rb +0 -15
  149. data/spec/chewy/query/pagination/kaminari_spec.rb +0 -5
  150. data/spec/chewy/query/pagination/will_paginate_spec.rb +0 -5
  151. data/spec/chewy/query/pagination_spec.rb +0 -39
  152. data/spec/chewy/query_spec.rb +0 -637
  153. data/spec/chewy/search/parameters/indices_boost_spec.rb +0 -83
  154. data/spec/chewy/search/parameters/types_spec.rb +0 -5
@@ -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,6 @@ 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]),
52
49
  body: {}
53
50
  )
54
51
  end
@@ -57,11 +54,11 @@ describe Chewy::Search::Request do
57
54
  describe '#inspect' do
58
55
  specify do
59
56
  expect(described_class.new(ProductsIndex).inspect)
60
- .to eq('<Chewy::Search::Request {:index=>["products"], :type=>["product", "city", "country"], :body=>{}}>')
57
+ .to eq('<Chewy::Search::Request {:index=>["products"], :body=>{}}>')
61
58
  end
62
59
  specify do
63
60
  expect(ProductsIndex.limit(10).inspect)
64
- .to eq('<ProductsIndex::Query {:index=>["products"], :type=>["product", "city", "country"], :body=>{:size=>10}}>')
61
+ .to eq('<ProductsIndex::Query {:index=>["products"], :body=>{:size=>10}}>')
65
62
  end
66
63
  end
67
64
 
@@ -151,13 +148,20 @@ describe Chewy::Search::Request do
151
148
  end
152
149
 
153
150
  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 }
151
+ specify { expect(subject.request_cache(true).render).to include(request_cache: true) }
152
+ specify { expect(subject.request_cache(true).request_cache(false).render).to include(request_cache: false) }
153
+ specify { expect(subject.request_cache(true).request_cache(nil).render[:request_cache]).to be_blank }
157
154
  specify { expect { subject.request_cache(true) }.not_to change { subject.render } }
158
155
  end
159
156
 
160
- %i[search_type preference timeout].each do |name|
157
+ describe '#search_type' do
158
+ specify { expect(subject.search_type('foo').render).to include(search_type: 'foo') }
159
+ specify { expect(subject.search_type('foo').search_type('bar').render).to include(search_type: 'bar') }
160
+ specify { expect(subject.search_type('foo').search_type(nil).render[:search_type]).to be_blank }
161
+ specify { expect { subject.search_type('foo') }.not_to change { subject.render } }
162
+ end
163
+
164
+ %i[preference timeout].each do |name|
161
165
  describe "##{name}" do
162
166
  specify { expect(subject.send(name, :foo).render[:body]).to include(name => 'foo') }
163
167
  specify { expect(subject.send(name, :foo).send(name, :bar).render[:body]).to include(name => 'bar') }
@@ -218,12 +222,12 @@ describe Chewy::Search::Request do
218
222
  specify { expect { subject.docvalue_fields(:foo) }.not_to change { subject.render } }
219
223
  end
220
224
 
221
- describe '#types' do
222
- 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]) }
225
- specify { expect(subject.types(nil).render[:body]).to be_blank }
226
- specify { expect { subject.types(:product) }.not_to change { subject.render } }
225
+ describe '#indices' do
226
+ specify { expect(described_class.new(:products).render[:index]).to eq(%w[products]) }
227
+ specify { expect(subject.indices(:cities).render[:index]).to eq(%w[cities products]) }
228
+ specify { expect(subject.indices(CitiesIndex, :whatever).render[:index]).to eq(%w[cities products whatever]) }
229
+ specify { expect(subject.indices([CitiesIndex, :products]).render[:index]).to eq(%w[cities products]) }
230
+ specify { expect { subject.indices(:cities) }.not_to change { subject.render } }
227
231
  end
228
232
 
229
233
  describe '#indices_boost' do
@@ -263,14 +267,16 @@ describe Chewy::Search::Request do
263
267
  define_type City do
264
268
  field :rating, type: 'integer'
265
269
  end
270
+ end
266
271
 
272
+ stub_index(:countries) do
267
273
  define_type Country do
268
274
  field :rating, type: 'integer'
269
275
  end
270
276
  end
271
277
  end
272
278
 
273
- before { PlacesIndex.import!(cities: cities, countries: countries) }
279
+ before { PlacesIndex.import!(cities) }
274
280
 
275
281
  let(:cities) { Array.new(2) { |i| City.create!(rating: i) } }
276
282
  let(:countries) { Array.new(2) { |i| Country.create!(rating: i + 2) } }
@@ -278,14 +284,14 @@ describe Chewy::Search::Request do
278
284
  subject { described_class.new(PlacesIndex).order(:rating) }
279
285
 
280
286
  describe '#objects' do
281
- specify { expect(subject.objects).to eq([*cities, *countries]) }
287
+ specify { expect(subject.objects).to eq([*cities]) }
282
288
  specify { expect(subject.objects.class).to eq(Array) }
283
289
  end
284
290
 
285
291
  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]) }
292
+ specify { expect(subject.load(only: 'city')).to eq([*cities]) }
293
+ specify { expect(subject.load(only: 'city').map(&:class).uniq).to eq([PlacesIndex::City]) }
294
+ specify { expect(subject.load(only: 'city').objects).to eq([*cities]) }
289
295
  end
290
296
  end
291
297
 
@@ -372,13 +378,13 @@ describe Chewy::Search::Request do
372
378
  end
373
379
 
374
380
  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! } }
381
+ let(:products_count) { 9 }
382
+ let(:products) { Array.new(products_count) { |i| {id: i.next.to_i, name: "Name#{i.next}", age: 10 * i.next}.stringify_keys! } }
383
+ let(:cities) { Array.new(3) { |i| {id: (i.next + 9).to_i}.stringify_keys! } }
384
+ let(:countries) { Array.new(3) { |i| {id: (i.next + 12).to_i}.stringify_keys! } }
378
385
  before do
379
386
  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) })
387
+ CountriesIndex::Country.import!(countries.map { |h| double(h) })
382
388
  CitiesIndex::City.import!(cities.map { |h| double(h) })
383
389
  end
384
390
 
@@ -391,18 +397,11 @@ describe Chewy::Search::Request do
391
397
  specify { expect(subject.size).to eq(3) }
392
398
  end
393
399
 
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
+ context 'mixed indexes' do
401
+ subject { described_class.new(CitiesIndex, ProductsIndex) }
400
402
 
401
- context 'mixed types' do
402
- subject { described_class.new(CitiesIndex, ProductsIndex::Product) }
403
-
404
- specify { expect(subject.count).to eq(9) }
405
- specify { expect(subject.size).to eq(9) }
403
+ specify { expect(subject.count).to eq(12) }
404
+ specify { expect(subject.size).to eq(10) } # pagination limit
406
405
  end
407
406
 
408
407
  context 'instrumentation' do
@@ -415,9 +414,7 @@ describe Chewy::Search::Request do
415
414
  expect(outer_payload).to eq(
416
415
  index: ProductsIndex,
417
416
  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]
417
+ request: {index: ['products'], body: {query: {match: {name: 'name3'}}}}
421
418
  )
422
419
  end
423
420
  end
@@ -429,10 +426,13 @@ describe Chewy::Search::Request do
429
426
  describe '#highlight' do
430
427
  specify { expect(subject.query(match: {name: 'name3'}).highlight(fields: {name: {}}).first.name).to eq('Name3') }
431
428
  specify { expect(subject.query(match: {name: 'name3'}).highlight(fields: {name: {}}).first.name_highlight).to eq('<em>Name3</em>') }
429
+ specify { expect(subject.query(match: {name: 'name3'}).highlight(fields: {name: {}}).first.name_highlights).to eq(['<em>Name3</em>']) }
432
430
  specify { expect(subject.query(match: {name: 'name3'}).highlight(fields: {name: {}}).first._data['_source']['name']).to eq('Name3') }
433
431
  end
434
432
 
435
433
  describe '#suggest' do
434
+ let(:products_count) { 3 }
435
+
436
436
  specify do
437
437
  expect(subject.suggest(
438
438
  foo: {
@@ -454,7 +454,7 @@ describe Chewy::Search::Request do
454
454
  describe '#aggs' do
455
455
  specify do
456
456
  expect(subject.aggs(avg_age: {avg: {field: :age}}).aggs)
457
- .to eq('avg_age' => {'value' => 20.0})
457
+ .to eq('avg_age' => {'value' => 50.0})
458
458
  end
459
459
  end
460
460
 
@@ -474,8 +474,7 @@ describe Chewy::Search::Request do
474
474
  specify { expect(subject.count).to eq(9) }
475
475
  specify { expect(subject.limit(6).count).to eq(9) }
476
476
  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) }
477
+ specify { expect(subject.indices(:products, CountriesIndex).count).to eq(12) }
479
478
  specify { expect(subject.filter(term: {age: 10}).count).to eq(1) }
480
479
  specify { expect(subject.query(term: {age: 10}).count).to eq(1) }
481
480
  specify { expect(subject.order(nil).count).to eq(9) }
@@ -514,7 +513,7 @@ describe Chewy::Search::Request do
514
513
  context do
515
514
  before { expect(Chewy.client).to receive(:search).once.and_call_original }
516
515
 
517
- specify { expect(subject.first).to be_a(ProductsIndex::Country).and have_attributes(id: 9) }
516
+ specify { expect(subject.first).to be_a(ProductsIndex::Product).and have_attributes(id: 9) }
518
517
  specify { expect(subject.first(3).map(&:id)).to eq([9, 8, 7]) }
519
518
  specify { expect(subject.first(10).map(&:id)).to have(9).items }
520
519
  specify { expect(subject.limit(5).first(10).map(&:id)).to have(9).items }
@@ -527,7 +526,7 @@ describe Chewy::Search::Request do
527
526
  expect(Chewy.client).not_to receive(:search)
528
527
  end
529
528
 
530
- 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) }
531
530
  specify { expect(subject.first(3).map(&:id)).to eq([9, 8, 7]) }
532
531
  specify { expect(subject.first(10).map(&:id)).to have(9).items }
533
532
 
@@ -560,7 +559,7 @@ describe Chewy::Search::Request do
560
559
  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
560
 
562
561
  context 'make sure it returns everything' do
563
- let(:countries) { Array.new(6) { |i| {id: (i.next + 6).to_i}.stringify_keys! } }
562
+ let(:products_count) { 12 }
564
563
  before { expect(Chewy.client).not_to receive(:scroll) }
565
564
 
566
565
  specify { expect(subject.find((1..12).to_a)).to have(12).items }
@@ -577,15 +576,15 @@ describe Chewy::Search::Request do
577
576
 
578
577
  describe '#pluck' do
579
578
  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]]) }
579
+ specify { expect(subject.limit(5).pluck(:_id, :age)).to eq([['1', 10], ['2', 20], ['3', 30], ['4', 40], ['5', 50]]) }
580
+ specify { expect(subject.limit(5).source(:name).pluck(:id, :age)).to eq([[1, 10], [2, 20], [3, 30], [4, 40], [5, 50]]) }
582
581
  specify do
583
- expect(subject.limit(5).pluck(:_index, :_type, :name)).to eq([
584
- %w[products product Name1],
585
- %w[products product Name2],
586
- %w[products product Name3],
587
- ['products', 'city', nil],
588
- ['products', 'city', nil]
582
+ expect(subject.limit(5).pluck(:_index, :name)).to eq([
583
+ %w[products Name1],
584
+ %w[products Name2],
585
+ %w[products Name3],
586
+ %w[products Name4],
587
+ %w[products Name5]
589
588
  ])
590
589
  end
591
590
 
@@ -616,24 +615,12 @@ describe Chewy::Search::Request do
616
615
  Chewy.client.indices.refresh(index: 'products')
617
616
  end.to change { described_class.new(ProductsIndex).total_count }.from(9).to(7)
618
617
  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
618
  specify do
626
619
  expect do
627
620
  subject.delete_all
628
621
  Chewy.client.indices.refresh(index: 'products')
629
622
  end.to change { described_class.new(ProductsIndex).total }.from(9).to(0)
630
623
  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
624
 
638
625
  specify do
639
626
  outer_payload = nil
@@ -644,9 +631,7 @@ describe Chewy::Search::Request do
644
631
  expect(outer_payload).to eq(
645
632
  index: ProductsIndex,
646
633
  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]
634
+ request: {index: ['products'], body: {query: {match: {name: 'name3'}}}, refresh: true}
650
635
  )
651
636
  end
652
637
 
@@ -659,11 +644,42 @@ describe Chewy::Search::Request do
659
644
  expect(outer_payload).to eq(
660
645
  index: ProductsIndex,
661
646
  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]
647
+ request: {index: ['products'], body: {query: {match: {name: 'name3'}}}, refresh: false}
665
648
  )
666
649
  end
667
650
  end
651
+
652
+ describe '#response=' do
653
+ let(:query) { ProductsIndex.limit(0) }
654
+ let(:raw_response) { Chewy.client.search(query.render) }
655
+
656
+ it 'wraps and assigns the raw response' do
657
+ query.response = raw_response
658
+ expect(query.response).to be_a(Chewy::Search::Response)
659
+ end
660
+ end
661
+
662
+ describe '#performed?' do
663
+ let(:query) { ProductsIndex.limit(0) }
664
+ let(:raw_response) { Chewy.client.search(query.render) }
665
+
666
+ it 'is false on a new query' do
667
+ expect(query.performed?).to eq(false)
668
+ end
669
+
670
+ it 'is true after the search request was issued' do
671
+ expect do
672
+ # The `response` method has a side effect of performing unperformed
673
+ # queries.
674
+ query.response
675
+ end.to change(query, :performed?).from(false).to(true)
676
+ end
677
+
678
+ it 'is true after assigning a raw response' do
679
+ expect do
680
+ query.response = raw_response
681
+ end.to change(query, :performed?).from(false).to(true)
682
+ end
683
+ end
668
684
  end
669
685
  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) }