searchkick-sinneduy 0.9.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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.travis.yml +28 -0
  4. data/CHANGELOG.md +272 -0
  5. data/Gemfile +7 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +1109 -0
  8. data/Rakefile +8 -0
  9. data/ci/before_install.sh +14 -0
  10. data/gemfiles/activerecord31.gemfile +7 -0
  11. data/gemfiles/activerecord32.gemfile +7 -0
  12. data/gemfiles/activerecord40.gemfile +8 -0
  13. data/gemfiles/activerecord41.gemfile +8 -0
  14. data/gemfiles/mongoid2.gemfile +7 -0
  15. data/gemfiles/mongoid3.gemfile +6 -0
  16. data/gemfiles/mongoid4.gemfile +7 -0
  17. data/gemfiles/nobrainer.gemfile +6 -0
  18. data/lib/searchkick.rb +72 -0
  19. data/lib/searchkick/index.rb +550 -0
  20. data/lib/searchkick/logging.rb +136 -0
  21. data/lib/searchkick/model.rb +102 -0
  22. data/lib/searchkick/query.rb +567 -0
  23. data/lib/searchkick/reindex_job.rb +28 -0
  24. data/lib/searchkick/reindex_v2_job.rb +24 -0
  25. data/lib/searchkick/results.rb +158 -0
  26. data/lib/searchkick/tasks.rb +35 -0
  27. data/lib/searchkick/version.rb +3 -0
  28. data/searchkick.gemspec +28 -0
  29. data/test/autocomplete_test.rb +67 -0
  30. data/test/boost_test.rb +126 -0
  31. data/test/facets_test.rb +91 -0
  32. data/test/highlight_test.rb +58 -0
  33. data/test/index_test.rb +119 -0
  34. data/test/inheritance_test.rb +80 -0
  35. data/test/match_test.rb +163 -0
  36. data/test/model_test.rb +38 -0
  37. data/test/query_test.rb +14 -0
  38. data/test/reindex_job_test.rb +33 -0
  39. data/test/reindex_v2_job_test.rb +34 -0
  40. data/test/routing_test.rb +14 -0
  41. data/test/should_index_test.rb +34 -0
  42. data/test/similar_test.rb +20 -0
  43. data/test/sql_test.rb +327 -0
  44. data/test/suggest_test.rb +82 -0
  45. data/test/synonyms_test.rb +50 -0
  46. data/test/test_helper.rb +276 -0
  47. metadata +194 -0
