FEATURE: include tag and category context in search (#217)

Previous to this we just included title/body.. tags and category
structure can be very critical for decision making.
This commit is contained in:
Sam 2023-09-12 16:09:28 +10:00 committed by GitHub
parent b0310f90d3
commit d75e3ca82b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 15 deletions

View File

@ -154,17 +154,33 @@ module DiscourseAi::AiBot::Commands
end
@last_num_results = posts.length
# this is the general pattern from core
# if there are millions of hidden tags it may fail
hidden_tags = nil
if posts.blank?
{ args: search_args, rows: [], instruction: "nothing was found, expand your search" }
else
format_results(posts, args: search_args) do |post|
{
category_names = [
post.topic.category&.parent_category&.name,
post.topic.category&.name,
].compact.join(" > ")
row = {
title: post.topic.title,
url: Discourse.base_path + post.url,
excerpt: post.excerpt,
created: post.created_at,
category: category_names,
}
if SiteSetting.tagging_enabled
hidden_tags ||= DiscourseTagging.hidden_tag_names
# using map over pluck to avoid n+1 (assuming caller preloading)
tags = post.topic.tags.map(&:name) - hidden_tags
row[:tags] = tags.join(", ") if tags.present?
end
row
end
end
end

View File

@ -9,19 +9,19 @@ RSpec.describe DiscourseAi::AiBot::Commands::ReadCommand do
fab!(:tag_funny) { Fabricate(:tag, name: "funny") }
fab!(:tag_sad) { Fabricate(:tag, name: "sad") }
fab!(:tag_hidden) { Fabricate(:tag, name: "hidden") }
fab!(:staff_tag_group) { Fabricate(:tag_group, name: "Staff only", tag_names: ["hidden"]) }
fab!(:staff_tag_group) do
tag_group = Fabricate.build(:tag_group, name: "Staff only", tag_names: ["hidden"])
tag_group.permissions = [
[Group::AUTO_GROUPS[:staff], TagGroupPermission.permission_types[:full]],
]
tag_group.save!
tag_group
end
fab!(:topic_with_tags) do
Fabricate(:topic, category: category, tags: [tag_funny, tag_sad, tag_hidden])
end
let(:staff) { Group::AUTO_GROUPS[:staff] }
let(:full) { TagGroupPermission.permission_types[:full] }
before do
staff_tag_group.permissions = [[staff, full]]
staff_tag_group.save!
end
describe "#process" do
it "can read a topic" do
topic_id = topic_with_tags.id

View File

@ -9,9 +9,28 @@ RSpec.describe DiscourseAi::AiBot::Commands::SearchCommand do
before { SearchIndexer.enable }
after { SearchIndexer.disable }
fab!(:parent_category) { Fabricate(:category, name: "animals") }
fab!(:category) { Fabricate(:category, parent_category: parent_category, name: "amazing-cat") }
fab!(:tag_funny) { Fabricate(:tag, name: "funny") }
fab!(:tag_sad) { Fabricate(:tag, name: "sad") }
fab!(:tag_hidden) { Fabricate(:tag, name: "hidden") }
fab!(:staff_tag_group) do
tag_group = Fabricate.build(:tag_group, name: "Staff only", tag_names: ["hidden"])
tag_group.permissions = [
[Group::AUTO_GROUPS[:staff], TagGroupPermission.permission_types[:full]],
]
tag_group.save!
tag_group
end
fab!(:topic_with_tags) do
Fabricate(:topic, category: category, tags: [tag_funny, tag_sad, tag_hidden])
end
describe "#process" do
it "can handle no results" do
post1 = Fabricate(:post)
post1 = Fabricate(:post, topic: topic_with_tags)
search = described_class.new(bot_user: bot_user, post: post1, args: nil)
results = search.process(query: "order:fake ABDDCDCEDGDG")
@ -42,7 +61,7 @@ RSpec.describe DiscourseAi::AiBot::Commands::SearchCommand do
hyde_embedding,
)
post1 = Fabricate(:post)
post1 = Fabricate(:post, topic: topic_with_tags)
search = described_class.new(bot_user: bot_user, post: post1, args: nil)
DiscourseAi::Embeddings::VectorRepresentations::AllMpnetBaseV2
@ -60,7 +79,7 @@ RSpec.describe DiscourseAi::AiBot::Commands::SearchCommand do
it "supports subfolder properly" do
Discourse.stubs(:base_path).returns("/subfolder")
post1 = Fabricate(:post)
post1 = Fabricate(:post, topic: topic_with_tags)
search = described_class.new(bot_user: bot_user, post: post1, args: nil)
@ -68,8 +87,22 @@ RSpec.describe DiscourseAi::AiBot::Commands::SearchCommand do
expect(results[:rows].to_s).to include("/subfolder" + post1.url)
end
it "returns category and tags" do
post1 = Fabricate(:post, topic: topic_with_tags)
search = described_class.new(bot_user: bot_user, post: post1, args: nil)
results = search.process(user: post1.user.username)
row = results[:rows].first
category = row[results[:column_names].index("category")]
expect(category).to eq("animals > amazing-cat")
tags = row[results[:column_names].index("tags")]
expect(tags).to eq("funny, sad")
end
it "can handle limits" do
post1 = Fabricate(:post)
post1 = Fabricate(:post, topic: topic_with_tags)
_post2 = Fabricate(:post, user: post1.user)
_post3 = Fabricate(:post, user: post1.user)
@ -78,7 +111,6 @@ RSpec.describe DiscourseAi::AiBot::Commands::SearchCommand do
results = search.process(limit: 2, user: post1.user.username)
expect(results[:column_names].length).to eq(4)
expect(results[:rows].length).to eq(2)
# just searching for everything