diff --git a/README.md b/README.md index a08f38d0..62ac2123 100644 --- a/README.md +++ b/README.md @@ -790,6 +790,21 @@ And set it up to run daily. UpdateConversionsJob.perform_later("Product", since: 1.day.ago) ``` +### Conversions Factor +By default, conversions are added to the score. To have a greater impact on the score with fewer conversions, you can define a conversion factor to multiply the number of conversions before adding them to the score. See [ElasticSearch docs](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html#function-field-value-factor). + +You can set the factor in the `searchkick` method: +```ruby +class Product < ApplicationRecord + searchkick conversions: :conversions, conversions_factor: 100 +end +``` + +Or, call the search method with the `conversions_factor` option: +```ruby +Product.search("ice cream", conversions_factor: 100) +``` + ## Personalized Results Order results differently for each user. For example, show a user’s previously purchased products before other results. diff --git a/lib/searchkick/model.rb b/lib/searchkick/model.rb index 41aa4875..18bbe1fc 100644 --- a/lib/searchkick/model.rb +++ b/lib/searchkick/model.rb @@ -3,8 +3,8 @@ module Model def searchkick(**options) options = Searchkick.model_options.merge(options) - unknown_keywords = options.keys - [:_all, :_type, :batch_size, :callbacks, :case_sensitive, :conversions, :deep_paging, :default_fields, - :filterable, :geo_shape, :highlight, :ignore_above, :index_name, :index_prefix, :inheritance, :language, + unknown_keywords = options.keys - [:_all, :_type, :batch_size, :callbacks, :case_sensitive, :conversions, :conversions_factor, + :deep_paging, :default_fields, :filterable, :geo_shape, :highlight, :ignore_above, :index_name, :index_prefix, :inheritance, :language, :locations, :mappings, :match, :max_result_window, :merge_mappings, :routing, :searchable, :search_synonyms, :settings, :similarity, :special_characters, :stem, :stemmer, :stem_conversions, :stem_exclusion, :stemmer_override, :suggest, :synonyms, :text_end, :text_middle, :text_start, :unscope, :word, :word_end, :word_middle, :word_start] diff --git a/lib/searchkick/query.rb b/lib/searchkick/query.rb index bef8be1b..513c897f 100644 --- a/lib/searchkick/query.rb +++ b/lib/searchkick/query.rb @@ -18,8 +18,8 @@ class Query 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, - :fields, :highlight, :includes, :index_name, :indices_boost, :limit, :load, + :boost_by, :boost_by_distance, :boost_by_recency, :boost_where, :conversions, :conversions_term, :conversions_factor, + :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, :request_params, :routing, :scope_results, :scroll, :select, :similar, :smart_aggs, :suggest, :total_entries, :track, :type, :where] raise ArgumentError, "unknown keywords: #{unknown_keywords.join(", ")}" if unknown_keywords.any? @@ -626,6 +626,8 @@ def build_query(query, filters, should, must_not, custom_filters, multiply_filte def set_conversions conversions_fields = Array(options[:conversions] || searchkick_options[:conversions]).map(&:to_s) + conversions_factor = options[:conversions_factor] || searchkick_options[:conversions_factor] + if conversions_fields.present? && options[:conversions] != false conversions_fields.map do |conversions_field| { @@ -641,7 +643,8 @@ def set_conversions } }, field_value_factor: { - field: "#{conversions_field}.count" + field: "#{conversions_field}.count", + factor: conversions_factor || 1 } } } diff --git a/test/conversions_test.rb b/test/conversions_test.rb index e96c7ef0..e1aa6b24 100644 --- a/test/conversions_test.rb +++ b/test/conversions_test.rb @@ -59,4 +59,25 @@ def test_conversions_weight ] assert_order "product", ["Product Conversions", "Product Boost"], boost: "orders_count" end + + def test_conversions_factor + Product.reindex + store [ + {name: "Tomato", conversions: {}}, + {name: "TomatE", conversions: {"tomato" => 1}}, + ] + assert_order "tomato", ["TomatE", "Tomato"] + assert_order "tomato", ["Tomato", "TomatE"], conversions: false + end + + def test_inline_conversions_factor + Speaker.reindex + store [ + {name: "SpeakRE", conversions_a: {"speaker" => 1}}, + {name: "Speaker"} + ], Speaker + + assert_order "speaker", ["Speaker", "SpeakRE"], {conversions: "conversions_a"}, Speaker + assert_order "speaker", ["SpeakRE", "Speaker"], {conversions: "conversions_a", conversions_factor: 100}, Speaker + end end diff --git a/test/models/product.rb b/test/models/product.rb index b6179df4..c0eecdeb 100644 --- a/test/models/product.rb +++ b/test/models/product.rb @@ -10,6 +10,7 @@ class Product ], suggest: [:name, :color], conversions: [:conversions], + conversions_factor: 100, locations: [:location, :multiple_locations], text_start: [:name], text_middle: [:name],