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,82 @@
1
+ require_relative "test_helper"
2
+
3
+ class TestSuggest < Minitest::Test
4
+
5
+ def test_basic
6
+ store_names ["Great White Shark", "Hammerhead Shark", "Tiger Shark"]
7
+ assert_suggest "How Big is a Tigre Shar", "how big is a tiger shark", fields: [:name]
8
+ end
9
+
10
+ def test_perfect
11
+ store_names ["Tiger Shark", "Great White Shark"]
12
+ assert_suggest "Tiger Shark", nil, fields: [:name] # no correction
13
+ end
14
+
15
+ def test_phrase
16
+ store_names ["Big Tiger Shark", "Tiger Sharp Teeth", "Tiger Sharp Mind"]
17
+ assert_suggest "How to catch a big tiger shar", "how to catch a big tiger shark", fields: [:name]
18
+ end
19
+
20
+ def test_without_option
21
+ store_names ["hi"] # needed to prevent ElasticsearchException - seed 668
22
+ assert_raises(RuntimeError) { Product.search("hi").suggestions }
23
+ end
24
+
25
+ def test_multiple_fields
26
+ store [
27
+ {name: "Shark", color: "Sharp"},
28
+ {name: "Shark", color: "Sharp"}
29
+ ]
30
+ assert_suggest_all "shar", ["shark", "sharp"]
31
+ end
32
+
33
+ def test_multiple_fields_highest_score_first
34
+ store [
35
+ {name: "Tiger Shark", color: "Sharp"}
36
+ ]
37
+ assert_suggest "tiger shar", "tiger shark"
38
+ end
39
+
40
+ def test_multiple_fields_same_value
41
+ store [
42
+ {name: "Shark", color: "Shark"}
43
+ ]
44
+ assert_suggest_all "shar", ["shark"]
45
+ end
46
+
47
+ def test_fields_option
48
+ store [
49
+ {name: "Shark", color: "Sharp"}
50
+ ]
51
+ assert_suggest_all "shar", ["shark"], fields: [:name]
52
+ end
53
+
54
+ def test_fields_option_multiple
55
+ store [
56
+ {name: "Shark"}
57
+ ]
58
+ assert_suggest "shar", "shark", fields: [:name, :unknown]
59
+ end
60
+
61
+ def test_fields_partial_match
62
+ store_names ["Great White Shark", "Hammerhead Shark", "Tiger Shark"]
63
+ assert_suggest "How Big is a Tigre Shar", "how big is a tiger shark", fields: [{name: :word_start}]
64
+ end
65
+
66
+ def test_fields_partial_match_boost
67
+ store_names ["Great White Shark", "Hammerhead Shark", "Tiger Shark"]
68
+ assert_suggest "How Big is a Tigre Shar", "how big is a tiger shark", fields: [{"name^2" => :word_start}]
69
+ end
70
+
71
+ protected
72
+
73
+ def assert_suggest(term, expected, options = {})
74
+ assert_equal expected, Product.search(term, options.merge(suggest: true)).suggestions.first
75
+ end
76
+
77
+ # any order
78
+ def assert_suggest_all(term, expected, options = {})
79
+ assert_equal expected.sort, Product.search(term, options.merge(suggest: true)).suggestions.sort
80
+ end
81
+
82
+ end
@@ -0,0 +1,50 @@
1
+ require_relative "test_helper"
2
+
3
+ class TestSynonyms < Minitest::Test
4
+
5
+ def test_bleach
6
+ store_names ["Clorox Bleach", "Kroger Bleach"]
7
+ assert_search "clorox", ["Clorox Bleach", "Kroger Bleach"]
8
+ end
9
+
10
+ def test_saran_wrap
11
+ store_names ["Saran Wrap", "Kroger Plastic Wrap"]
12
+ assert_search "saran wrap", ["Saran Wrap", "Kroger Plastic Wrap"]
13
+ end
14
+
15
+ def test_burger_buns
16
+ store_names ["Hamburger Buns"]
17
+ assert_search "burger buns", ["Hamburger Buns"]
18
+ end
19
+
20
+ def test_bandaids
21
+ store_names ["Band-Aid", "Kroger 12-Pack Bandages"]
22
+ assert_search "bandaids", ["Band-Aid", "Kroger 12-Pack Bandages"]
23
+ end
24
+
25
+ def test_qtips
26
+ store_names ["Q Tips", "Kroger Cotton Swabs"]
27
+ assert_search "q tips", ["Q Tips", "Kroger Cotton Swabs"]
28
+ end
29
+
30
+ def test_reverse
31
+ store_names ["Scallions"]
32
+ assert_search "green onions", ["Scallions"]
33
+ end
34
+
35
+ def test_exact
36
+ store_names ["Green Onions", "Yellow Onions"]
37
+ assert_search "scallion", ["Green Onions"]
38
+ end
39
+
40
+ def test_stemmed
41
+ store_names ["Green Onions", "Yellow Onions"]
42
+ assert_search "scallions", ["Green Onions"]
43
+ end
44
+
45
+ # def test_wordnet
46
+ # store_names ["Creature", "Beast", "Dragon"], Animal
47
+ # assert_search "animal", ["Creature", "Beast"], {}, Animal
48
+ # end
49
+
50
+ end
@@ -0,0 +1,276 @@
1
+ require "bundler/setup"
2
+ Bundler.require(:default)
3
+ require "minitest/autorun"
4
+ require "minitest/pride"
5
+ require "logger"
6
+
7
+ ENV["RACK_ENV"] = "test"
8
+
9
+ Minitest::Test = Minitest::Unit::TestCase unless defined?(Minitest::Test)
10
+
11
+ File.delete("elasticsearch.log") if File.exist?("elasticsearch.log")
12
+ Searchkick.client.transport.logger = Logger.new("elasticsearch.log")
13
+
14
+ puts "Running against Elasticsearch #{Searchkick.server_version}"
15
+
16
+ I18n.config.enforce_available_locales = true
17
+
18
+ ActiveJob::Base.logger = nil if defined?(ActiveJob)
19
+
20
+ if defined?(Mongoid)
21
+
22
+ def mongoid2?
23
+ Mongoid::VERSION.starts_with?("2.")
24
+ end
25
+
26
+ if mongoid2?
27
+ # enable comparison of BSON::ObjectIds
28
+ module BSON
29
+ class ObjectId
30
+ def <=>(other)
31
+ data <=> other.data
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ Mongoid.configure do |config|
38
+ if mongoid2?
39
+ config.master = Mongo::Connection.new.db("searchkick_test")
40
+ else
41
+ config.connect_to "searchkick_test"
42
+ end
43
+ end
44
+
45
+ class Product
46
+ include Mongoid::Document
47
+ include Mongoid::Timestamps
48
+
49
+ field :name
50
+ field :store_id, type: Integer
51
+ field :in_stock, type: Boolean
52
+ field :backordered, type: Boolean
53
+ field :orders_count, type: Integer
54
+ field :price, type: Integer
55
+ field :color
56
+ field :latitude, type: BigDecimal
57
+ field :longitude, type: BigDecimal
58
+ field :description
59
+ end
60
+
61
+ class Store
62
+ include Mongoid::Document
63
+
64
+ field :name
65
+ end
66
+
67
+ class Animal
68
+ include Mongoid::Document
69
+
70
+ field :name
71
+ end
72
+
73
+ class Dog < Animal
74
+ end
75
+
76
+ class Cat < Animal
77
+ end
78
+ elsif defined?(NoBrainer)
79
+ NoBrainer.configure do |config|
80
+ config.app_name = :searchkick
81
+ config.environment = :test
82
+ end
83
+
84
+ class Product
85
+ include NoBrainer::Document
86
+ include NoBrainer::Document::Timestamps
87
+
88
+ field :name, type: String
89
+ field :store_id, type: Integer
90
+ field :in_stock, type: Boolean
91
+ field :backordered, type: Boolean
92
+ field :orders_count, type: Integer
93
+ field :price, type: Integer
94
+ field :color, type: String
95
+ field :latitude
96
+ field :longitude
97
+ field :description, type: String
98
+ end
99
+
100
+ class Store
101
+ include NoBrainer::Document
102
+
103
+ field :name, type: String
104
+ end
105
+
106
+ class Animal
107
+ include NoBrainer::Document
108
+
109
+ field :name, type: String
110
+ end
111
+
112
+ class Dog < Animal
113
+ end
114
+
115
+ class Cat < Animal
116
+ end
117
+ else
118
+ require "active_record"
119
+
120
+ # for debugging
121
+ # ActiveRecord::Base.logger = Logger.new(STDOUT)
122
+
123
+ # rails does this in activerecord/lib/active_record/railtie.rb
124
+ ActiveRecord::Base.default_timezone = :utc
125
+ ActiveRecord::Base.time_zone_aware_attributes = true
126
+
127
+ # migrations
128
+ ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
129
+
130
+ ActiveRecord::Base.raise_in_transactional_callbacks = true if ActiveRecord::Base.respond_to?(:raise_in_transactional_callbacks=)
131
+
132
+ ActiveRecord::Migration.create_table :products do |t|
133
+ t.string :name
134
+ t.integer :store_id
135
+ t.boolean :in_stock
136
+ t.boolean :backordered
137
+ t.integer :orders_count
138
+ t.integer :price
139
+ t.string :color
140
+ t.decimal :latitude, precision: 10, scale: 7
141
+ t.decimal :longitude, precision: 10, scale: 7
142
+ t.text :description
143
+ t.timestamps null: true
144
+ end
145
+
146
+ ActiveRecord::Migration.create_table :stores do |t|
147
+ t.string :name
148
+ end
149
+
150
+ ActiveRecord::Migration.create_table :animals do |t|
151
+ t.string :name
152
+ t.string :type
153
+ end
154
+
155
+ class Product < ActiveRecord::Base
156
+ end
157
+
158
+ class Store < ActiveRecord::Base
159
+ end
160
+
161
+ class Animal < ActiveRecord::Base
162
+ end
163
+
164
+ class Dog < Animal
165
+ end
166
+
167
+ class Cat < Animal
168
+ end
169
+ end
170
+
171
+ class Product
172
+ belongs_to :store
173
+
174
+ searchkick \
175
+ synonyms: [
176
+ ["clorox", "bleach"],
177
+ ["scallion", "greenonion"],
178
+ ["saranwrap", "plasticwrap"],
179
+ ["qtip", "cottonswab"],
180
+ ["burger", "hamburger"],
181
+ ["bandaid", "bandag"]
182
+ ],
183
+ autocomplete: [:name],
184
+ suggest: [:name, :color],
185
+ conversions: "conversions",
186
+ personalize: "user_ids",
187
+ locations: ["location", "multiple_locations"],
188
+ text_start: [:name],
189
+ text_middle: [:name],
190
+ text_end: [:name],
191
+ word_start: [:name],
192
+ word_middle: [:name],
193
+ word_end: [:name],
194
+ highlight: [:name],
195
+ unsearchable: [:description]
196
+
197
+ attr_accessor :conversions, :user_ids, :aisle
198
+
199
+ def search_data
200
+ serializable_hash.except("id").merge(
201
+ conversions: conversions,
202
+ user_ids: user_ids,
203
+ location: [latitude, longitude],
204
+ multiple_locations: [[latitude, longitude], [0, 0]],
205
+ aisle: aisle
206
+ )
207
+ end
208
+
209
+ def should_index?
210
+ name != "DO NOT INDEX"
211
+ end
212
+ end
213
+
214
+ class Store
215
+ searchkick \
216
+ routing: :name,
217
+ merge_mappings: true,
218
+ mappings: {
219
+ store: {
220
+ properties: {
221
+ name: {type: "string", analyzer: "keyword"},
222
+ }
223
+ }
224
+ }
225
+ end
226
+
227
+ class Animal
228
+ searchkick \
229
+ autocomplete: [:name],
230
+ suggest: [:name],
231
+ index_name: -> { "#{name.tableize}-#{Date.today.year}" }
232
+ # wordnet: true
233
+ end
234
+
235
+ Product.searchkick_index.delete if Product.searchkick_index.exists?
236
+ Product.reindex
237
+ Product.reindex # run twice for both index paths
238
+
239
+ Store.reindex
240
+ Animal.reindex
241
+
242
+ class Minitest::Test
243
+
244
+ def setup
245
+ Product.destroy_all
246
+ Store.destroy_all
247
+ Animal.destroy_all
248
+ end
249
+
250
+ protected
251
+
252
+ def store(documents, klass = Product)
253
+ documents.shuffle.each do |document|
254
+ klass.create!(document)
255
+ end
256
+ klass.searchkick_index.refresh
257
+ end
258
+
259
+ def store_names(names, klass = Product)
260
+ store names.map { |name| {name: name} }, klass
261
+ end
262
+
263
+ # no order
264
+ def assert_search(term, expected, options = {}, klass = Product)
265
+ assert_equal expected.sort, klass.search(term, options).map(&:name).sort
266
+ end
267
+
268
+ def assert_order(term, expected, options = {}, klass = Product)
269
+ assert_equal expected, klass.search(term, options).map(&:name)
270
+ end
271
+
272
+ def assert_first(term, expected, options = {}, klass = Product)
273
+ assert_equal expected, klass.search(term, options).map(&:name).first
274
+ end
275
+
276
+ end
metadata ADDED
@@ -0,0 +1,194 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: searchkick-sinneduy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Kane
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activemodel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: elasticsearch-sinneduy
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.13
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.0.13
41
+ - !ruby/object:Gem::Dependency
42
+ name: hashie
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.6'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Intelligent search made easy
98
+ email:
99
+ - andrew@chartkick.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - .gitignore
105
+ - .travis.yml
106
+ - CHANGELOG.md
107
+ - Gemfile
108
+ - LICENSE.txt
109
+ - README.md
110
+ - Rakefile
111
+ - ci/before_install.sh
112
+ - gemfiles/activerecord31.gemfile
113
+ - gemfiles/activerecord32.gemfile
114
+ - gemfiles/activerecord40.gemfile
115
+ - gemfiles/activerecord41.gemfile
116
+ - gemfiles/mongoid2.gemfile
117
+ - gemfiles/mongoid3.gemfile
118
+ - gemfiles/mongoid4.gemfile
119
+ - gemfiles/nobrainer.gemfile
120
+ - lib/searchkick.rb
121
+ - lib/searchkick/index.rb
122
+ - lib/searchkick/logging.rb
123
+ - lib/searchkick/model.rb
124
+ - lib/searchkick/query.rb
125
+ - lib/searchkick/reindex_job.rb
126
+ - lib/searchkick/reindex_v2_job.rb
127
+ - lib/searchkick/results.rb
128
+ - lib/searchkick/tasks.rb
129
+ - lib/searchkick/version.rb
130
+ - searchkick.gemspec
131
+ - test/autocomplete_test.rb
132
+ - test/boost_test.rb
133
+ - test/facets_test.rb
134
+ - test/highlight_test.rb
135
+ - test/index_test.rb
136
+ - test/inheritance_test.rb
137
+ - test/match_test.rb
138
+ - test/model_test.rb
139
+ - test/query_test.rb
140
+ - test/reindex_job_test.rb
141
+ - test/reindex_v2_job_test.rb
142
+ - test/routing_test.rb
143
+ - test/should_index_test.rb
144
+ - test/similar_test.rb
145
+ - test/sql_test.rb
146
+ - test/suggest_test.rb
147
+ - test/synonyms_test.rb
148
+ - test/test_helper.rb
149
+ homepage: https://github.com/ankane/searchkick
150
+ licenses:
151
+ - MIT
152
+ metadata: {}
153
+ post_install_message:
154
+ rdoc_options: []
155
+ require_paths:
156
+ - lib
157
+ required_ruby_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - '>='
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ required_rubygems_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubyforge_project:
169
+ rubygems_version: 2.0.14
170
+ signing_key:
171
+ specification_version: 4
172
+ summary: Searchkick learns what your users are looking for. As more people search,
173
+ it gets smarter and the results get better. It’s friendly for developers - and magical
174
+ for your users.
175
+ test_files:
176
+ - test/autocomplete_test.rb
177
+ - test/boost_test.rb
178
+ - test/facets_test.rb
179
+ - test/highlight_test.rb
180
+ - test/index_test.rb
181
+ - test/inheritance_test.rb
182
+ - test/match_test.rb
183
+ - test/model_test.rb
184
+ - test/query_test.rb
185
+ - test/reindex_job_test.rb
186
+ - test/reindex_v2_job_test.rb
187
+ - test/routing_test.rb
188
+ - test/should_index_test.rb
189
+ - test/similar_test.rb
190
+ - test/sql_test.rb
191
+ - test/suggest_test.rb
192
+ - test/synonyms_test.rb
193
+ - test/test_helper.rb
194
+ has_rdoc: