FIX: Make search work with sub-sub-categories (#13901)

Searching in a category looked only one level down, ignoring the site
setting max_category_nesting. The user interface did not support the
third level of categories and did not display them in the "Categorized"
input of the advanced search options.
This commit is contained in:
Bianca Nenciu 2021-08-02 14:04:13 +03:00 committed by GitHub
parent 016efeadf6
commit fbf7627c8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 73 additions and 61 deletions

View File

@ -263,34 +263,33 @@ export default Component.extend({
const subcategories = match[0]
.replace(REGEXP_CATEGORY_PREFIX, "")
.split(":");
let userInput;
if (subcategories.length > 1) {
const userInput = Category.findBySlug(
subcategories[1],
subcategories[0]
userInput = Category.list().find(
(category) =>
category.get("parentCategory.slug") === subcategories[0] &&
category.slug === subcategories[1]
);
if (
(!existingInput && userInput) ||
(existingInput && userInput && existingInput.id !== userInput.id)
) {
this.set("searchedTerms.category", userInput);
}
} else if (isNaN(subcategories)) {
const userInput = Category.findSingleBySlug(subcategories[0]);
if (
(!existingInput && userInput) ||
(existingInput && userInput && existingInput.id !== userInput.id)
) {
this.set("searchedTerms.category", userInput);
}
} else {
const userInput = Category.findById(subcategories[0]);
if (
(!existingInput && userInput) ||
(existingInput && userInput && existingInput.id !== userInput.id)
) {
this.set("searchedTerms.category", userInput);
userInput = Category.list().find(
(category) =>
!category.parentCategory && category.slug === subcategories[0]
);
if (!userInput) {
userInput = Category.list().find(
(category) => category.slug === subcategories[0]
);
}
}
if (
(!existingInput && userInput) ||
(existingInput && userInput && existingInput.id !== userInput.id)
) {
this.set("searchedTerms.category", userInput);
}
} else {
this.set("searchedTerms.category", null);
}

View File

@ -4,7 +4,7 @@ import { categoryBadgeHTML } from "discourse/helpers/category-link";
// Right now it's RawHTML. Eventually it should emit nodes
export default class CategoryLink extends RawHtml {
constructor(attrs) {
attrs.html = categoryBadgeHTML(attrs.category, attrs);
attrs.html = `<span>${categoryBadgeHTML(attrs.category, attrs)}</span>`;
super(attrs);
}
}

View File

@ -374,9 +374,11 @@ createWidget("search-menu-assistant", {
switch (suggestionKeyword) {
case "#":
attrs.results.forEach((category) => {
const slug = prefix
? `${prefix} #${category.slug} `
: `#${category.slug} `;
const fullSlug = category.parentCategory
? `#${category.parentCategory.slug}:${category.slug}`
: `#${category.slug}`;
const slug = prefix ? `${prefix} ${fullSlug} ` : `${fullSlug} `;
content.push(
this.attach("search-menu-assistant-item", {
@ -433,6 +435,7 @@ createWidget("search-menu-assistant-item", {
this.attach("category-link", {
category: attrs.category,
allowUncategorized: true,
recursive: true,
}),
]
);

View File

@ -511,12 +511,7 @@ class Search
category_ids = Category.where('slug ilike ? OR name ilike ? OR id = ?',
match, match, match.to_i).pluck(:id)
if category_ids.present?
unless exact
category_ids +=
Category.where('parent_category_id = ?', category_ids.first).pluck(:id)
end
category_ids += Category.subcategory_ids(category_ids.first) unless exact
@category_filter_matched ||= true
posts.where("topics.category_id IN (?)", category_ids)
else
@ -525,44 +520,31 @@ class Search
end
advanced_filter(/^\#([\p{L}\p{M}0-9\-:=]+)$/i) do |posts, match|
exact = true
category_slug, subcategory_slug = match.to_s.split(":")
next unless category_slug
if subcategory_slug
category_id, _ = DB.query_single(<<~SQL, category_slug.downcase, subcategory_slug.downcase)
SELECT sub.id
FROM categories sub
JOIN categories c ON sub.parent_category_id = c.id
WHERE LOWER(c.slug) = ? AND LOWER(sub.slug) = ?
ORDER BY c.id
LIMIT 1
SQL
exact = true
if category_slug[0] == "="
category_slug = category_slug[1..-1]
else
# main category
if category_slug[0] == "="
category_slug = category_slug[1..-1]
else
exact = false
end
exact = false
end
category_id = Category.where("lower(slug) = ?", category_slug.downcase)
category_id = if subcategory_slug
Category
.where('lower(slug) = ?', subcategory_slug.downcase)
.where(parent_category_id: Category.where('lower(slug) = ?', category_slug.downcase).select(:id))
.pluck_first(:id)
else
Category
.where('lower(slug) = ?', category_slug.downcase)
.order('case when parent_category_id is null then 0 else 1 end')
.pluck(:id)
.first
.pluck_first(:id)
end
if category_id
category_ids = [category_id]
unless exact
category_ids +=
Category.where('parent_category_id = ?', category_id).pluck(:id)
end
category_ids += Category.subcategory_ids(category_id) if !exact
@category_filter_matched ||= true
posts.where("topics.category_id IN (?)", category_ids)

View File

@ -166,4 +166,32 @@ describe Search do
])
end
end
context "categories" do
it "finds topics in sub-sub-categories" do
SiteSetting.max_category_nesting = 3
category = Fabricate(:category_with_definition)
subcategory = Fabricate(:category_with_definition, parent_category_id: category.id)
subsubcategory = Fabricate(:category_with_definition, parent_category_id: subcategory.id)
topic = Fabricate(:topic, category: subsubcategory)
post = Fabricate(:post, topic: topic)
SearchIndexer.enable
SearchIndexer.index(post, force: true)
expect(Search.execute("test ##{category.slug}").posts).to contain_exactly(post)
expect(Search.execute("test ##{category.slug}:#{subcategory.slug}").posts).to contain_exactly(post)
expect(Search.execute("test ##{subcategory.slug}").posts).to contain_exactly(post)
expect(Search.execute("test ##{subcategory.slug}:#{subsubcategory.slug}").posts).to contain_exactly(post)
expect(Search.execute("test ##{subsubcategory.slug}").posts).to contain_exactly(post)
expect(Search.execute("test #=#{category.slug}").posts).to be_empty
expect(Search.execute("test #=#{category.slug}:#{subcategory.slug}").posts).to be_empty
expect(Search.execute("test #=#{subcategory.slug}").posts).to be_empty
expect(Search.execute("test #=#{subcategory.slug}:#{subsubcategory.slug}").posts).to contain_exactly(post)
expect(Search.execute("test #=#{subsubcategory.slug}").posts).to contain_exactly(post)
end
end
end