searchkick 4.0.2 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +17 -12
- data/lib/searchkick.rb +5 -1
- data/lib/searchkick/bulk_indexer.rb +2 -2
- data/lib/searchkick/index.rb +5 -2
- data/lib/searchkick/index_options.rb +1 -1
- data/lib/searchkick/model.rb +3 -2
- data/lib/searchkick/process_batch_job.rb +2 -2
- data/lib/searchkick/process_queue_job.rb +19 -11
- data/lib/searchkick/query.rb +40 -2
- data/lib/searchkick/results.rb +1 -1
- data/lib/searchkick/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 011efe75e8dd9c13d54a7278a8e3eb8e9d4e8ef3bca601842c4f022799c3d0f4
|
4
|
+
data.tar.gz: bc18d8b082717fe5ebbd8891c9eda584bd4f288af3898c19495ccdc9db9f04f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 03dc4717ee01f14c16725e98125865c1d3331fea5cb4086af979e4b06bdb6576e427ee06a1a478a5792b857e4c087c66e43db0d0799691bccc5782ce4a3100ca
|
7
|
+
data.tar.gz: b5ba0becde65ea65ca5e8ad7c4122119fbc9580ee445575ad90bae03ba868b09199eeae405a7203d91bda495289d37435dd9ec145672ec7e6141720e721123cf
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -10,7 +10,7 @@ Searchkick handles:
|
|
10
10
|
- special characters - `jalapeno` matches `jalapeño`
|
11
11
|
- extra whitespace - `dishwasher` matches `dish washer`
|
12
12
|
- misspellings - `zuchini` matches `zucchini`
|
13
|
-
- custom synonyms - `
|
13
|
+
- custom synonyms - `pop` matches `soda`
|
14
14
|
|
15
15
|
Plus:
|
16
16
|
|
@@ -99,14 +99,19 @@ Where
|
|
99
99
|
|
100
100
|
```ruby
|
101
101
|
where: {
|
102
|
-
expires_at: {gt: Time.now},
|
103
|
-
orders_count: 1..10,
|
104
|
-
aisle_id: [25, 30],
|
105
|
-
store_id: {not: 2},
|
106
|
-
aisle_id: {not: [25, 30]},
|
107
|
-
user_ids: {all: [1, 3]},
|
108
|
-
category:
|
109
|
-
|
102
|
+
expires_at: {gt: Time.now}, # lt, gte, lte also available
|
103
|
+
orders_count: 1..10, # equivalent to {gte: 1, lte: 10}
|
104
|
+
aisle_id: [25, 30], # in
|
105
|
+
store_id: {not: 2}, # not
|
106
|
+
aisle_id: {not: [25, 30]}, # not in
|
107
|
+
user_ids: {all: [1, 3]}, # all elements in array
|
108
|
+
category: {like: "%frozen%"}, # like
|
109
|
+
category: /frozen .+/, # regexp
|
110
|
+
category: {prefix: "frozen"}, # prefix
|
111
|
+
store_id: {exists: true}, # exists
|
112
|
+
_or: [{in_stock: true}, {backordered: true}],
|
113
|
+
_and: [{in_stock: true}, {backordered: true}],
|
114
|
+
_not: {store_id: 1} # negate a condition
|
110
115
|
}
|
111
116
|
```
|
112
117
|
|
@@ -313,7 +318,7 @@ A few languages require plugins:
|
|
313
318
|
|
314
319
|
```ruby
|
315
320
|
class Product < ApplicationRecord
|
316
|
-
searchkick synonyms: [["
|
321
|
+
searchkick synonyms: [["pop", "soda"], ["burger", "hamburger"]]
|
317
322
|
end
|
318
323
|
```
|
319
324
|
|
@@ -631,7 +636,7 @@ Autocomplete predicts what a user will type, making the search experience faster
|
|
631
636
|
|
632
637
|
![Autocomplete](https://gist.github.com/ankane/b6988db2802aca68a589b31e41b44195/raw/40febe948427e5bc53ec4e5dc248822855fef76f/autocomplete.png)
|
633
638
|
|
634
|
-
**Note:** To autocomplete on
|
639
|
+
**Note:** To autocomplete on search terms rather than results, check out [Autosuggest](https://github.com/ankane/autosuggest).
|
635
640
|
|
636
641
|
**Note 2:** If you only have a few thousand records, don’t use Searchkick for autocomplete. It’s *much* faster to load all records into JavaScript and autocomplete there (eliminates network requests).
|
637
642
|
|
@@ -1161,7 +1166,7 @@ If you run into issues on Windows, check out [this post](https://www.rastating.c
|
|
1161
1166
|
|
1162
1167
|
### Searchable Fields
|
1163
1168
|
|
1164
|
-
By default, all string fields are searchable (can be used in `fields` option). Speed up indexing and reduce index size by only making some fields searchable.
|
1169
|
+
By default, all string fields are searchable (can be used in `fields` option). Speed up indexing and reduce index size by only making some fields searchable.
|
1165
1170
|
|
1166
1171
|
```ruby
|
1167
1172
|
class Product < ApplicationRecord
|
data/lib/searchkick.rb
CHANGED
@@ -49,7 +49,7 @@ module Searchkick
|
|
49
49
|
|
50
50
|
def self.client
|
51
51
|
@client ||= begin
|
52
|
-
require "typhoeus/adapters/faraday" if defined?(Typhoeus)
|
52
|
+
require "typhoeus/adapters/faraday" if defined?(Typhoeus) && Gem::Version.new(Faraday::VERSION) < Gem::Version.new("0.14.0")
|
53
53
|
|
54
54
|
Elasticsearch::Client.new({
|
55
55
|
url: ENV["ELASTICSEARCH_URL"],
|
@@ -192,6 +192,10 @@ module Searchkick
|
|
192
192
|
end
|
193
193
|
end
|
194
194
|
|
195
|
+
def self.warn(message)
|
196
|
+
super("[searchkick] WARNING: #{message}")
|
197
|
+
end
|
198
|
+
|
195
199
|
# private
|
196
200
|
def self.load_records(records, ids)
|
197
201
|
records =
|
@@ -61,7 +61,7 @@ module Searchkick
|
|
61
61
|
if records.any?
|
62
62
|
if async
|
63
63
|
Searchkick::BulkReindexJob.perform_later(
|
64
|
-
class_name: records.first.class.
|
64
|
+
class_name: records.first.class.searchkick_options[:class_name],
|
65
65
|
record_ids: records.map(&:id),
|
66
66
|
index_name: index.name,
|
67
67
|
method_name: method_name ? method_name.to_s : nil
|
@@ -140,7 +140,7 @@ module Searchkick
|
|
140
140
|
def bulk_reindex_job(scope, batch_id, options)
|
141
141
|
Searchkick.with_redis { |r| r.sadd(batches_key, batch_id) }
|
142
142
|
Searchkick::BulkReindexJob.perform_later({
|
143
|
-
class_name: scope.
|
143
|
+
class_name: scope.searchkick_options[:class_name],
|
144
144
|
index_name: index.name,
|
145
145
|
batch_id: batch_id
|
146
146
|
}.merge(options))
|
data/lib/searchkick/index.rb
CHANGED
@@ -259,6 +259,10 @@ module Searchkick
|
|
259
259
|
@bulk_indexer ||= BulkIndexer.new(self)
|
260
260
|
end
|
261
261
|
|
262
|
+
def import_before_promotion(index, relation, **import_options)
|
263
|
+
index.import_scope(relation, **import_options)
|
264
|
+
end
|
265
|
+
|
262
266
|
# https://gist.github.com/jarosan/3124884
|
263
267
|
# http://www.elasticsearch.org/blog/changing-mapping-with-zero-downtime/
|
264
268
|
def reindex_scope(relation, import: true, resume: false, retain: false, async: false, refresh_interval: nil, scope: nil)
|
@@ -284,8 +288,7 @@ module Searchkick
|
|
284
288
|
# check if alias exists
|
285
289
|
alias_exists = alias_exists?
|
286
290
|
if alias_exists
|
287
|
-
|
288
|
-
index.import_scope(relation, **import_options) if import
|
291
|
+
import_before_promotion(index, relation, **import_options) if import
|
289
292
|
|
290
293
|
# get existing indices to remove
|
291
294
|
unless async
|
@@ -114,7 +114,7 @@ module Searchkick
|
|
114
114
|
type: "shingle",
|
115
115
|
token_separator: ""
|
116
116
|
},
|
117
|
-
# lucky find
|
117
|
+
# lucky find https://web.archiveorange.com/archive/v/AAfXfQ17f57FcRINsof7
|
118
118
|
searchkick_search_shingle: {
|
119
119
|
type: "shingle",
|
120
120
|
token_separator: "",
|
data/lib/searchkick/model.rb
CHANGED
@@ -15,6 +15,7 @@ module Searchkick
|
|
15
15
|
Searchkick.models << self
|
16
16
|
|
17
17
|
options[:_type] ||= -> { searchkick_index.klass_document_type(self, true) }
|
18
|
+
options[:class_name] = model_name.name
|
18
19
|
|
19
20
|
callbacks = options.key?(:callbacks) ? options[:callbacks] : :inline
|
20
21
|
unless [:inline, true, false, :async, :queue].include?(callbacks)
|
@@ -44,8 +45,8 @@ module Searchkick
|
|
44
45
|
end
|
45
46
|
alias_method Searchkick.search_method_name, :searchkick_search if Searchkick.search_method_name
|
46
47
|
|
47
|
-
def searchkick_index
|
48
|
-
index = class_variable_get(:@@searchkick_index)
|
48
|
+
def searchkick_index(name: nil)
|
49
|
+
index = name || class_variable_get(:@@searchkick_index)
|
49
50
|
index = index.call if index.respond_to?(:call)
|
50
51
|
index_cache = class_variable_get(:@@searchkick_index_cache)
|
51
52
|
index_cache[index] ||= Searchkick::Index.new(index, searchkick_options)
|
@@ -2,7 +2,7 @@ module Searchkick
|
|
2
2
|
class ProcessBatchJob < ActiveJob::Base
|
3
3
|
queue_as { Searchkick.queue_name }
|
4
4
|
|
5
|
-
def perform(class_name:, record_ids:)
|
5
|
+
def perform(class_name:, record_ids:, index_name: nil)
|
6
6
|
# separate routing from id
|
7
7
|
routing = Hash[record_ids.map { |r| r.split(/(?<!\|)\|(?!\|)/, 2).map { |v| v.gsub("||", "|") } }]
|
8
8
|
record_ids = routing.keys
|
@@ -26,7 +26,7 @@ module Searchkick
|
|
26
26
|
end
|
27
27
|
|
28
28
|
# bulk reindex
|
29
|
-
index = klass.searchkick_index
|
29
|
+
index = klass.searchkick_index(name: index_name)
|
30
30
|
Searchkick.callbacks(:bulk) do
|
31
31
|
index.bulk_index(records) if records.any?
|
32
32
|
index.bulk_delete(delete_records) if delete_records.any?
|
@@ -2,21 +2,29 @@ module Searchkick
|
|
2
2
|
class ProcessQueueJob < ActiveJob::Base
|
3
3
|
queue_as { Searchkick.queue_name }
|
4
4
|
|
5
|
-
def perform(class_name:)
|
5
|
+
def perform(class_name:, index_name: nil, inline: false)
|
6
6
|
model = class_name.constantize
|
7
|
+
limit = model.searchkick_options[:batch_size] || 1000
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
loop do
|
10
|
+
record_ids = model.searchkick_index(name: index_name).reindex_queue.reserve(limit: limit)
|
11
|
+
if record_ids.any?
|
12
|
+
batch_options = {
|
13
|
+
class_name: class_name,
|
14
|
+
record_ids: record_ids,
|
15
|
+
index_name: index_name
|
16
|
+
}
|
16
17
|
|
17
|
-
|
18
|
-
|
18
|
+
if inline
|
19
|
+
# use new.perform to avoid excessive logging
|
20
|
+
Searchkick::ProcessBatchJob.new.perform(**batch_options)
|
21
|
+
else
|
22
|
+
Searchkick::ProcessBatchJob.perform_later(**batch_options)
|
23
|
+
end
|
24
|
+
|
25
|
+
# TODO when moving to reliable queuing, mark as complete
|
19
26
|
end
|
27
|
+
break unless record_ids.size == limit
|
20
28
|
end
|
21
29
|
end
|
22
30
|
end
|
data/lib/searchkick/query.rb
CHANGED
@@ -137,6 +137,7 @@ module Searchkick
|
|
137
137
|
}
|
138
138
|
|
139
139
|
if options[:debug]
|
140
|
+
# can remove when minimum Ruby version is 2.5
|
140
141
|
require "pp"
|
141
142
|
|
142
143
|
puts "Searchkick Version: #{Searchkick::VERSION}"
|
@@ -432,7 +433,7 @@ module Searchkick
|
|
432
433
|
|
433
434
|
models = Array(options[:models])
|
434
435
|
if models.any? { |m| m != m.searchkick_klass }
|
435
|
-
warn
|
436
|
+
Searchkick.warn("Passing child models to models option throws off hits and pagination - use type option instead")
|
436
437
|
|
437
438
|
# uncomment once aliases are supported with _index
|
438
439
|
# see https://github.com/elastic/elasticsearch/issues/23306
|
@@ -873,6 +874,8 @@ module Searchkick
|
|
873
874
|
filters << {bool: {must_not: where_filters(value)}}
|
874
875
|
elsif field == :_and
|
875
876
|
filters << {bool: {must: value.map { |or_statement| {bool: {filter: where_filters(or_statement)}} }}}
|
877
|
+
# elsif field == :_script
|
878
|
+
# filters << {script: {script: {source: value, lang: "painless"}}}
|
876
879
|
else
|
877
880
|
# expand ranges
|
878
881
|
if value.is_a?(Range)
|
@@ -933,6 +936,14 @@ module Searchkick
|
|
933
936
|
}
|
934
937
|
}
|
935
938
|
}
|
939
|
+
when :like
|
940
|
+
# based on Postgres
|
941
|
+
# https://www.postgresql.org/docs/current/functions-matching.html
|
942
|
+
# % matches zero or more characters
|
943
|
+
# _ matches one character
|
944
|
+
# \ is escape character
|
945
|
+
regex = Regexp.escape(op_value).gsub(/(?<!\\)%/, ".*").gsub(/(?<!\\)_/, ".").gsub("\\%", "%").gsub("\\_", "_")
|
946
|
+
filters << {regexp: {field => {value: regex}}}
|
936
947
|
when :prefix
|
937
948
|
filters << {prefix: {field => op_value}}
|
938
949
|
when :regexp # support for regexp queries without using a regexp ruby object
|
@@ -945,6 +956,8 @@ module Searchkick
|
|
945
956
|
end
|
946
957
|
when :in
|
947
958
|
filters << term_filters(field, op_value)
|
959
|
+
when :exists
|
960
|
+
filters << {exists: {field: field}}
|
948
961
|
else
|
949
962
|
range_query =
|
950
963
|
case op
|
@@ -985,7 +998,32 @@ module Searchkick
|
|
985
998
|
elsif value.nil?
|
986
999
|
{bool: {must_not: {exists: {field: field}}}}
|
987
1000
|
elsif value.is_a?(Regexp)
|
988
|
-
|
1001
|
+
if value.casefold?
|
1002
|
+
Searchkick.warn("Case-insensitive flag does not work with Elasticsearch")
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
source = value.source
|
1006
|
+
unless source.start_with?("\\A") && source.end_with?("\\z")
|
1007
|
+
# https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-regexp-query.html
|
1008
|
+
Searchkick.warn("Regular expressions are always anchored in Elasticsearch")
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
# TODO handle other anchor characters, like ^, $, \Z
|
1012
|
+
if source.start_with?("\\A")
|
1013
|
+
source = source[2..-1]
|
1014
|
+
else
|
1015
|
+
# TODO uncomment in Searchkick 5
|
1016
|
+
# source = ".*#{source}"
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
if source.end_with?("\\z")
|
1020
|
+
source = source[0..-3]
|
1021
|
+
else
|
1022
|
+
# TODO uncomment in Searchkick 5
|
1023
|
+
# source = "#{source}.*"
|
1024
|
+
end
|
1025
|
+
|
1026
|
+
{regexp: {field => {value: source, flags: "NONE"}}}
|
989
1027
|
else
|
990
1028
|
{term: {field => value}}
|
991
1029
|
end
|
data/lib/searchkick/results.rb
CHANGED
@@ -62,7 +62,7 @@ module Searchkick
|
|
62
62
|
end
|
63
63
|
|
64
64
|
if missing_ids.any?
|
65
|
-
warn
|
65
|
+
Searchkick.warn("Records in search index do not exist in database: #{missing_ids.join(", ")}")
|
66
66
|
end
|
67
67
|
|
68
68
|
results
|
data/lib/searchkick/version.rb
CHANGED
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: 4.0
|
4
|
+
version: 4.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -145,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
145
|
- !ruby/object:Gem::Version
|
146
146
|
version: '0'
|
147
147
|
requirements: []
|
148
|
-
rubygems_version: 3.0.
|
148
|
+
rubygems_version: 3.0.4
|
149
149
|
signing_key:
|
150
150
|
specification_version: 4
|
151
151
|
summary: Intelligent search made easy with Rails and Elasticsearch
|