searchkick 3.0.0 → 3.0.1
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 +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
|