feat: add search and filter support to chats endpoint (#25391)

Fixes https://linear.app/codercom/issue/CODAGT-432

Adds structured search/filter capabilities to the `GET
/api/experimental/chats/` endpoint via the `q` query parameter. All
filters use explicit `key:value` syntax; bare terms are rejected to
reserve them for potential future full-text search.

> Generated by Coder Agents

Co-authored-by: Danielle Maywood <danielle@themaywoods.com>
Co-authored-by: Jaayden Halko <jaayden.halko@gmail.com>
This commit is contained in:
Cian Johnston
2026-05-21 10:18:55 +01:00
committed by GitHub
co-authored by Danielle Maywood Jaayden Halko
parent 46e93e6325
commit b7525a9b40
11 changed files with 680 additions and 25 deletions
+38
View File
@@ -531,6 +531,44 @@ WHERE
)
ELSE true
END
-- Filter by title substring (case-insensitive). Applied when the
-- caller provides a non-empty title_query.
AND CASE
WHEN @title_query :: text != '' THEN chats_expanded.title ILIKE '%' || @title_query || '%'
ELSE true
END
AND CASE
WHEN sqlc.narg('has_unread')::boolean IS NOT NULL THEN (
EXISTS (
SELECT 1 FROM chat_messages cm
WHERE cm.chat_id = chats_expanded.id
AND cm.role = 'assistant'
AND cm.deleted = false
AND cm.id > COALESCE(chats_expanded.last_read_message_id, 0)
)
) = sqlc.narg('has_unread')::boolean
ELSE true
END
-- Filter by pull request status. Unlike the diff_url filter above,
-- this intentionally checks only the root chat's own diff status.
-- Child chats share the same workspace and git branch as their
-- parent, so gitsync populates identical PR state on both; traversing
-- descendants would be redundant.
AND CASE
WHEN COALESCE(array_length(@pull_request_statuses::text[], 1), 0) > 0 THEN EXISTS (
SELECT 1
FROM chat_diff_statuses cds
WHERE cds.chat_id = chats_expanded.id
AND (
CASE
WHEN cds.pull_request_state = 'open' AND cds.pull_request_draft THEN 'draft'
WHEN cds.pull_request_state = 'open' THEN 'open'
ELSE cds.pull_request_state
END
) = ANY(@pull_request_statuses::text[])
)
ELSE true
END
-- Paginate over root chats only. Children are fetched
-- separately via GetChildChatsByParentIDs and embedded under
-- each parent. Other callers that need the full set should