searchkick 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
![Autocomplete](http://ankane.github.io/searchkick/autocomplete.png)
|
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
|
+
![Autocomplete](http://ankane.github.io/searchkick/autocomplete.png)
|
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
|
![Suggest](http://ankane.github.io/searchkick/recursion.png)
|
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
|