searchkick_bharthur 0.0.1

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 (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.travis.yml +44 -0
  4. data/CHANGELOG.md +360 -0
  5. data/Gemfile +8 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +1443 -0
  8. data/Rakefile +8 -0
  9. data/lib/searchkick/index.rb +662 -0
  10. data/lib/searchkick/logging.rb +185 -0
  11. data/lib/searchkick/middleware.rb +12 -0
  12. data/lib/searchkick/model.rb +105 -0
  13. data/lib/searchkick/query.rb +845 -0
  14. data/lib/searchkick/reindex_job.rb +26 -0
  15. data/lib/searchkick/reindex_v2_job.rb +23 -0
  16. data/lib/searchkick/results.rb +211 -0
  17. data/lib/searchkick/tasks.rb +33 -0
  18. data/lib/searchkick/version.rb +3 -0
  19. data/lib/searchkick.rb +159 -0
  20. data/searchkick.gemspec +28 -0
  21. data/test/aggs_test.rb +115 -0
  22. data/test/autocomplete_test.rb +65 -0
  23. data/test/boost_test.rb +144 -0
  24. data/test/callbacks_test.rb +27 -0
  25. data/test/ci/before_install.sh +21 -0
  26. data/test/dangerous_reindex_test.rb +27 -0
  27. data/test/facets_test.rb +90 -0
  28. data/test/gemfiles/activerecord31.gemfile +7 -0
  29. data/test/gemfiles/activerecord32.gemfile +7 -0
  30. data/test/gemfiles/activerecord40.gemfile +8 -0
  31. data/test/gemfiles/activerecord41.gemfile +8 -0
  32. data/test/gemfiles/activerecord50.gemfile +7 -0
  33. data/test/gemfiles/apartment.gemfile +8 -0
  34. data/test/gemfiles/mongoid2.gemfile +7 -0
  35. data/test/gemfiles/mongoid3.gemfile +6 -0
  36. data/test/gemfiles/mongoid4.gemfile +7 -0
  37. data/test/gemfiles/mongoid5.gemfile +7 -0
  38. data/test/gemfiles/nobrainer.gemfile +6 -0
  39. data/test/highlight_test.rb +63 -0
  40. data/test/index_test.rb +120 -0
  41. data/test/inheritance_test.rb +78 -0
  42. data/test/match_test.rb +227 -0
  43. data/test/misspellings_test.rb +46 -0
  44. data/test/model_test.rb +42 -0
  45. data/test/multi_search_test.rb +22 -0
  46. data/test/multi_tenancy_test.rb +22 -0
  47. data/test/order_test.rb +44 -0
  48. data/test/pagination_test.rb +53 -0
  49. data/test/query_test.rb +13 -0
  50. data/test/records_test.rb +8 -0
  51. data/test/reindex_job_test.rb +31 -0
  52. data/test/reindex_v2_job_test.rb +32 -0
  53. data/test/routing_test.rb +13 -0
  54. data/test/should_index_test.rb +32 -0
  55. data/test/similar_test.rb +28 -0
  56. data/test/sql_test.rb +196 -0
  57. data/test/suggest_test.rb +80 -0
  58. data/test/synonyms_test.rb +54 -0
  59. data/test/test_helper.rb +361 -0
  60. data/test/where_test.rb +171 -0
  61. metadata +231 -0
data/test/sql_test.rb ADDED
@@ -0,0 +1,196 @@
1
+ require_relative "test_helper"
2
+
3
+ class SqlTest < Minitest::Test
4
+ def test_partial
5
+ store_names ["Honey"]
6
+ assert_search "fresh honey", []
7
+ assert_search "fresh honey", ["Honey"], partial: true
8
+ end
9
+
10
+ def test_operator
11
+ store_names ["Honey"]
12
+ assert_search "fresh honey", []
13
+ assert_search "fresh honey", ["Honey"], operator: "or"
14
+ end
15
+
16
+ def test_fields_operator
17
+ store [
18
+ {name: "red", color: "red"},
19
+ {name: "blue", color: "blue"},
20
+ {name: "cyan", color: "blue green"},
21
+ {name: "magenta", color: "red blue"},
22
+ {name: "green", color: "green"}
23
+ ]
24
+ assert_search "red blue", ["red", "blue", "cyan", "magenta"], operator: "or", fields: ["color"]
25
+ end
26
+
27
+ def test_fields
28
+ store [
29
+ {name: "red", color: "light blue"},
30
+ {name: "blue", color: "red fish"}
31
+ ]
32
+ assert_search "blue", ["red"], fields: ["color"]
33
+ end
34
+
35
+ def test_non_existent_field
36
+ store_names ["Milk"]
37
+ assert_search "milk", [], fields: ["not_here"]
38
+ end
39
+
40
+ def test_fields_both_match
41
+ store [
42
+ {name: "Blue A", color: "red"},
43
+ {name: "Blue B", color: "light blue"}
44
+ ]
45
+ assert_first "blue", "Blue B", fields: [:name, :color]
46
+ end
47
+
48
+ def test_big_decimal
49
+ store [
50
+ {name: "Product", latitude: 80.0}
51
+ ]
52
+ assert_search "product", ["Product"], where: {latitude: {gt: 79}}
53
+ end
54
+
55
+ # load
56
+
57
+ def test_load_default
58
+ store_names ["Product A"]
59
+ assert_kind_of Product, Product.search("product").first
60
+ end
61
+
62
+ def test_load_false
63
+ store_names ["Product A"]
64
+ assert_kind_of Hash, Product.search("product", load: false).first
65
+ end
66
+
67
+ def test_load_false_methods
68
+ store_names ["Product A"]
69
+ assert_equal "Product A", Product.search("product", load: false).first.name
70
+ end
71
+
72
+ def test_load_false_with_include
73
+ store_names ["Product A"]
74
+ assert_kind_of Hash, Product.search("product", load: false, include: [:store]).first
75
+ end
76
+
77
+ # select
78
+
79
+ def test_select
80
+ skip unless elasticsearch_below50?
81
+ store [{name: "Product A", store_id: 1}]
82
+ result = Product.search("product", load: false, select: [:name, :store_id]).first
83
+ assert_equal %w(id name store_id), result.keys.reject { |k| k.start_with?("_") }.sort
84
+ assert_equal ["Product A"], result.name # this is not great
85
+ assert_equal [1], result.store_id
86
+ end
87
+
88
+ def test_select_array
89
+ skip unless elasticsearch_below50?
90
+ store [{name: "Product A", user_ids: [1, 2]}]
91
+ result = Product.search("product", load: false, select: [:user_ids]).first
92
+ assert_equal [1, 2], result.user_ids
93
+ end
94
+
95
+ def test_select_single_field
96
+ skip unless elasticsearch_below50?
97
+ store [{name: "Product A", store_id: 1}]
98
+ result = Product.search("product", load: false, select: :name).first
99
+ assert_equal %w(id name), result.keys.reject { |k| k.start_with?("_") }.sort
100
+ assert_equal ["Product A"], result.name
101
+ assert_nil result.store_id
102
+ end
103
+
104
+ def test_select_all
105
+ skip unless elasticsearch_below50?
106
+ store [{name: "Product A", user_ids: [1, 2]}]
107
+ hit = Product.search("product", select: true).hits.first
108
+ assert_equal hit["_source"]["name"], "Product A"
109
+ assert_equal hit["_source"]["user_ids"], [1, 2]
110
+ end
111
+
112
+ def test_select_none
113
+ skip unless elasticsearch_below50?
114
+ store [{name: "Product A", user_ids: [1, 2]}]
115
+ hit = Product.search("product", select: []).hits.first
116
+ assert_nil hit["_source"]
117
+ end
118
+
119
+ # select_v2
120
+
121
+ def test_select_v2
122
+ store [{name: "Product A", store_id: 1}]
123
+ result = Product.search("product", load: false, select_v2: [:name, :store_id]).first
124
+ assert_equal %w(id name store_id), result.keys.reject { |k| k.start_with?("_") }.sort
125
+ assert_equal "Product A", result.name
126
+ assert_equal 1, result.store_id
127
+ end
128
+
129
+ def test_select_v2_array
130
+ store [{name: "Product A", user_ids: [1, 2]}]
131
+ result = Product.search("product", load: false, select_v2: [:user_ids]).first
132
+ assert_equal [1, 2], result.user_ids
133
+ end
134
+
135
+ def test_select_v2_single_field
136
+ store [{name: "Product A", store_id: 1}]
137
+ result = Product.search("product", load: false, select_v2: :name).first
138
+ assert_equal %w(id name), result.keys.reject { |k| k.start_with?("_") }.sort
139
+ assert_equal "Product A", result.name
140
+ assert_nil result.store_id
141
+ end
142
+
143
+ def test_select_v2_all
144
+ store [{name: "Product A", user_ids: [1, 2]}]
145
+ hit = Product.search("product", select_v2: true).hits.first
146
+ assert_equal hit["_source"]["name"], "Product A"
147
+ assert_equal hit["_source"]["user_ids"], [1, 2]
148
+ end
149
+
150
+ def test_select_v2_none
151
+ store [{name: "Product A", user_ids: [1, 2]}]
152
+ hit = Product.search("product", select_v2: []).hits.first
153
+ assert_nil hit["_source"]
154
+ hit = Product.search("product", select_v2: false).hits.first
155
+ assert_nil hit["_source"]
156
+ end
157
+
158
+ def test_select_v2_include
159
+ store [{name: "Product A", user_ids: [1, 2]}]
160
+ result = Product.search("product", load: false, select_v2: {include: [:name]}).first
161
+ assert_equal %w(id name), result.keys.reject { |k| k.start_with?("_") }.sort
162
+ assert_equal "Product A", result.name
163
+ assert_nil result.store_id
164
+ end
165
+
166
+ def test_select_v2_exclude
167
+ store [{name: "Product A", user_ids: [1, 2], store_id: 1}]
168
+ result = Product.search("product", load: false, select_v2: {exclude: [:name]}).first
169
+ assert_nil result.name
170
+ assert_equal [1, 2], result.user_ids
171
+ assert_equal 1, result.store_id
172
+ end
173
+
174
+ def test_select_v2_include_and_exclude
175
+ # let's take this to the next level
176
+ store [{name: "Product A", user_ids: [1, 2], store_id: 1}]
177
+ result = Product.search("product", load: false, select_v2: {include: [:store_id], exclude: [:name]}).first
178
+ assert_equal 1, result.store_id
179
+ assert_nil result.name
180
+ assert_nil result.user_ids
181
+ end
182
+
183
+ # other tests
184
+
185
+ def test_nested_object
186
+ aisle = {"id" => 1, "name" => "Frozen"}
187
+ store [{name: "Product A", aisle: aisle}]
188
+ assert_equal aisle, Product.search("product", load: false).first.aisle.to_hash
189
+ end
190
+
191
+ def test_include
192
+ skip unless defined?(ActiveRecord)
193
+ store_names ["Product A"]
194
+ assert Product.search("product", include: [:store]).first.association(:store).loaded?
195
+ end
196
+ end
@@ -0,0 +1,80 @@
1
+ require_relative "test_helper"
2
+
3
+ class SuggestTest < Minitest::Test
4
+ def test_basic
5
+ store_names ["Great White Shark", "Hammerhead Shark", "Tiger Shark"]
6
+ assert_suggest "How Big is a Tigre Shar", "how big is a tiger shark", fields: [:name]
7
+ end
8
+
9
+ def test_perfect
10
+ store_names ["Tiger Shark", "Great White Shark"]
11
+ assert_suggest "Tiger Shark", nil, fields: [:name] # no correction
12
+ end
13
+
14
+ def test_phrase
15
+ store_names ["Big Tiger Shark", "Tiger Sharp Teeth", "Tiger Sharp Mind"]
16
+ assert_suggest "How to catch a big tiger shar", "how to catch a big tiger shark", fields: [:name]
17
+ end
18
+
19
+ def test_without_option
20
+ store_names ["hi"] # needed to prevent ElasticsearchException - seed 668
21
+ assert_raises(RuntimeError) { Product.search("hi").suggestions }
22
+ end
23
+
24
+ def test_multiple_fields
25
+ store [
26
+ {name: "Shark", color: "Sharp"},
27
+ {name: "Shark", color: "Sharp"}
28
+ ]
29
+ assert_suggest_all "shar", ["shark", "sharp"]
30
+ end
31
+
32
+ def test_multiple_fields_highest_score_first
33
+ store [
34
+ {name: "Tiger Shark", color: "Sharp"}
35
+ ]
36
+ assert_suggest "tiger shar", "tiger shark"
37
+ end
38
+
39
+ def test_multiple_fields_same_value
40
+ store [
41
+ {name: "Shark", color: "Shark"}
42
+ ]
43
+ assert_suggest_all "shar", ["shark"]
44
+ end
45
+
46
+ def test_fields_option
47
+ store [
48
+ {name: "Shark", color: "Sharp"}
49
+ ]
50
+ assert_suggest_all "shar", ["shark"], fields: [:name]
51
+ end
52
+
53
+ def test_fields_option_multiple
54
+ store [
55
+ {name: "Shark"}
56
+ ]
57
+ assert_suggest "shar", "shark", fields: [:name, :unknown]
58
+ end
59
+
60
+ def test_fields_partial_match
61
+ store_names ["Great White Shark", "Hammerhead Shark", "Tiger Shark"]
62
+ assert_suggest "How Big is a Tigre Shar", "how big is a tiger shark", fields: [{name: :word_start}]
63
+ end
64
+
65
+ def test_fields_partial_match_boost
66
+ store_names ["Great White Shark", "Hammerhead Shark", "Tiger Shark"]
67
+ assert_suggest "How Big is a Tigre Shar", "how big is a tiger shark", fields: [{"name^2" => :word_start}]
68
+ end
69
+
70
+ protected
71
+
72
+ def assert_suggest(term, expected, options = {})
73
+ assert_equal expected, Product.search(term, options.merge(suggest: true)).suggestions.first
74
+ end
75
+
76
+ # any order
77
+ def assert_suggest_all(term, expected, options = {})
78
+ assert_equal expected.sort, Product.search(term, options.merge(suggest: true)).suggestions.sort
79
+ end
80
+ end
@@ -0,0 +1,54 @@
1
+ require_relative "test_helper"
2
+
3
+ class SynonymsTest < Minitest::Test
4
+ def test_bleach
5
+ store_names ["Clorox Bleach", "Kroger Bleach"]
6
+ assert_search "clorox", ["Clorox Bleach", "Kroger Bleach"]
7
+ end
8
+
9
+ def test_saran_wrap
10
+ store_names ["Saran Wrap", "Kroger Plastic Wrap"]
11
+ assert_search "saran wrap", ["Saran Wrap", "Kroger Plastic Wrap"]
12
+ end
13
+
14
+ def test_burger_buns
15
+ store_names ["Hamburger Buns"]
16
+ assert_search "burger buns", ["Hamburger Buns"]
17
+ end
18
+
19
+ def test_bandaids
20
+ store_names ["Band-Aid", "Kroger 12-Pack Bandages"]
21
+ assert_search "bandaids", ["Band-Aid", "Kroger 12-Pack Bandages"]
22
+ end
23
+
24
+ def test_qtips
25
+ store_names ["Q Tips", "Kroger Cotton Swabs"]
26
+ assert_search "q tips", ["Q Tips", "Kroger Cotton Swabs"]
27
+ end
28
+
29
+ def test_reverse
30
+ store_names ["Scallions"]
31
+ assert_search "green onions", ["Scallions"]
32
+ end
33
+
34
+ def test_exact
35
+ store_names ["Green Onions", "Yellow Onions"]
36
+ assert_search "scallion", ["Green Onions"]
37
+ end
38
+
39
+ def test_stemmed
40
+ store_names ["Green Onions", "Yellow Onions"]
41
+ assert_search "scallions", ["Green Onions"]
42
+ end
43
+
44
+ def test_word_start
45
+ store_names ["Clorox Bleach", "Kroger Bleach"]
46
+ assert_search "clorox", ["Clorox Bleach", "Kroger Bleach"], fields: [{name: :word_start}]
47
+ end
48
+
49
+ def test_wordnet
50
+ skip unless ENV["TEST_WORDNET"]
51
+ store_names ["Creature", "Beast", "Dragon"], Animal
52
+ assert_search "animal", ["Creature", "Beast"], {}, Animal
53
+ end
54
+ end
@@ -0,0 +1,361 @@
1
+ require "bundler/setup"
2
+ Bundler.require(:default)
3
+ require "minitest/autorun"
4
+ require "minitest/pride"
5
+ require "logger"
6
+ require "active_support/core_ext" if defined?(NoBrainer)
7
+ require "active_support/notifications"
8
+
9
+ ENV["RACK_ENV"] = "test"
10
+
11
+ Minitest::Test = Minitest::Unit::TestCase unless defined?(Minitest::Test)
12
+
13
+ File.delete("elasticsearch.log") if File.exist?("elasticsearch.log")
14
+ Searchkick.client.transport.logger = Logger.new("elasticsearch.log")
15
+ Searchkick.search_timeout = 5
16
+
17
+ puts "Running against Elasticsearch #{Searchkick.server_version}"
18
+
19
+ I18n.config.enforce_available_locales = true
20
+
21
+ ActiveJob::Base.logger = nil if defined?(ActiveJob)
22
+ ActiveSupport::LogSubscriber.logger = Logger.new(STDOUT) if ENV["NOTIFICATIONS"]
23
+
24
+ def elasticsearch_below50?
25
+ Searchkick.server_below?("5.0.0-alpha1")
26
+ end
27
+
28
+ def elasticsearch_below20?
29
+ Searchkick.server_below?("2.0.0")
30
+ end
31
+
32
+ def elasticsearch_below14?
33
+ Searchkick.server_below?("1.4.0")
34
+ end
35
+
36
+ def mongoid2?
37
+ defined?(Mongoid) && Mongoid::VERSION.starts_with?("2.")
38
+ end
39
+
40
+ def nobrainer?
41
+ defined?(NoBrainer)
42
+ end
43
+
44
+ def activerecord_below41?
45
+ defined?(ActiveRecord) && Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new("4.1.0")
46
+ end
47
+
48
+ if defined?(Mongoid)
49
+ Mongoid.logger.level = Logger::INFO
50
+ Mongo::Logger.logger.level = Logger::INFO if defined?(Mongo::Logger)
51
+
52
+ if mongoid2?
53
+ # enable comparison of BSON::ObjectIds
54
+ module BSON
55
+ class ObjectId
56
+ def <=>(other)
57
+ data <=> other.data
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ Mongoid.configure do |config|
64
+ if mongoid2?
65
+ config.master = Mongo::Connection.new.db("searchkick_test")
66
+ else
67
+ config.connect_to "searchkick_test"
68
+ end
69
+ end
70
+
71
+ class Product
72
+ include Mongoid::Document
73
+ include Mongoid::Timestamps
74
+
75
+ field :name
76
+ field :store_id, type: Integer
77
+ field :in_stock, type: Boolean
78
+ field :backordered, type: Boolean
79
+ field :orders_count, type: Integer
80
+ field :found_rate, type: BigDecimal
81
+ field :price, type: Integer
82
+ field :color
83
+ field :latitude, type: BigDecimal
84
+ field :longitude, type: BigDecimal
85
+ field :description
86
+ field :alt_description
87
+ end
88
+
89
+ class Store
90
+ include Mongoid::Document
91
+ has_many :products
92
+
93
+ field :name
94
+ end
95
+
96
+ class Animal
97
+ include Mongoid::Document
98
+
99
+ field :name
100
+ end
101
+
102
+ class Dog < Animal
103
+ end
104
+
105
+ class Cat < Animal
106
+ end
107
+ elsif defined?(NoBrainer)
108
+ NoBrainer.configure do |config|
109
+ config.app_name = :searchkick
110
+ config.environment = :test
111
+ end
112
+
113
+ class Product
114
+ include NoBrainer::Document
115
+ include NoBrainer::Document::Timestamps
116
+
117
+ field :id, type: Object
118
+ field :name, type: String
119
+ field :in_stock, type: Boolean
120
+ field :backordered, type: Boolean
121
+ field :orders_count, type: Integer
122
+ field :found_rate
123
+ field :price, type: Integer
124
+ field :color, type: String
125
+ field :latitude
126
+ field :longitude
127
+ field :description, type: String
128
+ field :alt_description, type: String
129
+
130
+ belongs_to :store, validates: false
131
+ end
132
+
133
+ class Store
134
+ include NoBrainer::Document
135
+
136
+ field :id, type: Object
137
+ field :name, type: String
138
+ end
139
+
140
+ class Animal
141
+ include NoBrainer::Document
142
+
143
+ field :id, type: Object
144
+ field :name, type: String
145
+ end
146
+
147
+ class Dog < Animal
148
+ end
149
+
150
+ class Cat < Animal
151
+ end
152
+ else
153
+ require "active_record"
154
+
155
+ # for debugging
156
+ # ActiveRecord::Base.logger = Logger.new(STDOUT)
157
+
158
+ # rails does this in activerecord/lib/active_record/railtie.rb
159
+ ActiveRecord::Base.default_timezone = :utc
160
+ ActiveRecord::Base.time_zone_aware_attributes = true
161
+
162
+ # migrations
163
+ ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
164
+
165
+ ActiveRecord::Base.raise_in_transactional_callbacks = true if ActiveRecord::Base.respond_to?(:raise_in_transactional_callbacks=)
166
+
167
+ if defined?(Apartment)
168
+ class Rails
169
+ def self.env
170
+ ENV["RACK_ENV"]
171
+ end
172
+ end
173
+
174
+ tenants = ["tenant1", "tenant2"]
175
+ Apartment.configure do |config|
176
+ config.tenant_names = tenants
177
+ config.database_schema_file = false
178
+ config.excluded_models = ["Product", "Store", "Animal", "Dog", "Cat"]
179
+ end
180
+
181
+ class Tenant < ActiveRecord::Base
182
+ searchkick index_prefix: -> { Apartment::Tenant.current }
183
+ end
184
+
185
+ tenants.each do |tenant|
186
+ begin
187
+ Apartment::Tenant.create(tenant)
188
+ rescue Apartment::TenantExists
189
+ # do nothing
190
+ end
191
+ Apartment::Tenant.switch!(tenant)
192
+
193
+ ActiveRecord::Migration.create_table :tenants, force: true do |t|
194
+ t.string :name
195
+ t.timestamps null: true
196
+ end
197
+
198
+ Tenant.reindex
199
+ end
200
+
201
+ Apartment::Tenant.reset
202
+ end
203
+
204
+ ActiveRecord::Migration.create_table :products do |t|
205
+ t.string :name
206
+ t.integer :store_id
207
+ t.boolean :in_stock
208
+ t.boolean :backordered
209
+ t.integer :orders_count
210
+ t.decimal :found_rate
211
+ t.integer :price
212
+ t.string :color
213
+ t.decimal :latitude, precision: 10, scale: 7
214
+ t.decimal :longitude, precision: 10, scale: 7
215
+ t.text :description
216
+ t.text :alt_description
217
+ t.timestamps null: true
218
+ end
219
+
220
+ ActiveRecord::Migration.create_table :stores do |t|
221
+ t.string :name
222
+ end
223
+
224
+ ActiveRecord::Migration.create_table :animals do |t|
225
+ t.string :name
226
+ t.string :type
227
+ end
228
+
229
+ class Product < ActiveRecord::Base
230
+ end
231
+
232
+ class Store < ActiveRecord::Base
233
+ has_many :products
234
+ end
235
+
236
+ class Animal < ActiveRecord::Base
237
+ end
238
+
239
+ class Dog < Animal
240
+ end
241
+
242
+ class Cat < Animal
243
+ end
244
+ end
245
+
246
+ class Product
247
+ belongs_to :store
248
+
249
+ searchkick \
250
+ synonyms: [
251
+ ["clorox", "bleach"],
252
+ ["scallion", "greenonion"],
253
+ ["saranwrap", "plasticwrap"],
254
+ ["qtip", "cottonswab"],
255
+ ["burger", "hamburger"],
256
+ ["bandaid", "bandag"]
257
+ ],
258
+ autocomplete: [:name],
259
+ suggest: [:name, :color],
260
+ conversions: "conversions",
261
+ personalize: "user_ids",
262
+ locations: ["location", "multiple_locations"],
263
+ text_start: [:name],
264
+ text_middle: [:name],
265
+ text_end: [:name],
266
+ word_start: [:name],
267
+ word_middle: [:name],
268
+ word_end: [:name],
269
+ highlight: [:name],
270
+ # unsearchable: [:description],
271
+ searchable: [:name, :color],
272
+ only_analyzed: [:alt_description],
273
+ match: ENV["MATCH"] ? ENV["MATCH"].to_sym : nil
274
+
275
+ attr_accessor :conversions, :user_ids, :aisle
276
+
277
+ def search_data
278
+ serializable_hash.except("id").merge(
279
+ conversions: conversions,
280
+ user_ids: user_ids,
281
+ location: {lat: latitude, lon: longitude},
282
+ multiple_locations: [{lat: latitude, lon: longitude}, {lat: 0, lon: 0}],
283
+ aisle: aisle
284
+ )
285
+ end
286
+
287
+ def should_index?
288
+ name != "DO NOT INDEX"
289
+ end
290
+ end
291
+
292
+ class Store
293
+ searchkick \
294
+ routing: true,
295
+ merge_mappings: true,
296
+ mappings: {
297
+ store: {
298
+ properties: {
299
+ name: elasticsearch_below50? ? {type: "string", analyzer: "keyword"} : {type: "keyword"}
300
+ }
301
+ }
302
+ }
303
+
304
+ def search_document_id
305
+ id
306
+ end
307
+
308
+ def search_routing
309
+ name
310
+ end
311
+ end
312
+
313
+ class Animal
314
+ searchkick \
315
+ autocomplete: [:name],
316
+ suggest: [:name],
317
+ index_name: -> { "#{name.tableize}-#{Date.today.year}" }
318
+ # wordnet: true
319
+ end
320
+
321
+ Product.searchkick_index.delete if Product.searchkick_index.exists?
322
+ Product.reindex
323
+ Product.reindex # run twice for both index paths
324
+ Product.create!(name: "Set mapping")
325
+
326
+ Store.reindex
327
+ Animal.reindex
328
+
329
+ class Minitest::Test
330
+ def setup
331
+ Product.destroy_all
332
+ Store.destroy_all
333
+ Animal.destroy_all
334
+ end
335
+
336
+ protected
337
+
338
+ def store(documents, klass = Product)
339
+ documents.shuffle.each do |document|
340
+ klass.create!(document)
341
+ end
342
+ klass.searchkick_index.refresh
343
+ end
344
+
345
+ def store_names(names, klass = Product)
346
+ store names.map { |name| {name: name} }, klass
347
+ end
348
+
349
+ # no order
350
+ def assert_search(term, expected, options = {}, klass = Product)
351
+ assert_equal expected.sort, klass.search(term, options).map(&:name).sort
352
+ end
353
+
354
+ def assert_order(term, expected, options = {}, klass = Product)
355
+ assert_equal expected, klass.search(term, options).map(&:name)
356
+ end
357
+
358
+ def assert_first(term, expected, options = {}, klass = Product)
359
+ assert_equal expected, klass.search(term, options).map(&:name).first
360
+ end
361
+ end