From 5754327bad31592bf2463b5d032c36c5b4b09329 Mon Sep 17 00:00:00 2001 From: iamchristianalex Date: Mon, 27 Jun 2022 16:25:07 +0200 Subject: [PATCH 1/6] when boost_and is true and the operator is 'or', fields matching using the 'and' operator are boosted --- lib/searchkick/query.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/searchkick/query.rb b/lib/searchkick/query.rb index a195e739..8569cdbb 100644 --- a/lib/searchkick/query.rb +++ b/lib/searchkick/query.rb @@ -17,7 +17,7 @@ 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, + unknown_keywords = options.keys - [:aggs, :block, :body, :body_options, :boost, :boost_and, :boost_by, :boost_by_distance, :boost_by_recency, :boost_where, :conversions, :conversions_term, :debug, :emoji, :exclude, :explain, :fields, :highlight, :includes, :index_name, :indices_boost, :limit, :load, :match, :misspellings, :models, :model_includes, :offset, :operator, :order, :padding, :page, :per_page, :profile, @@ -410,6 +410,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 From f000cafb07b5e9db080301e0a321368c72465981 Mon Sep 17 00:00:00 2001 From: iamchristianalex Date: Mon, 27 Jun 2022 16:25:07 +0200 Subject: [PATCH 2/6] when boost_and is true and the operator is 'or', fields matching using the 'and' operator are boosted --- lib/searchkick/query.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/searchkick/query.rb b/lib/searchkick/query.rb index f4335a55..9825e22a 100644 --- a/lib/searchkick/query.rb +++ b/lib/searchkick/query.rb @@ -17,7 +17,7 @@ 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, + unknown_keywords = options.keys - [:aggs, :block, :body, :body_options, :boost, :boost_and, :boost_by, :boost_by_distance, :boost_by_recency, :boost_where, :conversions, :conversions_term, :debug, :emoji, :exclude, :explain, :fields, :highlight, :includes, :index_name, :indices_boost, :limit, :load, :match, :misspellings, :models, :model_includes, :offset, :operator, :order, :padding, :page, :per_page, :profile, @@ -414,6 +414,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 From ab97be3efbb002428e8d24d14208388d1f02b13e Mon Sep 17 00:00:00 2001 From: iamchristianalex Date: Mon, 16 Sep 2024 12:29:19 +0200 Subject: [PATCH 3/6] added support for boost_by_script --- lib/searchkick/query.rb | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/searchkick/query.rb b/lib/searchkick/query.rb index 6279e109..c1324376 100644 --- a/lib/searchkick/query.rb +++ b/lib/searchkick/query.rb @@ -18,7 +18,7 @@ class Query def initialize(klass, term = "*", **options) unknown_keywords = options.keys - [:aggs, :block, :body, :body_options, :boost, :boost_and, - :boost_by, :boost_by_distance, :boost_by_recency, :boost_where, :conversions, :conversions_term, :debug, :emoji, :exclude, :explain, + :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? @@ -423,7 +423,7 @@ def prepare should: q2 } }, - should: qs.map { |q| {match_type => {field => q.merge({ operator: "and"})}} } + should: qs.map { |q| {match_type => {field => q.merge({ operator: "and"})}} } } } else @@ -502,6 +502,7 @@ def prepare set_boost_where(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(custom_filters) if options[:boost_by_script] payload[:query] = build_query(query, filters, should, must_not, custom_filters, multiply_filters) @@ -718,6 +719,17 @@ def set_boost_by_recency(custom_filters) end end + def set_boost_by_script(custom_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 + script_score = { script: { source: boost_by.source, lang: boost_by.lang, params: boost_by.params } } + custom_filters.concat { script_score: } + end + end + def set_boost_by(multiply_filters, custom_filters) boost_by = options[:boost_by] || {} if boost_by.is_a?(Array) From 71247f53f90ee6222e6ff6c268237f512a996c67 Mon Sep 17 00:00:00 2001 From: iamchristianalex Date: Mon, 16 Sep 2024 12:33:04 +0200 Subject: [PATCH 4/6] syntax typo --- lib/searchkick/query.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/searchkick/query.rb b/lib/searchkick/query.rb index c1324376..9e6ed125 100644 --- a/lib/searchkick/query.rb +++ b/lib/searchkick/query.rb @@ -726,7 +726,7 @@ def set_boost_by_script(custom_filters) raise TypeError, "expected Searchkick::Script" end script_score = { script: { source: boost_by.source, lang: boost_by.lang, params: boost_by.params } } - custom_filters.concat { script_score: } + custom_filters << { script_score: } end end From 9e4561f57dbc8e97d0154ecf1cc4aaabfc3f6bef Mon Sep 17 00:00:00 2001 From: iamchristianalex Date: Mon, 16 Sep 2024 12:37:45 +0200 Subject: [PATCH 5/6] some minor cleanup to make script boosting more readable --- lib/searchkick/query.rb | 3 +-- lib/searchkick/script.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/searchkick/query.rb b/lib/searchkick/query.rb index 9e6ed125..ab945d3e 100644 --- a/lib/searchkick/query.rb +++ b/lib/searchkick/query.rb @@ -725,8 +725,7 @@ def set_boost_by_script(custom_filters) unless value.is_a?(Searchkick::Script) raise TypeError, "expected Searchkick::Script" end - script_score = { script: { source: boost_by.source, lang: boost_by.lang, params: boost_by.params } } - custom_filters << { script_score: } + custom_filters << { script_score: { script: value.to_h } } 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 From 04afaa5fa122a694f0dfce8a30b87a5cbb6df0cd Mon Sep 17 00:00:00 2001 From: iamchristianalex Date: Mon, 16 Sep 2024 14:20:19 +0200 Subject: [PATCH 6/6] added support for default boost_mode across all boost_where filters --- lib/searchkick/query.rb | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/searchkick/query.rb b/lib/searchkick/query.rb index ab945d3e..81b7600d 100644 --- a/lib/searchkick/query.rb +++ b/lib/searchkick/query.rb @@ -17,7 +17,7 @@ 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_and, + 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, @@ -499,10 +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(custom_filters) if options[:boost_by_script] + set_boost_by_script(multiply_filters) if options[:boost_by_script] payload[:query] = build_query(query, filters, should, must_not, custom_filters, multiply_filters) @@ -615,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: { @@ -719,13 +718,13 @@ def set_boost_by_recency(custom_filters) end end - def set_boost_by_script(custom_filters) + 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 - custom_filters << { script_score: { script: value.to_h } } + multiply_filters << { script_score: { script: value.to_h } } end end @@ -742,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