@@ -0,0 +1,38 @@
1
+ require_relative "test_helper"
2
+
3
+ class TestModel < Minitest::Test
4
+
5
+ def test_disable_callbacks_model
6
+ store_names ["product a"]
7
+
8
+ Product.disable_search_callbacks
9
+ assert !Product.search_callbacks?
10
+
11
+ store_names ["product b"]
12
+ assert_search "product", ["product a"]
13
+
14
+ Product.enable_search_callbacks
15
+ Product.reindex
16
+
17
+ assert_search "product", ["product a", "product b"]
18
+ end
19
+
20
+ def test_disable_callbacks_global
21
+ # make sure callbacks default to on
22
+ assert Searchkick.callbacks?
23
+
24
+ store_names ["product a"]
25
+
26
+ Searchkick.disable_callbacks
27
+ assert !Searchkick.callbacks?
28
+
29
+ store_names ["product b"]
30
+ assert_search "product", ["product a"]
31
+
32
+ Searchkick.enable_callbacks
33
+ Product.reindex
34
+
35
+ assert_search "product", ["product a", "product b"]
36
+ end
37
+
38
+ end
@@ -0,0 +1,14 @@
1
+ require_relative "test_helper"
2
+
3
+ class TestQuery < Minitest::Test
4
+
5
+ def test_basic
6
+ store_names ["Milk", "Apple"]
7
+ query = Product.search("milk", execute: false)
8
+ # query.body = {query: {match_all: {}}}
9
+ # query.body = {query: {match: {name: "Apple"}}}
10
+ query.body[:query] = {match_all: {}}
11
+ assert_equal ["Apple", "Milk"], query.execute.map(&:name).sort
12
+ end
13
+
14
+ end
@@ -0,0 +1,33 @@
1
+ require_relative "test_helper"
2
+
3
+ class TestReindexJob < Minitest::Test
4
+
5
+ def setup
6
+ super
7
+ Searchkick.disable_callbacks
8
+ end
9
+
10
+ def teardown
11
+ Searchkick.enable_callbacks
12
+ end
13
+
14
+ def test_create
15
+ product = Product.create!(name: "Boom")
16
+ Product.searchkick_index.refresh
17
+ assert_search "*", []
18
+ Searchkick::ReindexJob.new("Product", product.id.to_s).perform
19
+ Product.searchkick_index.refresh
20
+ assert_search "*", ["Boom"]
21
+ end
22
+
23
+ def test_destroy
24
+ product = Product.create!(name: "Boom")
25
+ Product.reindex
26
+ assert_search "*", ["Boom"]
27
+ product.destroy
28
+ Searchkick::ReindexJob.new("Product", product.id.to_s).perform
29
+ Product.searchkick_index.refresh
30
+ assert_search "*", []
31
+ end
32
+
33
+ end
@@ -0,0 +1,34 @@
1
+ require_relative "test_helper"
2
+
3
+ class TestReindexV2Job < Minitest::Test
4
+
5
+ def setup
6
+ skip unless defined?(ActiveJob)
7
+ super
8
+ Searchkick.disable_callbacks
9
+ end
10
+
11
+ def teardown
12
+ Searchkick.enable_callbacks
13
+ end
14
+
15
+ def test_create
16
+ product = Product.create!(name: "Boom")
17
+ Product.searchkick_index.refresh
18
+ assert_search "*", []
19
+ Searchkick::ReindexV2Job.perform_later("Product", product.id.to_s)
20
+ Product.searchkick_index.refresh
21
+ assert_search "*", ["Boom"]
22
+ end
23
+
24
+ def test_destroy
25
+ product = Product.create!(name: "Boom")
26
+ Product.reindex
27
+ assert_search "*", ["Boom"]
28
+ product.destroy
29
+ Searchkick::ReindexV2Job.perform_later("Product", product.id.to_s)
30
+ Product.searchkick_index.refresh
31
+ assert_search "*", []
32
+ end
33
+
34
+ end
@@ -0,0 +1,14 @@
1
+ require_relative "test_helper"
2
+
3
+ class TestRouting < Minitest::Test
4
+
5
+ def test_routing_query
6
+ query = Store.search("Dollar Tree", routing: "Dollar Tree", execute: false)
7
+ assert_equal query.params[:routing], "Dollar Tree"
8
+ end
9
+
10
+ def test_routing_mappings
11
+ index_options = Store.searchkick_index.index_options
12
+ assert_equal index_options[:mappings][:_default_][:_routing], {required: true, path: "name"}
13
+ end
14
+ end
@@ -0,0 +1,34 @@
1
+ require_relative "test_helper"
2
+
3
+ class TestShouldIndex < Minitest::Test
4
+
5
+ def test_basic
6
+ store_names ["INDEX", "DO NOT INDEX"]
7
+ assert_search "index", ["INDEX"]
8
+ end
9
+
10
+ def test_default_true
11
+ assert Animal.new.should_index?
12
+ end
13
+
14
+ def test_change_to_true
15
+ store_names ["DO NOT INDEX"]
16
+ assert_search "index", []
17
+ product = Product.first
18
+ product.name = "INDEX"
19
+ product.save!
20
+ Product.searchkick_index.refresh
21
+ assert_search "index", ["INDEX"]
22
+ end
23
+
24
+ def test_change_to_false
25
+ store_names ["INDEX"]
26
+ assert_search "index", ["INDEX"]
27
+ product = Product.first
28
+ product.name = "DO NOT INDEX"
29
+ product.save!
30
+ Product.searchkick_index.refresh
31
+ assert_search "index", []
32
+ end
33
+
34
+ end
@@ -0,0 +1,20 @@
1
+ require_relative "test_helper"
2
+
3
+ class TestSimilar < Minitest::Test
4
+
5
+ def test_similar
6
+ store_names ["Annie's Naturals Organic Shiitake & Sesame Dressing"]
7
+ assert_search "Annie's Naturals Shiitake & Sesame Vinaigrette", ["Annie's Naturals Organic Shiitake & Sesame Dressing"], similar: true
8
+ end
9
+
10
+ def test_fields
11
+ store_names ["1% Organic Milk", "2% Organic Milk", "Popcorn"]
12
+ assert_equal ["2% Organic Milk"], Product.where(name: "1% Organic Milk").first.similar(fields: ["name"]).map(&:name)
13
+ end
14
+
15
+ def test_order
16
+ store_names ["Lucerne Milk Chocolate Fat Free", "Clover Fat Free Milk"]
17
+ assert_order "Lucerne Fat Free Chocolate Milk", ["Lucerne Milk Chocolate Fat Free", "Clover Fat Free Milk"], similar: true
18
+ end
19
+
20
+ end
@@ -0,0 +1,327 @@
1
+ require_relative "test_helper"
2
+
3
+ class TestSql < Minitest::Test
4
+
5
+ def test_limit
6
+ store_names ["Product A", "Product B", "Product C", "Product D"]
7
+ assert_order "product", ["Product A", "Product B"], order: {name: :asc}, limit: 2
8
+ end
9
+
10
+ def test_no_limit
11
+ names = 20.times.map { |i| "Product #{i}" }
12
+ store_names names
13
+ assert_search "product", names
14
+ end
15
+
16
+ def test_offset
17
+ store_names ["Product A", "Product B", "Product C", "Product D"]
18
+ assert_order "product", ["Product C", "Product D"], order: {name: :asc}, offset: 2
19
+ end
20
+
21
+ def test_pagination
22
+ store_names ["Product A", "Product B", "Product C", "Product D", "Product E", "Product F"]
23
+ products = Product.search("product", order: {name: :asc}, page: 2, per_page: 2, padding: 1)
24
+ assert_equal ["Product D", "Product E"], products.map(&:name)
25
+ assert_equal "product", products.entry_name
26
+ assert_equal 2, products.current_page
27
+ assert_equal 1, products.padding
28
+ assert_equal 2, products.per_page
29
+ assert_equal 2, products.size
30
+ assert_equal 2, products.length
31
+ assert_equal 3, products.total_pages
32
+ assert_equal 6, products.total_count
33
+ assert_equal 6, products.total_entries
34
+ assert_equal 2, products.limit_value
35
+ assert_equal 3, products.offset_value
36
+ assert_equal 3, products.offset
37
+ assert_equal 3, products.next_page
38
+ assert_equal 1, products.previous_page
39
+ assert_equal 1, products.prev_page
40
+ assert !products.first_page?
41
+ assert !products.last_page?
42
+ assert !products.empty?
43
+ assert products.any?
44
+ end
45
+
46
+ def test_pagination_nil_page
47
+ store_names ["Product A", "Product B", "Product C", "Product D", "Product E"]
48
+ products = Product.search("product", order: {name: :asc}, page: nil, per_page: 2)
49
+ assert_equal ["Product A", "Product B"], products.map(&:name)
50
+ assert_equal 1, products.current_page
51
+ assert products.first_page?
52
+ end
53
+
54
+ def test_where
55
+ now = Time.now
56
+ store [
57
+ {name: "Product A", store_id: 1, in_stock: true, backordered: true, created_at: now, orders_count: 4, user_ids: [1, 2, 3]},
58
+ {name: "Product B", store_id: 2, in_stock: true, backordered: false, created_at: now - 1, orders_count: 3, user_ids: [1]},
59
+ {name: "Product C", store_id: 3, in_stock: false, backordered: true, created_at: now - 2, orders_count: 2, user_ids: [1, 3]},
60
+ {name: "Product D", store_id: 4, in_stock: false, backordered: false, created_at: now - 3, orders_count: 1}
61
+ ]
62
+ assert_search "product", ["Product A", "Product B"], where: {in_stock: true}
63
+ # date
64
+ assert_search "product", ["Product A"], where: {created_at: {gt: now - 1}}
65
+ assert_search "product", ["Product A", "Product B"], where: {created_at: {gte: now - 1}}
66
+ assert_search "product", ["Product D"], where: {created_at: {lt: now - 2}}
67
+ assert_search "product", ["Product C", "Product D"], where: {created_at: {lte: now - 2}}
68
+ # integer
69
+ assert_search "product", ["Product A"], where: {store_id: {lt: 2}}
70
+ assert_search "product", ["Product A", "Product B"], where: {store_id: {lte: 2}}
71
+ assert_search "product", ["Product D"], where: {store_id: {gt: 3}}
72
+ assert_search "product", ["Product C", "Product D"], where: {store_id: {gte: 3}}
73
+ # range
74
+ assert_search "product", ["Product A", "Product B"], where: {store_id: 1..2}
75
+ assert_search "product", ["Product A"], where: {store_id: 1...2}
76
+ assert_search "product", ["Product A", "Product B"], where: {store_id: [1, 2]}
77
+ assert_search "product", ["Product B", "Product C", "Product D"], where: {store_id: {not: 1}}
78
+ assert_search "product", ["Product C", "Product D"], where: {store_id: {not: [1, 2]}}
79
+ assert_search "product", ["Product A"], where: {user_ids: {lte: 2, gte: 2}}
80
+ # or
81
+ assert_search "product", ["Product A", "Product B", "Product C"], where: {or: [[{in_stock: true}, {store_id: 3}]]}
82
+ assert_search "product", ["Product A", "Product B", "Product C"], where: {or: [[{orders_count: [2, 4]}, {store_id: [1, 2]}]]}
83
+ assert_search "product", ["Product A", "Product D"], where: {or: [[{orders_count: 1}, {created_at: {gte: now - 1}, backordered: true}]]}
84
+ # all
85
+ assert_search "product", ["Product A", "Product C"], where: {user_ids: {all: [1, 3]}}
86
+ assert_search "product", [], where: {user_ids: {all: [1, 2, 3, 4]}}
87
+ # any / nested terms
88
+ assert_search "product", ["Product B", "Product C"], where: {user_ids: {not: [2], in: [1, 3]}}
89
+ # not / exists
90
+ assert_search "product", ["Product D"], where: {user_ids: nil}
91
+ assert_search "product", ["Product A", "Product B", "Product C"], where: {user_ids: {not: nil}}
92
+ assert_search "product", ["Product A", "Product C", "Product D"], where: {user_ids: [3, nil]}
93
+ assert_search "product", ["Product B"], where: {user_ids: {not: [3, nil]}}
94
+ end
95
+
96
+ def test_regexp
97
+ store_names ["Product A"]
98
+ assert_search "*", ["Product A"], where: {name: /Pro.+/}
99
+ end
100
+
101
+ def test_where_string
102
+ store [
103
+ {name: "Product A", color: "RED"}
104
+ ]
105
+ assert_search "product", ["Product A"], where: {color: "RED"}
106
+ end
107
+
108
+ def test_where_nil
109
+ store [
110
+ {name: "Product A"},
111
+ {name: "Product B", color: "red"}
112
+ ]
113
+ assert_search "product", ["Product A"], where: {color: nil}
114
+ end
115
+
116
+ def test_where_id
117
+ store_names ["Product A"]
118
+ product = Product.last
119
+ assert_search "product", ["Product A"], where: {id: product.id.to_s}
120
+ end
121
+
122
+ def test_where_empty
123
+ store_names ["Product A"]
124
+ assert_search "product", ["Product A"], where: {}
125
+ end
126
+
127
+ def test_where_empty_array
128
+ store_names ["Product A"]
129
+ assert_search "product", [], where: {store_id: []}
130
+ end
131
+
132
+ # http://elasticsearch-users.115913.n3.nabble.com/Numeric-range-quey-or-filter-in-an-array-field-possible-or-not-td4042967.html
133
+ # https://gist.github.com/jprante/7099463
134
+ def test_where_range_array
135
+ store [
136
+ {name: "Product A", user_ids: [11, 23, 13, 16, 17, 23.6]},
137
+ {name: "Product B", user_ids: [1, 2, 3, 4, 5, 6, 7, 8, 8.9, 9.1, 9.4]},
138
+ {name: "Product C", user_ids: [101, 230, 150, 200]}
139
+ ]
140
+ assert_search "product", ["Product A"], where: {user_ids: {gt: 10, lt: 23.9}}
141
+ end
142
+
143
+ def test_where_range_array_again
144
+ store [
145
+ {name: "Product A", user_ids: [19, 32, 42]},
146
+ {name: "Product B", user_ids: [13, 40, 52]}
147
+ ]
148
+ assert_search "product", ["Product A"], where: {user_ids: {gt: 26, lt: 36}}
149
+ end
150
+
151
+ def test_near
152
+ store [
153
+ {name: "San Francisco", latitude: 37.7833, longitude: -122.4167},
154
+ {name: "San Antonio", latitude: 29.4167, longitude: -98.5000}
155
+ ]
156
+ assert_search "san", ["San Francisco"], where: {location: {near: [37.5, -122.5]}}
157
+ end
158
+
159
+ def test_near_within
160
+ store [
161
+ {name: "San Francisco", latitude: 37.7833, longitude: -122.4167},
162
+ {name: "San Antonio", latitude: 29.4167, longitude: -98.5000},
163
+ {name: "San Marino", latitude: 43.9333, longitude: 12.4667}
164
+ ]
165
+ assert_search "san", ["San Francisco", "San Antonio"], where: {location: {near: [37, -122], within: "2000mi"}}
166
+ end
167
+
168
+ def test_top_left_bottom_right
169
+ store [
170
+ {name: "San Francisco", latitude: 37.7833, longitude: -122.4167},
171
+ {name: "San Antonio", latitude: 29.4167, longitude: -98.5000}
172
+ ]
173
+ assert_search "san", ["San Francisco"], where: {location: {top_left: [38, -123], bottom_right: [37, -122]}}
174
+ end
175
+
176
+ def test_multiple_locations
177
+ store [
178
+ {name: "San Francisco", latitude: 37.7833, longitude: -122.4167},
179
+ {name: "San Antonio", latitude: 29.4167, longitude: -98.5000}
180
+ ]
181
+ assert_search "san", ["San Francisco"], where: {multiple_locations: {near: [37.5, -122.5]}}
182
+ end
183
+
184
+ def test_order_hash
185
+ store_names ["Product A", "Product B", "Product C", "Product D"]
186
+ assert_order "product", ["Product D", "Product C", "Product B", "Product A"], order: {name: :desc}
187
+ end
188
+
189
+ def test_order_string
190
+ store_names ["Product A", "Product B", "Product C", "Product D"]
191
+ assert_order "product", ["Product A", "Product B", "Product C", "Product D"], order: "name"
192
+ end
193
+
194
+ def test_order_id
195
+ store_names ["Product A", "Product B"]
196
+ product_a = Product.where(name: "Product A").first
197
+ product_b = Product.where(name: "Product B").first
198
+ assert_order "product", [product_a, product_b].sort_by(&:id).map(&:name), order: {id: :asc}
199
+ end
200
+
201
+ def test_order_multiple
202
+ store [
203
+ {name: "Product A", color: "blue", store_id: 1},
204
+ {name: "Product B", color: "red", store_id: 3},
205
+ {name: "Product C", color: "red", store_id: 2}
206
+ ]
207
+ assert_order "product", ["Product A", "Product B", "Product C"], order: {color: :asc, store_id: :desc}
208
+ end
209
+
210
+ def test_order_ignore_unmapped
211
+ assert_order "product", [], order: {not_mapped: {ignore_unmapped: true}}, conversions: false
212
+ end
213
+
214
+ def test_order_array
215
+ store [{name: "San Francisco", latitude: 37.7833, longitude: -122.4167}]
216
+ assert_order "francisco", ["San Francisco"], order: [{_geo_distance: {location: "0,0"}}], conversions: false
217
+ end
218
+
219
+ def test_partial
220
+ store_names ["Honey"]
221
+ assert_search "fresh honey", []
222
+ assert_search "fresh honey", ["Honey"], partial: true
223
+ end
224
+
225
+ def test_operator
226
+ store_names ["Honey"]
227
+ assert_search "fresh honey", []
228
+ assert_search "fresh honey", ["Honey"], operator: "or"
229
+ end
230
+
231
+ def test_misspellings
232
+ store_names ["abc", "abd", "aee"]
233
+ assert_search "abc", ["abc"], misspellings: false
234
+ end
235
+
236
+ def test_misspellings_distance
237
+ store_names ["abbb", "aabb"]
238
+ assert_search "aaaa", ["aabb"], misspellings: {distance: 2}
239
+ end
240
+
241
+ def test_fields
242
+ store [
243
+ {name: "red", color: "light blue"},
244
+ {name: "blue", color: "red fish"}
245
+ ]
246
+ assert_search "blue", ["red"], fields: ["color"]
247
+ end
248
+
249
+ def test_non_existent_field
250
+ store_names ["Milk"]
251
+ assert_search "milk", [], fields: ["not_here"]
252
+ end
253
+
254
+ def test_fields_both_match
255
+ store [
256
+ {name: "Blue A", color: "red"},
257
+ {name: "Blue B", color: "light blue"}
258
+ ]
259
+ assert_first "blue", "Blue B", fields: [:name, :color]
260
+ end
261
+
262
+ def test_big_decimal
263
+ store [
264
+ {name: "Product", latitude: 100.0}
265
+ ]
266
+ assert_search "product", ["Product"], where: {latitude: {gt: 99}}
267
+ end
268
+
269
+ # load
270
+
271
+ def test_load_default
272
+ store_names ["Product A"]
273
+ assert_kind_of Product, Product.search("product").first
274
+ end
275
+
276
+ def test_load_false
277
+ store_names ["Product A"]
278
+ assert_kind_of Hash, Product.search("product", load: false).first
279
+ end
280
+
281
+ def test_load_false_methods
282
+ store_names ["Product A"]
283
+ assert_equal "Product A", Product.search("product", load: false).first.name
284
+ end
285
+
286
+ def test_load_false_with_include
287
+ store_names ["Product A"]
288
+ assert_kind_of Hash, Product.search("product", load: false, include: [:store]).first
289
+ end
290
+
291
+ # select
292
+
293
+ def test_select
294
+ store [{name: "Product A", store_id: 1}]
295
+ result = Product.search("product", load: false, select: [:name, :store_id]).first
296
+ assert_equal %w[id name store_id], result.keys.reject { |k| k.start_with?("_") }.sort
297
+ assert_equal ["Product A"], result.name # this is not great
298
+ end
299
+
300
+ def test_select_array
301
+ store [{name: "Product A", user_ids: [1, 2]}]
302
+ result = Product.search("product", load: false, select: [:user_ids]).first
303
+ assert_equal [1, 2], result.user_ids
304
+ end
305
+
306
+ def test_select_all
307
+ store [{name: "Product A", user_ids: [1, 2]}]
308
+ hit = Product.search("product", select: true).hits.first
309
+ assert_equal hit["_source"]["name"], "Product A"
310
+ assert_equal hit["_source"]["user_ids"], [1, 2]
311
+ end
312
+
313
+ def test_nested_object
314
+ aisle = {"id" => 1, "name" => "Frozen"}
315
+ store [{name: "Product A", aisle: aisle}]
316
+ assert_equal aisle, Product.search("product", load: false).first.aisle.to_hash
317
+ end
318
+
319
+ # TODO see if Mongoid is loaded
320
+ unless defined?(Mongoid) || defined?(NoBrainer)
321
+ def test_include
322
+ store_names ["Product A"]
323
+ assert Product.search("product", include: [:store]).first.association(:store).loaded?
324
+ end
325
+ end
326
+
327
+ end