Search does not index card comments #164
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Grandiras/Ponente#164
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
SearchService.SnapshotAsync(added inclaude/issue-159) indexes a card's title, description, tag names, assignee and author names, and its board and column. It deliberately does not index the discussion.Why not, today: comments would be the third collection hanging off
Cardin that query, which is the cartesian blow-upCardService.GetColumnsAsyncis written to avoid (seedocs/data.md), and indexing them means AES-decrypting every comment on every board to find the handful that mention the word.What it costs a member: a decision that only ever got written down in a comment thread is not findable. On a board where the description is a one-liner and the argument happens underneath it, that is most of the content.
What a fix would look like: a second query rather than an include (the same shape
GetColumnsAsyncalready uses to count comments), feeding a sixthSearchFieldweighted belowDescription. The thing to measure first is what decrypting every comment body adds to the first keystroke of a search session — the snapshot is built once per session, so the cost lands in one place and is easy to time. If it is significant, an alternative is indexing comments lazily: search titles/descriptions first and only fall back to comments when the query returns nothing.Filed by Claude while working on #159 (run).
🤖 On it — follow along in the action run; the reply will link the full session transcript.
📜 Live transcript — updating every ~5 minutes while the task runs.
Search now indexes card comments. Branch
claude/issue-164is pushed.What changed
A sixth
SearchField,Comments, weighted 0.7 — belowDescription(1.0) on purpose: a thread is longer than everything else on a card put together, so a word turns up in one by accident more often, and the card a word is written on has to outrank the card where somebody mentioned it in a reply.A second query, never a third
Include.SearchService.DiscussionsAsyncprojects two columns a comment (CardId,BodyMarkdown), ordered by card, and runs each thread into one string — the same shapeCardService.GetColumnsAsyncuses to count comments, for the same cartesian-product reason. It runs beside the card read rather than after it (ownDbContext,Task.WhenAllso neither exception lands on nobody), because both are mostly decryption.A hit found that way says so —
SearchHit.InComments, set only when a query word scored best in the thread and nowhere better, rendered as three words in the meta line. Without it, a result whose title and preview hold none of what was typed reads as the box being broken.What it costs — measured first, as the issue asked
A throwaway harness drove the real
SearchService.SearchAsyncagainst a real Postgres with a seeded corpus (cold snapshot, median of 25, CI container with Postgres on the same box, so absolutes are pessimistic):Paid once, on the search that builds the snapshot — a later keystroke went from ~2.0 ms to ~3.0 ms at 720 cards, which the 160 ms debounce swallows whole. Running the two queries concurrently is what made it acceptable: sequential, the bottom two rows were 267 ms and 358 ms.
I did not take the lazy-fallback option the issue offered as the alternative. Searching titles first and only reaching for comments when nothing came back means a card whose entire thread is about the word stays invisible because something else matched — which cards a search can reach would depend on what else it found. Consistency was worth more than the tail of the cost; the reasoning and the numbers are now in
docs/search.md.Verified
dotnet build Ponente.slnclean,dotnet test Ponente.sln448 passed (4 newCardSearchTests: found by a comment, ranked below a description match, the marker set/not set, a word in the fourth reply).tests/e2e/search.spec.jsextended and run green against a live app (dotnet run+ local Postgres, Playwright chromium): the new case comments on a fixture card, finds it by a word that exists nowhere else, and asserts both the marker and its absence on an ordinary hit. Also re-ranboard.spec.jsandfilter.spec.jsgreen. Onei18n.spec.jscase fails in this setup only because it hard-codes the compose stack'shttp://localhost:8090when adding a cookie — unrelated to this change, and it passes on the real stack.docs/search.md(six fields and weights, the marker, the measurement table, the rejected alternative, the memory note) anddocs/data.md(the "third include" rule is about the include, not about never reading comment bodies). Changelog: the still-unreleased search bullet gained a clause in both locales rather than a second entry, since search has not shipped yet.⏱ 22m 55s · 120 turns · tokens: 159.4k in / 53.5k out (+13322.2k cache-read)
📜 Full session transcript
→ Opened PR: #168