FEATURE: align with /filter and allow multiple category search (#27440)
This introduces the syntax of `category:a,b,c` which will search across multiple categories. Previously there was no way to allow search across a wide selection of categories.
This commit is contained in:
parent
be4f1e3350
commit
dc8249c08a
|
@ -542,20 +542,46 @@ class Search
|
|||
|
||||
advanced_filter(/\Awith:images\z/i) { |posts| posts.where("posts.image_upload_id IS NOT NULL") }
|
||||
|
||||
advanced_filter(/\Acategory:(.+)\z/i) do |posts, match|
|
||||
exact = false
|
||||
advanced_filter(/\Acategor(?:y|ies):(.+)\z/i) do |posts, terms|
|
||||
category_ids = []
|
||||
|
||||
if match[0] == "="
|
||||
exact = true
|
||||
match = match[1..-1]
|
||||
matches =
|
||||
terms
|
||||
.split(",")
|
||||
.map do |term|
|
||||
if term[0] == "="
|
||||
[term[1..-1], true]
|
||||
else
|
||||
[term, false]
|
||||
end
|
||||
end
|
||||
.to_h
|
||||
|
||||
if matches.present?
|
||||
sql = <<~SQL
|
||||
SELECT c.id, term
|
||||
FROM
|
||||
categories c
|
||||
JOIN
|
||||
unnest(ARRAY[:matches]) AS term ON
|
||||
c.slug ILIKE term OR
|
||||
c.name ILIKE term OR
|
||||
(term ~ '^[0-9]{1,10}$' AND c.id = term::int)
|
||||
SQL
|
||||
|
||||
found = DB.query(sql, matches: matches.keys)
|
||||
|
||||
if found.present?
|
||||
found.each do |row|
|
||||
category_ids << row.id
|
||||
@category_filter_matched ||= true
|
||||
category_ids += Category.subcategory_ids(row.id) if !matches[row.term]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
category_ids =
|
||||
Category.where("slug ilike ? OR name ilike ? OR id = ?", match, match, match.to_i).pluck(:id)
|
||||
if category_ids.present?
|
||||
category_ids += Category.subcategory_ids(category_ids.first) unless exact
|
||||
@category_filter_matched ||= true
|
||||
posts.where("topics.category_id IN (?)", category_ids)
|
||||
posts.where("topics.category_id IN (?)", category_ids.uniq)
|
||||
else
|
||||
posts.where("1 = 0")
|
||||
end
|
||||
|
|
|
@ -1255,6 +1255,41 @@ RSpec.describe Search do
|
|||
)
|
||||
end
|
||||
|
||||
it "allow searching for multiple categories" do
|
||||
category2 = Fabricate(:category, name: "abc")
|
||||
topic2 = Fabricate(:topic, category: category2)
|
||||
post2 = Fabricate(:post, topic: topic2, raw: "snow monkey")
|
||||
|
||||
category3 = Fabricate(:category, name: "def")
|
||||
topic3 = Fabricate(:topic, category: category3)
|
||||
post3 = Fabricate(:post, topic: topic3, raw: "snow monkey")
|
||||
|
||||
search = Search.execute("monkey category:abc,def")
|
||||
expect(search.posts.map(&:id)).to contain_exactly(post2.id, post3.id)
|
||||
|
||||
search = Search.execute("monkey categories:abc,def")
|
||||
expect(search.posts.map(&:id)).to contain_exactly(post2.id, post3.id)
|
||||
|
||||
search = Search.execute("monkey categories:xxxxx,=abc,=def")
|
||||
expect(search.posts.map(&:id)).to contain_exactly(post2.id, post3.id)
|
||||
|
||||
search = Search.execute("snow category:abc,#{category.id}")
|
||||
expect(search.posts.map(&:id)).to contain_exactly(post.id, post2.id)
|
||||
|
||||
child_category = Fabricate(:category, parent_category: category2)
|
||||
child_topic = Fabricate(:topic, category: child_category)
|
||||
child_post = Fabricate(:post, topic: child_topic, raw: "snow monkey")
|
||||
|
||||
search = Search.execute("monkey category:zzz,nnn,=abc,mmm")
|
||||
expect(search.posts.map(&:id)).to contain_exactly(post2.id)
|
||||
|
||||
search =
|
||||
Search.execute(
|
||||
"monkey category:0007847874874874874748749398384398439843984938439843948394834984934839483984983498394834983498349834983,zzz,nnn,abc,mmm",
|
||||
)
|
||||
expect(search.posts.map(&:id)).to contain_exactly(post2.id, child_post.id)
|
||||
end
|
||||
|
||||
it "should return the right categories" do
|
||||
search = Search.execute("monkey")
|
||||
|
||||
|
|
Loading…
Reference in New Issue