searchkick 5.5.2 → 6.0.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 +21 -0
- data/README.md +249 -209
- data/lib/searchkick/bulk_reindex_job.rb +3 -5
- data/lib/searchkick/hash_wrapper.rb +30 -9
- data/lib/searchkick/index.rb +25 -16
- data/lib/searchkick/index_options.rb +34 -18
- data/lib/searchkick/indexer.rb +10 -2
- data/lib/searchkick/log_subscriber.rb +1 -1
- data/lib/searchkick/model.rb +18 -8
- data/lib/searchkick/multi_search.rb +7 -2
- data/lib/searchkick/process_batch_job.rb +2 -2
- data/lib/searchkick/process_queue_job.rb +4 -3
- data/lib/searchkick/query.rb +90 -100
- data/lib/searchkick/record_data.rb +19 -0
- data/lib/searchkick/record_indexer.rb +20 -10
- data/lib/searchkick/reindex_queue.rb +1 -24
- data/lib/searchkick/reindex_v2_job.rb +3 -3
- data/lib/searchkick/relation.rb +504 -72
- data/lib/searchkick/relation_indexer.rb +39 -10
- data/lib/searchkick/results.rb +14 -9
- data/lib/searchkick/version.rb +1 -1
- data/lib/searchkick.rb +12 -31
- metadata +4 -18
data/lib/searchkick/query.rb
CHANGED
@@ -17,10 +17,18 @@ module Searchkick
|
|
17
17
|
:with_score, :misspellings?, :scroll_id, :clear_scroll, :missing_records, :with_hit
|
18
18
|
|
19
19
|
def initialize(klass, term = "*", **options)
|
20
|
+
if options[:conversions]
|
21
|
+
Searchkick.warn("The `conversions` option is deprecated in favor of `conversions_v2`, which provides much better search performance. Upgrade to `conversions_v2` or rename `conversions` to `conversions_v1`")
|
22
|
+
end
|
23
|
+
|
24
|
+
if options.key?(:conversions_v1)
|
25
|
+
options[:conversions] = options.delete(:conversions_v1)
|
26
|
+
end
|
27
|
+
|
20
28
|
unknown_keywords = options.keys - [:aggs, :block, :body, :body_options, :boost,
|
21
|
-
:boost_by, :boost_by_distance, :boost_by_recency, :boost_where, :conversions, :conversions_term, :debug, :emoji, :exclude, :explain,
|
29
|
+
:boost_by, :boost_by_distance, :boost_by_recency, :boost_where, :conversions, :conversions_v2, :conversions_term, :debug, :emoji, :exclude, :explain,
|
22
30
|
:fields, :highlight, :includes, :index_name, :indices_boost, :knn, :limit, :load,
|
23
|
-
:match, :misspellings, :models, :model_includes, :offset, :operator, :order, :padding, :page, :per_page, :profile,
|
31
|
+
:match, :misspellings, :models, :model_includes, :offset, :opaque_id, :operator, :order, :padding, :page, :per_page, :profile,
|
24
32
|
:request_params, :routing, :scope_results, :scroll, :select, :similar, :smart_aggs, :suggest, :total_entries, :track, :type, :where]
|
25
33
|
raise ArgumentError, "unknown keywords: #{unknown_keywords.join(", ")}" if unknown_keywords.any?
|
26
34
|
|
@@ -86,6 +94,7 @@ module Searchkick
|
|
86
94
|
params[:type] = @type if @type
|
87
95
|
params[:routing] = @routing if @routing
|
88
96
|
params[:scroll] = @scroll if @scroll
|
97
|
+
params[:opaque_id] = @opaque_id if @opaque_id
|
89
98
|
params.merge!(options[:request_params]) if options[:request_params]
|
90
99
|
params
|
91
100
|
end
|
@@ -98,34 +107,13 @@ module Searchkick
|
|
98
107
|
prepare
|
99
108
|
response = execute_search
|
100
109
|
end
|
101
|
-
rescue => e
|
110
|
+
rescue => e
|
102
111
|
handle_error(e)
|
103
112
|
end
|
104
113
|
handle_response(response)
|
105
114
|
end
|
106
115
|
end
|
107
116
|
|
108
|
-
def to_curl
|
109
|
-
query = params
|
110
|
-
type = query[:type]
|
111
|
-
index = query[:index].is_a?(Array) ? query[:index].join(",") : query[:index]
|
112
|
-
request_params = query.except(:index, :type, :body)
|
113
|
-
|
114
|
-
# no easy way to tell which host the client will use
|
115
|
-
host =
|
116
|
-
if Searchkick.client.transport.respond_to?(:transport)
|
117
|
-
Searchkick.client.transport.transport.hosts.first
|
118
|
-
else
|
119
|
-
Searchkick.client.transport.hosts.first
|
120
|
-
end
|
121
|
-
credentials = host[:user] || host[:password] ? "#{host[:user]}:#{host[:password]}@" : nil
|
122
|
-
params = ["pretty"]
|
123
|
-
request_params.each do |k, v|
|
124
|
-
params << "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"
|
125
|
-
end
|
126
|
-
"curl #{host[:protocol]}://#{credentials}#{host[:host]}:#{host[:port]}/#{CGI.escape(index)}#{type ? "/#{type.map { |t| CGI.escape(t) }.join(',')}" : ''}/_search?#{params.join('&')} -H 'Content-Type: application/json' -d '#{query[:body].to_json}'"
|
127
|
-
end
|
128
|
-
|
129
117
|
def handle_response(response)
|
130
118
|
opts = {
|
131
119
|
page: @page,
|
@@ -144,15 +132,17 @@ module Searchkick
|
|
144
132
|
total_entries: options[:total_entries],
|
145
133
|
index_mapping: @index_mapping,
|
146
134
|
suggest: options[:suggest],
|
147
|
-
scroll: options[:scroll]
|
135
|
+
scroll: options[:scroll],
|
136
|
+
opaque_id: options[:opaque_id]
|
148
137
|
}
|
149
138
|
|
150
139
|
if options[:debug]
|
151
|
-
|
152
|
-
puts "
|
140
|
+
server = Searchkick.opensearch? ? "OpenSearch" : "Elasticsearch"
|
141
|
+
puts "Searchkick #{Searchkick::VERSION}"
|
142
|
+
puts "#{server} #{Searchkick.server_version}"
|
153
143
|
puts
|
154
144
|
|
155
|
-
puts "Model
|
145
|
+
puts "Model Options"
|
156
146
|
pp searchkick_options
|
157
147
|
puts
|
158
148
|
|
@@ -161,7 +151,7 @@ module Searchkick
|
|
161
151
|
puts
|
162
152
|
|
163
153
|
if searchkick_index
|
164
|
-
puts "
|
154
|
+
puts "Record Data"
|
165
155
|
begin
|
166
156
|
pp klass.limit(3).map { |r| RecordData.new(searchkick_index, r).index_data }
|
167
157
|
rescue => e
|
@@ -169,21 +159,21 @@ module Searchkick
|
|
169
159
|
end
|
170
160
|
puts
|
171
161
|
|
172
|
-
puts "
|
162
|
+
puts "Mapping"
|
173
163
|
puts JSON.pretty_generate(searchkick_index.mapping)
|
174
164
|
puts
|
175
165
|
|
176
|
-
puts "
|
166
|
+
puts "Settings"
|
177
167
|
puts JSON.pretty_generate(searchkick_index.settings)
|
178
168
|
puts
|
179
169
|
end
|
180
170
|
|
181
|
-
puts "
|
182
|
-
puts
|
171
|
+
puts "Query"
|
172
|
+
puts JSON.pretty_generate(params[:body])
|
183
173
|
puts
|
184
174
|
|
185
|
-
puts "
|
186
|
-
puts JSON.pretty_generate(response)
|
175
|
+
puts "Results"
|
176
|
+
puts JSON.pretty_generate(response.to_h)
|
187
177
|
end
|
188
178
|
|
189
179
|
# set execute for multi search
|
@@ -257,6 +247,7 @@ module Searchkick
|
|
257
247
|
padding = [options[:padding].to_i, 0].max
|
258
248
|
offset = (options[:offset] || (page - 1) * per_page + padding).to_i
|
259
249
|
scroll = options[:scroll]
|
250
|
+
opaque_id = options[:opaque_id]
|
260
251
|
|
261
252
|
max_result_window = searchkick_options[:max_result_window]
|
262
253
|
original_per_page = per_page
|
@@ -374,7 +365,6 @@ module Searchkick
|
|
374
365
|
field_misspellings = misspellings && (!misspellings_fields || misspellings_fields.include?(base_field(field)))
|
375
366
|
|
376
367
|
if field == "_all" || field.end_with?(".analyzed")
|
377
|
-
shared_options[:cutoff_frequency] = 0.001 unless operator.to_s == "and" || field_misspellings == false || (!below73? && !track_total_hits?) || match_type == :match_phrase || !below80? || Searchkick.opensearch?
|
378
368
|
qs << shared_options.merge(analyzer: "searchkick_search")
|
379
369
|
|
380
370
|
# searchkick_search and searchkick_search2 are the same for some languages
|
@@ -442,6 +432,7 @@ module Searchkick
|
|
442
432
|
}
|
443
433
|
|
444
434
|
should.concat(set_conversions)
|
435
|
+
should.concat(set_conversions_v2)
|
445
436
|
end
|
446
437
|
|
447
438
|
query = payload
|
@@ -457,20 +448,14 @@ module Searchkick
|
|
457
448
|
|
458
449
|
models = Array(options[:models])
|
459
450
|
if models.any? { |m| m != m.searchkick_klass }
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
models.map do |m|
|
467
|
-
v = {_index: m.searchkick_index.name}
|
468
|
-
v[:type] = m.searchkick_index.klass_document_type(m, true) if m != m.searchkick_klass
|
469
|
-
v
|
470
|
-
end
|
451
|
+
index_type_or =
|
452
|
+
models.map do |m|
|
453
|
+
v = {_index: m.searchkick_index.name}
|
454
|
+
v[:type] = m.searchkick_index.klass_document_type(m, true) if m != m.searchkick_klass
|
455
|
+
v
|
456
|
+
end
|
471
457
|
|
472
|
-
|
473
|
-
end
|
458
|
+
where[:or] = Array(where[:or]) + [index_type_or]
|
474
459
|
end
|
475
460
|
|
476
461
|
# start everything as efficient filters
|
@@ -554,7 +539,7 @@ module Searchkick
|
|
554
539
|
# run block
|
555
540
|
options[:block].call(payload) if options[:block]
|
556
541
|
|
557
|
-
# scroll optimization when
|
542
|
+
# scroll optimization when iterating over all docs
|
558
543
|
# https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-scroll.html
|
559
544
|
if options[:scroll] && payload[:query] == {match_all: {}}
|
560
545
|
payload[:sort] ||= ["_doc"]
|
@@ -566,6 +551,7 @@ module Searchkick
|
|
566
551
|
@padding = padding
|
567
552
|
@load = load
|
568
553
|
@scroll = scroll
|
554
|
+
@opaque_id = opaque_id
|
569
555
|
end
|
570
556
|
|
571
557
|
def set_fields
|
@@ -656,6 +642,43 @@ module Searchkick
|
|
656
642
|
end
|
657
643
|
end
|
658
644
|
|
645
|
+
def set_conversions_v2
|
646
|
+
conversions_v2 = options[:conversions_v2]
|
647
|
+
return [] if conversions_v2.nil? && !searchkick_options[:conversions_v2]
|
648
|
+
return [] if conversions_v2 == false
|
649
|
+
|
650
|
+
# disable if searchkick_options[:conversions] to make it easy to upgrade without downtime
|
651
|
+
return [] if conversions_v2.nil? && searchkick_options[:conversions]
|
652
|
+
|
653
|
+
unless conversions_v2.is_a?(Hash)
|
654
|
+
conversions_v2 = {field: conversions_v2}
|
655
|
+
end
|
656
|
+
|
657
|
+
conversions_fields =
|
658
|
+
case conversions_v2[:field]
|
659
|
+
when true, nil
|
660
|
+
Array(searchkick_options[:conversions_v2]).map(&:to_s)
|
661
|
+
else
|
662
|
+
[conversions_v2[:field].to_s]
|
663
|
+
end
|
664
|
+
|
665
|
+
conversions_term = (conversions_v2[:term] || options[:conversions_term] || term).to_s
|
666
|
+
unless searchkick_options[:case_sensitive]
|
667
|
+
conversions_term = conversions_term.downcase
|
668
|
+
end
|
669
|
+
conversions_term = conversions_term.gsub(".", "*")
|
670
|
+
|
671
|
+
conversions_fields.map do |conversions_field|
|
672
|
+
{
|
673
|
+
rank_feature: {
|
674
|
+
field: "#{conversions_field}.#{conversions_term}",
|
675
|
+
linear: {},
|
676
|
+
boost: conversions_v2[:factor] || 1
|
677
|
+
}
|
678
|
+
}
|
679
|
+
end
|
680
|
+
end
|
681
|
+
|
659
682
|
def set_exclude(field, analyzer)
|
660
683
|
Array(options[:exclude]).map do |phrase|
|
661
684
|
{
|
@@ -712,7 +735,7 @@ module Searchkick
|
|
712
735
|
if boost_by.is_a?(Array)
|
713
736
|
boost_by = boost_by.to_h { |f| [f, {factor: 1}] }
|
714
737
|
elsif boost_by.is_a?(Hash)
|
715
|
-
multiply_by, boost_by = boost_by.partition { |_, v| v.delete(:boost_mode) == "multiply" }.map(&:to_h)
|
738
|
+
multiply_by, boost_by = boost_by.transform_values(&:dup).partition { |_, v| v.delete(:boost_mode) == "multiply" }.map(&:to_h)
|
716
739
|
end
|
717
740
|
boost_by[options[:boost]] = {factor: 1} if options[:boost]
|
718
741
|
|
@@ -935,11 +958,11 @@ module Searchkick
|
|
935
958
|
space_type: space_type
|
936
959
|
}
|
937
960
|
},
|
938
|
-
boost: distance == "cosine" && Searchkick.server_below?("2.19.0"
|
961
|
+
boost: distance == "cosine" && Searchkick.server_below?("2.19.0") ? 0.5 : 1.0
|
939
962
|
}
|
940
963
|
}
|
941
964
|
else
|
942
|
-
if ef_search && Searchkick.server_below?("2.16.0"
|
965
|
+
if ef_search && Searchkick.server_below?("2.16.0")
|
943
966
|
raise Error, "ef_search requires OpenSearch 2.16+"
|
944
967
|
end
|
945
968
|
|
@@ -956,7 +979,7 @@ module Searchkick
|
|
956
979
|
else
|
957
980
|
if exact
|
958
981
|
# prevent incorrect distances/results with Elasticsearch 9.0.0-rc1
|
959
|
-
if !
|
982
|
+
if !Searchkick.server_below?("9.0.0") && field_options[:distance] == "cosine" && distance != "cosine"
|
960
983
|
raise ArgumentError, "distance must match searchkick options"
|
961
984
|
end
|
962
985
|
|
@@ -1113,11 +1136,7 @@ module Searchkick
|
|
1113
1136
|
regex = regex.gsub(/(?<!\\)%/, ".*").gsub(/(?<!\\)_/, ".").gsub("\\%", "%").gsub("\\_", "_")
|
1114
1137
|
|
1115
1138
|
if op == :ilike
|
1116
|
-
|
1117
|
-
raise ArgumentError, "ilike requires Elasticsearch 7.10+"
|
1118
|
-
else
|
1119
|
-
filters << {regexp: {field => {value: regex, flags: "NONE", case_insensitive: true}}}
|
1120
|
-
end
|
1139
|
+
filters << {regexp: {field => {value: regex, flags: "NONE", case_insensitive: true}}}
|
1121
1140
|
else
|
1122
1141
|
filters << {regexp: {field => {value: regex, flags: "NONE"}}}
|
1123
1142
|
end
|
@@ -1134,27 +1153,25 @@ module Searchkick
|
|
1134
1153
|
when :in
|
1135
1154
|
filters << term_filters(field, op_value)
|
1136
1155
|
when :exists
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1156
|
+
case op_value
|
1157
|
+
when true
|
1158
|
+
filters << {exists: {field: field}}
|
1159
|
+
when false
|
1160
|
+
filters << {bool: {must_not: {exists: {field: field}}}}
|
1161
|
+
else
|
1162
|
+
raise ArgumentError, "Passing a value other than true or false to exists is not supported"
|
1141
1163
|
end
|
1142
|
-
filters << {exists: {field: field}}
|
1143
1164
|
else
|
1144
1165
|
range_query =
|
1145
1166
|
case op
|
1146
1167
|
when :gt
|
1147
|
-
|
1148
|
-
below90? ? {from: op_value, include_lower: false} : {gt: op_value}
|
1168
|
+
{gt: op_value}
|
1149
1169
|
when :gte
|
1150
|
-
|
1151
|
-
below90? ? {from: op_value, include_lower: true} : {gte: op_value}
|
1170
|
+
{gte: op_value}
|
1152
1171
|
when :lt
|
1153
|
-
|
1154
|
-
below90? ? {to: op_value, include_upper: false} : {lt: op_value}
|
1172
|
+
{lt: op_value}
|
1155
1173
|
when :lte
|
1156
|
-
|
1157
|
-
below90? ? {to: op_value, include_upper: true} : {lte: op_value}
|
1174
|
+
{lte: op_value}
|
1158
1175
|
else
|
1159
1176
|
raise ArgumentError, "Unknown where operator: #{op.inspect}"
|
1160
1177
|
end
|
@@ -1201,14 +1218,7 @@ module Searchkick
|
|
1201
1218
|
source = "#{source}.*"
|
1202
1219
|
end
|
1203
1220
|
|
1204
|
-
|
1205
|
-
if value.casefold?
|
1206
|
-
raise ArgumentError, "Case-insensitive flag does not work with Elasticsearch < 7.10"
|
1207
|
-
end
|
1208
|
-
{regexp: {field => {value: source, flags: "NONE"}}}
|
1209
|
-
else
|
1210
|
-
{regexp: {field => {value: source, flags: "NONE", case_insensitive: value.casefold?}}}
|
1211
|
-
end
|
1221
|
+
{regexp: {field => {value: source, flags: "NONE", case_insensitive: value.casefold?}}}
|
1212
1222
|
else
|
1213
1223
|
# TODO add this for other values
|
1214
1224
|
if value.as_json.is_a?(Enumerable)
|
@@ -1299,25 +1309,5 @@ module Searchkick
|
|
1299
1309
|
def body_options
|
1300
1310
|
options[:body_options] || {}
|
1301
1311
|
end
|
1302
|
-
|
1303
|
-
def below73?
|
1304
|
-
Searchkick.server_below?("7.3.0")
|
1305
|
-
end
|
1306
|
-
|
1307
|
-
def below75?
|
1308
|
-
Searchkick.server_below?("7.5.0")
|
1309
|
-
end
|
1310
|
-
|
1311
|
-
def below710?
|
1312
|
-
Searchkick.server_below?("7.10.0")
|
1313
|
-
end
|
1314
|
-
|
1315
|
-
def below80?
|
1316
|
-
Searchkick.server_below?("8.0.0")
|
1317
|
-
end
|
1318
|
-
|
1319
|
-
def below90?
|
1320
|
-
Searchkick.server_below?("9.0.0")
|
1321
|
-
end
|
1322
1312
|
end
|
1323
1313
|
end
|
@@ -58,6 +58,25 @@ module Searchkick
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
+
index.conversions_v2_fields.each do |conversions_field|
|
62
|
+
key = source.key?(conversions_field) ? conversions_field : conversions_field.to_sym
|
63
|
+
if !partial_reindex || source[key]
|
64
|
+
if index.options[:case_sensitive]
|
65
|
+
source[key] =
|
66
|
+
(source[key] || {}).reduce(Hash.new(0)) do |memo, (k, v)|
|
67
|
+
memo[k.to_s.gsub(".", "*")] += v
|
68
|
+
memo
|
69
|
+
end
|
70
|
+
else
|
71
|
+
source[key] =
|
72
|
+
(source[key] || {}).reduce(Hash.new(0)) do |memo, (k, v)|
|
73
|
+
memo[k.to_s.downcase.gsub(".", "*")] += v
|
74
|
+
memo
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
61
80
|
# hack to prevent generator field doesn't exist error
|
62
81
|
if !partial_reindex
|
63
82
|
index.suggest_fields.each do |field|
|
@@ -6,7 +6,7 @@ module Searchkick
|
|
6
6
|
@index = index
|
7
7
|
end
|
8
8
|
|
9
|
-
def reindex(records, mode:, method_name:, full: false, single: false)
|
9
|
+
def reindex(records, mode:, method_name:, ignore_missing:, full: false, single: false, job_options: nil)
|
10
10
|
# prevents exists? check if records is a relation
|
11
11
|
records = records.to_a
|
12
12
|
return if records.empty?
|
@@ -17,6 +17,14 @@ module Searchkick
|
|
17
17
|
raise Error, "Active Job not found"
|
18
18
|
end
|
19
19
|
|
20
|
+
job_options ||= {}
|
21
|
+
|
22
|
+
# only add if set for backwards compatibility
|
23
|
+
extra_options = {}
|
24
|
+
if ignore_missing
|
25
|
+
extra_options[:ignore_missing] = ignore_missing
|
26
|
+
end
|
27
|
+
|
20
28
|
# we could likely combine ReindexV2Job, BulkReindexJob, and ProcessBatchJob
|
21
29
|
# but keep them separate for now
|
22
30
|
if single
|
@@ -28,19 +36,21 @@ module Searchkick
|
|
28
36
|
routing = record.search_routing
|
29
37
|
end
|
30
38
|
|
31
|
-
Searchkick::ReindexV2Job.perform_later(
|
39
|
+
Searchkick::ReindexV2Job.set(**job_options).perform_later(
|
32
40
|
record.class.name,
|
33
41
|
record.id.to_s,
|
34
42
|
method_name ? method_name.to_s : nil,
|
35
43
|
routing: routing,
|
36
|
-
index_name: index.name
|
44
|
+
index_name: index.name,
|
45
|
+
**extra_options
|
37
46
|
)
|
38
47
|
else
|
39
|
-
Searchkick::BulkReindexJob.perform_later(
|
48
|
+
Searchkick::BulkReindexJob.set(**job_options).perform_later(
|
40
49
|
class_name: records.first.class.searchkick_options[:class_name],
|
41
50
|
record_ids: records.map { |r| r.id.to_s },
|
42
51
|
index_name: index.name,
|
43
|
-
method_name: method_name ? method_name.to_s : nil
|
52
|
+
method_name: method_name ? method_name.to_s : nil,
|
53
|
+
**extra_options
|
44
54
|
)
|
45
55
|
end
|
46
56
|
when :queue
|
@@ -51,7 +61,7 @@ module Searchkick
|
|
51
61
|
index.reindex_queue.push_records(records)
|
52
62
|
when true, :inline
|
53
63
|
index_records, other_records = records.partition { |r| index_record?(r) }
|
54
|
-
import_inline(index_records, !full ? other_records : [], method_name: method_name, single: single)
|
64
|
+
import_inline(index_records, !full ? other_records : [], method_name: method_name, ignore_missing: ignore_missing, single: single)
|
55
65
|
else
|
56
66
|
raise ArgumentError, "Invalid value for mode"
|
57
67
|
end
|
@@ -60,7 +70,7 @@ module Searchkick
|
|
60
70
|
true
|
61
71
|
end
|
62
72
|
|
63
|
-
def reindex_items(klass, items, method_name:, single: false)
|
73
|
+
def reindex_items(klass, items, method_name:, ignore_missing:, single: false)
|
64
74
|
routing = items.to_h { |r| [r[:id], r[:routing]] }
|
65
75
|
record_ids = routing.keys
|
66
76
|
|
@@ -76,7 +86,7 @@ module Searchkick
|
|
76
86
|
construct_record(klass, id, routing[id])
|
77
87
|
end
|
78
88
|
|
79
|
-
import_inline(records, delete_records, method_name: method_name, single: single)
|
89
|
+
import_inline(records, delete_records, method_name: method_name, ignore_missing: ignore_missing, single: single)
|
80
90
|
end
|
81
91
|
|
82
92
|
private
|
@@ -86,13 +96,13 @@ module Searchkick
|
|
86
96
|
end
|
87
97
|
|
88
98
|
# import in single request with retries
|
89
|
-
def import_inline(index_records, delete_records, method_name:, single:)
|
99
|
+
def import_inline(index_records, delete_records, method_name:, ignore_missing:, single:)
|
90
100
|
return if index_records.empty? && delete_records.empty?
|
91
101
|
|
92
102
|
maybe_bulk(index_records, delete_records, method_name, single) do
|
93
103
|
if index_records.any?
|
94
104
|
if method_name
|
95
|
-
index.bulk_update(index_records, method_name)
|
105
|
+
index.bulk_update(index_records, method_name, ignore_missing: ignore_missing)
|
96
106
|
else
|
97
107
|
index.bulk_index(index_records)
|
98
108
|
end
|
@@ -33,17 +33,7 @@ module Searchkick
|
|
33
33
|
|
34
34
|
# TODO use reliable queuing
|
35
35
|
def reserve(limit: 1000)
|
36
|
-
|
37
|
-
Searchkick.with_redis { |r| r.call("RPOP", redis_key, limit) }.to_a
|
38
|
-
else
|
39
|
-
record_ids = []
|
40
|
-
Searchkick.with_redis do |r|
|
41
|
-
while record_ids.size < limit && (record_id = r.call("RPOP", redis_key))
|
42
|
-
record_ids << record_id
|
43
|
-
end
|
44
|
-
end
|
45
|
-
record_ids
|
46
|
-
end
|
36
|
+
Searchkick.with_redis { |r| r.call("RPOP", redis_key, limit) }.to_a
|
47
37
|
end
|
48
38
|
|
49
39
|
def clear
|
@@ -60,19 +50,6 @@ module Searchkick
|
|
60
50
|
"searchkick:reindex_queue:#{name}"
|
61
51
|
end
|
62
52
|
|
63
|
-
def supports_rpop_with_count?
|
64
|
-
redis_version >= Gem::Version.new("6.2")
|
65
|
-
end
|
66
|
-
|
67
|
-
def redis_version
|
68
|
-
@redis_version ||=
|
69
|
-
Searchkick.with_redis do |r|
|
70
|
-
info = r.call("INFO")
|
71
|
-
matches = /redis_version:(\S+)/.match(info)
|
72
|
-
Gem::Version.new(matches[1])
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
53
|
def escape(value)
|
77
54
|
value.to_s.gsub("|", "||")
|
78
55
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Searchkick
|
2
|
-
class ReindexV2Job <
|
2
|
+
class ReindexV2Job < Searchkick.parent_job.constantize
|
3
3
|
queue_as { Searchkick.queue_name }
|
4
4
|
|
5
|
-
def perform(class_name, id, method_name = nil, routing: nil, index_name: nil)
|
5
|
+
def perform(class_name, id, method_name = nil, routing: nil, index_name: nil, ignore_missing: nil)
|
6
6
|
model = Searchkick.load_model(class_name, allow_child: true)
|
7
7
|
index = model.searchkick_index(name: index_name)
|
8
8
|
# use should_index? to decide whether to index (not default scope)
|
@@ -11,7 +11,7 @@ module Searchkick
|
|
11
11
|
# but keep for now for backwards compatibility
|
12
12
|
model = model.unscoped if model.respond_to?(:unscoped)
|
13
13
|
items = [{id: id, routing: routing}]
|
14
|
-
RecordIndexer.new(index).reindex_items(model, items, method_name: method_name, single: true)
|
14
|
+
RecordIndexer.new(index).reindex_items(model, items, method_name: method_name, ignore_missing: ignore_missing, single: true)
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|