Appearance
Search
Document Store deliberately has no built-in search. Search indexes are a rendered view — your render handler materialises them from accepted writes, the same way it materialises any other table. This keeps the store's remit at "content-addressed sync" instead of bleeding into "queryable database engine", and it means you get your database's real search instead of a lowest-common-denominator one.
Coming from the TS document-store?
The TS reference had a search callback on registerType and a store.find() keyword lookup. The Rust store drops both by design — the pattern below replaces them with strictly more capable database-native search.
The pattern: index in the render handler
Extract whatever you want to search while rendering. SQLite's FTS5 gives you real full-text search for a few lines:
typescript
db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS note_fts
USING fts5(hash UNINDEXED, title, body)`);
const store = await createStore({
storage: './data/ds.sqlite',
renderHandler: async (ops) => {
for (const op of ops) {
if (op.docType !== 'note') continue;
if (op.type === 'upsert') {
const c = op.content as any;
// ...your normal table upsert, plus:
db.prepare('DELETE FROM note_fts WHERE hash = ?').run(op.hash);
db.prepare('INSERT INTO note_fts (hash, title, body) VALUES (?, ?, ?)')
.run(op.hash, c.title, c.body);
} else if (op.type === 'delete') {
db.prepare('DELETE FROM note_fts WHERE hash = ?').run(op.hash);
}
}
},
});Query it like any FTS table:
typescript
const hits = db.prepare(`
SELECT hash, title FROM note_fts WHERE note_fts MATCH ? ORDER BY rank
`).all('hello');Because the handler also sees synced writes, peers' documents become searchable the moment they arrive — no extra wiring.
Other databases
The same shape works everywhere: SurrealDB's SEARCH ANALYZER ... BM25 indexes, Postgres tsvector, an external engine if you really need one — the render handler is the single place where documents meet your index. See Rendering for the handler contract.