Postgres full-text search with the simple configuration
Document metadata
- Type
- Decision
- Status
- accepted
- Description
- Search uses a generated weighted tsvector in Postgres with the language-neutral simple text search configuration. Words match in every language, at the cost of no stemming.
- Sources
- openspec/changes/archive/2026-10-14-docmyrepos-mvp/design.md
- packages/server/src/db/schema.ts
- packages/server/src/queries/search.ts
- Generated
- Verified
- Stale after
- (in 972 years)
Postgres full-text search with the simple configuration
Context
Documents come from many repositories, in any language, and the search requirements make no promise about language or stemming. The project already needs Postgres for everything else, and a search service of its own would be one more thing to run for a portal-sized corpus.
Decision
Search uses a generated, stored tsvector column with weights (title A, headings B, description and tags C, body D) and a GIN index. Queries use websearch_to_tsquery with the user's text as a bound parameter, and ts_headline for snippets.
The text search configuration is simple. It is fixed in the migration and exported as SEARCH_TEXT_CONFIG, and every query passes the same configuration. It is not an environment option: the vector is computed at write time, so a runtime switch would silently compare queries against lexemes made with another configuration.
Consequences
- A word matches in every language, with no list of stop words to maintain.
- There is no stemming.
deploydoes not matchdeployment, andwebhookdoes not matchwebhooks. The search guide tells users to search the form the documents use. - Changing the configuration later is a new migration that recomputes the column. That rewrites the table and rebuilds the index, which is cheap at this scale.
- No separate service to deploy, back up or keep in sync with the database.
Alternatives considered
- An English configuration. Stemming helps English prose but silently degrades every other language, and drops English stop words from phrase queries.
- SQLite FTS5. Simpler to self-host, but the project chose Postgres for everything else.
- An external engine such as Meilisearch. An extra service and a second copy of the data, not needed at this scale.
See also the data model for the vector's definition.