History search now looks inside your documents

Searching your history used to match file names only. Signed in, it now also finds a document by what is written inside it — the name box still works exactly as before, it just stops being the only way in.

Nobody remembers what they called a file. They remember a sentence that was in it, the name of the customer it was about, or the one command the runbook contained. The box at the top of your history used to be unable to help with any of that, because it only ever compared what you typed against file names.

Two searches, one box

Type into it and two things now happen at once. The name filter runs where it always did — in the browser, over every row on the page, instantly, on saved and unsaved conversions alike. And a moment later, if you are signed in, the server answers with the saved documents whose text matches, ranked by how well it matches, and those rows join the ones the name already found.

You do not choose between them. A query that is half a file name and half a remembered phrase finds both kinds of row, and a query that matches nothing by content simply leaves the name filter as it was. The request is debounced, so typing is not a request per keystroke, and a search that fails leaves the list working rather than replacing it with an error.

Where the index comes from

Each saved document carries a tsvector of its Markdown, written at the moment the document is saved and indexed in Postgres. Matching is websearch_to_tsquery, so the syntax is the one every search box has taught people: "a quoted phrase" for words in order, or between alternatives, a leading - to exclude.

It is a deliberately plain configuration — no stemming and no stop-word list. Words match as they were typed, which means convert does not find converting, and also means nothing you search for is silently reinterpreted. Searching the Markdown source rather than the rendered page has a side effect worth knowing: a link's address, a fenced code block and a heading are all searchable text.

What it does not reach

A conversion that is only in this browser has no row in the database, so nothing is indexed for it and it still matches by name alone — which is the honest consequence of conversions staying local until you save them. Documents somebody shared with you are not covered either; the chip that lists them filters by name.

From a script the same thing is one parameter: GET /api/v1/documents?q=…, or tp list --q "…", returning the matches best first, the newer of two equal ones ahead of the older.

Related: batch-converting Markdown files, and documentation that lives in the repository.