search_flip 2.0.0.beta2 → 2.0.0.beta3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+