searchkick 4.0.2 → 4.1.0
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 +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
|

|
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
|