mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: Add full text search over chat messages (#27126)
Closes CODAGT-721 Closes CODAGT-722 Closes CODAGT-723 Closes CODAGT-724 Closes CODAGT-725 This PR adds the database and API pieces necessary to support full-text chat message search. - Adds required chat schema for full-text search - Adds dbpurge job to populate search_tsv in the background - Adds `search` parameter to GetChats query - Adds `search` filter to `searchquery.Chats` - Wires chat search filter into chats API > Implemented by Coder Agents, reviewed and tested by a human.
This commit is contained in:
@@ -260,6 +260,80 @@ func TestNoopMessageUpdateDoesNotAdvanceHistoryVersion(t *testing.T) {
|
||||
"no-op update must NOT advance message revision")
|
||||
}
|
||||
|
||||
// TestSearchTsvBackfillDoesNotTouchChatState verifies that the
|
||||
// search_tsv backfill UPDATE leaves message revision, history_version,
|
||||
// generation_attempt, and retry_state untouched. search_tsv is a
|
||||
// system-maintained column; populating it is not a content change.
|
||||
func TestSearchTsvBackfillDoesNotTouchChatState(t *testing.T) {
|
||||
t.Parallel()
|
||||
tf := newTriggerFixture(t)
|
||||
f := tf.f
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
created := createTestChat(t, f)
|
||||
|
||||
msgs, err := f.DB.GetChatMessagesByChatID(ctx, database.GetChatMessagesByChatIDParams{
|
||||
ChatID: created.Chat.ID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, msgs)
|
||||
target := msgs[0]
|
||||
require.Equal(t, database.ChatMessageRoleUser, target.Role)
|
||||
require.False(t, target.Deleted)
|
||||
originalRevision := target.Revision
|
||||
|
||||
var tsvPending bool
|
||||
err = tf.sqlDB.QueryRowContext(ctx,
|
||||
`SELECT search_tsv IS NULL FROM chat_messages WHERE id = $1`, target.ID,
|
||||
).Scan(&tsvPending)
|
||||
require.NoError(t, err)
|
||||
require.True(t, tsvPending, "fresh message starts with search_tsv pending")
|
||||
|
||||
attempt, err := f.DB.IncrementChatGenerationAttempt(ctx, created.Chat.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(1), attempt)
|
||||
|
||||
withRetry, err := f.DB.UpdateChatRetryState(ctx, database.UpdateChatRetryStateParams{
|
||||
ID: created.Chat.ID,
|
||||
RetryState: []byte(`{"attempt":1,"delay_ms":250,"error":"retry","retrying_at":"2026-05-29T00:00:00Z"}`),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.True(t, withRetry.RetryState.Valid)
|
||||
|
||||
bumped, err := f.DB.LockChatAndBumpSnapshotVersion(ctx, created.Chat.ID)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, bumped.SnapshotVersion, bumped.HistoryVersion,
|
||||
"snapshot bump leaves history_version trailing")
|
||||
|
||||
// Backfill-shaped UPDATE: only search_tsv changes.
|
||||
_, err = tf.sqlDB.ExecContext(ctx, `
|
||||
UPDATE chat_messages
|
||||
SET search_tsv = COALESCE(to_tsvector('simple', chat_message_search_text(content)), ''::tsvector)
|
||||
WHERE id = $1
|
||||
`, target.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
reloadedMsg, err := f.DB.GetChatMessageByID(ctx, target.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, originalRevision, reloadedMsg.Revision,
|
||||
"backfill must NOT advance message revision")
|
||||
err = tf.sqlDB.QueryRowContext(ctx,
|
||||
`SELECT search_tsv IS NULL FROM chat_messages WHERE id = $1`, target.ID,
|
||||
).Scan(&tsvPending)
|
||||
require.NoError(t, err)
|
||||
require.False(t, tsvPending, "backfill populates search_tsv")
|
||||
|
||||
after, err := f.DB.GetChatByID(ctx, created.Chat.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, bumped.HistoryVersion, after.HistoryVersion,
|
||||
"backfill must NOT advance history_version")
|
||||
require.Equal(t, int64(1), after.GenerationAttempt,
|
||||
"backfill must NOT reset generation_attempt")
|
||||
require.True(t, after.RetryState.Valid,
|
||||
"backfill must NOT clear retry_state")
|
||||
require.Equal(t, withRetry.RetryStateVersion, after.RetryStateVersion,
|
||||
"backfill must NOT change retry_state_version")
|
||||
}
|
||||
|
||||
// Queue version triggers
|
||||
|
||||
// TestQueueInsertUpdatesQueueVersion verifies that an INSERT into
|
||||
|
||||
Reference in New Issue
Block a user