Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'boost_and' option to Searchkick::Query #1628

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
52 changes: 43 additions & 9 deletions lib/searchkick/query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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?
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down
8 changes: 8 additions & 0 deletions lib/searchkick/script.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,13 @@ def initialize(source, lang: "painless", params: {})
@lang = lang
@params = params
end

def to_h
{
source:,
lang:,
params:
}
end
end
end