searchkick 3.0.0 → 3.0.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 +6 -0
- data/Gemfile +3 -3
- data/README.md +7 -7
- data/lib/searchkick/bulk_indexer.rb +13 -10
- data/lib/searchkick/index.rb +17 -10
- data/lib/searchkick/index_options.rb +26 -1
- data/lib/searchkick/query.rb +7 -5
- data/lib/searchkick/version.rb +1 -1
- data/test/language_test.rb +50 -6
- data/test/reindex_test.rb +5 -0
- data/test/synonyms_test.rb +3 -1
- data/test/test_helper.rb +13 -2
- data/test/where_test.rb +5 -0
- 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: bf72e82ba04fdb0dad02352ce84d8d7844b9097d
|
4
|
+
data.tar.gz: 53739b3ac5968665fb53d73f6966b63e2c9bf3c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f1e26f41eda3b67dae70f0fe0502f46bef521565bdf69b70d1762430292e941f1fb581f6a485779705afe869f2728d9bd5e18abcd7537de1bcb2307831a84c75
|
7
|
+
data.tar.gz: 03c79e33d55fd712d4b75ff9830d00c9fa78acdc65aeb80a78cc23931055ecec0a3e892d239491236ecedc7184c8d37a062a0ece6a45779b1244e9117eed28d9
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
+
## 3.0.1
|
2
|
+
|
3
|
+
- Added `scope` option for partial reindex
|
4
|
+
- Added support for Japanese, Polish, and Ukranian
|
5
|
+
|
1
6
|
## 3.0.0
|
2
7
|
|
3
8
|
- Added support for Chinese
|
9
|
+
- No longer requires fields to query for Elasticsearch 6
|
4
10
|
- Results can be marshaled by default (unless using `highlight` option)
|
5
11
|
|
6
12
|
Breaking changes
|
data/Gemfile
CHANGED
@@ -4,13 +4,13 @@ source "https://rubygems.org"
|
|
4
4
|
gemspec
|
5
5
|
|
6
6
|
gem "sqlite3"
|
7
|
-
gem "activerecord"
|
7
|
+
gem "activerecord"
|
8
8
|
gem "gemoji-parser"
|
9
9
|
gem "typhoeus"
|
10
|
-
gem "activejob"
|
10
|
+
gem "activejob"
|
11
11
|
gem "redis"
|
12
12
|
gem "connection_pool"
|
13
13
|
|
14
14
|
# kaminari
|
15
|
-
gem "actionpack"
|
15
|
+
gem "actionpack"
|
16
16
|
gem "kaminari"
|
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
|
+
- supports many languages
|
22
23
|
- works with ActiveRecord, Mongoid, and NoBrainer
|
23
24
|
|
24
25
|
:speech_balloon: Get [handcrafted updates](http://chartkick.us7.list-manage.com/subscribe?u=952c861f99eb43084e0a49f98&id=6ea6541e8e&group[0][4]=true) for new features
|
@@ -299,13 +300,12 @@ end
|
|
299
300
|
|
300
301
|
[See the list of stemmers](https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-stemmer-tokenfilter.html)
|
301
302
|
|
302
|
-
|
303
|
+
A few languages require plugins:
|
303
304
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
```
|
305
|
+
- `chinese` - [elasticsearch-analysis-ik plugin](https://github.com/medcl/elasticsearch-analysis-ik)
|
306
|
+
- `japanese` - [analysis-kuromoji plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/6.2/analysis-kuromoji.html)
|
307
|
+
- `polish` - [analysis-stempel plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/6.2/analysis-stempel.html)
|
308
|
+
- `ukrainian` - [analysis-ukrainian plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/6.2/analysis-ukrainian.html)
|
309
309
|
|
310
310
|
### Synonyms
|
311
311
|
|
@@ -1344,7 +1344,7 @@ end
|
|
1344
1344
|
Create a job to update the cache and reindex records with new conversions.
|
1345
1345
|
|
1346
1346
|
```ruby
|
1347
|
-
class ReindexConversionsJob <
|
1347
|
+
class ReindexConversionsJob < ApplicationJob
|
1348
1348
|
def perform(class_name)
|
1349
1349
|
# get records that have a recent conversion
|
1350
1350
|
recently_converted_ids =
|
@@ -6,31 +6,34 @@ module Searchkick
|
|
6
6
|
@index = index
|
7
7
|
end
|
8
8
|
|
9
|
-
def import_scope(
|
10
|
-
|
11
|
-
|
9
|
+
def import_scope(relation, resume: false, method_name: nil, async: false, batch: false, batch_id: nil, full: false, scope: nil)
|
10
|
+
if scope
|
11
|
+
relation = relation.send(scope)
|
12
|
+
elsif relation.respond_to?(:search_import)
|
13
|
+
relation = relation.search_import
|
14
|
+
end
|
12
15
|
|
13
16
|
if batch
|
14
|
-
import_or_update
|
17
|
+
import_or_update relation.to_a, method_name, async
|
15
18
|
Searchkick.with_redis { |r| r.srem(batches_key, batch_id) } if batch_id
|
16
19
|
elsif full && async
|
17
|
-
full_reindex_async(
|
18
|
-
elsif
|
20
|
+
full_reindex_async(relation)
|
21
|
+
elsif relation.respond_to?(:find_in_batches)
|
19
22
|
if resume
|
20
23
|
# use total docs instead of max id since there's not a great way
|
21
24
|
# to get the max _id without scripting since it's a string
|
22
25
|
|
23
26
|
# TODO use primary key and prefix with table name
|
24
|
-
|
27
|
+
relation = relation.where("id > ?", total_docs)
|
25
28
|
end
|
26
29
|
|
27
|
-
|
30
|
+
relation = relation.select("id").except(:includes, :preload) if async
|
28
31
|
|
29
|
-
|
32
|
+
relation.find_in_batches batch_size: batch_size do |items|
|
30
33
|
import_or_update items, method_name, async
|
31
34
|
end
|
32
35
|
else
|
33
|
-
each_batch(
|
36
|
+
each_batch(relation) do |items|
|
34
37
|
import_or_update items, method_name, async
|
35
38
|
end
|
36
39
|
end
|
data/lib/searchkick/index.rb
CHANGED
@@ -178,22 +178,22 @@ module Searchkick
|
|
178
178
|
|
179
179
|
# reindex
|
180
180
|
|
181
|
-
def reindex(
|
181
|
+
def reindex(relation, method_name, scoped:, full: false, scope: nil, **options)
|
182
182
|
refresh = options.fetch(:refresh, !scoped)
|
183
183
|
|
184
184
|
if method_name
|
185
185
|
# update
|
186
|
-
import_scope(
|
186
|
+
import_scope(relation, method_name: method_name, scope: scope)
|
187
187
|
self.refresh if refresh
|
188
188
|
true
|
189
189
|
elsif scoped && !full
|
190
190
|
# reindex association
|
191
|
-
import_scope(scope)
|
191
|
+
import_scope(relation, scope: scope)
|
192
192
|
self.refresh if refresh
|
193
193
|
true
|
194
194
|
else
|
195
195
|
# full reindex
|
196
|
-
reindex_scope(scope, options)
|
196
|
+
reindex_scope(relation, scope: scope, **options)
|
197
197
|
end
|
198
198
|
end
|
199
199
|
|
@@ -204,8 +204,8 @@ module Searchkick
|
|
204
204
|
index
|
205
205
|
end
|
206
206
|
|
207
|
-
def import_scope(
|
208
|
-
bulk_indexer.import_scope(
|
207
|
+
def import_scope(relation, **options)
|
208
|
+
bulk_indexer.import_scope(relation, **options)
|
209
209
|
end
|
210
210
|
|
211
211
|
def batches_left
|
@@ -257,7 +257,7 @@ module Searchkick
|
|
257
257
|
|
258
258
|
# https://gist.github.com/jarosan/3124884
|
259
259
|
# http://www.elasticsearch.org/blog/changing-mapping-with-zero-downtime/
|
260
|
-
def reindex_scope(
|
260
|
+
def reindex_scope(relation, import: true, resume: false, retain: false, async: false, refresh_interval: nil, scope: nil)
|
261
261
|
if resume
|
262
262
|
index_name = all_indices.sort.last
|
263
263
|
raise Searchkick::Error, "No index to resume" unless index_name
|
@@ -265,16 +265,23 @@ module Searchkick
|
|
265
265
|
else
|
266
266
|
clean_indices unless retain
|
267
267
|
|
268
|
-
index_options =
|
268
|
+
index_options = relation.searchkick_index_options
|
269
269
|
index_options.deep_merge!(settings: {index: {refresh_interval: refresh_interval}}) if refresh_interval
|
270
270
|
index = create_index(index_options: index_options)
|
271
271
|
end
|
272
272
|
|
273
|
+
import_options = {
|
274
|
+
resume: resume,
|
275
|
+
async: async,
|
276
|
+
full: true,
|
277
|
+
scope: scope
|
278
|
+
}
|
279
|
+
|
273
280
|
# check if alias exists
|
274
281
|
alias_exists = alias_exists?
|
275
282
|
if alias_exists
|
276
283
|
# import before promotion
|
277
|
-
index.import_scope(
|
284
|
+
index.import_scope(relation, **import_options) if import
|
278
285
|
|
279
286
|
# get existing indices to remove
|
280
287
|
unless async
|
@@ -286,7 +293,7 @@ module Searchkick
|
|
286
293
|
promote(index.name, update_refresh_interval: !refresh_interval.nil?)
|
287
294
|
|
288
295
|
# import after promotion
|
289
|
-
index.import_scope(
|
296
|
+
index.import_scope(relation, **import_options) if import
|
290
297
|
end
|
291
298
|
|
292
299
|
if async
|
@@ -142,7 +142,8 @@ module Searchkick
|
|
142
142
|
}
|
143
143
|
}
|
144
144
|
|
145
|
-
|
145
|
+
case language
|
146
|
+
when "chinese"
|
146
147
|
settings[:analysis][:analyzer].merge!(
|
147
148
|
default_analyzer => {
|
148
149
|
type: "ik_smart"
|
@@ -156,6 +157,30 @@ module Searchkick
|
|
156
157
|
)
|
157
158
|
|
158
159
|
settings[:analysis][:filter].delete(:searchkick_stemmer)
|
160
|
+
when "japanese"
|
161
|
+
settings[:analysis][:analyzer].merge!(
|
162
|
+
default_analyzer => {
|
163
|
+
type: "kuromoji"
|
164
|
+
},
|
165
|
+
searchkick_search: {
|
166
|
+
type: "kuromoji"
|
167
|
+
},
|
168
|
+
searchkick_search2: {
|
169
|
+
type: "kuromoji"
|
170
|
+
}
|
171
|
+
)
|
172
|
+
when "polish", "ukrainian", "smartcn"
|
173
|
+
settings[:analysis][:analyzer].merge!(
|
174
|
+
default_analyzer => {
|
175
|
+
type: language
|
176
|
+
},
|
177
|
+
searchkick_search: {
|
178
|
+
type: language
|
179
|
+
},
|
180
|
+
searchkick_search2: {
|
181
|
+
type: language
|
182
|
+
}
|
183
|
+
)
|
159
184
|
end
|
160
185
|
|
161
186
|
if Searchkick.env == "test"
|
data/lib/searchkick/query.rb
CHANGED
@@ -314,10 +314,12 @@ module Searchkick
|
|
314
314
|
|
315
315
|
if field == "_all" || field.end_with?(".analyzed")
|
316
316
|
shared_options[:cutoff_frequency] = 0.001 unless operator.to_s == "and" || misspellings == false
|
317
|
-
qs.
|
318
|
-
|
319
|
-
|
320
|
-
]
|
317
|
+
qs << shared_options.merge(analyzer: "searchkick_search")
|
318
|
+
|
319
|
+
# searchkick_search and searchkick_search2 are the same for ukrainian
|
320
|
+
unless %w(japanese polish ukrainian).include?(searchkick_options[:language])
|
321
|
+
qs << shared_options.merge(analyzer: "searchkick_search2")
|
322
|
+
end
|
321
323
|
exclude_analyzer = "searchkick_search2"
|
322
324
|
elsif field.end_with?(".exact")
|
323
325
|
f = field.split(".")[0..-2].join(".")
|
@@ -850,7 +852,7 @@ module Searchkick
|
|
850
852
|
}
|
851
853
|
when :regexp # support for regexp queries without using a regexp ruby object
|
852
854
|
filters << {regexp: {field => {value: op_value}}}
|
853
|
-
when :not # not equal
|
855
|
+
when :not, :_not # not equal
|
854
856
|
filters << {bool: {must_not: term_filters(field, op_value)}}
|
855
857
|
when :all
|
856
858
|
op_value.each do |val|
|
data/lib/searchkick/version.rb
CHANGED
data/test/language_test.rb
CHANGED
@@ -2,15 +2,59 @@ require_relative "test_helper"
|
|
2
2
|
|
3
3
|
class LanguageTest < Minitest::Test
|
4
4
|
def setup
|
5
|
+
skip unless ENV["LANGUAGE"]
|
6
|
+
|
5
7
|
Song.destroy_all
|
6
8
|
end
|
7
9
|
|
8
10
|
def test_chinese
|
9
|
-
|
10
|
-
Song
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
# requires https://github.com/medcl/elasticsearch-analysis-ik
|
12
|
+
with_options(Song, language: "chinese") do
|
13
|
+
store_names ["中华人民共和国国歌"], Song
|
14
|
+
assert_language_search "中华人民共和国", ["中华人民共和国国歌"]
|
15
|
+
assert_language_search "国歌", ["中华人民共和国国歌"]
|
16
|
+
assert_language_search "人", []
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# experimental
|
21
|
+
def test_smartcn
|
22
|
+
# requires https://www.elastic.co/guide/en/elasticsearch/plugins/6.2/analysis-smartcn.html
|
23
|
+
with_options(Song, language: "smartcn") do
|
24
|
+
store_names ["中华人民共和国国歌"], Song
|
25
|
+
assert_language_search "中华人民共和国", ["中华人民共和国国歌"]
|
26
|
+
# assert_language_search "国歌", ["中华人民共和国国歌"]
|
27
|
+
assert_language_search "人", []
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_japanese
|
32
|
+
# requires https://www.elastic.co/guide/en/elasticsearch/plugins/6.2/analysis-kuromoji.html
|
33
|
+
with_options(Song, language: "japanese") do
|
34
|
+
store_names ["JR新宿駅の近くにビールを飲みに行こうか"], Song
|
35
|
+
assert_language_search "飲む", ["JR新宿駅の近くにビールを飲みに行こうか"]
|
36
|
+
assert_language_search "jr", ["JR新宿駅の近くにビールを飲みに行こうか"]
|
37
|
+
assert_language_search "新", []
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_polish
|
42
|
+
# requires https://www.elastic.co/guide/en/elasticsearch/plugins/6.2/analysis-stempel.html
|
43
|
+
with_options(Song, language: "polish") do
|
44
|
+
store_names ["polski"], Song
|
45
|
+
assert_language_search "polskimi", ["polski"]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_ukrainian
|
50
|
+
# requires https://www.elastic.co/guide/en/elasticsearch/plugins/6.2/analysis-ukrainian.html
|
51
|
+
with_options(Song, language: "ukrainian") do
|
52
|
+
store_names ["ресторани"], Song
|
53
|
+
assert_language_search "ресторан", ["ресторани"]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def assert_language_search(term, expected)
|
58
|
+
assert_search term, expected, {misspellings: false}, Song
|
15
59
|
end
|
16
60
|
end
|
data/test/reindex_test.rb
CHANGED
data/test/synonyms_test.rb
CHANGED
@@ -47,7 +47,9 @@ class SynonymsTest < Minitest::Test
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def test_wordnet
|
50
|
-
|
50
|
+
# requires WordNet
|
51
|
+
skip unless ENV["WORDNET"]
|
52
|
+
|
51
53
|
store_names ["Creature", "Beast", "Dragon"], Animal
|
52
54
|
assert_search "animal", ["Creature", "Beast"], {}, Animal
|
53
55
|
end
|
data/test/test_helper.rb
CHANGED
@@ -528,7 +528,7 @@ class Sku
|
|
528
528
|
end
|
529
529
|
|
530
530
|
class Song
|
531
|
-
searchkick
|
531
|
+
searchkick
|
532
532
|
end
|
533
533
|
|
534
534
|
Product.searchkick_index.delete if Product.searchkick_index.exists?
|
@@ -547,7 +547,6 @@ class Minitest::Test
|
|
547
547
|
Store.destroy_all
|
548
548
|
Animal.destroy_all
|
549
549
|
Speaker.destroy_all
|
550
|
-
Sku.destroy_all
|
551
550
|
end
|
552
551
|
|
553
552
|
protected
|
@@ -579,4 +578,16 @@ class Minitest::Test
|
|
579
578
|
def assert_first(term, expected, options = {}, klass = Product)
|
580
579
|
assert_equal expected, klass.search(term, options).map(&:name).first
|
581
580
|
end
|
581
|
+
|
582
|
+
def with_options(klass, options)
|
583
|
+
previous_options = klass.searchkick_options.dup
|
584
|
+
begin
|
585
|
+
klass.searchkick_options.merge!(options)
|
586
|
+
klass.reindex
|
587
|
+
yield
|
588
|
+
ensure
|
589
|
+
klass.searchkick_options.clear
|
590
|
+
klass.searchkick_options.merge!(previous_options)
|
591
|
+
end
|
592
|
+
end
|
582
593
|
end
|
data/test/where_test.rb
CHANGED
@@ -31,7 +31,9 @@ class WhereTest < Minitest::Test
|
|
31
31
|
assert_search "product", ["Product A"], where: {store_id: 1...2}
|
32
32
|
assert_search "product", ["Product A", "Product B"], where: {store_id: [1, 2]}
|
33
33
|
assert_search "product", ["Product B", "Product C", "Product D"], where: {store_id: {not: 1}}
|
34
|
+
assert_search "product", ["Product B", "Product C", "Product D"], where: {store_id: {_not: 1}}
|
34
35
|
assert_search "product", ["Product C", "Product D"], where: {store_id: {not: [1, 2]}}
|
36
|
+
assert_search "product", ["Product C", "Product D"], where: {store_id: {_not: [1, 2]}}
|
35
37
|
assert_search "product", ["Product A"], where: {user_ids: {lte: 2, gte: 2}}
|
36
38
|
|
37
39
|
# or
|
@@ -56,12 +58,15 @@ class WhereTest < Minitest::Test
|
|
56
58
|
|
57
59
|
# any / nested terms
|
58
60
|
assert_search "product", ["Product B", "Product C"], where: {user_ids: {not: [2], in: [1, 3]}}
|
61
|
+
assert_search "product", ["Product B", "Product C"], where: {user_ids: {_not: [2], in: [1, 3]}}
|
59
62
|
|
60
63
|
# not / exists
|
61
64
|
assert_search "product", ["Product D"], where: {user_ids: nil}
|
62
65
|
assert_search "product", ["Product A", "Product B", "Product C"], where: {user_ids: {not: nil}}
|
66
|
+
assert_search "product", ["Product A", "Product B", "Product C"], where: {user_ids: {_not: nil}}
|
63
67
|
assert_search "product", ["Product A", "Product C", "Product D"], where: {user_ids: [3, nil]}
|
64
68
|
assert_search "product", ["Product B"], where: {user_ids: {not: [3, nil]}}
|
69
|
+
assert_search "product", ["Product B"], where: {user_ids: {_not: [3, nil]}}
|
65
70
|
end
|
66
71
|
|
67
72
|
def test_regexp
|
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: 3.0.
|
4
|
+
version: 3.0.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: 2018-03-
|
11
|
+
date: 2018-03-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|