diff --git a/lib/searchkick.rb b/lib/searchkick.rb index 998a124..4ec1c89 100644 --- a/lib/searchkick.rb +++ b/lib/searchkick.rb @@ -102,10 +102,8 @@ module Searchkick end end - def self.multi_search(queries) - search = Searchkick::MultiSearch.new(queries) - search.perform - search.queries + def self.multi_search(queries, retry_misspellings: false) + Searchkick::MultiSearch.new(queries, retry_misspellings: retry_misspellings).perform end # callbacks diff --git a/lib/searchkick/logging.rb b/lib/searchkick/logging.rb index 84f528e..a095195 100644 --- a/lib/searchkick/logging.rb +++ b/lib/searchkick/logging.rb @@ -129,7 +129,7 @@ module Searchkick end module SearchkickWithInstrumentation - def multi_search(searches) + def multi_search(searches, **options) event = { name: "Multi Search", body: searches.flat_map { |q| [q.params.except(:body).to_json, q.body.to_json] }.map { |v| "#{v}\n" }.join diff --git a/lib/searchkick/multi_search.rb b/lib/searchkick/multi_search.rb index 304ab68..dcd0301 100644 --- a/lib/searchkick/multi_search.rb +++ b/lib/searchkick/multi_search.rb @@ -2,34 +2,37 @@ module Searchkick class MultiSearch attr_reader :queries - def initialize(queries) + def initialize(queries, retry_misspellings: false) @queries = queries + @retry_misspellings = retry_misspellings end def perform if queries.any? - perform_search(queries) + perform_search(queries, retry_misspellings: @retry_misspellings) end end private - def perform_search(queries, retry_below_misspellings_threshold: true) + def perform_search(queries, retry_misspellings: true) responses = client.msearch(body: queries.flat_map { |q| [q.params.except(:body), q.body] })["responses"] - queries_below_misspellings_threshold = [] + retry_queries = [] queries.each_with_index do |query, i| - if query.below_misspellings_threshold?(responses[i]) - query.prepare - queries_below_misspellings_threshold << query + if retry_misspellings && query.retry_misspellings?(responses[i]) + query.send(:prepare) # okay, since we don't want to expose this method outside Searchkick + retry_queries << query else query.handle_response(responses[i]) end end - if retry_below_misspellings_threshold && queries_below_misspellings_threshold.any? - perform_search(queries_below_misspellings_threshold, retry_below_misspellings_threshold: false) + if retry_misspellings && retry_queries.any? + perform_search(retry_queries, retry_misspellings: false) end + + queries end def client diff --git a/lib/searchkick/query.rb b/lib/searchkick/query.rb index 1cec5ed..8da6430 100644 --- a/lib/searchkick/query.rb +++ b/lib/searchkick/query.rb @@ -4,7 +4,7 @@ module Searchkick @@metric_aggs = [:avg, :cardinality, :max, :min, :sum] - attr_reader :klass, :term, :options, :misspellings_below + attr_reader :klass, :term, :options attr_accessor :body def_delegators :execute, :map, :each, :any?, :empty?, :size, :length, :slice, :[], :to_ary, @@ -79,7 +79,7 @@ module Searchkick @execute ||= begin begin response = execute_search - if below_misspellings_threshold?(response) + if retry_misspellings?(response) prepare response = execute_search end @@ -159,10 +159,49 @@ module Searchkick @execute = Searchkick::Results.new(searchkick_klass, response, opts) end - def below_misspellings_threshold?(response) + def retry_misspellings?(response) @misspellings_below && response["hits"]["total"] < @misspellings_below end - + + private + + def handle_error(e) + status_code = e.message[1..3].to_i + if status_code == 404 + raise MissingIndexError, "Index missing - run #{reindex_command}" + elsif status_code == 500 && ( + e.message.include?("IllegalArgumentException[minimumSimilarity >= 1]") || + e.message.include?("No query registered for [multi_match]") || + e.message.include?("[match] query does not support [cutoff_frequency]") || + e.message.include?("No query registered for [function_score]") + ) + + raise UnsupportedVersionError, "This version of Searchkick requires Elasticsearch 2 or greater" + elsif status_code == 400 + if ( + e.message.include?("bool query does not support [filter]") || + e.message.include?("[bool] filter does not support [filter]") + ) + + raise UnsupportedVersionError, "This version of Searchkick requires Elasticsearch 2 or greater" + elsif e.message.include?("[multi_match] analyzer [searchkick_search] not found") + raise InvalidQueryError, "Bad mapping - run #{reindex_command}" + else + raise InvalidQueryError, e.message + end + else + raise e + end + end + + def reindex_command + searchkick_klass ? "#{searchkick_klass.name}.reindex" : "reindex" + end + + def execute_search + Searchkick.client.search(params) + end + def prepare boost_fields, fields = set_fields @@ -452,45 +491,6 @@ module Searchkick @load = load end - private - - def handle_error(e) - status_code = e.message[1..3].to_i - if status_code == 404 - raise MissingIndexError, "Index missing - run #{reindex_command}" - elsif status_code == 500 && ( - e.message.include?("IllegalArgumentException[minimumSimilarity >= 1]") || - e.message.include?("No query registered for [multi_match]") || - e.message.include?("[match] query does not support [cutoff_frequency]") || - e.message.include?("No query registered for [function_score]") - ) - - raise UnsupportedVersionError, "This version of Searchkick requires Elasticsearch 2 or greater" - elsif status_code == 400 - if ( - e.message.include?("bool query does not support [filter]") || - e.message.include?("[bool] filter does not support [filter]") - ) - - raise UnsupportedVersionError, "This version of Searchkick requires Elasticsearch 2 or greater" - elsif e.message.include?("[multi_match] analyzer [searchkick_search] not found") - raise InvalidQueryError, "Bad mapping - run #{reindex_command}" - else - raise InvalidQueryError, e.message - end - else - raise e - end - end - - def reindex_command - searchkick_klass ? "#{searchkick_klass.name}.reindex" : "reindex" - end - - def execute_search - Searchkick.client.search(params) - end - def set_fields boost_fields = {} fields = options[:fields] || searchkick_options[:default_fields] || searchkick_options[:searchable] diff --git a/test/multi_search_test.rb b/test/multi_search_test.rb index 56be3f7..7eabb73 100644 --- a/test/multi_search_test.rb +++ b/test/multi_search_test.rb @@ -21,10 +21,16 @@ class MultiSearchTest < Minitest::Test end def test_misspellings_below_unmet - store_names ["Product A"] - store_names ["Store A"], Store - stores = Store.search("Stre A", misspellings: { below: 2 }, execute: false) - Searchkick.multi_search([stores]) - assert_equal ["Store A"], stores.map(&:name) + store_names ["abc", "abd", "aee"] + products = Product.search("abc", misspellings: {below: 2}, execute: false) + Searchkick.multi_search([products]) + assert_equal ["abc"], products.map(&:name) + end + + def test_misspellings_below_unmet_retry + store_names ["abc", "abd", "aee"] + products = Product.search("abc", misspellings: {below: 2}, execute: false) + Searchkick.multi_search([products], retry_misspellings: true) + assert_equal ["abc", "abd"], products.map(&:name) end end -- libgit2 0.21.0