search_flip 2.0.0.beta2 → 2.0.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1028 @@
1
+
2
+ require File.expand_path("../spec_helper", __dir__)
3
+
4
+ RSpec.describe SearchFlip::Criteria do
5
+ describe "delegation" do
6
+ subject { SearchFlip::Criteria.new(target: ProductIndex) }
7
+
8
+ methods = [
9
+ :total_entries, :current_page, :previous_page, :prev_page, :next_page,
10
+ :first_page?, :last_page?, :out_of_range?, :total_pages, :hits, :ids,
11
+ :count, :size, :length, :took, :aggregations, :suggestions, :scope,
12
+ :results, :records, :scroll_id, :raw_response
13
+ ]
14
+
15
+ methods.each do |method|
16
+ it { should delegate(method).to(:response) }
17
+ end
18
+
19
+ it { should delegate(:connection).to(:target) }
20
+ end
21
+
22
+ describe "#merge" do
23
+ it "merges criterias" do
24
+ product1 = create(:product, price: 100, category: "category1")
25
+ product2 = create(:product, price: 200, category: "category2")
26
+ product3 = create(:product, price: 300, category: "category1")
27
+
28
+ ProductIndex.import [product1, product2, product3]
29
+
30
+ query = ProductIndex.where(price: 50..250).aggregate(:category).merge(ProductIndex.where(category: "category1"))
31
+
32
+ expect(query.records).to eq([product1])
33
+ end
34
+
35
+ describe "assignments" do
36
+ methods = [
37
+ :profile_value, :failsafe_value, :terminate_after_value, :timeout_value,
38
+ :offset_value, :limit_value, :scroll_args
39
+ ]
40
+
41
+ methods.each do |method|
42
+ it "replaces the values" do
43
+ criteria1 = SearchFlip::Criteria.new(target: TestIndex)
44
+ criteria1.send("#{method}=", "value1")
45
+
46
+ criteria2 = SearchFlip::Criteria.new(target: TestIndex)
47
+ criteria2.send("#{method}=", "value2")
48
+
49
+ expect(criteria1.merge(criteria2).send(method)).to eq("value2")
50
+ end
51
+ end
52
+ end
53
+
54
+ describe "array concatenations" do
55
+ methods = [
56
+ :source_value, :sort_values, :includes_values, :preload_values, :eager_load_values,
57
+ :search_values, :must_values, :must_not_values, :should_values, :filter_values,
58
+ :post_search_values, :post_must_values, :post_must_not_values, :post_should_values,
59
+ :post_filter_values
60
+ ]
61
+
62
+ methods.each do |method|
63
+ it "concatenates the values for #{method}" do
64
+ criteria1 = SearchFlip::Criteria.new(target: TestIndex)
65
+ criteria1.send("#{method}=", ["value1"])
66
+
67
+ criteria2 = SearchFlip::Criteria.new(target: TestIndex)
68
+ criteria2.send("#{method}=", ["value2"])
69
+
70
+ result = criteria1.merge(criteria2)
71
+
72
+ expect(result.send(method)).to eq(["value1", "value2"])
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "hash merges" do
78
+ methods = [
79
+ :highlight_values, :suggest_values, :custom_value, :aggregation_values
80
+ ]
81
+
82
+ methods.each do |method|
83
+ it "merges the values for #{method}" do
84
+ criteria1 = SearchFlip::Criteria.new(target: TestIndex)
85
+ criteria1.send("#{method}=", key1: "value1")
86
+
87
+ criteria2 = SearchFlip::Criteria.new(target: TestIndex)
88
+ criteria2.send("#{method}=", key2: "value2")
89
+
90
+ result = criteria1.merge(criteria2)
91
+
92
+ expect(result.send(method)).to eq(key1: "value1", key2: "value2")
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ describe "#criteria" do
99
+ it "returns self" do
100
+ criteria = ProductIndex.criteria
101
+
102
+ expect(criteria.criteria.object_id).to eq(criteria.object_id)
103
+ end
104
+ end
105
+
106
+ describe "#timeout" do
107
+ it "sets the query timeout" do
108
+ query = ProductIndex.timeout("1s")
109
+
110
+ expect(query.request[:timeout]).to eq("1s")
111
+ expect { query.execute }.not_to raise_error
112
+ end
113
+ end
114
+
115
+ describe "#terminate_after" do
116
+ it "sets the terminate after value" do
117
+ query = ProductIndex.terminate_after(1)
118
+
119
+ expect(query.request[:terminate_after]).to eq(1)
120
+ expect { query.execute }.not_to raise_error
121
+ end
122
+ end
123
+
124
+ describe "#where" do
125
+ it "sets up the constraints correctly and is chainable" do
126
+ product1 = create(:product, price: 100, category: "category1")
127
+ product2 = create(:product, price: 200, category: "category2")
128
+ product3 = create(:product, price: 300, category: "category1")
129
+
130
+ ProductIndex.import [product1, product2, product3]
131
+
132
+ query1 = ProductIndex.where(price: 100..200)
133
+ query2 = query1.where(category: "category1")
134
+
135
+ expect(query1.records.to_set).to eq([product1, product2].to_set)
136
+ expect(query2.records).to eq([product1])
137
+ end
138
+
139
+ it "works with arrays" do
140
+ expected1 = create(:product, title: "expected1")
141
+ expected2 = create(:product, title: "expected2")
142
+ rejected = create(:product, title: "rejected")
143
+
144
+ ProductIndex.import [expected1, expected2, rejected]
145
+
146
+ query = ProductIndex.where(title: ["expected1", "expected2"])
147
+
148
+ expect(query.records.to_set).to eq([expected1, expected2].to_set)
149
+ end
150
+
151
+ it "works with ranges" do
152
+ expected1 = create(:product, price: 100)
153
+ expected2 = create(:product, price: 200)
154
+ rejected = create(:product, price: 300)
155
+
156
+ ProductIndex.import [expected1, expected2, rejected]
157
+
158
+ query = ProductIndex.where(price: 100..200)
159
+
160
+ expect(query.records.to_set).to eq([expected1, expected2].to_set)
161
+ end
162
+
163
+ it "works with nils" do
164
+ expected = create(:product, price: nil)
165
+ rejected = create(:product, price: 100)
166
+
167
+ ProductIndex.import [expected, rejected]
168
+
169
+ query = ProductIndex.where(price: nil)
170
+
171
+ expect(query.records).to eq([expected])
172
+ end
173
+ end
174
+
175
+ describe "#where_not" do
176
+ it "sets up the constraints correctly and is chainable" do
177
+ product1 = create(:product, price: 100, category: "category1")
178
+ product2 = create(:product, price: 200, category: "category2")
179
+ product3 = create(:product, price: 300, category: "category1")
180
+
181
+ ProductIndex.import [product1, product2, product3]
182
+
183
+ query1 = ProductIndex.where_not(price: 250..350)
184
+ query2 = query1.where_not(category: "category2")
185
+
186
+ expect(query1.records.to_set).to eq([product1, product2].to_set)
187
+ expect(query2.records).to eq([product1])
188
+ end
189
+
190
+ it "works with arrays" do
191
+ expected = create(:product, title: "expected")
192
+ rejected1 = create(:product, title: "rejected1")
193
+ rejected2 = create(:product, title: "rejected2")
194
+
195
+ ProductIndex.import [expected, rejected1, rejected2]
196
+
197
+ query = ProductIndex.where_not(title: ["rejected1", "rejected2"])
198
+
199
+ expect(query.records).to eq([expected])
200
+ end
201
+
202
+ it "works with ranges" do
203
+ expected = create(:product, price: 100)
204
+ rejected1 = create(:product, price: 200)
205
+ rejected2 = create(:product, price: 300)
206
+
207
+ ProductIndex.import [expected, rejected1, rejected2]
208
+
209
+ query = ProductIndex.where_not(price: 200..300)
210
+
211
+ expect(query.records).to eq([expected])
212
+ end
213
+
214
+ it "works with nils" do
215
+ expected = create(:product, price: 100)
216
+ rejected = create(:product, price: nil)
217
+
218
+ ProductIndex.import [expected, rejected]
219
+
220
+ query = ProductIndex.where_not(price: nil)
221
+
222
+ expect(query.records).to eq([expected])
223
+ end
224
+ end
225
+
226
+ describe "#with_settings" do
227
+ it "sets the target to the new anonymous class" do
228
+ query = ProductIndex.where(id: 1).with_settings(index_name: "new_user_index")
229
+
230
+ expect(query.target.name).to be_nil
231
+ expect(query.target.index_name).to eq("new_user_index")
232
+ end
233
+
234
+ it "keeps the constraints" do
235
+ expected = create(:product)
236
+ rejected = create(:product)
237
+
238
+ ProductIndex.import [expected, rejected]
239
+
240
+ query = ProductIndex.where(id: expected.id).with_settings(index_name: ProductIndex.index_name)
241
+
242
+ expect(query.records).to eq([expected])
243
+ end
244
+ end
245
+
246
+ describe "#filter" do
247
+ it "sets up the constraints correctly and is chainable" do
248
+ product1 = create(:product, price: 100, category: "category1")
249
+ product2 = create(:product, price: 200, category: "category2")
250
+ product3 = create(:product, price: 300, category: "category1")
251
+
252
+ ProductIndex.import [product1, product2, product3]
253
+
254
+ query1 = ProductIndex.filter(range: { price: { gte: 100, lte: 200 } })
255
+ query2 = query1.filter(term: { category: "category1" })
256
+
257
+ expect(query1.records.to_set).to eq([product1, product2].to_set)
258
+ expect(query2.records).to eq([product1])
259
+ end
260
+ end
261
+
262
+ describe "#range" do
263
+ it "sets up the constraints correctly and is chainable" do
264
+ product1 = create(:product, price: 100)
265
+ product2 = create(:product, price: 200)
266
+ product3 = create(:product, price: 300)
267
+
268
+ ProductIndex.import [product1, product2, product3]
269
+
270
+ query1 = ProductIndex.range(:price, gte: 100, lte: 200)
271
+ query2 = query1.range(:price, gte: 200, lte: 300)
272
+
273
+ expect(query1.records.to_set).to eq([product1, product2].to_set)
274
+ expect(query2.records).to eq([product2])
275
+ end
276
+ end
277
+
278
+ describe "#match_all" do
279
+ it "matches all documents" do
280
+ expected1 = create(:product)
281
+ expected2 = create(:product)
282
+
283
+ ProductIndex.import [expected1, expected2]
284
+
285
+ query = ProductIndex.match_all
286
+
287
+ expect(query.records.to_set).to eq([expected1, expected2].to_set)
288
+ end
289
+ end
290
+
291
+ describe "#exists" do
292
+ it "sets up the constraints correctly and is chainable" do
293
+ product1 = create(:product, title: "title1", description: "description1")
294
+ product2 = create(:product, title: "title2", description: nil)
295
+ product3 = create(:product, title: nil, description: "description2")
296
+
297
+ ProductIndex.import [product1, product2, product3]
298
+
299
+ query1 = ProductIndex.exists(:title)
300
+ query2 = query1.exists(:description)
301
+
302
+ expect(query1.records.to_set).to eq([product1, product2].to_set)
303
+ expect(query2.records).to eq([product1])
304
+ end
305
+ end
306
+
307
+ describe "#exists_not" do
308
+ it "sets up the constraints correctly and is chainable" do
309
+ product1 = create(:product, title: nil, description: nil)
310
+ product2 = create(:product, title: nil, description: "description2")
311
+ product3 = create(:product, title: "title3", description: "description3")
312
+
313
+ ProductIndex.import [product1, product2, product3]
314
+
315
+ query1 = ProductIndex.exists_not(:title)
316
+ query2 = query1.exists_not(:description)
317
+
318
+ expect(query1.records.to_set).to eq([product1, product2].to_set)
319
+ expect(query2.records).to eq([product1])
320
+ end
321
+ end
322
+
323
+ describe "#post_search" do
324
+ it "sets up the constraints correctly and is chainable" do
325
+ if ProductIndex.connection.version.to_i >= 2
326
+ product1 = create(:product, title: "title1", category: "category1")
327
+ product2 = create(:product, title: "title2", category: "category2")
328
+ product3 = create(:product, title: "title3", category: "category1")
329
+
330
+ ProductIndex.import [product1, product2, product3]
331
+
332
+ query1 = ProductIndex.aggregate(:category).post_search("title1 OR title2")
333
+ query2 = query1.post_search("category1")
334
+
335
+ expect(query1.records.to_set).to eq([product1, product2].to_set)
336
+ expect(query2.records).to eq([product1])
337
+
338
+ aggregations1 = query1.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
339
+ expect(aggregations1).to eq("category1" => 2, "category2" => 1)
340
+
341
+ aggregations2 = query2.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
342
+ expect(aggregations2).to eq("category1" => 2, "category2" => 1)
343
+ end
344
+ end
345
+ end
346
+
347
+ describe "#post_where" do
348
+ it "sets up the constraints correctly and is chainable" do
349
+ product1 = create(:product, price: 100, category: "category1")
350
+ product2 = create(:product, price: 200, category: "category2")
351
+ product3 = create(:product, price: 300, category: "category1")
352
+
353
+ ProductIndex.import [product1, product2, product3]
354
+
355
+ query1 = ProductIndex.aggregate(:category).post_where(price: 100..200)
356
+ query2 = query1.post_where(category: "category1")
357
+
358
+ expect(query1.records.to_set).to eq([product1, product2].to_set)
359
+ expect(query2.records).to eq([product1])
360
+
361
+ aggregations1 = query1.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
362
+ expect(aggregations1).to eq("category1" => 2, "category2" => 1)
363
+
364
+ aggregations2 = query2.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
365
+ expect(aggregations2).to eq("category1" => 2, "category2" => 1)
366
+ end
367
+
368
+ it "works with arrays" do
369
+ expected1 = create(:product, title: "expected1", category: "category1")
370
+ expected2 = create(:product, title: "expected2", category: "category2")
371
+ rejected = create(:product, title: "rejected", category: "category1")
372
+
373
+ ProductIndex.import [expected1, expected2, rejected]
374
+
375
+ query = ProductIndex.aggregate(:category).post_where(title: ["expected1", "expected2"])
376
+
377
+ expect(query.records.to_set).to eq([expected1, expected2].to_set)
378
+
379
+ aggregations = query.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
380
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
381
+ end
382
+
383
+ it "works with ranges" do
384
+ expected1 = create(:product, price: 100, category: "category1")
385
+ expected2 = create(:product, price: 200, category: "category2")
386
+ rejected = create(:product, price: 300, category: "category1")
387
+
388
+ ProductIndex.import [expected1, expected2, rejected]
389
+
390
+ query = ProductIndex.aggregate(:category).post_where(price: 100..200)
391
+
392
+ expect(query.records.to_set).to eq([expected1, expected2].to_set)
393
+
394
+ aggregations = query.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
395
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
396
+ end
397
+ end
398
+
399
+ describe "#post_where_not" do
400
+ it "sets up the constraints correctly and is chainable" do
401
+ product1 = create(:product, price: 100, category: "category1")
402
+ product2 = create(:product, price: 200, category: "category2")
403
+ product3 = create(:product, price: 300, category: "category1")
404
+
405
+ ProductIndex.import [product1, product2, product3]
406
+
407
+ query1 = ProductIndex.aggregate(:category).post_where_not(price: 250..350)
408
+ query2 = query1.post_where_not(category: "category2")
409
+
410
+ expect(query1.records.to_set).to eq([product1, product2].to_set)
411
+ expect(query2.records).to eq([product1])
412
+
413
+ aggregations = query1.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
414
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
415
+
416
+ aggregations = query2.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
417
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
418
+ end
419
+
420
+ it "works with arrays" do
421
+ expected = create(:product, title: "expected", category: "category1")
422
+ rejected1 = create(:product, title: "rejected1", category: "category2")
423
+ rejected2 = create(:product, title: "rejected2", category: "category1")
424
+
425
+ ProductIndex.import [expected, rejected1, rejected2]
426
+
427
+ query = ProductIndex.aggregate(:category).post_where_not(title: ["rejected1", "rejected2"])
428
+
429
+ expect(query.records).to eq([expected])
430
+
431
+ aggregations = query.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
432
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
433
+ end
434
+
435
+ it "works with ranges" do
436
+ expected = create(:product, price: 100, category: "category1")
437
+ rejected1 = create(:product, price: 200, category: "category2")
438
+ rejected2 = create(:product, price: 300, category: "category1")
439
+
440
+ ProductIndex.import [expected, rejected1, rejected2]
441
+
442
+ query = ProductIndex.aggregate(:category).post_where_not(price: 200..300)
443
+
444
+ expect(query.records).to eq([expected])
445
+
446
+ aggregations = query.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
447
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
448
+ end
449
+ end
450
+
451
+ describe "#post_filter" do
452
+ it "sets up the constraints correctly and is chainable" do
453
+ product1 = create(:product, price: 100, category: "category1")
454
+ product2 = create(:product, price: 200, category: "category2")
455
+ product3 = create(:product, price: 300, category: "category1")
456
+
457
+ ProductIndex.import [product1, product2, product3]
458
+
459
+ query1 = ProductIndex.aggregate(:category).post_filter(range: { price: { gte: 100, lte: 200 } })
460
+ query2 = query1.post_filter(term: { category: "category1" })
461
+
462
+ expect(query1.records.to_set).to eq([product1, product2].to_set)
463
+ expect(query2.records).to eq([product1])
464
+
465
+ aggregations = query1.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
466
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
467
+
468
+ aggregations = query2.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
469
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
470
+ end
471
+ end
472
+
473
+ describe "#post_range" do
474
+ it "sets up the constraints correctly and is chainable" do
475
+ product1 = create(:product, price: 100, category: "category1")
476
+ product2 = create(:product, price: 200, category: "category2")
477
+ product3 = create(:product, price: 300, category: "category1")
478
+
479
+ ProductIndex.import [product1, product2, product3]
480
+
481
+ query1 = ProductIndex.aggregate(:category).post_range(:price, gte: 100, lte: 200)
482
+ query2 = query1.post_range(:price, gte: 200, lte: 300)
483
+
484
+ expect(query1.records.to_set).to eq([product1, product2].to_set)
485
+ expect(query2.records).to eq([product2])
486
+
487
+ aggregations = query1.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
488
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
489
+
490
+ aggregations = query2.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
491
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
492
+ end
493
+ end
494
+
495
+ describe "#post_exists" do
496
+ it "sets up the constraints correctly and is chainable" do
497
+ product1 = create(:product, title: "title1", description: "description1", category: "category1")
498
+ product2 = create(:product, title: "title2", description: nil, category: "category2")
499
+ product3 = create(:product, title: nil, description: "description2", category: "category1")
500
+
501
+ ProductIndex.import [product1, product2, product3]
502
+
503
+ query1 = ProductIndex.aggregate(:category).post_exists(:title)
504
+ query2 = query1.post_exists(:description)
505
+
506
+ expect(query1.records.to_set).to eq([product1, product2].to_set)
507
+ expect(query2.records).to eq([product1])
508
+
509
+ aggregations = query1.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
510
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
511
+
512
+ aggregations = query2.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
513
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
514
+ end
515
+ end
516
+
517
+ describe "#post_exists" do
518
+ it "sets up the constraints correctly and is chainable" do
519
+ product1 = create(:product, title: "title1", description: "description1", category: "category1")
520
+ product2 = create(:product, title: "title2", description: nil, category: "category2")
521
+ product3 = create(:product, title: nil, description: "description2", category: "category1")
522
+
523
+ ProductIndex.import [product1, product2, product3]
524
+
525
+ query1 = ProductIndex.aggregate(:category).post_exists(:title)
526
+ query2 = query1.post_exists(:description)
527
+
528
+ expect(query1.records.to_set).to eq([product1, product2].to_set)
529
+ expect(query2.records).to eq([product1])
530
+
531
+ aggregations = query1.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
532
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
533
+
534
+ aggregations = query2.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
535
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
536
+ end
537
+ end
538
+
539
+ describe "#post_exists" do
540
+ it "sets up the constraints correctly and is chainable" do
541
+ product1 = create(:product, title: "title1", description: "description1", category: "category1")
542
+ product2 = create(:product, title: "title2", description: nil, category: "category2")
543
+ product3 = create(:product, title: nil, description: "description2", category: "category1")
544
+
545
+ ProductIndex.import [product1, product2, product3]
546
+
547
+ query1 = ProductIndex.aggregate(:category).post_exists(:title)
548
+ query2 = query1.post_exists(:description)
549
+
550
+ expect(query1.records.to_set).to eq([product1, product2].to_set)
551
+ expect(query2.records).to eq([product1])
552
+
553
+ aggregations = query1.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
554
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
555
+
556
+ aggregations = query2.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
557
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
558
+ end
559
+ end
560
+
561
+ describe "#post_exists_not" do
562
+ it "sets up the constraints correctly and is chainable" do
563
+ product1 = create(:product, title: nil, description: nil, category: "category1")
564
+ product2 = create(:product, title: nil, description: "description2", category: "category2")
565
+ product3 = create(:product, title: "title3", description: "description3", category: "category1")
566
+
567
+ ProductIndex.import [product1, product2, product3]
568
+
569
+ query1 = ProductIndex.aggregate(:category).post_exists_not(:title)
570
+ query2 = query1.post_exists_not(:description)
571
+
572
+ expect(query1.records.to_set).to eq([product1, product2].to_set)
573
+ expect(query2.records).to eq([product1])
574
+
575
+ aggregations = query1.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
576
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
577
+
578
+ aggregations = query2.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
579
+ expect(aggregations).to eq("category1" => 2, "category2" => 1)
580
+ end
581
+ end
582
+
583
+ describe "#aggregate" do
584
+ it "sets up the constraints correctly and is chainable" do
585
+ ProductIndex.import create_list(:product, 3, category: "category1", price: 10)
586
+ ProductIndex.import create_list(:product, 2, category: "category2", price: 20)
587
+ ProductIndex.import create_list(:product, 1, category: "category3", price: 30)
588
+
589
+ query = ProductIndex.aggregate(:category, size: 2).aggregate(price_sum: { sum: { field: "price" } })
590
+
591
+ category_aggregations = query.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
592
+ price_aggregation = query.aggregations(:price_sum).value
593
+
594
+ expect(category_aggregations).to eq("category1" => 3, "category2" => 2)
595
+ expect(price_aggregation).to eq(100)
596
+ end
597
+
598
+ it "works with hashes" do
599
+ ProductIndex.import create_list(:product, 3, category: "category1")
600
+ ProductIndex.import create_list(:product, 2, category: "category2")
601
+ ProductIndex.import create_list(:product, 1, category: "category3")
602
+
603
+ query = ProductIndex.aggregate(category: { terms: { field: :category } })
604
+ aggregations = query.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
605
+
606
+ expect(aggregations).to eq("category1" => 3, "category2" => 2, "category3" => 1)
607
+ end
608
+
609
+ it "allows sub-aggregations" do
610
+ ProductIndex.import create_list(:product, 3, category: "category1", price: 15)
611
+ ProductIndex.import create_list(:product, 2, category: "category2", price: 20)
612
+ ProductIndex.import create_list(:product, 1, category: "category3", price: 25)
613
+
614
+ query = ProductIndex.aggregate(:category) do |aggregation|
615
+ aggregation.aggregate(price_sum: { sum: { field: "price" } })
616
+ end
617
+
618
+ category_aggregations = query.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
619
+ price_sum_aggregations = query.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.price_sum.value }
620
+
621
+ expect(category_aggregations).to eq("category1" => 3, "category2" => 2, "category3" => 1)
622
+ expect(price_sum_aggregations).to eq("category1" => 45, "category2" => 40, "category3" => 25)
623
+ end
624
+ end
625
+
626
+ describe "#profile" do
627
+ it "sets up the constraints correctly" do
628
+ if ProductIndex.connection.version.to_i >= 2
629
+ expect(ProductIndex.profile(true).raw_response["profile"]).not_to be_nil
630
+ end
631
+ end
632
+ end
633
+
634
+ describe "#scroll" do
635
+ it "scrolls over the full result set" do
636
+ products = create_list(:product, 15)
637
+
638
+ ProductIndex.import products
639
+
640
+ criteria = ProductIndex.limit(10).scroll(timeout: "1m")
641
+
642
+ result = []
643
+ iterations = 0
644
+
645
+ until criteria.records.empty?
646
+ result += criteria.records
647
+ iterations += 1
648
+
649
+ criteria = criteria.scroll(id: criteria.scroll_id, timeout: "1m")
650
+ end
651
+
652
+ expect(products.to_set).to eq(result.to_set)
653
+ expect(iterations).to eq(2)
654
+ end
655
+ end
656
+
657
+ describe "#delete" do
658
+ it "delets the matching documents" do
659
+ product1, product2, product3 = create_list(:product, 3)
660
+
661
+ ProductIndex.import [product1, product2, product3]
662
+
663
+ expect { ProductIndex.where(id: [product1.id, product2.id]).delete }.to change { ProductIndex.total_count }.by(-2)
664
+ end
665
+ end
666
+
667
+ describe "#source" do
668
+ it "constraints the returned source fields" do
669
+ product = create(:product, title: "Title", price: 10)
670
+
671
+ ProductIndex.import product
672
+
673
+ results = ProductIndex.where(id: product.id).results
674
+
675
+ expect(results.first.id).not_to be_nil
676
+ expect(results.first.title).not_to be_nil
677
+ expect(results.first.price).not_to be_nil
678
+
679
+ results = ProductIndex.where(id: product.id).source([:id, :price]).results
680
+
681
+ expect(results.first.id).not_to be_nil
682
+ expect(results.first.title).to be_nil
683
+ expect(results.first.price).not_to be_nil
684
+ end
685
+ end
686
+
687
+ describe "#includes" do
688
+ it "does not raise any errors" do
689
+ user = create(:user)
690
+ comments = create_list(:comment, 2)
691
+ product = create(:product, user: user, comments: comments)
692
+
693
+ ProductIndex.import product
694
+
695
+ record = ProductIndex.includes(:user).includes(:comments).records.first
696
+
697
+ expect(record).not_to be_nil
698
+ expect(record.user).to eq(user)
699
+ expect(record.comments.to_set).to eq(comments.to_set)
700
+ end
701
+ end
702
+
703
+ describe "#eager_load" do
704
+ it "does not raise any errors" do
705
+ user = create(:user)
706
+ comments = create_list(:comment, 2)
707
+ product = create(:product, user: user, comments: comments)
708
+
709
+ ProductIndex.import product
710
+
711
+ record = ProductIndex.eager_load(:user).eager_load(:comments).records.first
712
+
713
+ expect(record).not_to be_nil
714
+ expect(record.user).to eq(user)
715
+ expect(record.comments.to_set).to eq(comments.to_set)
716
+ end
717
+ end
718
+
719
+ describe "#preload" do
720
+ it "does not raise any errors" do
721
+ user = create(:user)
722
+ comments = create_list(:comment, 2)
723
+ product = create(:product, user: user, comments: comments)
724
+
725
+ ProductIndex.import product
726
+
727
+ record = ProductIndex.preload(:user).preload(:comments).records.first
728
+
729
+ expect(record).not_to be_nil
730
+ expect(record.user).to eq(user)
731
+ expect(record.comments.to_set).to eq(comments.to_set)
732
+ end
733
+ end
734
+
735
+ describe "#sort" do
736
+ it "sorts correctly and is chainable" do
737
+ product1 = create(:product, rank: 2, price: 100)
738
+ product2 = create(:product, rank: 2, price: 90)
739
+ product3 = create(:product, rank: 1, price: 120)
740
+ product4 = create(:product, rank: 0, price: 110)
741
+
742
+ ProductIndex.import [product1, product2, product3, product4]
743
+
744
+ expect(ProductIndex.sort({ rank: :desc }, { price: :asc }).records).to eq([product2, product1, product3, product4])
745
+ expect(ProductIndex.sort(rank: :desc).sort(:price).records).to eq([product2, product1, product3, product4])
746
+ expect(ProductIndex.sort(:price).sort(rank: :desc).records).to eq([product2, product1, product4, product3])
747
+ end
748
+ end
749
+
750
+ describe "#resort" do
751
+ it "overwrites existing sort criterias" do
752
+ product1 = create(:product, rank: 2, price: 100)
753
+ product2 = create(:product, rank: 2, price: 90)
754
+ product3 = create(:product, rank: 1, price: 120)
755
+ product4 = create(:product, rank: 0, price: 110)
756
+
757
+ ProductIndex.import [product1, product2, product3, product4]
758
+
759
+ expect(ProductIndex.sort(:price).resort({ rank: :desc }, { price: :asc }).records).to eq([product2, product1, product3, product4])
760
+ expect(ProductIndex.sort(rank: :desc).resort(:price).records).to eq([product2, product1, product4, product3])
761
+ end
762
+ end
763
+
764
+ describe "#offset" do
765
+ it "sets the query document offset" do
766
+ product1 = create(:product, rank: 1)
767
+ product2 = create(:product, rank: 2)
768
+ product3 = create(:product, rank: 3)
769
+
770
+ ProductIndex.import [product1, product2, product3]
771
+
772
+ query = ProductIndex.sort(:rank).offset(1)
773
+
774
+ expect(query.records).to eq([product2, product3])
775
+ expect(query.offset(2).records).to eq([product3])
776
+ end
777
+ end
778
+
779
+ describe "#limit" do
780
+ it "sets the query document limit" do
781
+ product1 = create(:product, rank: 1)
782
+ product2 = create(:product, rank: 2)
783
+ product3 = create(:product, rank: 3)
784
+
785
+ ProductIndex.import [product1, product2, product3]
786
+
787
+ query = ProductIndex.sort(:rank).limit(1)
788
+
789
+ expect(query.records).to eq([product1])
790
+ expect(query.limit(2).records).to eq([product1, product2])
791
+ end
792
+ end
793
+
794
+ describe "#paginate" do
795
+ it "sets the query document offset and limit" do
796
+ product1 = create(:product, rank: 1)
797
+ product2 = create(:product, rank: 2)
798
+ product3 = create(:product, rank: 3)
799
+
800
+ ProductIndex.import [product1, product2, product3]
801
+
802
+ query = ProductIndex.sort(:rank).paginate(page: 1, per_page: 2)
803
+
804
+ expect(query.records).to eq([product1, product2])
805
+ expect(query.paginate(page: 2, per_page: 2).records).to eq([product3])
806
+ end
807
+ end
808
+
809
+ describe "#page" do
810
+ it "sets the query document offset" do
811
+ expect(ProductIndex.page(1).offset_value).to eq(0)
812
+ expect(ProductIndex.page(2).offset_value).to eq(30)
813
+ expect(ProductIndex.page(3).per(50).offset_value).to eq(100)
814
+ end
815
+ end
816
+
817
+ describe "#limit" do
818
+ it "sets the query document limit" do
819
+ expect(ProductIndex.per(50).limit_value).to eq(50)
820
+ end
821
+ end
822
+
823
+ describe "#search" do
824
+ it "sets up the constraints correctly" do
825
+ product1 = create(:product, title: "Title1", description: "Description1", price: 10)
826
+ product2 = create(:product, title: "Title2", description: "Description2", price: 20)
827
+ product3 = create(:product, title: "Title3", description: "Description2", price: 30)
828
+
829
+ ProductIndex.import [product1, product2, product3]
830
+
831
+ expect(ProductIndex.search("Title1 OR Title3").records.to_set).to eq([product1, product3].to_set)
832
+ expect(ProductIndex.search("Title1 Title3", default_operator: :OR).records.to_set).to eq([product1, product3].to_set)
833
+ expect(ProductIndex.search("Title1 OR Title2").search("Title1 OR Title3").records).to eq([product1])
834
+ expect(ProductIndex.search("Title1 OR Title3").where(price: 5..15).records).to eq([product1])
835
+ end
836
+ end
837
+
838
+ describe "#unscope" do
839
+ it "removes the specified constraints and is chainable" do
840
+ product1 = create(:product, title: "Title1", description: "Description1", price: 10)
841
+ product2 = create(:product, title: "Title2", description: "Description2", price: 20)
842
+ product3 = create(:product, title: "Title3", description: "Description2", price: 30)
843
+
844
+ ProductIndex.import [product1, product2, product3]
845
+
846
+ expect(ProductIndex.search("Title1 OR Title2").search("Title1 OR Title3").records).to eq([product1])
847
+ expect(ProductIndex.search("Title1 OR Title2").unscope(:search).search("Title1 OR Title3").records.to_set).to eq([product1, product3].to_set)
848
+ end
849
+ end
850
+
851
+ describe "#highlight" do
852
+ it "adds highlighting to the query and is chainable" do
853
+ product1 = create(:product, title: "Title1 highlight", description: "Description1 highlight")
854
+ product2 = create(:product, title: "Title2 highlight", description: "Description2 highlight")
855
+
856
+ ProductIndex.import [product1, product2]
857
+
858
+ results = ProductIndex.sort(:id).highlight([:title, :description]).search("title:highlight description:highlight").results
859
+
860
+ expect(results[0]._hit.highlight.title).to eq(["Title1 <em>highlight</em>"])
861
+ expect(results[0]._hit.highlight.description).to eq(["Description1 <em>highlight</em>"])
862
+
863
+ expect(results[1]._hit.highlight.title).to eq(["Title2 <em>highlight</em>"])
864
+ expect(results[1]._hit.highlight.description).to eq(["Description2 <em>highlight</em>"])
865
+
866
+ results = ProductIndex.sort(:id).highlight([:title, :description], require_field_match: false).search("highlight").results
867
+
868
+ expect(results[0]._hit.highlight.title).to eq(["Title1 <em>highlight</em>"])
869
+ expect(results[0]._hit.highlight.description).to eq(["Description1 <em>highlight</em>"])
870
+
871
+ expect(results[1]._hit.highlight.title).to eq(["Title2 <em>highlight</em>"])
872
+ expect(results[1]._hit.highlight.description).to eq(["Description2 <em>highlight</em>"])
873
+
874
+ query = ProductIndex.sort(:id).search("title:highlight")
875
+ query = query.highlight(:title, require_field_match: true).highlight(:description, require_field_match: true)
876
+
877
+ results = query.results
878
+
879
+ expect(results[0]._hit.highlight.title).to eq(["Title1 <em>highlight</em>"])
880
+ expect(results[0]._hit.highlight.description).to be_nil
881
+
882
+ expect(results[1]._hit.highlight.title).to eq(["Title2 <em>highlight</em>"])
883
+ expect(results[1]._hit.highlight.description).to be_nil
884
+ end
885
+ end
886
+
887
+ describe "#suggest" do
888
+ it "adds suggest to the query" do
889
+ product = create(:product, title: "Title", description: "Description")
890
+
891
+ ProductIndex.import product
892
+
893
+ suggestions = ProductIndex.suggest(:suggestion, text: "Desciption", term: { field: "description" }).suggestions(:suggestion)
894
+
895
+ expect(suggestions.first["text"]).to eq("description")
896
+ end
897
+ end
898
+
899
+ describe "#find_in_batches" do
900
+ it "iterates the records in batches of the specified size" do
901
+ expected1 = create(:product, title: "expected", rank: 1)
902
+ expected2 = create(:product, title: "expected", rank: 2)
903
+ expected3 = create(:product, title: "expected", rank: 3)
904
+ rejected = create(:product, title: "rejected")
905
+
906
+ create :product, title: "rejected"
907
+
908
+ ProductIndex.import [expected1, expected2, expected3, rejected]
909
+
910
+ actual = ProductIndex.where(title: "expected").sort(:rank).find_in_batches(batch_size: 2).to_a
911
+
912
+ expect(actual).to eq([[expected1, expected2], [expected3]])
913
+ end
914
+ end
915
+
916
+ describe "#find_results_in_batches" do
917
+ it "iterates the results in batches of the specified size" do
918
+ expected1 = create(:product, title: "expected", rank: 1)
919
+ expected2 = create(:product, title: "expected", rank: 2)
920
+ expected3 = create(:product, title: "expected", rank: 3)
921
+ rejected = create(:product, title: "rejected")
922
+
923
+ create :product, title: "rejected"
924
+
925
+ ProductIndex.import [expected1, expected2, expected3, rejected]
926
+
927
+ actual = ProductIndex.where(title: "expected").sort(:rank).find_results_in_batches(batch_size: 2).map { |batch| batch.map(&:id) }
928
+
929
+ expect(actual).to eq([[expected1.id, expected2.id], [expected3.id]])
930
+ end
931
+ end
932
+
933
+ describe "#find_each" do
934
+ it "iterates the records" do
935
+ expected1 = create(:product, title: "expected", rank: 1)
936
+ expected2 = create(:product, title: "expected", rank: 2)
937
+ expected3 = create(:product, title: "expected", rank: 3)
938
+ rejected = create(:product, title: "rejected")
939
+
940
+ create :product, title: "rejected"
941
+
942
+ ProductIndex.import [expected1, expected2, expected3, rejected]
943
+
944
+ actual = ProductIndex.where(title: "expected").sort(:rank).find_each(batch_size: 2).to_a
945
+
946
+ expect(actual).to eq([expected1, expected2, expected3])
947
+ end
948
+ end
949
+
950
+ describe "#find_each_result" do
951
+ it "iterates the results" do
952
+ expected1 = create(:product, title: "expected", rank: 1)
953
+ expected2 = create(:product, title: "expected", rank: 2)
954
+ expected3 = create(:product, title: "expected", rank: 3)
955
+ rejected = create(:product, title: "rejected")
956
+
957
+ create :product, title: "rejected"
958
+
959
+ ProductIndex.import [expected1, expected2, expected3, rejected]
960
+
961
+ actual = ProductIndex.where(title: "expected").sort(:rank).find_each_result(batch_size: 2).map(&:id)
962
+
963
+ expect(actual).to eq([expected1.id, expected2.id, expected3.id])
964
+ end
965
+ end
966
+
967
+ describe "#failsafe" do
968
+ it "prevents query syntax exceptions" do
969
+ expect { ProductIndex.search("syntax/error").records }.to raise_error(SearchFlip::ResponseError)
970
+
971
+ query = ProductIndex.failsafe(true).search("syntax/error")
972
+
973
+ expect(query.records).to eq([])
974
+ expect(query.total_entries).to eq(0)
975
+ end
976
+ end
977
+
978
+ describe "#fresh" do
979
+ it "returns a new criteria without a cached response" do
980
+ create :product
981
+
982
+ query = ProductIndex.criteria.tap(&:records)
983
+
984
+ expect(query.instance_variable_get(:@response)).not_to be_nil
985
+
986
+ expect(query.object_id).not_to eq(query.fresh.object_id)
987
+ expect(query.fresh.instance_variable_get(:@response)).to be_nil
988
+ end
989
+ end
990
+
991
+ describe "#respond_to?" do
992
+ it "checks whether or not the index class responds to the method" do
993
+ temp_index = Class.new(ProductIndex)
994
+
995
+ expect(temp_index.criteria.respond_to?(:test_scope)).to eq(false)
996
+
997
+ temp_index.scope(:test_scope) { match_all }
998
+
999
+ expect(temp_index.criteria.respond_to?(:test_scope)).to eq(true)
1000
+ end
1001
+ end
1002
+
1003
+ describe "#method_missing" do
1004
+ it "delegates to the index class" do
1005
+ temp_index = Class.new(ProductIndex)
1006
+
1007
+ expected = create(:product, title: "expected")
1008
+ rejected = create(:product, title: "rejected")
1009
+
1010
+ temp_index.import [expected, rejected]
1011
+
1012
+ temp_index.scope(:with_title) { |title| where(title: title) }
1013
+
1014
+ records = temp_index.criteria.with_title("expected").records
1015
+
1016
+ expect(records).to eq([expected])
1017
+ end
1018
+ end
1019
+
1020
+ describe "#custom" do
1021
+ it "adds a custom entry to the request" do
1022
+ request = ProductIndex.custom(custom_key: "custom_value").request
1023
+
1024
+ expect(request[:custom_key]).to eq("custom_value")
1025
+ end
1026
+ end
1027
+ end
1028
+