searchkick 0.5.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +50 -7
- data/lib/searchkick/model.rb +10 -10
- data/lib/searchkick/reindex.rb +9 -5
- data/lib/searchkick/search.rb +8 -3
- data/lib/searchkick/version.rb +1 -1
- data/test/match_test.rb +5 -0
- data/test/sql_test.rb +26 -0
- data/test/test_helper.rb +2 -1
- 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: c2afa6ea99c2995b003f51cbe409392097fdfa8e
|
4
|
+
data.tar.gz: dc2e4add51195454636f94db1ae6417c2a8933bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1895d905c2b916de3e5e5fb5640d8132318f0dccffa553cfd6770d1c38d84bcd5dc9c92d1749711fa16f0bfdf677962318a5ec50e366f91e0dd1324121bf169f
|
7
|
+
data.tar.gz: 8c9bc21cf09a320ee676d2307f7ebbdbf1cc2f2dce37633e799f6282ee2f3f1d8e5ba0af1696c23c2271f401ca1173b4803779f6f50f43cd8674db15c7483620
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
## 0.5.1
|
2
|
+
|
3
|
+
- Replaced stop words with common terms query
|
4
|
+
- Added language option
|
5
|
+
- Fixed bug with empty array in where clause
|
6
|
+
- Fixed bug with MongoDB integer _id
|
7
|
+
- Fixed reindex bug when callbacks disabled
|
8
|
+
|
1
9
|
## 0.5.0
|
2
10
|
|
3
11
|
- Better control over partial matches
|
data/README.md
CHANGED
@@ -21,12 +21,14 @@ Plus:
|
|
21
21
|
- “Did you mean” suggestions
|
22
22
|
- works with ActiveRecord and Mongoid
|
23
23
|
|
24
|
-
:zap: Even better with [Searchjoy](http://ankane.github.io/searchjoy/)
|
25
|
-
|
26
24
|
:tangerine: Battle-tested at [Instacart](https://www.instacart.com)
|
27
25
|
|
28
26
|
[![Build Status](https://travis-ci.org/ankane/searchkick.png?branch=master)](https://travis-ci.org/ankane/searchkick)
|
29
27
|
|
28
|
+
We highly recommend tracking queries and conversions
|
29
|
+
|
30
|
+
:zap: [Searchjoy](http://ankane.github.io/searchjoy/) makes it easy
|
31
|
+
|
30
32
|
## Get Started
|
31
33
|
|
32
34
|
[Install Elasticsearch](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/setup.html). For Homebrew, use:
|
@@ -174,6 +176,18 @@ Available options are:
|
|
174
176
|
:text_end
|
175
177
|
```
|
176
178
|
|
179
|
+
### Language
|
180
|
+
|
181
|
+
Searchkick defaults to English for stemming. To change this, use:
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
class Product < ActiveRecord::Base
|
185
|
+
searchkick language: "German"
|
186
|
+
end
|
187
|
+
```
|
188
|
+
|
189
|
+
[See the list of languages](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/analysis-snowball-tokenfilter.html)
|
190
|
+
|
177
191
|
### Synonyms
|
178
192
|
|
179
193
|
```ruby
|
@@ -219,7 +233,7 @@ Searchkick uses `find_in_batches` to import documents. To eager load associatio
|
|
219
233
|
|
220
234
|
```ruby
|
221
235
|
class Product < ActiveRecord::Base
|
222
|
-
scope :search_import, includes(:searches)
|
236
|
+
scope :search_import, -> { includes(:searches) }
|
223
237
|
end
|
224
238
|
```
|
225
239
|
|
@@ -556,7 +570,7 @@ And use the `query` option to search:
|
|
556
570
|
Product.search query: {match: {name: "milk"}}
|
557
571
|
```
|
558
572
|
|
559
|
-
|
573
|
+
To keep the mappings and settings generated by Searchkick, use:
|
560
574
|
|
561
575
|
```ruby
|
562
576
|
class Product < ActiveRecord::Base
|
@@ -608,9 +622,9 @@ end
|
|
608
622
|
or temporarily
|
609
623
|
|
610
624
|
```ruby
|
611
|
-
Product.disable_search_callbacks # use Searchkick.disable_callbacks for all models
|
625
|
+
Product.disable_search_callbacks # or use Searchkick.disable_callbacks for all models
|
612
626
|
ExpensiveProductsTask.execute
|
613
|
-
Product.enable_search_callbacks
|
627
|
+
Product.enable_search_callbacks # or use Searchkick.enable_callbacks for all models
|
614
628
|
Product.reindex
|
615
629
|
```
|
616
630
|
|
@@ -635,7 +649,7 @@ class Product < ActiveRecord::Base
|
|
635
649
|
end
|
636
650
|
```
|
637
651
|
|
638
|
-
Change import batch size
|
652
|
+
Change import batch size
|
639
653
|
|
640
654
|
```ruby
|
641
655
|
class Product < ActiveRecord::Base
|
@@ -643,6 +657,35 @@ class Product < ActiveRecord::Base
|
|
643
657
|
end
|
644
658
|
```
|
645
659
|
|
660
|
+
Reindex conditionally
|
661
|
+
|
662
|
+
```ruby
|
663
|
+
class Product < ActiveRecord::Base
|
664
|
+
searchkick callbacks: false
|
665
|
+
|
666
|
+
# add the callbacks manually
|
667
|
+
after_save :reindex, if: proc{|model| model.name_changed? } # use your own condition
|
668
|
+
after_destroy :reindex
|
669
|
+
end
|
670
|
+
```
|
671
|
+
|
672
|
+
Asynchronous reindexing
|
673
|
+
|
674
|
+
```ruby
|
675
|
+
class Product < ActiveRecord::Base
|
676
|
+
searchkick callbacks: false
|
677
|
+
|
678
|
+
# add the callbacks manually
|
679
|
+
after_save :reindex_async
|
680
|
+
after_destroy :reindex_async
|
681
|
+
|
682
|
+
def reindex_async
|
683
|
+
# delayed job
|
684
|
+
delay.reindex
|
685
|
+
end
|
686
|
+
end
|
687
|
+
```
|
688
|
+
|
646
689
|
Reindex all models (Rails only)
|
647
690
|
|
648
691
|
```sh
|
data/lib/searchkick/model.rb
CHANGED
@@ -19,8 +19,8 @@ module Searchkick
|
|
19
19
|
extend Searchkick::Reindex
|
20
20
|
include Searchkick::Similar
|
21
21
|
|
22
|
-
after_save :reindex
|
23
|
-
after_destroy :reindex
|
22
|
+
after_save :reindex, if: proc { self.class.search_callbacks? }
|
23
|
+
after_destroy :reindex, if: proc { self.class.search_callbacks? }
|
24
24
|
|
25
25
|
def self.enable_search_callbacks
|
26
26
|
class_variable_set :@@searchkick_callbacks, true
|
@@ -39,13 +39,11 @@ module Searchkick
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def reindex
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
index.store self
|
48
|
-
end
|
42
|
+
index = self.class.searchkick_index
|
43
|
+
if destroyed? or !should_index?
|
44
|
+
index.remove self
|
45
|
+
else
|
46
|
+
index.store self
|
49
47
|
end
|
50
48
|
end
|
51
49
|
|
@@ -60,7 +58,9 @@ module Searchkick
|
|
60
58
|
source = source.inject({}){|memo,(k,v)| memo[k.to_s] = v; memo}
|
61
59
|
|
62
60
|
# Mongoid 4 hack
|
63
|
-
|
61
|
+
if defined?(BSON::ObjectId) and source["_id"].is_a?(BSON::ObjectId)
|
62
|
+
source["_id"] = source["_id"].to_s
|
63
|
+
end
|
64
64
|
|
65
65
|
options = self.class.searchkick_options
|
66
66
|
|
data/lib/searchkick/reindex.rb
CHANGED
@@ -94,24 +94,24 @@ module Searchkick
|
|
94
94
|
searchkick_keyword: {
|
95
95
|
type: "custom",
|
96
96
|
tokenizer: "keyword",
|
97
|
-
filter: ["lowercase", "
|
97
|
+
filter: ["lowercase", "searchkick_stemmer"]
|
98
98
|
},
|
99
99
|
default_index: {
|
100
100
|
type: "custom",
|
101
101
|
tokenizer: "standard",
|
102
102
|
# synonym should come last, after stemming and shingle
|
103
|
-
# shingle must come before
|
104
|
-
filter: ["standard", "lowercase", "asciifolding", "
|
103
|
+
# shingle must come before searchkick_stemmer
|
104
|
+
filter: ["standard", "lowercase", "asciifolding", "searchkick_index_shingle", "searchkick_stemmer"]
|
105
105
|
},
|
106
106
|
searchkick_search: {
|
107
107
|
type: "custom",
|
108
108
|
tokenizer: "standard",
|
109
|
-
filter: ["standard", "lowercase", "asciifolding", "
|
109
|
+
filter: ["standard", "lowercase", "asciifolding", "searchkick_search_shingle", "searchkick_stemmer"]
|
110
110
|
},
|
111
111
|
searchkick_search2: {
|
112
112
|
type: "custom",
|
113
113
|
tokenizer: "standard",
|
114
|
-
filter: ["standard", "lowercase", "asciifolding", "
|
114
|
+
filter: ["standard", "lowercase", "asciifolding", "searchkick_stemmer"]
|
115
115
|
},
|
116
116
|
# https://github.com/leschenko/elasticsearch_autocomplete/blob/master/lib/elasticsearch_autocomplete/analyzers.rb
|
117
117
|
searchkick_autocomplete_index: {
|
@@ -190,6 +190,10 @@ module Searchkick
|
|
190
190
|
type: "nGram",
|
191
191
|
min_gram: 1,
|
192
192
|
max_gram: 50
|
193
|
+
},
|
194
|
+
searchkick_stemmer: {
|
195
|
+
type: "snowball",
|
196
|
+
language: options[:language] || "English"
|
193
197
|
}
|
194
198
|
},
|
195
199
|
tokenizer: {
|
data/lib/searchkick/search.rb
CHANGED
@@ -77,7 +77,8 @@ module Searchkick
|
|
77
77
|
fields: [field],
|
78
78
|
query: term,
|
79
79
|
use_dis_max: false,
|
80
|
-
operator: operator
|
80
|
+
operator: operator,
|
81
|
+
cutoff_frequency: 0.001
|
81
82
|
}
|
82
83
|
queries.concat [
|
83
84
|
{multi_match: shared_options.merge(boost: 10, analyzer: "searchkick_search")},
|
@@ -178,13 +179,17 @@ module Searchkick
|
|
178
179
|
# order
|
179
180
|
if options[:order]
|
180
181
|
order = options[:order].is_a?(Enumerable) ? options[:order] : {options[:order] => :asc}
|
181
|
-
payload[:sort] = order
|
182
|
+
payload[:sort] = Hash[ order.map{|k, v| [k.to_s == "id" ? :_id : k, v] } ]
|
182
183
|
end
|
183
184
|
|
184
185
|
term_filters =
|
185
186
|
proc do |field, value|
|
186
187
|
if value.is_a?(Array) # in query
|
187
|
-
|
188
|
+
if value.any?
|
189
|
+
{or: value.map{|v| term_filters.call(field, v) }}
|
190
|
+
else
|
191
|
+
{terms: {field => value}} # match nothing
|
192
|
+
end
|
188
193
|
elsif value.nil?
|
189
194
|
{missing: {"field" => field, existence: true, null_value: true}}
|
190
195
|
else
|
data/lib/searchkick/version.rb
CHANGED
data/test/match_test.rb
CHANGED
data/test/sql_test.rb
CHANGED
@@ -95,6 +95,16 @@ class TestSql < Minitest::Unit::TestCase
|
|
95
95
|
assert_search "product", ["Product A"], where: {id: product.id.to_s}
|
96
96
|
end
|
97
97
|
|
98
|
+
def test_where_empty
|
99
|
+
store_names ["Product A"]
|
100
|
+
assert_search "product", ["Product A"], where: {}
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_where_empty_array
|
104
|
+
store_names ["Product A"]
|
105
|
+
assert_search "product", [], where: {store_id: []}
|
106
|
+
end
|
107
|
+
|
98
108
|
def test_near
|
99
109
|
store [
|
100
110
|
{name: "San Francisco", latitude: 37.7833, longitude: -122.4167},
|
@@ -138,6 +148,22 @@ class TestSql < Minitest::Unit::TestCase
|
|
138
148
|
assert_order "product", ["Product A", "Product B", "Product C", "Product D"], order: "name"
|
139
149
|
end
|
140
150
|
|
151
|
+
def test_order_id
|
152
|
+
store_names ["Product A", "Product B"]
|
153
|
+
product_a = Product.where(name: "Product A").first
|
154
|
+
product_b = Product.where(name: "Product B").first
|
155
|
+
assert_order "product", [product_a, product_b].sort_by(&:id).map(&:name), order: {id: :asc}
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_order_multiple
|
159
|
+
store [
|
160
|
+
{name: "Product A", color: "blue", store_id: 1},
|
161
|
+
{name: "Product B", color: "red", store_id: 3},
|
162
|
+
{name: "Product C", color: "red", store_id: 2}
|
163
|
+
]
|
164
|
+
assert_order "product", ["Product A", "Product B", "Product C"], order: {color: :asc, store_id: :desc}
|
165
|
+
end
|
166
|
+
|
141
167
|
def test_partial
|
142
168
|
store_names ["Honey"]
|
143
169
|
assert_search "fresh honey", []
|
data/test/test_helper.rb
CHANGED
@@ -126,7 +126,7 @@ class Product
|
|
126
126
|
attr_accessor :conversions, :user_ids
|
127
127
|
|
128
128
|
def search_data
|
129
|
-
serializable_hash.merge conversions: conversions, user_ids: user_ids, location: [latitude, longitude], multiple_locations: [[latitude, longitude], [0, 0]]
|
129
|
+
serializable_hash.except("id").merge conversions: conversions, user_ids: user_ids, location: [latitude, longitude], multiple_locations: [[latitude, longitude], [0, 0]]
|
130
130
|
end
|
131
131
|
|
132
132
|
def should_index?
|
@@ -159,6 +159,7 @@ class Minitest::Unit::TestCase
|
|
159
159
|
|
160
160
|
def setup
|
161
161
|
Product.destroy_all
|
162
|
+
Store.destroy_all
|
162
163
|
Animal.destroy_all
|
163
164
|
end
|
164
165
|
|
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.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-02-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: tire
|