diff --git a/CHANGELOG.md b/CHANGELOG.md index ae8ee1b..77ea4b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## 3.1.1 [unreleased] +- Added per-field misspellings - Made `exclude` option work with match all ## 3.1.0 diff --git a/README.md b/README.md index 4d3ca34..5b691c8 100644 --- a/README.md +++ b/README.md @@ -410,6 +410,14 @@ Turn off misspellings with: Product.search "zuchini", misspellings: false # no zucchini ``` +Specify which fields can include misspellings with: [master] + +```ruby +Product.search "zucini", fields: [:name, :color], misspellings: {fields: [:name]} +``` + +> When doing this, you must also specify fields to search + ### Bad Matches If a user searches `butter`, they may also get results for `peanut butter`. To prevent this, use: diff --git a/lib/searchkick/query.rb b/lib/searchkick/query.rb index d3d2cdf..1df5fd6 100644 --- a/lib/searchkick/query.rb +++ b/lib/searchkick/query.rb @@ -280,6 +280,15 @@ module Searchkick prefix_length = (misspellings.is_a?(Hash) && misspellings[:prefix_length]) || 0 default_max_expansions = @misspellings_below ? 20 : 3 max_expansions = (misspellings.is_a?(Hash) && misspellings[:max_expansions]) || default_max_expansions + misspellings_fields = misspellings.is_a?(Hash) && misspellings.key?(:fields) && misspellings[:fields].map { |f| "#{f}.#{@match_suffix}" } + + if misspellings_fields + missing_fields = misspellings_fields - fields + if missing_fields.any? + raise ArgumentError, "All fields in per-field misspellings must also be specified in fields option" + end + end + @misspellings = true else @misspellings = false @@ -314,8 +323,10 @@ module Searchkick exclude_analyzer = nil exclude_field = field + field_misspellings = misspellings && (!misspellings_fields || misspellings_fields.include?(field)) + if field == "_all" || field.end_with?(".analyzed") - shared_options[:cutoff_frequency] = 0.001 unless operator.to_s == "and" || misspellings == false + shared_options[:cutoff_frequency] = 0.001 unless operator.to_s == "and" || field_misspellings == false qs << shared_options.merge(analyzer: "searchkick_search") # searchkick_search and searchkick_search2 are the same for ukrainian @@ -334,7 +345,7 @@ module Searchkick exclude_analyzer = analyzer end - if misspellings != false && match_type == :match + if field_misspellings != false && match_type == :match qs.concat(qs.map { |q| q.except(:cutoff_frequency).merge(fuzziness: edit_distance, prefix_length: prefix_length, max_expansions: max_expansions, boost: factor).merge(transpositions) }) end diff --git a/test/misspellings_test.rb b/test/misspellings_test.rb index ae01f64..80971ae 100644 --- a/test/misspellings_test.rb +++ b/test/misspellings_test.rb @@ -53,4 +53,48 @@ class MisspellingsTest < Minitest::Test store_names ["abc", "abd", "aee"] assert !Product.search("abc", misspellings: {below: 1}).misspellings? end + + def test_misspellings_field_correct_spelling_still_works + store [{name: "Sriracha", color: "blue"}] + assert_misspellings "Sriracha", ["Sriracha"], {fields: [:name, :color]} + assert_misspellings "blue", ["Sriracha"], {fields: [:name, :color]} + end + + def test_misspellings_field_enabled + store [{name: "Sriracha", color: "blue"}] + assert_misspellings "siracha", ["Sriracha"], {fields: [:name]} + assert_misspellings "clue", ["Sriracha"], {fields: [:color]} + end + + def test_misspellings_field_disabled + store [{name: "Sriracha", color: "blue"}] + assert_misspellings "siracha", [], {fields: [:color]} + assert_misspellings "clue", [], {fields: [:name]} + end + + def test_misspellings_field_with_transpositions + store [{name: "Sriracha", color: "blue"}] + assert_misspellings "lbue", [], {transpositions: false, fields: [:color]} + end + + def test_misspellings_field_with_edit_distance + store [{name: "Sriracha", color: "blue"}] + assert_misspellings "crue", ["Sriracha"], {edit_distance: 2, fields: [:color]} + end + + def test_misspellings_field_multiple + store [ + {name: "Greek Yogurt", color: "white"}, + {name: "Green Onions", color: "yellow"} + ] + assert_misspellings "greed", ["Greek Yogurt", "Green Onions"], {fields: [:name, :color]} + assert_misspellings "mellow", ["Green Onions"], {fields: [:name, :color]} + end + + def test_misspellings_field_requires_explicit_search_fields + store_names ["Sriracha"] + assert_raises(ArgumentError) do + assert_search "siracha", ["Sriracha"], {misspellings: {fields: [:name]}} + end + end end diff --git a/test/test_helper.rb b/test/test_helper.rb index c5a83a0..d0bc67e 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -579,6 +579,14 @@ class Minitest::Test assert_equal expected, klass.search(term, options).map(&:name).first end + def assert_misspellings(term, expected, misspellings = {}, klass = Product) + options = { + fields: [:name, :color], + misspellings: misspellings + } + assert_search(term, expected, options, klass) + end + def with_options(klass, options) previous_options = klass.searchkick_options.dup begin -- libgit2 0.21.0