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:
parent
016efeadf6
commit
fbf7627c8e
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}),
|
||||
]
|
||||
);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue