Appearance
Querying
Document-store renders the latest state of each document into your database. You query the database directly using its native capabilities.
How rendering works
When you add() or edit() a document, the store:
- Computes the new rendered state (all edits applied)
- Writes it to a type-specific table/collection in your database
For SQLite, each type gets a <type> table. For MongoDB, a collection per type.
Query your database directly
Use your database's native query tools. You know your query patterns — use the right indexes, joins, and features for your use case.
SurrealDB
typescript
const db = store.storage.db;
// All bookmarks, newest first
const [bookmarks] = await db.query(
'SELECT doc FROM bookmark ORDER BY doc.updatedAt DESC'
);
// Filter by field
const [matches] = await db.query(
'SELECT doc FROM bookmark WHERE doc.url = $url',
{ url: 'https://example.com' }
);
// Relations and graph traversal
const [tree] = await db.query(
'SELECT doc, ->contains->bookmark.doc AS bookmarks FROM folder WHERE hash = $hash',
{ hash: folderHashAsArrayBuffer }
);Create indexes for your query patterns:
surql
DEFINE INDEX updated_idx ON bookmark FIELDS doc.updatedAt;
DEFINE INDEX uid_idx ON bookmark FIELDS doc.uid;
DEFINE INDEX title_search ON bookmark FIELDS doc.title SEARCH ANALYZER simple BM25;SQLite
typescript
const db = new Database('./data/app.db');
// All bookmarks, newest first
const bookmarks = db.prepare(
'SELECT * FROM bookmark ORDER BY updatedAt DESC'
).all();
// Search by field
const matches = db.prepare(
`SELECT * FROM bookmark WHERE json_extract(doc, '$.url') LIKE ?`
).all('%example.com%');MongoDB
typescript
const collection = mongoDb.collection('bookmark');
const bookmarks = await collection
.find({ uid: myUid })
.sort({ updatedAt: -1 })
.limit(20)
.toArray();Rendered document format
Each rendered document contains:
- All your app fields (
url,title, etc.) hash,uid,parent,share,write— system fields, always present when setcreatedAt,updatedAt— timestamps, on by default and renameable per type viaregisterType({ render: { ... } })
typescript
store.registerType('bookmark'); // defaults render createdAt/updatedAt
// Rendered document looks like:
{
hash: Buffer, // always
createdAt: 1710000000,
updatedAt: 1710001000,
uid: Buffer,
url: 'https://...',
title: 'Example',
}Why not a query abstraction?
Document-store supports SQLite, MongoDB, and SurrealDB. Each has fundamentally different query capabilities — indexes, full-text search, aggregation pipelines, JSON operators, geospatial queries. A wrapper that covers all of that across backends would be a massive surface area to maintain, and would always be a worse version of the real thing.
The persistence layer for writes is simple — insert and update rendered documents. That's easy to abstract. Reads are where databases diverge, and where your app needs full control.