search_flip 1.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +34 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +606 -0
- data/Rakefile +9 -0
- data/irb.rb +7 -0
- data/lib/search_flip/aggregatable.rb +69 -0
- data/lib/search_flip/aggregation.rb +57 -0
- data/lib/search_flip/bulk.rb +152 -0
- data/lib/search_flip/config.rb +21 -0
- data/lib/search_flip/criteria.rb +737 -0
- data/lib/search_flip/filterable.rb +240 -0
- data/lib/search_flip/http_client.rb +49 -0
- data/lib/search_flip/index.rb +545 -0
- data/lib/search_flip/json.rb +18 -0
- data/lib/search_flip/model.rb +21 -0
- data/lib/search_flip/post_filterable.rb +252 -0
- data/lib/search_flip/response.rb +319 -0
- data/lib/search_flip/result.rb +12 -0
- data/lib/search_flip/to_json.rb +31 -0
- data/lib/search_flip/version.rb +5 -0
- data/lib/search_flip.rb +82 -0
- data/search_flip.gemspec +35 -0
- data/test/database.yml +4 -0
- data/test/search_flip/aggregation_test.rb +212 -0
- data/test/search_flip/bulk_test.rb +55 -0
- data/test/search_flip/criteria_test.rb +825 -0
- data/test/search_flip/http_client_test.rb +35 -0
- data/test/search_flip/index_test.rb +350 -0
- data/test/search_flip/model_test.rb +39 -0
- data/test/search_flip/response_test.rb +136 -0
- data/test/search_flip/to_json_test.rb +30 -0
- data/test/search_flip_test.rb +26 -0
- data/test/test_helper.rb +243 -0
- metadata +258 -0
@@ -0,0 +1,825 @@
|
|
1
|
+
|
2
|
+
require File.expand_path("../../test_helper", __FILE__)
|
3
|
+
|
4
|
+
class SearchFlip::CriteriaTest < SearchFlip::TestCase
|
5
|
+
should_delegate_methods :total_entries, :current_page, :previous_page, :prev_page, :next_page, :first_page?, :last_page?, :out_of_range?,
|
6
|
+
:total_pages, :hits, :ids, :count, :size, :length, :took, :aggregations, :suggestions, :scope, :results, :records, :scroll_id, :raw_response,
|
7
|
+
to: :response, subject: SearchFlip::Criteria.new(target: ProductIndex)
|
8
|
+
|
9
|
+
def test_merge
|
10
|
+
product1 = create(:product, price: 100, category: "category1")
|
11
|
+
product2 = create(:product, price: 200, category: "category2")
|
12
|
+
product3 = create(:product, price: 300, category: "category1")
|
13
|
+
|
14
|
+
ProductIndex.import [product1, product2, product3]
|
15
|
+
|
16
|
+
query = ProductIndex.where(price: 50 .. 250).aggregate(:category).merge(ProductIndex.where(category: "category1"))
|
17
|
+
|
18
|
+
assert_includes query.records, product1
|
19
|
+
refute_includes query.records, product2
|
20
|
+
refute_includes query.records, product3
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_criteria
|
24
|
+
criteria = ProductIndex.criteria
|
25
|
+
|
26
|
+
assert criteria.criteria === criteria
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_timeout
|
30
|
+
query = ProductIndex.timeout("1s")
|
31
|
+
|
32
|
+
assert_equal "1s", query.request[:timeout]
|
33
|
+
|
34
|
+
query.execute
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_terminate_after
|
38
|
+
query = ProductIndex.terminate_after(1)
|
39
|
+
|
40
|
+
assert_equal 1, query.request[:terminate_after]
|
41
|
+
|
42
|
+
query.execute
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_where
|
46
|
+
product1 = create(:product, price: 100, category: "category1")
|
47
|
+
product2 = create(:product, price: 200, category: "category2")
|
48
|
+
product3 = create(:product, price: 300, category: "category1")
|
49
|
+
|
50
|
+
ProductIndex.import [product1, product2, product3]
|
51
|
+
|
52
|
+
query1 = ProductIndex.where(price: 100 .. 200)
|
53
|
+
query2 = query1.where(category: "category1")
|
54
|
+
|
55
|
+
assert_includes query1.records, product1
|
56
|
+
assert_includes query1.records, product2
|
57
|
+
refute_includes query1.records, product3
|
58
|
+
|
59
|
+
assert_includes query2.records, product1
|
60
|
+
refute_includes query2.records, product2
|
61
|
+
refute_includes query2.records, product3
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_where_with_array
|
65
|
+
expected1 = create(:product, title: "expected1")
|
66
|
+
expected2 = create(:product, title: "expected2")
|
67
|
+
rejected = create(:product, title: "rejected")
|
68
|
+
|
69
|
+
ProductIndex.import [expected1, expected2, rejected]
|
70
|
+
|
71
|
+
records = ProductIndex.where(title: ["expected1", "expected2"]).records
|
72
|
+
|
73
|
+
assert_includes records, expected1
|
74
|
+
assert_includes records, expected2
|
75
|
+
refute_includes records, rejected
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_where_with_range
|
79
|
+
expected1 = create(:product, price: 100)
|
80
|
+
expected2 = create(:product, price: 200)
|
81
|
+
rejected = create(:product, price: 300)
|
82
|
+
|
83
|
+
ProductIndex.import [expected1, expected2, rejected]
|
84
|
+
|
85
|
+
records = ProductIndex.where(price: 100 .. 200).records
|
86
|
+
|
87
|
+
assert_includes records, expected1
|
88
|
+
assert_includes records, expected2
|
89
|
+
refute_includes records, rejected
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_where_with_nil
|
93
|
+
expected = create(:product, price: nil)
|
94
|
+
rejected = create(:product, price: 100)
|
95
|
+
|
96
|
+
ProductIndex.import [expected, rejected]
|
97
|
+
|
98
|
+
records = ProductIndex.where(price: nil).records
|
99
|
+
|
100
|
+
assert_includes records, expected
|
101
|
+
refute_includes records, rejected
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_where_not
|
105
|
+
product1 = create(:product, price: 100, category: "category1")
|
106
|
+
product2 = create(:product, price: 200, category: "category2")
|
107
|
+
product3 = create(:product, price: 300, category: "category1")
|
108
|
+
|
109
|
+
ProductIndex.import [product1, product2, product3]
|
110
|
+
|
111
|
+
query1 = ProductIndex.where_not(price: 250 .. 350)
|
112
|
+
query2 = query1.where_not(category: "category2")
|
113
|
+
|
114
|
+
assert_includes query1.records, product1
|
115
|
+
assert_includes query1.records, product2
|
116
|
+
refute_includes query1.records, product3
|
117
|
+
|
118
|
+
assert_includes query2.records, product1
|
119
|
+
refute_includes query2.records, product2
|
120
|
+
refute_includes query2.records, product3
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_where_not_with_array
|
124
|
+
expected = create(:product, title: "expected")
|
125
|
+
rejected1 = create(:product, title: "rejected1")
|
126
|
+
rejected2 = create(:product, title: "rejected2")
|
127
|
+
|
128
|
+
ProductIndex.import [expected, rejected1, rejected2]
|
129
|
+
|
130
|
+
records = ProductIndex.where_not(title: ["rejected1", "rejected2"]).records
|
131
|
+
|
132
|
+
assert_includes records, expected
|
133
|
+
refute_includes records, rejected1
|
134
|
+
refute_includes records, rejected2
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_where_not_with_range
|
138
|
+
expected = create(:product, price: 100)
|
139
|
+
rejected1 = create(:product, price: 200)
|
140
|
+
rejected2 = create(:product, price: 300)
|
141
|
+
|
142
|
+
ProductIndex.import [expected, rejected1, rejected2]
|
143
|
+
|
144
|
+
records = ProductIndex.where_not(price: 200 .. 300).records
|
145
|
+
|
146
|
+
assert_includes records, expected
|
147
|
+
refute_includes records, rejected1
|
148
|
+
refute_includes records, rejected2
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_where_not_with_nil
|
152
|
+
expected = create(:product, price: 100)
|
153
|
+
rejected = create(:product, price: nil)
|
154
|
+
|
155
|
+
ProductIndex.import [expected, rejected]
|
156
|
+
|
157
|
+
records = ProductIndex.where_not(price: nil).records
|
158
|
+
|
159
|
+
assert_includes records, expected
|
160
|
+
refute_includes records, rejected
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_filter
|
164
|
+
product1 = create(:product, price: 100, category: "category1")
|
165
|
+
product2 = create(:product, price: 200, category: "category2")
|
166
|
+
product3 = create(:product, price: 300, category: "category1")
|
167
|
+
|
168
|
+
ProductIndex.import [product1, product2, product3]
|
169
|
+
|
170
|
+
query1 = ProductIndex.filter(range: { price: { gte: 100, lte: 200 }})
|
171
|
+
query2 = query1.filter(term: { category: "category1" })
|
172
|
+
|
173
|
+
assert_includes query1.records, product1
|
174
|
+
assert_includes query1.records, product2
|
175
|
+
refute_includes query1.records, product3
|
176
|
+
|
177
|
+
assert_includes query2.records, product1
|
178
|
+
refute_includes query2.records, product2
|
179
|
+
refute_includes query2.records, product3
|
180
|
+
end
|
181
|
+
|
182
|
+
def test_range
|
183
|
+
product1 = create(:product, price: 100)
|
184
|
+
product2 = create(:product, price: 200)
|
185
|
+
product3 = create(:product, price: 300)
|
186
|
+
|
187
|
+
ProductIndex.import [product1, product2, product3]
|
188
|
+
|
189
|
+
query1 = ProductIndex.range(:price, gte: 100, lte: 200)
|
190
|
+
query2 = query1.range(:price, gte: 200, lte: 300)
|
191
|
+
|
192
|
+
assert_includes query1.records, product1
|
193
|
+
assert_includes query1.records, product2
|
194
|
+
refute_includes query1.records, product3
|
195
|
+
|
196
|
+
refute_includes query2.records, product1
|
197
|
+
assert_includes query2.records, product2
|
198
|
+
refute_includes query2.records, product3
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_match_all
|
202
|
+
expected1 = create(:product)
|
203
|
+
expected2 = create(:product)
|
204
|
+
|
205
|
+
ProductIndex.import [expected1, expected2]
|
206
|
+
|
207
|
+
records = ProductIndex.match_all.records
|
208
|
+
|
209
|
+
assert_includes records, expected1
|
210
|
+
assert_includes records, expected2
|
211
|
+
end
|
212
|
+
|
213
|
+
def test_exists
|
214
|
+
product1 = create(:product, title: "title1", description: "description1")
|
215
|
+
product2 = create(:product, title: "title2", description: nil)
|
216
|
+
product3 = create(:product, title: nil, description: "description2")
|
217
|
+
|
218
|
+
ProductIndex.import [product1, product2, product3]
|
219
|
+
|
220
|
+
query1 = ProductIndex.exists(:title)
|
221
|
+
query2 = query1.exists(:description)
|
222
|
+
|
223
|
+
assert_includes query1.records, product1
|
224
|
+
assert_includes query1.records, product2
|
225
|
+
refute_includes query1.records, product3
|
226
|
+
|
227
|
+
assert_includes query2.records, product1
|
228
|
+
refute_includes query2.records, product2
|
229
|
+
refute_includes query2.records, product3
|
230
|
+
end
|
231
|
+
|
232
|
+
def test_exists_not
|
233
|
+
product1 = create(:product, title: nil, description: nil)
|
234
|
+
product2 = create(:product, title: nil, description: "description2")
|
235
|
+
product3 = create(:product, title: "title3", description: "description3")
|
236
|
+
|
237
|
+
ProductIndex.import [product1, product2, product3]
|
238
|
+
|
239
|
+
query1 = ProductIndex.exists_not(:title)
|
240
|
+
query2 = query1.exists_not(:description)
|
241
|
+
|
242
|
+
assert_includes query1.records, product1
|
243
|
+
assert_includes query1.records, product2
|
244
|
+
refute_includes query1.records, product3
|
245
|
+
|
246
|
+
assert_includes query2.records, product1
|
247
|
+
refute_includes query2.records, product2
|
248
|
+
refute_includes query2.records, product3
|
249
|
+
end
|
250
|
+
|
251
|
+
def test_post_search
|
252
|
+
return if SearchFlip.version.to_i < 2
|
253
|
+
|
254
|
+
product1 = create(:product, title: "title1", category: "category1")
|
255
|
+
product2 = create(:product, title: "title2", category: "category2")
|
256
|
+
product3 = create(:product, title: "title3", category: "category1")
|
257
|
+
|
258
|
+
ProductIndex.import [product1, product2, product3]
|
259
|
+
|
260
|
+
query1 = ProductIndex.aggregate(:category).post_search("title1 OR title2")
|
261
|
+
query2 = query1.post_search("category1")
|
262
|
+
|
263
|
+
assert_includes query1.records, product1
|
264
|
+
assert_includes query1.records, product2
|
265
|
+
refute_includes query1.records, product3
|
266
|
+
|
267
|
+
assert_includes query2.records, product1
|
268
|
+
refute_includes query2.records, product2
|
269
|
+
refute_includes query2.records, product3
|
270
|
+
|
271
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query1.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
272
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query2.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
273
|
+
end
|
274
|
+
|
275
|
+
def test_post_where
|
276
|
+
product1 = create(:product, price: 100, category: "category1")
|
277
|
+
product2 = create(:product, price: 200, category: "category2")
|
278
|
+
product3 = create(:product, price: 300, category: "category1")
|
279
|
+
|
280
|
+
ProductIndex.import [product1, product2, product3]
|
281
|
+
|
282
|
+
query1 = ProductIndex.aggregate(:category).post_where(price: 100 .. 200)
|
283
|
+
query2 = query1.post_where(category: "category1")
|
284
|
+
|
285
|
+
assert_includes query1.records, product1
|
286
|
+
assert_includes query1.records, product2
|
287
|
+
refute_includes query1.records, product3
|
288
|
+
|
289
|
+
assert_includes query2.records, product1
|
290
|
+
refute_includes query2.records, product2
|
291
|
+
refute_includes query2.records, product3
|
292
|
+
|
293
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query1.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
294
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query2.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
295
|
+
end
|
296
|
+
|
297
|
+
def test_post_where_with_array
|
298
|
+
expected1 = create(:product, title: "expected1", category: "category1")
|
299
|
+
expected2 = create(:product, title: "expected2", category: "category2")
|
300
|
+
rejected = create(:product, title: "rejected", category: "category1")
|
301
|
+
|
302
|
+
ProductIndex.import [expected1, expected2, rejected]
|
303
|
+
|
304
|
+
query = ProductIndex.aggregate(:category).post_where(title: ["expected1", "expected2"])
|
305
|
+
|
306
|
+
assert_includes query.records, expected1
|
307
|
+
assert_includes query.records, expected2
|
308
|
+
refute_includes query.records, rejected
|
309
|
+
|
310
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
311
|
+
end
|
312
|
+
|
313
|
+
def test_post_where_with_range
|
314
|
+
expected1 = create(:product, price: 100, category: "category1")
|
315
|
+
expected2 = create(:product, price: 200, category: "category2")
|
316
|
+
rejected = create(:product, price: 300, category: "category1")
|
317
|
+
|
318
|
+
ProductIndex.import [expected1, expected2, rejected]
|
319
|
+
|
320
|
+
query = ProductIndex.aggregate(:category).post_where(price: 100 .. 200)
|
321
|
+
|
322
|
+
assert_includes query.records, expected1
|
323
|
+
assert_includes query.records, expected2
|
324
|
+
refute_includes query.records, rejected
|
325
|
+
|
326
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
327
|
+
end
|
328
|
+
|
329
|
+
def test_post_where_not
|
330
|
+
product1 = create(:product, price: 100, category: "category1")
|
331
|
+
product2 = create(:product, price: 200, category: "category2")
|
332
|
+
product3 = create(:product, price: 300, category: "category1")
|
333
|
+
|
334
|
+
ProductIndex.import [product1, product2, product3]
|
335
|
+
|
336
|
+
query1 = ProductIndex.aggregate(:category).post_where_not(price: 250 .. 350)
|
337
|
+
query2 = query1.post_where_not(category: "category2")
|
338
|
+
|
339
|
+
assert_includes query1.records, product1
|
340
|
+
assert_includes query1.records, product2
|
341
|
+
refute_includes query1.records, product3
|
342
|
+
|
343
|
+
assert_includes query2.records, product1
|
344
|
+
refute_includes query2.records, product2
|
345
|
+
refute_includes query2.records, product3
|
346
|
+
|
347
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query1.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
348
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query2.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
349
|
+
end
|
350
|
+
|
351
|
+
def test_post_where_not_with_array
|
352
|
+
expected = create(:product, title: "expected", category: "category1")
|
353
|
+
rejected1 = create(:product, title: "rejected1", category: "category2")
|
354
|
+
rejected2 = create(:product, title: "rejected2", category: "category1")
|
355
|
+
|
356
|
+
ProductIndex.import [expected, rejected1, rejected2]
|
357
|
+
|
358
|
+
query = ProductIndex.aggregate(:category).post_where_not(title: ["rejected1", "rejected2"])
|
359
|
+
|
360
|
+
assert_includes query.records, expected
|
361
|
+
refute_includes query.records, rejected1
|
362
|
+
refute_includes query.records, rejected2
|
363
|
+
|
364
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
365
|
+
end
|
366
|
+
|
367
|
+
def test_post_where_not_with_range
|
368
|
+
expected = create(:product, price: 100, category: "category1")
|
369
|
+
rejected1 = create(:product, price: 200, category: "category2")
|
370
|
+
rejected2 = create(:product, price: 300, category: "category1")
|
371
|
+
|
372
|
+
ProductIndex.import [expected, rejected1, rejected2]
|
373
|
+
|
374
|
+
query = ProductIndex.aggregate(:category).post_where_not(price: 200 .. 300)
|
375
|
+
|
376
|
+
assert_includes query.records, expected
|
377
|
+
refute_includes query.records, rejected1
|
378
|
+
refute_includes query.records, rejected2
|
379
|
+
|
380
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
381
|
+
end
|
382
|
+
|
383
|
+
def test_post_filter
|
384
|
+
product1 = create(:product, price: 100, category: "category1")
|
385
|
+
product2 = create(:product, price: 200, category: "category2")
|
386
|
+
product3 = create(:product, price: 300, category: "category1")
|
387
|
+
|
388
|
+
ProductIndex.import [product1, product2, product3]
|
389
|
+
|
390
|
+
query1 = ProductIndex.aggregate(:category).post_filter(range: { price: { gte: 100, lte: 200 }})
|
391
|
+
query2 = query1.post_filter(term: { category: "category1" })
|
392
|
+
|
393
|
+
assert_includes query1.records, product1
|
394
|
+
assert_includes query1.records, product2
|
395
|
+
refute_includes query1.records, product3
|
396
|
+
|
397
|
+
assert_includes query2.records, product1
|
398
|
+
refute_includes query2.records, product2
|
399
|
+
refute_includes query2.records, product3
|
400
|
+
|
401
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query1.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
402
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query2.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
403
|
+
end
|
404
|
+
|
405
|
+
def test_post_range
|
406
|
+
product1 = create(:product, price: 100, category: "category1")
|
407
|
+
product2 = create(:product, price: 200, category: "category2")
|
408
|
+
product3 = create(:product, price: 300, category: "category1")
|
409
|
+
|
410
|
+
ProductIndex.import [product1, product2, product3]
|
411
|
+
|
412
|
+
query1 = ProductIndex.aggregate(:category).post_range(:price, gte: 100, lte: 200)
|
413
|
+
query2 = query1.post_range(:price, gte: 200, lte: 300)
|
414
|
+
|
415
|
+
assert_includes query1.records, product1
|
416
|
+
assert_includes query1.records, product2
|
417
|
+
refute_includes query1.records, product3
|
418
|
+
|
419
|
+
refute_includes query2.records, product1
|
420
|
+
assert_includes query2.records, product2
|
421
|
+
refute_includes query2.records, product3
|
422
|
+
|
423
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query1.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
424
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query2.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
425
|
+
end
|
426
|
+
|
427
|
+
def test_post_exists
|
428
|
+
product1 = create(:product, title: "title1", description: "description1", category: "category1")
|
429
|
+
product2 = create(:product, title: "title2", description: nil, category: "category2")
|
430
|
+
product3 = create(:product, title: nil, description: "description2", category: "category1")
|
431
|
+
|
432
|
+
ProductIndex.import [product1, product2, product3]
|
433
|
+
|
434
|
+
query1 = ProductIndex.aggregate(:category).post_exists(:title)
|
435
|
+
query2 = query1.post_exists(:description)
|
436
|
+
|
437
|
+
assert_includes query1.records, product1
|
438
|
+
assert_includes query1.records, product2
|
439
|
+
refute_includes query1.records, product3
|
440
|
+
|
441
|
+
assert_includes query2.records, product1
|
442
|
+
refute_includes query2.records, product2
|
443
|
+
refute_includes query2.records, product3
|
444
|
+
|
445
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query1.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
446
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query2.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
447
|
+
end
|
448
|
+
|
449
|
+
def test_post_exists_not
|
450
|
+
product1 = create(:product, title: nil, description: nil, category: "category1")
|
451
|
+
product2 = create(:product, title: nil, description: "description2", category: "category2")
|
452
|
+
product3 = create(:product, title: "title3", description: "description3", category: "category1")
|
453
|
+
|
454
|
+
ProductIndex.import [product1, product2, product3]
|
455
|
+
|
456
|
+
query1 = ProductIndex.aggregate(:category).post_exists_not(:title)
|
457
|
+
query2 = query1.post_exists_not(:description)
|
458
|
+
|
459
|
+
assert_includes query1.records, product1
|
460
|
+
assert_includes query1.records, product2
|
461
|
+
refute_includes query1.records, product3
|
462
|
+
|
463
|
+
assert_includes query2.records, product1
|
464
|
+
refute_includes query2.records, product2
|
465
|
+
refute_includes query2.records, product3
|
466
|
+
|
467
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query1.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
468
|
+
assert_equal Hash["category1" => 2, "category2" => 1], query2.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
469
|
+
end
|
470
|
+
|
471
|
+
def test_aggregate
|
472
|
+
ProductIndex.import create_list(:product, 3, category: "category1", price: 10)
|
473
|
+
ProductIndex.import create_list(:product, 2, category: "category2", price: 20)
|
474
|
+
ProductIndex.import create_list(:product, 1, category: "category3", price: 30)
|
475
|
+
|
476
|
+
query = ProductIndex.aggregate(:category, size: 2).aggregate(price_sum: { sum: { field: "price" }})
|
477
|
+
|
478
|
+
category_aggregations = query.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
479
|
+
price_aggregation = query.aggregations(:price_sum).value
|
480
|
+
|
481
|
+
assert_equal Hash["category1" => 3, "category2" => 2], category_aggregations
|
482
|
+
assert_equal 100, price_aggregation
|
483
|
+
end
|
484
|
+
|
485
|
+
def test_aggregate_with_hash
|
486
|
+
ProductIndex.import create_list(:product, 3, category: "category1")
|
487
|
+
ProductIndex.import create_list(:product, 2, category: "category2")
|
488
|
+
ProductIndex.import create_list(:product, 1, category: "category3")
|
489
|
+
|
490
|
+
aggregations = ProductIndex.aggregate(category: { terms: { field: :category }}).aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
491
|
+
|
492
|
+
assert_equal Hash["category1" => 3, "category2" => 2, "category3" => 1], aggregations
|
493
|
+
end
|
494
|
+
|
495
|
+
def test_aggregate_with_subaggregation
|
496
|
+
ProductIndex.import create_list(:product, 3, category: "category1", price: 15)
|
497
|
+
ProductIndex.import create_list(:product, 2, category: "category2", price: 20)
|
498
|
+
ProductIndex.import create_list(:product, 1, category: "category3", price: 25)
|
499
|
+
|
500
|
+
query = ProductIndex.aggregate(:category) do |aggregation|
|
501
|
+
aggregation.aggregate(price_sum: { sum: { field: "price" }})
|
502
|
+
end
|
503
|
+
|
504
|
+
category_aggregations = query.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.doc_count }
|
505
|
+
price_sum_aggregations = query.aggregations(:category).each_with_object({}) { |(key, agg), hash| hash[key] = agg.price_sum.value }
|
506
|
+
|
507
|
+
assert_equal Hash["category1" => 3, "category2" => 2, "category3" => 1], category_aggregations
|
508
|
+
assert_equal Hash["category1" => 45, "category2" => 40, "category3" => 25], price_sum_aggregations
|
509
|
+
end
|
510
|
+
|
511
|
+
def test_profile
|
512
|
+
return if SearchFlip.version.to_i < 2
|
513
|
+
|
514
|
+
assert_not_nil ProductIndex.profile(true).raw_response["profile"]
|
515
|
+
end
|
516
|
+
|
517
|
+
def test_scroll
|
518
|
+
products = create_list(:product, 15)
|
519
|
+
|
520
|
+
ProductIndex.import products
|
521
|
+
|
522
|
+
criteria = ProductIndex.limit(10).scroll(timeout: "1m")
|
523
|
+
|
524
|
+
result = []
|
525
|
+
iterations = 0
|
526
|
+
|
527
|
+
until criteria.records.empty?
|
528
|
+
result += criteria.records
|
529
|
+
iterations += 1
|
530
|
+
|
531
|
+
criteria = criteria.scroll(id: criteria.scroll_id, timeout: "1m")
|
532
|
+
end
|
533
|
+
|
534
|
+
assert_equal result.to_set, products.to_set
|
535
|
+
assert_equal 2, iterations
|
536
|
+
end
|
537
|
+
|
538
|
+
def test_delete
|
539
|
+
product1, product2, product3 = create_list(:product, 3)
|
540
|
+
|
541
|
+
ProductIndex.import [product1, product2, product3]
|
542
|
+
|
543
|
+
assert_difference "ProductIndex.total_entries", -2 do
|
544
|
+
ProductIndex.where(id: [product1.id, product2.id]).delete
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
def test_source
|
549
|
+
product = create(:product, title: "Title", price: 10)
|
550
|
+
|
551
|
+
ProductIndex.import product
|
552
|
+
|
553
|
+
results = ProductIndex.where(id: product.id).results
|
554
|
+
|
555
|
+
assert_present results.first.id
|
556
|
+
assert_equal "Title", results.first.title
|
557
|
+
assert_equal 10, results.first.price
|
558
|
+
|
559
|
+
results = ProductIndex.where(id: product.id).source([:id, :price]).results
|
560
|
+
|
561
|
+
assert_present results.first.id
|
562
|
+
assert_blank results.first.title
|
563
|
+
assert_present results.first.price
|
564
|
+
end
|
565
|
+
|
566
|
+
def test_includes
|
567
|
+
user = create(:user)
|
568
|
+
comments = create_list(:comment, 2)
|
569
|
+
product = create(:product, user: user, comments: comments)
|
570
|
+
|
571
|
+
ProductIndex.import product
|
572
|
+
|
573
|
+
record = ProductIndex.includes(:user).includes(:comments).records.first
|
574
|
+
|
575
|
+
assert_not_nil record
|
576
|
+
assert_equal user, record.user
|
577
|
+
assert_equal comments.to_set, record.comments.to_set
|
578
|
+
end
|
579
|
+
|
580
|
+
def test_eager_load
|
581
|
+
user = create(:user)
|
582
|
+
comments = create_list(:comment, 2)
|
583
|
+
product = create(:product, user: user, comments: comments)
|
584
|
+
|
585
|
+
ProductIndex.import product
|
586
|
+
|
587
|
+
record = ProductIndex.eager_load(:user).eager_load(:comments).records.first
|
588
|
+
|
589
|
+
assert_not_nil record
|
590
|
+
assert_equal user, record.user
|
591
|
+
assert_equal comments.to_set, record.comments.to_set
|
592
|
+
end
|
593
|
+
|
594
|
+
def test_preload
|
595
|
+
user = create(:user)
|
596
|
+
comments = create_list(:comment, 2)
|
597
|
+
product = create(:product, user: user, comments: comments)
|
598
|
+
|
599
|
+
ProductIndex.import product
|
600
|
+
|
601
|
+
record = ProductIndex.preload(:user).preload(:comments).records.first
|
602
|
+
|
603
|
+
assert_not_nil record
|
604
|
+
assert_equal user, record.user
|
605
|
+
assert_equal comments.to_set, record.comments.to_set
|
606
|
+
end
|
607
|
+
|
608
|
+
def test_sort
|
609
|
+
product1 = create(:product, rank: 2, price: 100)
|
610
|
+
product2 = create(:product, rank: 2, price: 90)
|
611
|
+
product3 = create(:product, rank: 1, price: 120)
|
612
|
+
product4 = create(:product, rank: 0, price: 110)
|
613
|
+
|
614
|
+
ProductIndex.import [product1, product2, product3, product4]
|
615
|
+
|
616
|
+
assert_equal [product2, product1, product3, product4], ProductIndex.sort({ rank: :desc }, { price: :asc }).records
|
617
|
+
assert_equal [product2, product1, product3, product4], ProductIndex.sort(rank: :desc).sort(:price).records
|
618
|
+
assert_equal [product2, product1, product4, product3], ProductIndex.sort(:price).sort(rank: :desc).records
|
619
|
+
end
|
620
|
+
|
621
|
+
def test_resort
|
622
|
+
product1 = create(:product, rank: 2, price: 100)
|
623
|
+
product2 = create(:product, rank: 2, price: 90)
|
624
|
+
product3 = create(:product, rank: 1, price: 120)
|
625
|
+
product4 = create(:product, rank: 0, price: 110)
|
626
|
+
|
627
|
+
ProductIndex.import [product1, product2, product3, product4]
|
628
|
+
|
629
|
+
assert_equal [product2, product1, product3, product4], ProductIndex.sort(:price).resort({ rank: :desc }, { price: :asc }).records
|
630
|
+
assert_equal [product2, product1, product4, product3], ProductIndex.sort(rank: :desc).resort(:price).records
|
631
|
+
end
|
632
|
+
|
633
|
+
def test_offset
|
634
|
+
product1 = create(:product, rank: 1)
|
635
|
+
product2 = create(:product, rank: 2)
|
636
|
+
product3 = create(:product, rank: 3)
|
637
|
+
|
638
|
+
ProductIndex.import [product1, product2, product3]
|
639
|
+
|
640
|
+
query = ProductIndex.sort(:rank).offset(1)
|
641
|
+
|
642
|
+
assert_equal [product2, product3], query.records
|
643
|
+
assert_equal [product3], query.offset(2).records
|
644
|
+
end
|
645
|
+
|
646
|
+
def test_limit
|
647
|
+
product1 = create(:product, rank: 1)
|
648
|
+
product2 = create(:product, rank: 2)
|
649
|
+
product3 = create(:product, rank: 3)
|
650
|
+
|
651
|
+
ProductIndex.import [product1, product2, product3]
|
652
|
+
|
653
|
+
query = ProductIndex.sort(:rank).limit(1)
|
654
|
+
|
655
|
+
assert_equal [product1], query.records
|
656
|
+
assert_equal [product1, product2], query.limit(2).records
|
657
|
+
end
|
658
|
+
|
659
|
+
def test_paginate
|
660
|
+
product1 = create(:product, rank: 1)
|
661
|
+
product2 = create(:product, rank: 2)
|
662
|
+
product3 = create(:product, rank: 3)
|
663
|
+
|
664
|
+
ProductIndex.import [product1, product2, product3]
|
665
|
+
|
666
|
+
query = ProductIndex.sort(:rank).paginate(page: 1, per_page: 2)
|
667
|
+
|
668
|
+
assert_equal [product1, product2], query.records
|
669
|
+
assert_equal [product3], query.paginate(page: 2, per_page: 2).records
|
670
|
+
end
|
671
|
+
|
672
|
+
def test_page
|
673
|
+
assert_equal 0, ProductIndex.page(1).offset_value
|
674
|
+
assert_equal 30, ProductIndex.page(2).offset_value
|
675
|
+
assert_equal 100, ProductIndex.page(3).per(50).offset_value
|
676
|
+
end
|
677
|
+
|
678
|
+
def test_per
|
679
|
+
assert_equal 50, ProductIndex.per(50).limit_value
|
680
|
+
end
|
681
|
+
|
682
|
+
def test_search
|
683
|
+
product1 = create(:product, title: "Title1", description: "Description1", price: 10)
|
684
|
+
product2 = create(:product, title: "Title2", description: "Description2", price: 20)
|
685
|
+
product3 = create(:product, title: "Title3", description: "Description2", price: 30)
|
686
|
+
|
687
|
+
ProductIndex.import [product1, product2, product3]
|
688
|
+
|
689
|
+
assert_equal [product1, product3].to_set, ProductIndex.search("Title1 OR Title3").records.to_set
|
690
|
+
assert_equal [product1, product3].to_set, ProductIndex.search("Title1 Title3", default_operator: :OR).records.to_set
|
691
|
+
assert_equal [product1], ProductIndex.search("Title1 OR Title2").search("Title1 OR Title3").records
|
692
|
+
assert_equal [product1], ProductIndex.search("Title1 OR Title3").where(price: 5 .. 15).records
|
693
|
+
end
|
694
|
+
|
695
|
+
def test_unscope
|
696
|
+
product1 = create(:product, title: "Title1", description: "Description1", price: 10)
|
697
|
+
product2 = create(:product, title: "Title2", description: "Description2", price: 20)
|
698
|
+
product3 = create(:product, title: "Title3", description: "Description2", price: 30)
|
699
|
+
|
700
|
+
ProductIndex.import [product1, product2, product3]
|
701
|
+
|
702
|
+
assert_equal [product1], ProductIndex.search("Title1 OR Title2").search("Title1 OR Title3").records
|
703
|
+
assert_equal [product1, product3].to_set, ProductIndex.search("Title1 OR Title2").unscope(:search).search("Title1 OR Title3").records.to_set
|
704
|
+
end
|
705
|
+
|
706
|
+
def test_highlight
|
707
|
+
product1 = create(:product, title: "Title1 highlight", description: "Description1 highlight")
|
708
|
+
product2 = create(:product, title: "Title2 highlight", description: "Description2 highlight")
|
709
|
+
|
710
|
+
ProductIndex.import [product1, product2]
|
711
|
+
|
712
|
+
results = ProductIndex.sort(:id).highlight([:title, :description]).search("title:highlight description:highlight").results
|
713
|
+
|
714
|
+
assert_equal ["Title1 <em>highlight</em>"], results[0].highlight.title
|
715
|
+
assert_equal ["Description1 <em>highlight</em>"], results[0].highlight.description
|
716
|
+
|
717
|
+
assert_equal ["Title2 <em>highlight</em>"], results[1].highlight.title
|
718
|
+
assert_equal ["Description2 <em>highlight</em>"], results[1].highlight.description
|
719
|
+
|
720
|
+
results = ProductIndex.sort(:id).highlight([:title, :description], require_field_match: false).search("highlight").results
|
721
|
+
|
722
|
+
assert_equal ["Title1 <em>highlight</em>"], results[0].highlight.title
|
723
|
+
assert_equal ["Description1 <em>highlight</em>"], results[0].highlight.description
|
724
|
+
|
725
|
+
assert_equal ["Title2 <em>highlight</em>"], results[1].highlight.title
|
726
|
+
assert_equal ["Description2 <em>highlight</em>"], results[1].highlight.description
|
727
|
+
|
728
|
+
results = ProductIndex.sort(:id).highlight(:title, require_field_match: true).highlight(:description, require_field_match: true).search("title:highlight").results
|
729
|
+
|
730
|
+
assert_equal ["Title1 <em>highlight</em>"], results[0].highlight.title
|
731
|
+
assert_nil results[0].highlight.description
|
732
|
+
|
733
|
+
assert_equal ["Title2 <em>highlight</em>"], results[1].highlight.title
|
734
|
+
assert_nil results[1].highlight.description
|
735
|
+
end
|
736
|
+
|
737
|
+
def test_suggest
|
738
|
+
product = create(:product, title: "Title", description: "Description")
|
739
|
+
|
740
|
+
ProductIndex.import product
|
741
|
+
|
742
|
+
assert_equal "description", ProductIndex.suggest(:suggestion, text: "Desciption", term: { field: "description" }).suggestions(:suggestion).first["text"]
|
743
|
+
end
|
744
|
+
|
745
|
+
def test_find_in_batches
|
746
|
+
expected1 = create(:product, title: "expected", rank: 1)
|
747
|
+
expected2 = create(:product, title: "expected", rank: 2)
|
748
|
+
expected3 = create(:product, title: "expected", rank: 3)
|
749
|
+
rejected = create(:product, title: "rejected")
|
750
|
+
|
751
|
+
create :product, title: "rejected"
|
752
|
+
|
753
|
+
ProductIndex.import [expected1, expected2, expected3, rejected]
|
754
|
+
|
755
|
+
assert_equal [[expected1, expected2], [expected3]], ProductIndex.where(title: "expected").sort(:rank).find_in_batches(batch_size: 2).to_a
|
756
|
+
end
|
757
|
+
|
758
|
+
def test_find_each
|
759
|
+
expected1 = create(:product, title: "expected", rank: 1)
|
760
|
+
expected2 = create(:product, title: "expected", rank: 2)
|
761
|
+
expected3 = create(:product, title: "expected", rank: 3)
|
762
|
+
rejected = create(:product, title: "rejected")
|
763
|
+
|
764
|
+
create :product, title: "rejected"
|
765
|
+
|
766
|
+
ProductIndex.import [expected1, expected2, expected3, rejected]
|
767
|
+
|
768
|
+
assert_equal [expected1, expected2, expected3], ProductIndex.where(title: "expected").sort(:rank).find_each(batch_size: 2).to_a
|
769
|
+
end
|
770
|
+
|
771
|
+
def test_failsafe
|
772
|
+
assert_raises SearchFlip::ResponseError do
|
773
|
+
ProductIndex.search("syntax/error").records
|
774
|
+
end
|
775
|
+
|
776
|
+
query = ProductIndex.failsafe(true).search("syntax/error")
|
777
|
+
|
778
|
+
assert_equal [], query.records
|
779
|
+
assert_equal 0, query.total_entries
|
780
|
+
end
|
781
|
+
|
782
|
+
def test_fresh
|
783
|
+
create :product
|
784
|
+
|
785
|
+
query = ProductIndex.criteria.tap(&:records)
|
786
|
+
|
787
|
+
assert_not_nil query.instance_variable_get(:@response)
|
788
|
+
|
789
|
+
refute query.fresh === query
|
790
|
+
assert_nil query.fresh.instance_variable_get(:@response)
|
791
|
+
end
|
792
|
+
|
793
|
+
def test_respond_to?
|
794
|
+
temp_index = Class.new(ProductIndex)
|
795
|
+
|
796
|
+
refute temp_index.criteria.respond_to?(:test_scope)
|
797
|
+
|
798
|
+
temp_index.scope(:test_scope) { match_all }
|
799
|
+
|
800
|
+
assert temp_index.criteria.respond_to?(:test_scope)
|
801
|
+
end
|
802
|
+
|
803
|
+
def test_method_missing
|
804
|
+
temp_index = Class.new(ProductIndex)
|
805
|
+
|
806
|
+
expected = create(:product, title: "expected")
|
807
|
+
rejected = create(:product, title: "rejected")
|
808
|
+
|
809
|
+
temp_index.import [expected, rejected]
|
810
|
+
|
811
|
+
temp_index.scope(:with_title) { |title| where(title: title) }
|
812
|
+
|
813
|
+
records = temp_index.criteria.with_title("expected").records
|
814
|
+
|
815
|
+
assert_includes records, expected
|
816
|
+
refute_includes records, rejected
|
817
|
+
end
|
818
|
+
|
819
|
+
def test_custom
|
820
|
+
request = ProductIndex.custom(custom_key: "custom_value").request
|
821
|
+
|
822
|
+
assert_equal "custom_value", request[:custom_key]
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|