diff --git a/app/assets/javascripts/discourse/components/edit-category-settings.js.es6 b/app/assets/javascripts/discourse/components/edit-category-settings.js.es6 index 82af15b8f0d..b8a63ee653a 100644 --- a/app/assets/javascripts/discourse/components/edit-category-settings.js.es6 +++ b/app/assets/javascripts/discourse/components/edit-category-settings.js.es6 @@ -69,7 +69,7 @@ export default buildCategoryPanel("settings", { }); } - return options.sort((a, b) => a.value <= b.value); + return options; }, @computed diff --git a/app/models/concerns/searchable.rb b/app/models/concerns/searchable.rb index e655a2e4784..09fc8f2a4e0 100644 --- a/app/models/concerns/searchable.rb +++ b/app/models/concerns/searchable.rb @@ -2,8 +2,12 @@ module Searchable extend ActiveSupport::Concern PRIORITIES = Enum.new( + ignore: 1, + very_low: 2, + low: 3, normal: 0, - ignore: 1 + high: 4, + very_high: 5 ) included do diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index be3e5c7496b..3c392e41d54 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -2459,6 +2459,10 @@ en: options: normal: "Normal" ignore: "Ignore" + very_low: "Very Low" + low: "Low" + high: "High" + very_high: "Very High" sort_options: default: "default" likes: "Likes" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index aa40d87e49a..37f28c1e945 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1291,6 +1291,10 @@ en: search_query_log_max_size: "Maximum amount of search queries to keep" search_query_log_max_retention_days: "Maximum amount of time to keep search queries, in days." search_ignore_accents: "Ignore accents when searching for text." + category_search_priority_very_low_weight: "Weight applied to ranking for very low category search priority." + category_search_priority_low_weight: "Weight applied to ranking for low category search priority." + category_search_priority_high_weight: "Weight applied to ranking for high category search priority." + category_search_priority_very_high_weight: "Weight applied to ranking for very high category search priority." allow_uncategorized_topics: "Allow topics to be created without a category. WARNING: If there are any uncategorized topics, you must recategorize them before turning this off." allow_duplicate_topic_titles: "Allow topics with identical, duplicate titles." unique_posts_mins: "How many minutes before a user can make a post with the same content again" @@ -2030,6 +2034,11 @@ en: max_username_length_exists: "You cannot set the maximum username length below the longest username (%{username})." max_username_length_range: "You cannot set the maximum below the minimum." invalid_hex_value: "Color values have to be 6-digit hexadecimal codes." + category_search_priority: + very_low_weight_invalid: "You cannot set the weight to be greater than 'category_search_priority_low_weight'." + low_weight_invalid: "You cannot set the weight to be greater or equal to 1 or smaller than 'category_search_priority_very_low_weight'." + high_weight_invalid: "You cannot set the weight to be greater or equal to 1 or greater than 'category_search_priority_very_high_weight'." + very_high_weight_invalid: "You cannot set the weight to be smaller than 'category_search_priority_high_weight'." placeholder: sso_provider_secrets: diff --git a/config/site_settings.yml b/config/site_settings.yml index 2a4c9051ae6..6dcb7eb6eca 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -1614,6 +1614,22 @@ search: ro: true sk: true tr_TR: true + category_search_priority_very_low_weight: + default: 0.6 + hidden: true + validator: "CategorySearchPriorityWeightsValidator" + category_search_priority_low_weight: + default: 0.8 + hidden: true + validator: "CategorySearchPriorityWeightsValidator" + category_search_priority_high_weight: + default: 1.2 + hidden: true + validator: "CategorySearchPriorityWeightsValidator" + category_search_priority_very_high_weight: + default: 1.4 + hidden: true + validator: "CategorySearchPriorityWeightsValidator" uncategorized: version_checks: diff --git a/lib/search.rb b/lib/search.rb index 33ae811ff18..2721f662557 100644 --- a/lib/search.rb +++ b/lib/search.rb @@ -836,9 +836,27 @@ class Search posts = posts.order("posts.like_count DESC") end else + # 0|32 default normalization scaled into the range zero to one data_ranking = <<~SQL - TS_RANK_CD( - post_search_data.search_data, #{ts_query(weight_filter: weights)} + ( + TS_RANK_CD( + post_search_data.search_data, + #{ts_query(weight_filter: weights)}, + 0|32 + ) * + ( + CASE categories.search_priority + WHEN #{Searchable::PRIORITIES[:very_low]} + THEN #{SiteSetting.category_search_priority_very_low_weight} + WHEN #{Searchable::PRIORITIES[:low]} + THEN #{SiteSetting.category_search_priority_low_weight} + WHEN #{Searchable::PRIORITIES[:high]} + THEN #{SiteSetting.category_search_priority_high_weight} + WHEN #{Searchable::PRIORITIES[:very_high]} + THEN #{SiteSetting.category_search_priority_very_high_weight} + ELSE 1 + END + ) ) SQL diff --git a/lib/site_settings/type_supervisor.rb b/lib/site_settings/type_supervisor.rb index d3549464027..2137561b88b 100644 --- a/lib/site_settings/type_supervisor.rb +++ b/lib/site_settings/type_supervisor.rb @@ -103,7 +103,9 @@ class SiteSettings::TypeSupervisor opts[:validator] = opts[:validator].try(:constantize) if (validator_type = (opts[:validator] || validator_for(@types[name]))) - @validators[name] = { class: validator_type, opts: opts.slice(*VALIDATOR_OPTS) } + validator_opts = opts.slice(*VALIDATOR_OPTS) + validator_opts[:name] = name + @validators[name] = { class: validator_type, opts: validator_opts } end end diff --git a/lib/validators/category_search_priority_weights_validator.rb b/lib/validators/category_search_priority_weights_validator.rb new file mode 100644 index 00000000000..14ea809b5ff --- /dev/null +++ b/lib/validators/category_search_priority_weights_validator.rb @@ -0,0 +1,25 @@ +class CategorySearchPriorityWeightsValidator + def initialize(opts = {}) + @name = opts[:name].to_s + end + + def valid_value?(val) + val = val.to_f + + case @name + when "category_search_priority_very_low_weight" + val < SiteSetting.category_search_priority_low_weight + when "category_search_priority_low_weight" + val < 1 && val > SiteSetting.category_search_priority_very_low_weight + when "category_search_priority_high_weight" + val > 1 && val < SiteSetting.category_search_priority_very_high_weight + when "category_search_priority_very_high_weight" + val > SiteSetting.category_search_priority_high_weight + end + end + + def error_message + key = @name[/category_search_priority_(\w+)_weight/, 1] + I18n.t("site_settings.errors.category_search_priority.#{key}_weight_invalid") + end +end diff --git a/spec/components/search_spec.rb b/spec/components/search_spec.rb index 0116e74434d..484776522df 100644 --- a/spec/components/search_spec.rb +++ b/spec/components/search_spec.rb @@ -486,6 +486,26 @@ describe Search do end end + describe 'categories with different priorities' do + let(:category2) { Fabricate(:category) } + + it "should return posts in the right order" do + raw = "The pure genuine evian" + post = Fabricate(:post, topic: category.topic, raw: raw) + post2 = Fabricate(:post, topic: category2.topic, raw: raw) + + search = Search.execute(raw) + + expect(search.posts).to eq([post2, post]) + + category.update!(search_priority: Searchable::PRIORITIES[:high]) + + search = Search.execute(raw) + + expect(search.posts).to eq([post, post2]) + end + end + end context 'groups' do diff --git a/spec/components/validators/category_searrch_priority_weights_validator_spec.rb b/spec/components/validators/category_searrch_priority_weights_validator_spec.rb new file mode 100644 index 00000000000..91000756778 --- /dev/null +++ b/spec/components/validators/category_searrch_priority_weights_validator_spec.rb @@ -0,0 +1,26 @@ +require 'rails_helper' +require 'validators/category_search_priority_weights_validator' + +RSpec.describe CategorySearchPriorityWeightsValidator do + it "should validate the results correctly" do + expect do + SiteSetting.category_search_priority_very_low_weight = 0.9 + end.to raise_error(Discourse::InvalidParameters) + + [1, 0].each do |value| + expect do + SiteSetting.category_search_priority_low_weight = value + end.to raise_error(Discourse::InvalidParameters) + end + + ['0.2', 10].each do |value| + expect do + SiteSetting.category_search_priority_high_weight = value + end.to raise_error(Discourse::InvalidParameters) + end + + expect do + SiteSetting.category_search_priority_very_high_weight = 1.1 + end.to raise_error(Discourse::InvalidParameters) + end +end