FEATURE: allow searching for whispers and bots (#32252)

Add new advanced search filters for post types

- `in:bot` or `in:bots`: Filters for posts made by bot users (user_id <
0)
- `in:human` or `in:humans`: Filters for posts made by human users
(user_id >= 0)
- `in:whisper` or `in:whispers`: Filters for whisper posts (respects
permissions)
- `in:regular`: Filters for regular posts only
This commit is contained in:
Sam
2025-04-10 16:21:46 +10:00
committed by GitHub
parent 7de9f79f55
commit da088a24c3
2 changed files with 99 additions and 2 deletions

View File

@ -415,6 +415,19 @@ class Search
posts.where("EXISTS (SELECT 1 FROM topic_tags WHERE topic_tags.topic_id = posts.topic_id)")
end
advanced_filter(/\Ain:bots?\z/i) { |posts| posts.where("posts.user_id < 0") }
advanced_filter(/\Ain:humans?\z/i) { |posts| posts.where("posts.user_id >= 0") }
advanced_filter(/\Ain:whispers?\z/i) do |posts|
if @guardian.can_see_whispers?
posts.where(post_type: Post.types[:whisper])
else
posts.where("1 = 0")
end
end
advanced_filter(/\Ain:regular\z/i) { |posts| posts.where(post_type: Post.types[:regular]) }
advanced_filter(/\Ain:untagged\z/i) do |posts|
posts.joins(
"LEFT JOIN topic_tags ON
@ -1459,7 +1472,6 @@ class Search
def aggregate_search(opts = {})
post_sql = aggregate_post_sql(opts)
added = 0
aggregate_posts(post_sql[:default]).each do |p|

View File

@ -2990,7 +2990,6 @@ RSpec.describe Search do
context "when max_duplicate_search_index_terms limits duplication" do
before { SearchIndexer.enable }
after { SearchIndexer.disable }
it "correctly ranks topics" do
@ -3034,4 +3033,90 @@ RSpec.describe Search do
expect(sql).to match(/where.*topics.closed/i)
end
end
describe "bot search" do
fab!(:bot)
fab!(:bot_topic) { Fabricate(:topic, title: "this is a topic by a bot") }
fab!(:bot_post) do
Fabricate(:post, user: bot, topic: bot_topic, raw: "this is a regular post by a bot")
end
fab!(:human_post) { Fabricate(:post, topic: topic, raw: "this is a regular post not by a bot") }
before do
SearchIndexer.enable
SearchIndexer.index(bot_post, force: true)
SearchIndexer.index(human_post, force: true)
end
it "works as expected" do
# include bot posts by default
results = Search.execute("bot", guardian: Guardian.new)
expect(results.posts).to contain_exactly(bot_post, human_post)
# bots only
results = Search.execute("bot in:bot", guardian: Guardian.new)
expect(results.posts).to contain_exactly(bot_post)
# allows searching for human only
results = Search.execute("bot in:human", guardian: Guardian.new)
expect(results.posts).to contain_exactly(human_post)
end
end
describe "whisper search" do
fab!(:topic2) { Fabricate(:topic) }
fab!(:user)
fab!(:whisperer) { Fabricate(:user) }
fab!(:whisperers_group) { Fabricate(:group) }
fab!(:regular_post) do
Fabricate(:post, topic: topic, raw: "this is a regular post with whisper content")
end
fab!(:whisper_post) do
Fabricate(
:post,
topic: topic2,
raw: "this is a whisper post",
post_type: Post.types[:whisper],
)
end
before do
SiteSetting.whispers_allowed_groups = "#{Group::AUTO_GROUPS[:staff]}|#{whisperers_group.id}"
whisperers_group.add(whisperer)
SearchIndexer.enable
[regular_post, whisper_post].each { |post| SearchIndexer.index(post, force: true) }
end
it "works as expected" do
# note this is simple enough, saving up on all the reindexing over and over
# by running in a big batch
# anon
results = Search.execute("whisper", guardian: Guardian.new(user))
expect(results.posts).to contain_exactly(regular_post)
# staff
results = Search.execute("whisper", guardian: Guardian.new(admin))
expect(results.posts).to contain_exactly(regular_post, whisper_post)
# whisperer
results = Search.execute("whisper", guardian: Guardian.new(whisperer))
expect(results.posts).to contain_exactly(regular_post, whisper_post)
# in:whispers
results = Search.execute("whisper in:whispers", guardian: Guardian.new(admin))
expect(results.posts).to contain_exactly(whisper_post)
results = Search.execute("whisper in:whispers", guardian: Guardian.new(user))
expect(results.posts).to be_empty
# in:regular
results = Search.execute("whisper in:regular", guardian: Guardian.new(admin))
expect(results.posts).to contain_exactly(regular_post)
results = Search.execute("content in:regular", guardian: Guardian.new(admin))
expect(results.posts).to contain_exactly(regular_post)
end
end
end