FEATURE: Allow categories to be prioritized/deprioritized in search. (#7209)
This commit is contained in:
parent
ce75e30bf5
commit
ac661e856a
|
@ -69,7 +69,7 @@ export default buildCategoryPanel("settings", {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return options.sort((a, b) => a.value <= b.value);
|
return options;
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
|
|
|
@ -2,8 +2,12 @@ module Searchable
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
PRIORITIES = Enum.new(
|
PRIORITIES = Enum.new(
|
||||||
|
ignore: 1,
|
||||||
|
very_low: 2,
|
||||||
|
low: 3,
|
||||||
normal: 0,
|
normal: 0,
|
||||||
ignore: 1
|
high: 4,
|
||||||
|
very_high: 5
|
||||||
)
|
)
|
||||||
|
|
||||||
included do
|
included do
|
||||||
|
|
|
@ -2459,6 +2459,10 @@ en:
|
||||||
options:
|
options:
|
||||||
normal: "Normal"
|
normal: "Normal"
|
||||||
ignore: "Ignore"
|
ignore: "Ignore"
|
||||||
|
very_low: "Very Low"
|
||||||
|
low: "Low"
|
||||||
|
high: "High"
|
||||||
|
very_high: "Very High"
|
||||||
sort_options:
|
sort_options:
|
||||||
default: "default"
|
default: "default"
|
||||||
likes: "Likes"
|
likes: "Likes"
|
||||||
|
|
|
@ -1291,6 +1291,10 @@ en:
|
||||||
search_query_log_max_size: "Maximum amount of search queries to keep"
|
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_query_log_max_retention_days: "Maximum amount of time to keep search queries, in days."
|
||||||
search_ignore_accents: "Ignore accents when searching for text."
|
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_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."
|
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"
|
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_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."
|
max_username_length_range: "You cannot set the maximum below the minimum."
|
||||||
invalid_hex_value: "Color values have to be 6-digit hexadecimal codes."
|
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:
|
placeholder:
|
||||||
sso_provider_secrets:
|
sso_provider_secrets:
|
||||||
|
|
|
@ -1614,6 +1614,22 @@ search:
|
||||||
ro: true
|
ro: true
|
||||||
sk: true
|
sk: true
|
||||||
tr_TR: 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:
|
uncategorized:
|
||||||
version_checks:
|
version_checks:
|
||||||
|
|
|
@ -836,9 +836,27 @@ class Search
|
||||||
posts = posts.order("posts.like_count DESC")
|
posts = posts.order("posts.like_count DESC")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
# 0|32 default normalization scaled into the range zero to one
|
||||||
data_ranking = <<~SQL
|
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
|
SQL
|
||||||
|
|
||||||
|
|
|
@ -103,7 +103,9 @@ class SiteSettings::TypeSupervisor
|
||||||
|
|
||||||
opts[:validator] = opts[:validator].try(:constantize)
|
opts[:validator] = opts[:validator].try(:constantize)
|
||||||
if (validator_type = (opts[:validator] || validator_for(@types[name])))
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -486,6 +486,26 @@ describe Search do
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
context 'groups' do
|
context 'groups' do
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue