Commit 6088da6d735511fc411902899a18dfba159ed14d

Authored by Andrew Kane
1 parent 7b7a445f

Added retry_misspellings flag for backwards compatibility

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 &lt; Minitest::Test @@ -21,10 +21,16 @@ class MultiSearchTest &lt; 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