Commit 6088da6d735511fc411902899a18dfba159ed14d
1 parent
7b7a445f
Exists in
master
and in
19 other branches
Added retry_misspellings flag for backwards compatibility
Showing
5 changed files
with
69 additions
and
62 deletions
Show diff stats
lib/searchkick.rb
... | ... | @@ -102,10 +102,8 @@ module Searchkick |
102 | 102 | end |
103 | 103 | end |
104 | 104 | |
105 | - def self.multi_search(queries) | |
106 | - search = Searchkick::MultiSearch.new(queries) | |
107 | - search.perform | |
108 | - search.queries | |
105 | + def self.multi_search(queries, retry_misspellings: false) | |
106 | + Searchkick::MultiSearch.new(queries, retry_misspellings: retry_misspellings).perform | |
109 | 107 | end |
110 | 108 | |
111 | 109 | # callbacks | ... | ... |
lib/searchkick/logging.rb
... | ... | @@ -129,7 +129,7 @@ module Searchkick |
129 | 129 | end |
130 | 130 | |
131 | 131 | module SearchkickWithInstrumentation |
132 | - def multi_search(searches) | |
132 | + def multi_search(searches, **options) | |
133 | 133 | event = { |
134 | 134 | name: "Multi Search", |
135 | 135 | body: searches.flat_map { |q| [q.params.except(:body).to_json, q.body.to_json] }.map { |v| "#{v}\n" }.join | ... | ... |
lib/searchkick/multi_search.rb
... | ... | @@ -2,34 +2,37 @@ module Searchkick |
2 | 2 | class MultiSearch |
3 | 3 | attr_reader :queries |
4 | 4 | |
5 | - def initialize(queries) | |
5 | + def initialize(queries, retry_misspellings: false) | |
6 | 6 | @queries = queries |
7 | + @retry_misspellings = retry_misspellings | |
7 | 8 | end |
8 | 9 | |
9 | 10 | def perform |
10 | 11 | if queries.any? |
11 | - perform_search(queries) | |
12 | + perform_search(queries, retry_misspellings: @retry_misspellings) | |
12 | 13 | end |
13 | 14 | end |
14 | 15 | |
15 | 16 | private |
16 | 17 | |
17 | - def perform_search(queries, retry_below_misspellings_threshold: true) | |
18 | + def perform_search(queries, retry_misspellings: true) | |
18 | 19 | responses = client.msearch(body: queries.flat_map { |q| [q.params.except(:body), q.body] })["responses"] |
19 | 20 | |
20 | - queries_below_misspellings_threshold = [] | |
21 | + retry_queries = [] | |
21 | 22 | queries.each_with_index do |query, i| |
22 | - if query.below_misspellings_threshold?(responses[i]) | |
23 | - query.prepare | |
24 | - queries_below_misspellings_threshold << query | |
23 | + if retry_misspellings && query.retry_misspellings?(responses[i]) | |
24 | + query.send(:prepare) # okay, since we don't want to expose this method outside Searchkick | |
25 | + retry_queries << query | |
25 | 26 | else |
26 | 27 | query.handle_response(responses[i]) |
27 | 28 | end |
28 | 29 | end |
29 | 30 | |
30 | - if retry_below_misspellings_threshold && queries_below_misspellings_threshold.any? | |
31 | - perform_search(queries_below_misspellings_threshold, retry_below_misspellings_threshold: false) | |
31 | + if retry_misspellings && retry_queries.any? | |
32 | + perform_search(retry_queries, retry_misspellings: false) | |
32 | 33 | end |
34 | + | |
35 | + queries | |
33 | 36 | end |
34 | 37 | |
35 | 38 | def client | ... | ... |
lib/searchkick/query.rb
... | ... | @@ -4,7 +4,7 @@ module Searchkick |
4 | 4 | |
5 | 5 | @@metric_aggs = [:avg, :cardinality, :max, :min, :sum] |
6 | 6 | |
7 | - attr_reader :klass, :term, :options, :misspellings_below | |
7 | + attr_reader :klass, :term, :options | |
8 | 8 | attr_accessor :body |
9 | 9 | |
10 | 10 | def_delegators :execute, :map, :each, :any?, :empty?, :size, :length, :slice, :[], :to_ary, |
... | ... | @@ -79,7 +79,7 @@ module Searchkick |
79 | 79 | @execute ||= begin |
80 | 80 | begin |
81 | 81 | response = execute_search |
82 | - if below_misspellings_threshold?(response) | |
82 | + if retry_misspellings?(response) | |
83 | 83 | prepare |
84 | 84 | response = execute_search |
85 | 85 | end |
... | ... | @@ -159,10 +159,49 @@ module Searchkick |
159 | 159 | @execute = Searchkick::Results.new(searchkick_klass, response, opts) |
160 | 160 | end |
161 | 161 | |
162 | - def below_misspellings_threshold?(response) | |
162 | + def retry_misspellings?(response) | |
163 | 163 | @misspellings_below && response["hits"]["total"] < @misspellings_below |
164 | 164 | end |
165 | - | |
165 | + | |
166 | + private | |
167 | + | |
168 | + def handle_error(e) | |
169 | + status_code = e.message[1..3].to_i | |
170 | + if status_code == 404 | |
171 | + raise MissingIndexError, "Index missing - run #{reindex_command}" | |
172 | + elsif status_code == 500 && ( | |
173 | + e.message.include?("IllegalArgumentException[minimumSimilarity >= 1]") || | |
174 | + e.message.include?("No query registered for [multi_match]") || | |
175 | + e.message.include?("[match] query does not support [cutoff_frequency]") || | |
176 | + e.message.include?("No query registered for [function_score]") | |
177 | + ) | |
178 | + | |
179 | + raise UnsupportedVersionError, "This version of Searchkick requires Elasticsearch 2 or greater" | |
180 | + elsif status_code == 400 | |
181 | + if ( | |
182 | + e.message.include?("bool query does not support [filter]") || | |
183 | + e.message.include?("[bool] filter does not support [filter]") | |
184 | + ) | |
185 | + | |
186 | + raise UnsupportedVersionError, "This version of Searchkick requires Elasticsearch 2 or greater" | |
187 | + elsif e.message.include?("[multi_match] analyzer [searchkick_search] not found") | |
188 | + raise InvalidQueryError, "Bad mapping - run #{reindex_command}" | |
189 | + else | |
190 | + raise InvalidQueryError, e.message | |
191 | + end | |
192 | + else | |
193 | + raise e | |
194 | + end | |
195 | + end | |
196 | + | |
197 | + def reindex_command | |
198 | + searchkick_klass ? "#{searchkick_klass.name}.reindex" : "reindex" | |
199 | + end | |
200 | + | |
201 | + def execute_search | |
202 | + Searchkick.client.search(params) | |
203 | + end | |
204 | + | |
166 | 205 | def prepare |
167 | 206 | boost_fields, fields = set_fields |
168 | 207 | |
... | ... | @@ -452,45 +491,6 @@ module Searchkick |
452 | 491 | @load = load |
453 | 492 | end |
454 | 493 | |
455 | - private | |
456 | - | |
457 | - def handle_error(e) | |
458 | - status_code = e.message[1..3].to_i | |
459 | - if status_code == 404 | |
460 | - raise MissingIndexError, "Index missing - run #{reindex_command}" | |
461 | - elsif status_code == 500 && ( | |
462 | - e.message.include?("IllegalArgumentException[minimumSimilarity >= 1]") || | |
463 | - e.message.include?("No query registered for [multi_match]") || | |
464 | - e.message.include?("[match] query does not support [cutoff_frequency]") || | |
465 | - e.message.include?("No query registered for [function_score]") | |
466 | - ) | |
467 | - | |
468 | - raise UnsupportedVersionError, "This version of Searchkick requires Elasticsearch 2 or greater" | |
469 | - elsif status_code == 400 | |
470 | - if ( | |
471 | - e.message.include?("bool query does not support [filter]") || | |
472 | - e.message.include?("[bool] filter does not support [filter]") | |
473 | - ) | |
474 | - | |
475 | - raise UnsupportedVersionError, "This version of Searchkick requires Elasticsearch 2 or greater" | |
476 | - elsif e.message.include?("[multi_match] analyzer [searchkick_search] not found") | |
477 | - raise InvalidQueryError, "Bad mapping - run #{reindex_command}" | |
478 | - else | |
479 | - raise InvalidQueryError, e.message | |
480 | - end | |
481 | - else | |
482 | - raise e | |
483 | - end | |
484 | - end | |
485 | - | |
486 | - def reindex_command | |
487 | - searchkick_klass ? "#{searchkick_klass.name}.reindex" : "reindex" | |
488 | - end | |
489 | - | |
490 | - def execute_search | |
491 | - Searchkick.client.search(params) | |
492 | - end | |
493 | - | |
494 | 494 | def set_fields |
495 | 495 | boost_fields = {} |
496 | 496 | fields = options[:fields] || searchkick_options[:default_fields] || searchkick_options[:searchable] | ... | ... |
test/multi_search_test.rb
... | ... | @@ -21,10 +21,16 @@ class MultiSearchTest < Minitest::Test |
21 | 21 | end |
22 | 22 | |
23 | 23 | def test_misspellings_below_unmet |
24 | - store_names ["Product A"] | |
25 | - store_names ["Store A"], Store | |
26 | - stores = Store.search("Stre A", misspellings: { below: 2 }, execute: false) | |
27 | - Searchkick.multi_search([stores]) | |
28 | - assert_equal ["Store A"], stores.map(&:name) | |
24 | + store_names ["abc", "abd", "aee"] | |
25 | + products = Product.search("abc", misspellings: {below: 2}, execute: false) | |
26 | + Searchkick.multi_search([products]) | |
27 | + assert_equal ["abc"], products.map(&:name) | |
28 | + end | |
29 | + | |
30 | + def test_misspellings_below_unmet_retry | |
31 | + store_names ["abc", "abd", "aee"] | |
32 | + products = Product.search("abc", misspellings: {below: 2}, execute: false) | |
33 | + Searchkick.multi_search([products], retry_misspellings: true) | |
34 | + assert_equal ["abc", "abd"], products.map(&:name) | |
29 | 35 | end |
30 | 36 | end | ... | ... |