diff --git a/lib/searchkick/query.rb b/lib/searchkick/query.rb index f3c2f657..81b7600d 100644 --- a/lib/searchkick/query.rb +++ b/lib/searchkick/query.rb @@ -17,8 +17,8 @@ class Query :with_score, :misspellings?, :scroll_id, :clear_scroll, :missing_records, :with_hit def initialize(klass, term = "*", **options) - unknown_keywords = options.keys - [:aggs, :block, :body, :body_options, :boost, - :boost_by, :boost_by_distance, :boost_by_recency, :boost_where, :conversions, :conversions_term, :debug, :emoji, :exclude, :explain, + unknown_keywords = options.keys - [:aggs, :block, :body, :body_options, :boost, :boost_mode, :boost_and, + :boost_by, :boost_by_distance, :boost_by_recency, :boost_by_script, :boost_where, :conversions, :conversions_term, :debug, :emoji, :exclude, :explain, :fields, :highlight, :includes, :index_name, :indices_boost, :knn, :limit, :load, :match, :misspellings, :models, :model_includes, :offset, :operator, :order, :padding, :page, :per_page, :profile, :request_params, :routing, :scope_results, :scroll, :select, :similar, :smart_aggs, :suggest, :total_entries, :track, :type, :where] @@ -273,7 +273,7 @@ def prepare @json = options[:body] if @json ignored_options = options.keys & [:aggs, :boost, - :boost_by, :boost_by_distance, :boost_by_recency, :boost_where, :conversions, :conversions_term, :exclude, :explain, + :boost_by, :boost_by_distance, :boost_by_recency, :boost_by_script, :boost_where, :conversions, :conversions_term, :exclude, :explain, :fields, :highlight, :indices_boost, :match, :misspellings, :operator, :order, :profile, :select, :smart_aggs, :suggest, :where] raise ArgumentError, "Options incompatible with body option: #{ignored_options.join(", ")}" if ignored_options.any? @@ -415,6 +415,17 @@ def prepare should: {match_type => {field.sub(/\.word_(start|middle|end)\z/, ".analyzed") => qs.first}} } } + elsif options[:boost_and] && operator.to_s == "or" + queries_to_add << { + bool: { + must: { + bool: { + should: q2 + } + }, + should: qs.map { |q| {match_type => {field => q.merge({ operator: "and"})}} } + } + } else queries_to_add.concat(q2) end @@ -488,9 +499,10 @@ def prepare multiply_filters = [] set_boost_by(multiply_filters, custom_filters) - set_boost_where(custom_filters) + set_boost_where(multiply_filters, custom_filters) set_boost_by_distance(custom_filters) if options[:boost_by_distance] set_boost_by_recency(custom_filters) if options[:boost_by_recency] + set_boost_by_script(multiply_filters) if options[:boost_by_script] payload[:query] = build_query(query, filters, should, must_not, custom_filters, multiply_filters) @@ -603,7 +615,6 @@ def build_query(query, filters, should, must_not, custom_filters, multiply_filte bool[:should] = should if should.any? # conversions query = {bool: bool} end - if custom_filters.any? query = { function_score: { @@ -707,6 +718,16 @@ def set_boost_by_recency(custom_filters) end end + def set_boost_by_script(multiply_filters) + options[:boost_by_script] = [options[:boost_by_script]] if options[:boost_by_script].is_a?(Searchkick::Script) + options[:boost_by_script].each do |value| + unless value.is_a?(Searchkick::Script) + raise TypeError, "expected Searchkick::Script" + end + multiply_filters << { script_score: { script: value.to_h } } + end + end + def set_boost_by(multiply_filters, custom_filters) boost_by = options[:boost_by] || {} if boost_by.is_a?(Array) @@ -720,18 +741,31 @@ def set_boost_by(multiply_filters, custom_filters) multiply_filters.concat boost_filters(multiply_by || {}) end - def set_boost_where(custom_filters) + def boost_where_filter(value = {}, multiply_filters, custom_filters) + filters = { + multiply: multiply_filters, + sum: custom_filters + } + default = options[:boost_mode]&.to_sym || :sum + key = value&.delete(:boost_mode)&.to_sym || default + filters[key] + end + + def set_boost_where(multiply_filters, custom_filters) boost_where = options[:boost_where] || {} boost_where.each do |field, value| if value.is_a?(Array) && value.first.is_a?(Hash) value.each do |value_factor| - custom_filters << custom_filter(field, value_factor[:value], value_factor[:factor]) + filter = boost_where_filter(value_factor, multiply_filters, custom_filters) + filter << custom_filter(field, value_factor[:value], value_factor[:factor]) end elsif value.is_a?(Hash) - custom_filters << custom_filter(field, value[:value], value[:factor]) + filter = boost_where_filter(value, multiply_filters, custom_filters) + filter << custom_filter(field, value[:value], value[:factor]) else factor = 1000 - custom_filters << custom_filter(field, value, factor) + filter = boost_where_filter(value, multiply_filters, custom_filters) + filter << custom_filter(field, value, factor) end end end diff --git a/lib/searchkick/script.rb b/lib/searchkick/script.rb index 0cac5f92..ce283a0e 100644 --- a/lib/searchkick/script.rb +++ b/lib/searchkick/script.rb @@ -7,5 +7,13 @@ def initialize(source, lang: "painless", params: {}) @lang = lang @params = params end + + def to_h + { + source:, + lang:, + params: + } + end end end