searchkick 0.2.3 → 0.2.4
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 +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile +5 -0
- data/README.md +66 -18
- data/lib/searchkick.rb +1 -1
- data/lib/searchkick/model.rb +1 -1
- data/lib/searchkick/reindex.rb +23 -3
- data/lib/searchkick/search.rb +1 -1
- data/lib/searchkick/version.rb +1 -1
- data/test/index_test.rb +1 -1
- data/test/similar_test.rb +1 -1
- data/test/test_helper.rb +49 -34
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7e8d412864f6c87ba170611a9b2cdfb6d6f6cc8
|
4
|
+
data.tar.gz: 98d9385aa9ad6c967ccdae9924f15d8bf0c1ee69
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 00cfd0a2da2ce78127466636d5176c89356ef90311335b26f0050d58375cd3eb082113255a7eb3c71a5e0860e5c2b45a0a463363d1665e8e765ce14bb0d4d371
|
7
|
+
data.tar.gz: a90e126bfe9c460538ef89570e1da4512b93c804b45d43b01384c0bd5cb29d128db88668c3fa2fd009dcdc5abd4870f9216b6b6b3f4f580833703c42709e5d35
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -19,6 +19,7 @@ Plus:
|
|
19
19
|
- easily personalize results for each user
|
20
20
|
- autocomplete
|
21
21
|
- “Did you mean” suggestions
|
22
|
+
- works with ActiveRecord and Mongoid
|
22
23
|
|
23
24
|
:tangerine: Battle-tested at [Instacart](https://www.instacart.com)
|
24
25
|
|
@@ -134,24 +135,6 @@ To change this, use:
|
|
134
135
|
Product.search "fresh honey", partial: true # fresh OR honey
|
135
136
|
```
|
136
137
|
|
137
|
-
### Autocomplete
|
138
|
-
|
139
|
-

|
140
|
-
|
141
|
-
You must specify which fields use this feature since this can increase the index size significantly. Don’t worry - this gives you blazing faster queries.
|
142
|
-
|
143
|
-
```ruby
|
144
|
-
class Website < ActiveRecord::Base
|
145
|
-
searchkick autocomplete: ["title"]
|
146
|
-
end
|
147
|
-
```
|
148
|
-
|
149
|
-
Reindex and search with:
|
150
|
-
|
151
|
-
```ruby
|
152
|
-
Website.search "where", autocomplete: true
|
153
|
-
```
|
154
|
-
|
155
138
|
### Synonyms
|
156
139
|
|
157
140
|
```ruby
|
@@ -187,6 +170,19 @@ class Product < ActiveRecord::Base
|
|
187
170
|
end
|
188
171
|
```
|
189
172
|
|
173
|
+
### To Reindex, or Not to Reindex
|
174
|
+
|
175
|
+
#### Reindex
|
176
|
+
|
177
|
+
- when you install or upgrade searchkick
|
178
|
+
- change the `search_data` method
|
179
|
+
- change the `searchkick` method
|
180
|
+
|
181
|
+
#### No need to reindex
|
182
|
+
|
183
|
+
- App starts
|
184
|
+
- Records are inserted, updated or deleted (syncs automatically)
|
185
|
+
|
190
186
|
### Keep Getting Better
|
191
187
|
|
192
188
|
Searchkick uses conversion data to learn what users are looking for. If a user searches for “ice cream” and adds Ben & Jerry’s Chunky Monkey to the cart (our conversion metric at Instacart), that item gets a little more weight for similar searches.
|
@@ -250,6 +246,58 @@ Reindex and search with:
|
|
250
246
|
Product.search "milk", user_id: 8
|
251
247
|
```
|
252
248
|
|
249
|
+
### Autocomplete
|
250
|
+
|
251
|
+
Autocomplete predicts what a user will type, making the search experience faster and easier.
|
252
|
+
|
253
|
+

|
254
|
+
|
255
|
+
First, specify which fields use this feature. This is necessary since autocomplete can increase the index size significantly, but don’t worry - this gives you blazing faster queries.
|
256
|
+
|
257
|
+
```ruby
|
258
|
+
class City < ActiveRecord::Base
|
259
|
+
searchkick autocomplete: ["name"]
|
260
|
+
end
|
261
|
+
```
|
262
|
+
|
263
|
+
Reindex and search with:
|
264
|
+
|
265
|
+
```ruby
|
266
|
+
City.search "san fr", autocomplete: true
|
267
|
+
```
|
268
|
+
|
269
|
+
Typically, you want to use a Javascript library like [typeahead.js](http://twitter.github.io/typeahead.js/) or [jQuery UI](http://jqueryui.com/autocomplete/).
|
270
|
+
|
271
|
+
#### Here’s how to make it work with Rails
|
272
|
+
|
273
|
+
First, add a controller action.
|
274
|
+
|
275
|
+
```ruby
|
276
|
+
# app/controllers/cities_controller.rb
|
277
|
+
class CitiesController < ApplicationController
|
278
|
+
|
279
|
+
def autocomplete
|
280
|
+
render json: City.search(params[:q], autocomplete: true, limit: 10).map(&:name)
|
281
|
+
end
|
282
|
+
|
283
|
+
end
|
284
|
+
```
|
285
|
+
|
286
|
+
Then add the search box and Javascript code to a view.
|
287
|
+
|
288
|
+
```html
|
289
|
+
<input type="text" id="q" name="q" />
|
290
|
+
|
291
|
+
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
|
292
|
+
<script src="//cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.9.3/typeahead.min.js"></script>
|
293
|
+
<script>
|
294
|
+
$("#q").typeahead({
|
295
|
+
name: "city",
|
296
|
+
remote: "/cities/autocomplete?q=%QUERY"
|
297
|
+
});
|
298
|
+
</script>
|
299
|
+
```
|
300
|
+
|
253
301
|
### Suggestions
|
254
302
|
|
255
303
|

|
data/lib/searchkick.rb
CHANGED
@@ -9,5 +9,5 @@ require "searchkick/tasks"
|
|
9
9
|
require "searchkick/logger" if defined?(Rails)
|
10
10
|
|
11
11
|
# TODO find better ActiveModel hook
|
12
|
-
ActiveModel::
|
12
|
+
ActiveModel::Callbacks.send(:include, Searchkick::Model)
|
13
13
|
ActiveRecord::Base.send(:extend, Searchkick::Model) if defined?(ActiveRecord)
|
data/lib/searchkick/model.rb
CHANGED
data/lib/searchkick/reindex.rb
CHANGED
@@ -14,8 +14,22 @@ module Searchkick
|
|
14
14
|
|
15
15
|
# use scope for import
|
16
16
|
scope = respond_to?(:search_import) ? search_import : self
|
17
|
-
scope.find_in_batches
|
18
|
-
|
17
|
+
if scope.respond_to?(:find_in_batches)
|
18
|
+
scope.find_in_batches do |batch|
|
19
|
+
index.import batch
|
20
|
+
end
|
21
|
+
else
|
22
|
+
# https://github.com/karmi/tire/blob/master/lib/tire/model/import.rb
|
23
|
+
# use cursor for Mongoid
|
24
|
+
items = []
|
25
|
+
scope.all.each do |item|
|
26
|
+
items << item
|
27
|
+
if items.length % 1000 == 0
|
28
|
+
index.import items
|
29
|
+
items = []
|
30
|
+
end
|
31
|
+
end
|
32
|
+
index.import items
|
19
33
|
end
|
20
34
|
|
21
35
|
if a = Tire::Alias.find(alias_name)
|
@@ -121,7 +135,13 @@ module Searchkick
|
|
121
135
|
}
|
122
136
|
}
|
123
137
|
}
|
124
|
-
}
|
138
|
+
}
|
139
|
+
|
140
|
+
if ENV["RACK_ENV"] == "test"
|
141
|
+
settings.merge!(number_of_shards: 1, number_of_replicas: 0)
|
142
|
+
end
|
143
|
+
|
144
|
+
settings.merge!(options[:settings] || {})
|
125
145
|
|
126
146
|
# synonyms
|
127
147
|
synonyms = options[:synonyms] || []
|
data/lib/searchkick/search.rb
CHANGED
@@ -28,7 +28,7 @@ module Searchkick
|
|
28
28
|
page = [options[:page].to_i, 1].max
|
29
29
|
per_page = options[:limit] || options[:per_page] || 100000
|
30
30
|
offset = options[:offset] || (page - 1) * per_page
|
31
|
-
index_name = options[:index_name] || index.name
|
31
|
+
index_name = options[:index_name] || tire.index.name
|
32
32
|
|
33
33
|
conversions_field = @searchkick_options[:conversions]
|
34
34
|
personalize_field = @searchkick_options[:personalize]
|
data/lib/searchkick/version.rb
CHANGED
data/test/index_test.rb
CHANGED
data/test/similar_test.rb
CHANGED
@@ -4,7 +4,7 @@ class TestSimilar < Minitest::Unit::TestCase
|
|
4
4
|
|
5
5
|
def test_fields
|
6
6
|
store_names ["1% Organic Milk", "2% Organic Milk", "Popcorn"]
|
7
|
-
assert_equal ["2% Organic Milk"], Product.
|
7
|
+
assert_equal ["2% Organic Milk"], Product.where(name: "1% Organic Milk").first.similar(fields: ["name"]).map(&:name)
|
8
8
|
end
|
9
9
|
|
10
10
|
end
|
data/test/test_helper.rb
CHANGED
@@ -2,47 +2,65 @@ require "bundler/setup"
|
|
2
2
|
Bundler.require(:default)
|
3
3
|
require "minitest/autorun"
|
4
4
|
require "minitest/pride"
|
5
|
-
require "active_record"
|
6
5
|
|
7
6
|
ENV["RACK_ENV"] = "test"
|
8
7
|
|
9
|
-
|
10
|
-
|
8
|
+
File.delete("elasticsearch.log") if File.exists?("elasticsearch.log")
|
9
|
+
Tire.configure do
|
10
|
+
logger "elasticsearch.log", :level => "debug"
|
11
|
+
pretty true
|
12
|
+
end
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
if ENV["MONGOID"]
|
15
|
+
Mongoid.configure do |config|
|
16
|
+
config.connect_to "searchkick_test"
|
17
|
+
end
|
15
18
|
|
16
|
-
|
17
|
-
|
19
|
+
class Product
|
20
|
+
include Mongoid::Document
|
21
|
+
# include Mongoid::Attributes::Dynamic
|
22
|
+
end
|
18
23
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
class Store
|
25
|
+
include Mongoid::Document
|
26
|
+
end
|
27
|
+
else
|
28
|
+
require "active_record"
|
29
|
+
|
30
|
+
# for debugging
|
31
|
+
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
32
|
+
|
33
|
+
# rails does this in activerecord/lib/active_record/railtie.rb
|
34
|
+
ActiveRecord::Base.default_timezone = :utc
|
35
|
+
ActiveRecord::Base.time_zone_aware_attributes = true
|
36
|
+
|
37
|
+
# migrations
|
38
|
+
ActiveRecord::Base.establish_connection :adapter => "postgresql", :database => "searchkick_test"
|
39
|
+
|
40
|
+
ActiveRecord::Migration.create_table :products, :force => true do |t|
|
41
|
+
t.string :name
|
42
|
+
t.integer :store_id
|
43
|
+
t.boolean :in_stock
|
44
|
+
t.boolean :backordered
|
45
|
+
t.integer :orders_count
|
46
|
+
t.string :color
|
47
|
+
t.timestamps
|
48
|
+
end
|
28
49
|
|
29
|
-
ActiveRecord::Migration.create_table :store, :force => true do |t|
|
30
|
-
end
|
50
|
+
ActiveRecord::Migration.create_table :store, :force => true do |t|
|
51
|
+
end
|
31
52
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
53
|
+
class Product < ActiveRecord::Base
|
54
|
+
end
|
55
|
+
|
56
|
+
class Store < ActiveRecord::Base
|
57
|
+
end
|
36
58
|
end
|
37
59
|
|
38
|
-
class Product
|
60
|
+
class Product
|
39
61
|
belongs_to :store
|
40
62
|
|
41
63
|
searchkick \
|
42
|
-
settings: {
|
43
|
-
number_of_shards: 1,
|
44
|
-
number_of_replicas: 0
|
45
|
-
},
|
46
64
|
synonyms: [
|
47
65
|
["clorox", "bleach"],
|
48
66
|
["scallion", "greenonion"],
|
@@ -59,14 +77,11 @@ class Product < ActiveRecord::Base
|
|
59
77
|
attr_accessor :conversions, :user_ids
|
60
78
|
|
61
79
|
def search_data
|
62
|
-
|
80
|
+
to_hash.merge conversions: conversions, user_ids: user_ids
|
63
81
|
end
|
64
82
|
end
|
65
83
|
|
66
|
-
|
67
|
-
end
|
68
|
-
|
69
|
-
Product.index.delete if Product.index.exists?
|
84
|
+
Product.tire.index.delete if Product.tire.index.exists?
|
70
85
|
Product.reindex
|
71
86
|
Product.reindex # run twice for both index paths
|
72
87
|
|
@@ -82,7 +97,7 @@ class MiniTest::Unit::TestCase
|
|
82
97
|
documents.shuffle.each do |document|
|
83
98
|
Product.create!(document)
|
84
99
|
end
|
85
|
-
Product.index.refresh
|
100
|
+
Product.tire.index.refresh
|
86
101
|
end
|
87
102
|
|
88
103
|
def store_names(names)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: searchkick
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-08-
|
11
|
+
date: 2013-08-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: tire
|