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,10 +102,8 @@ module Searchkick | ||
102 | end | 102 | end |
103 | end | 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 | end | 107 | end |
110 | 108 | ||
111 | # callbacks | 109 | # callbacks |
lib/searchkick/logging.rb
@@ -129,7 +129,7 @@ module Searchkick | @@ -129,7 +129,7 @@ module Searchkick | ||
129 | end | 129 | end |
130 | 130 | ||
131 | module SearchkickWithInstrumentation | 131 | module SearchkickWithInstrumentation |
132 | - def multi_search(searches) | 132 | + def multi_search(searches, **options) |
133 | event = { | 133 | event = { |
134 | name: "Multi Search", | 134 | name: "Multi Search", |
135 | body: searches.flat_map { |q| [q.params.except(:body).to_json, q.body.to_json] }.map { |v| "#{v}\n" }.join | 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,34 +2,37 @@ module Searchkick | ||
2 | class MultiSearch | 2 | class MultiSearch |
3 | attr_reader :queries | 3 | attr_reader :queries |
4 | 4 | ||
5 | - def initialize(queries) | 5 | + def initialize(queries, retry_misspellings: false) |
6 | @queries = queries | 6 | @queries = queries |
7 | + @retry_misspellings = retry_misspellings | ||
7 | end | 8 | end |
8 | 9 | ||
9 | def perform | 10 | def perform |
10 | if queries.any? | 11 | if queries.any? |
11 | - perform_search(queries) | 12 | + perform_search(queries, retry_misspellings: @retry_misspellings) |
12 | end | 13 | end |
13 | end | 14 | end |
14 | 15 | ||
15 | private | 16 | private |
16 | 17 | ||
17 | - def perform_search(queries, retry_below_misspellings_threshold: true) | 18 | + def perform_search(queries, retry_misspellings: true) |
18 | responses = client.msearch(body: queries.flat_map { |q| [q.params.except(:body), q.body] })["responses"] | 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 | queries.each_with_index do |query, i| | 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 | else | 26 | else |
26 | query.handle_response(responses[i]) | 27 | query.handle_response(responses[i]) |
27 | end | 28 | end |
28 | end | 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 | end | 33 | end |
34 | + | ||
35 | + queries | ||
33 | end | 36 | end |
34 | 37 | ||
35 | def client | 38 | def client |
lib/searchkick/query.rb
@@ -4,7 +4,7 @@ module Searchkick | @@ -4,7 +4,7 @@ module Searchkick | ||
4 | 4 | ||
5 | @@metric_aggs = [:avg, :cardinality, :max, :min, :sum] | 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 | attr_accessor :body | 8 | attr_accessor :body |
9 | 9 | ||
10 | def_delegators :execute, :map, :each, :any?, :empty?, :size, :length, :slice, :[], :to_ary, | 10 | def_delegators :execute, :map, :each, :any?, :empty?, :size, :length, :slice, :[], :to_ary, |
@@ -79,7 +79,7 @@ module Searchkick | @@ -79,7 +79,7 @@ module Searchkick | ||
79 | @execute ||= begin | 79 | @execute ||= begin |
80 | begin | 80 | begin |
81 | response = execute_search | 81 | response = execute_search |
82 | - if below_misspellings_threshold?(response) | 82 | + if retry_misspellings?(response) |
83 | prepare | 83 | prepare |
84 | response = execute_search | 84 | response = execute_search |
85 | end | 85 | end |
@@ -159,10 +159,49 @@ module Searchkick | @@ -159,10 +159,49 @@ module Searchkick | ||
159 | @execute = Searchkick::Results.new(searchkick_klass, response, opts) | 159 | @execute = Searchkick::Results.new(searchkick_klass, response, opts) |
160 | end | 160 | end |
161 | 161 | ||
162 | - def below_misspellings_threshold?(response) | 162 | + def retry_misspellings?(response) |
163 | @misspellings_below && response["hits"]["total"] < @misspellings_below | 163 | @misspellings_below && response["hits"]["total"] < @misspellings_below |
164 | end | 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 | def prepare | 205 | def prepare |
167 | boost_fields, fields = set_fields | 206 | boost_fields, fields = set_fields |
168 | 207 | ||
@@ -452,45 +491,6 @@ module Searchkick | @@ -452,45 +491,6 @@ module Searchkick | ||
452 | @load = load | 491 | @load = load |
453 | end | 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 | def set_fields | 494 | def set_fields |
495 | boost_fields = {} | 495 | boost_fields = {} |
496 | fields = options[:fields] || searchkick_options[:default_fields] || searchkick_options[:searchable] | 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,10 +21,16 @@ class MultiSearchTest < Minitest::Test | ||
21 | end | 21 | end |
22 | 22 | ||
23 | def test_misspellings_below_unmet | 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 | end | 35 | end |
30 | end | 36 | end |