Commit d5312f12f451c27fcf9570c6627f4823ae25452b
1 parent
13fd0f4e
Exists in
allow_missing
Added allow_missing option to reindex
Showing
9 changed files
with
51 additions
and
21 deletions
Show diff stats
lib/searchkick/bulk_reindex_job.rb
@@ -14,7 +14,8 @@ module Searchkick | @@ -14,7 +14,8 @@ module Searchkick | ||
14 | relation = Searchkick.load_records(relation, record_ids) | 14 | relation = Searchkick.load_records(relation, record_ids) |
15 | relation = relation.search_import if relation.respond_to?(:search_import) | 15 | relation = relation.search_import if relation.respond_to?(:search_import) |
16 | 16 | ||
17 | - RecordIndexer.new(index).reindex(relation, mode: :inline, method_name: method_name, full: false) | 17 | + # TODO support allow_missing |
18 | + RecordIndexer.new(index).reindex(relation, mode: :inline, method_name: method_name, allow_missing: false, full: false) | ||
18 | RelationIndexer.new(index).batch_completed(batch_id) if batch_id | 19 | RelationIndexer.new(index).batch_completed(batch_id) if batch_id |
19 | end | 20 | end |
20 | end | 21 | end |
lib/searchkick/index.rb
@@ -163,11 +163,11 @@ module Searchkick | @@ -163,11 +163,11 @@ module Searchkick | ||
163 | end | 163 | end |
164 | alias_method :import, :bulk_index | 164 | alias_method :import, :bulk_index |
165 | 165 | ||
166 | - def bulk_update(records, method_name) | 166 | + def bulk_update(records, method_name, allow_missing: false) |
167 | return if records.empty? | 167 | return if records.empty? |
168 | 168 | ||
169 | notify_bulk(records, "Update") do | 169 | notify_bulk(records, "Update") do |
170 | - queue_update(records, method_name) | 170 | + queue_update(records, method_name, allow_missing: allow_missing) |
171 | end | 171 | end |
172 | end | 172 | end |
173 | 173 | ||
@@ -211,10 +211,10 @@ module Searchkick | @@ -211,10 +211,10 @@ module Searchkick | ||
211 | 211 | ||
212 | # note: this is designed to be used internally | 212 | # note: this is designed to be used internally |
213 | # so it does not check object matches index class | 213 | # so it does not check object matches index class |
214 | - def reindex(object, method_name: nil, full: false, **options) | 214 | + def reindex(object, method_name: nil, allow_missing: false, full: false, **options) |
215 | if object.is_a?(Array) | 215 | if object.is_a?(Array) |
216 | # note: purposefully skip full | 216 | # note: purposefully skip full |
217 | - return reindex_records(object, method_name: method_name, **options) | 217 | + return reindex_records(object, method_name: method_name, allow_missing: allow_missing, **options) |
218 | end | 218 | end |
219 | 219 | ||
220 | if !object.respond_to?(:searchkick_klass) | 220 | if !object.respond_to?(:searchkick_klass) |
@@ -233,7 +233,7 @@ module Searchkick | @@ -233,7 +233,7 @@ module Searchkick | ||
233 | raise ArgumentError, "unsupported keywords: #{options.keys.map(&:inspect).join(", ")}" if options.any? | 233 | raise ArgumentError, "unsupported keywords: #{options.keys.map(&:inspect).join(", ")}" if options.any? |
234 | 234 | ||
235 | # import only | 235 | # import only |
236 | - import_scope(relation, method_name: method_name, mode: mode) | 236 | + import_scope(relation, method_name: method_name, mode: mode, allow_missing: allow_missing) |
237 | self.refresh if refresh | 237 | self.refresh if refresh |
238 | true | 238 | true |
239 | else | 239 | else |
@@ -322,8 +322,10 @@ module Searchkick | @@ -322,8 +322,10 @@ module Searchkick | ||
322 | Searchkick.indexer.queue(records.reject { |r| r.id.blank? }.map { |r| RecordData.new(self, r).delete_data }) | 322 | Searchkick.indexer.queue(records.reject { |r| r.id.blank? }.map { |r| RecordData.new(self, r).delete_data }) |
323 | end | 323 | end |
324 | 324 | ||
325 | - def queue_update(records, method_name) | ||
326 | - Searchkick.indexer.queue(records.map { |r| RecordData.new(self, r).update_data(method_name) }) | 325 | + def queue_update(records, method_name, allow_missing:) |
326 | + items = records.map { |r| RecordData.new(self, r).update_data(method_name) } | ||
327 | + items.each { |i| i.instance_variable_set(:@allow_missing, true) } if allow_missing | ||
328 | + Searchkick.indexer.queue(items) | ||
327 | end | 329 | end |
328 | 330 | ||
329 | def relation_indexer | 331 | def relation_indexer |
lib/searchkick/indexer.rb
@@ -24,12 +24,20 @@ module Searchkick | @@ -24,12 +24,20 @@ module Searchkick | ||
24 | # note: delete does not set error when item not found | 24 | # note: delete does not set error when item not found |
25 | first_with_error = response["items"].map do |item| | 25 | first_with_error = response["items"].map do |item| |
26 | (item["index"] || item["delete"] || item["update"]) | 26 | (item["index"] || item["delete"] || item["update"]) |
27 | - end.find { |item| item["error"] } | ||
28 | - raise ImportError, "#{first_with_error["error"]} on item with id '#{first_with_error["_id"]}'" | 27 | + end.find.with_index { |item, i| item["error"] && !allow_missing?(items[i], item["error"]) } |
28 | + if first_with_error | ||
29 | + raise ImportError, "#{first_with_error["error"]} on item with id '#{first_with_error["_id"]}'" | ||
30 | + end | ||
29 | end | 31 | end |
30 | 32 | ||
31 | # maybe return response in future | 33 | # maybe return response in future |
32 | nil | 34 | nil |
33 | end | 35 | end |
36 | + | ||
37 | + private | ||
38 | + | ||
39 | + def allow_missing?(item, error) | ||
40 | + error["type"] == "document_missing_exception" && item.instance_variable_defined?(:@allow_missing) | ||
41 | + end | ||
34 | end | 42 | end |
35 | end | 43 | end |
lib/searchkick/model.rb
@@ -27,8 +27,8 @@ module Searchkick | @@ -27,8 +27,8 @@ module Searchkick | ||
27 | mod = Module.new | 27 | mod = Module.new |
28 | include(mod) | 28 | include(mod) |
29 | mod.module_eval do | 29 | mod.module_eval do |
30 | - def reindex(method_name = nil, mode: nil, refresh: false) | ||
31 | - self.class.searchkick_index.reindex([self], method_name: method_name, mode: mode, refresh: refresh, single: true) | 30 | + def reindex(method_name = nil, mode: nil, refresh: false, allow_missing: false) |
31 | + self.class.searchkick_index.reindex([self], method_name: method_name, mode: mode, refresh: refresh, allow_missing: allow_missing, single: true) | ||
32 | end unless base.method_defined?(:reindex) | 32 | end unless base.method_defined?(:reindex) |
33 | 33 | ||
34 | def similar(**options) | 34 | def similar(**options) |
lib/searchkick/process_batch_job.rb
@@ -14,7 +14,7 @@ module Searchkick | @@ -14,7 +14,7 @@ module Searchkick | ||
14 | end | 14 | end |
15 | 15 | ||
16 | relation = Searchkick.scope(model) | 16 | relation = Searchkick.scope(model) |
17 | - RecordIndexer.new(index).reindex_items(relation, items, method_name: nil) | 17 | + RecordIndexer.new(index).reindex_items(relation, items, method_name: nil, allow_missing: false) |
18 | end | 18 | end |
19 | end | 19 | end |
20 | end | 20 | end |
lib/searchkick/record_indexer.rb
@@ -6,7 +6,7 @@ module Searchkick | @@ -6,7 +6,7 @@ module Searchkick | ||
6 | @index = index | 6 | @index = index |
7 | end | 7 | end |
8 | 8 | ||
9 | - def reindex(records, mode:, method_name:, full: false, single: false) | 9 | + def reindex(records, mode:, method_name:, allow_missing:, full: false, single: false) |
10 | # prevents exists? check if records is a relation | 10 | # prevents exists? check if records is a relation |
11 | records = records.to_a | 11 | records = records.to_a |
12 | return if records.empty? | 12 | return if records.empty? |
@@ -17,6 +17,10 @@ module Searchkick | @@ -17,6 +17,10 @@ module Searchkick | ||
17 | raise Error, "Active Job not found" | 17 | raise Error, "Active Job not found" |
18 | end | 18 | end |
19 | 19 | ||
20 | + if allow_missing | ||
21 | + raise Error, "Allow missing not supported with async option yet" | ||
22 | + end | ||
23 | + | ||
20 | # we could likely combine ReindexV2Job, BulkReindexJob, and ProcessBatchJob | 24 | # we could likely combine ReindexV2Job, BulkReindexJob, and ProcessBatchJob |
21 | # but keep them separate for now | 25 | # but keep them separate for now |
22 | if single | 26 | if single |
@@ -51,7 +55,7 @@ module Searchkick | @@ -51,7 +55,7 @@ module Searchkick | ||
51 | index.reindex_queue.push_records(records) | 55 | index.reindex_queue.push_records(records) |
52 | when true, :inline | 56 | when true, :inline |
53 | index_records, other_records = records.partition { |r| index_record?(r) } | 57 | index_records, other_records = records.partition { |r| index_record?(r) } |
54 | - import_inline(index_records, !full ? other_records : [], method_name: method_name, single: single) | 58 | + import_inline(index_records, !full ? other_records : [], method_name: method_name, allow_missing: allow_missing, single: single) |
55 | else | 59 | else |
56 | raise ArgumentError, "Invalid value for mode" | 60 | raise ArgumentError, "Invalid value for mode" |
57 | end | 61 | end |
@@ -60,7 +64,7 @@ module Searchkick | @@ -60,7 +64,7 @@ module Searchkick | ||
60 | true | 64 | true |
61 | end | 65 | end |
62 | 66 | ||
63 | - def reindex_items(klass, items, method_name:, single: false) | 67 | + def reindex_items(klass, items, method_name:, allow_missing:, single: false) |
64 | routing = items.to_h { |r| [r[:id], r[:routing]] } | 68 | routing = items.to_h { |r| [r[:id], r[:routing]] } |
65 | record_ids = routing.keys | 69 | record_ids = routing.keys |
66 | 70 | ||
@@ -76,7 +80,7 @@ module Searchkick | @@ -76,7 +80,7 @@ module Searchkick | ||
76 | construct_record(klass, id, routing[id]) | 80 | construct_record(klass, id, routing[id]) |
77 | end | 81 | end |
78 | 82 | ||
79 | - import_inline(records, delete_records, method_name: method_name, single: single) | 83 | + import_inline(records, delete_records, method_name: method_name, allow_missing: allow_missing, single: single) |
80 | end | 84 | end |
81 | 85 | ||
82 | private | 86 | private |
@@ -86,13 +90,13 @@ module Searchkick | @@ -86,13 +90,13 @@ module Searchkick | ||
86 | end | 90 | end |
87 | 91 | ||
88 | # import in single request with retries | 92 | # import in single request with retries |
89 | - def import_inline(index_records, delete_records, method_name:, single:) | 93 | + def import_inline(index_records, delete_records, method_name:, allow_missing:, single:) |
90 | return if index_records.empty? && delete_records.empty? | 94 | return if index_records.empty? && delete_records.empty? |
91 | 95 | ||
92 | maybe_bulk(index_records, delete_records, method_name, single) do | 96 | maybe_bulk(index_records, delete_records, method_name, single) do |
93 | if index_records.any? | 97 | if index_records.any? |
94 | if method_name | 98 | if method_name |
95 | - index.bulk_update(index_records, method_name) | 99 | + index.bulk_update(index_records, method_name, allow_missing: allow_missing) |
96 | else | 100 | else |
97 | index.bulk_index(index_records) | 101 | index.bulk_index(index_records) |
98 | end | 102 | end |
lib/searchkick/reindex_v2_job.rb
@@ -11,7 +11,8 @@ module Searchkick | @@ -11,7 +11,8 @@ module Searchkick | ||
11 | # but keep for now for backwards compatibility | 11 | # but keep for now for backwards compatibility |
12 | model = model.unscoped if model.respond_to?(:unscoped) | 12 | model = model.unscoped if model.respond_to?(:unscoped) |
13 | items = [{id: id, routing: routing}] | 13 | items = [{id: id, routing: routing}] |
14 | - RecordIndexer.new(index).reindex_items(model, items, method_name: method_name, single: true) | 14 | + # TODO support allow_missing |
15 | + RecordIndexer.new(index).reindex_items(model, items, method_name: method_name, allow_missing: false, single: true) | ||
15 | end | 16 | end |
16 | end | 17 | end |
17 | end | 18 | end |
lib/searchkick/relation_indexer.rb
@@ -6,7 +6,7 @@ module Searchkick | @@ -6,7 +6,7 @@ module Searchkick | ||
6 | @index = index | 6 | @index = index |
7 | end | 7 | end |
8 | 8 | ||
9 | - def reindex(relation, mode:, method_name: nil, full: false, resume: false, scope: nil) | 9 | + def reindex(relation, mode:, method_name: nil, allow_missing: false, full: false, resume: false, scope: nil) |
10 | # apply scopes | 10 | # apply scopes |
11 | if scope | 11 | if scope |
12 | relation = relation.send(scope) | 12 | relation = relation.send(scope) |
@@ -32,6 +32,7 @@ module Searchkick | @@ -32,6 +32,7 @@ module Searchkick | ||
32 | reindex_options = { | 32 | reindex_options = { |
33 | mode: mode, | 33 | mode: mode, |
34 | method_name: method_name, | 34 | method_name: method_name, |
35 | + allow_missing: allow_missing, | ||
35 | full: full | 36 | full: full |
36 | } | 37 | } |
37 | record_indexer = RecordIndexer.new(index) | 38 | record_indexer = RecordIndexer.new(index) |
test/partial_reindex_test.rb
@@ -71,4 +71,17 @@ class PartialReindexTest < Minitest::Test | @@ -71,4 +71,17 @@ class PartialReindexTest < Minitest::Test | ||
71 | end | 71 | end |
72 | assert_match "document missing", error.message | 72 | assert_match "document missing", error.message |
73 | end | 73 | end |
74 | + | ||
75 | + def test_allow_missing | ||
76 | + store [{name: "Hi", color: "Blue"}] | ||
77 | + | ||
78 | + product = Product.first | ||
79 | + Product.search_index.remove(product) | ||
80 | + | ||
81 | + product.reindex(:search_name, allow_missing: true) | ||
82 | + Product.where(id: product.id).reindex(:search_name, allow_missing: true) | ||
83 | + Searchkick.callbacks(:bulk) do | ||
84 | + product.reindex(:search_name, allow_missing: true) | ||
85 | + end | ||
86 | + end | ||
74 | end | 87 | end |