Skip to content

MongoDB

MongoDB backend for server deployments. Documents are rendered into collections named by type (e.g., bookmark, folder).

Setup

typescript
import { MongoClient } from 'mongodb';
import { DocumentStore, MongoDbPersistence } from 'document-store';

const client = new MongoClient('mongodb://localhost:27017');
await client.connect();
const db = client.db('myapp');

const store = new DocumentStore({
    storage: new MongoDbPersistence(db)
});

store.registerType('bookmark', {
    render: { time: true, uid: true, updated: true }
});

Collection structure

Each type gets a MongoDB collection. Documents are stored as regular BSON with Buffer fields preserved:

javascript
{
    hash: Binary(Buffer),    // always present
    uid: Binary(Buffer),     // if render.uid = true
    time: 1710000000,        // if render.time = true
    updated: 1710001000,     // if render.updated = true
    url: "https://...",      // app fields
    title: "Example",
}

Reading documents

Simple queries

typescript
const collection = db.collection('bookmark');

// All bookmarks
const all = await collection.find({}).toArray();

// By hash
const doc = await collection.findOne({ hash: someHash });

// Filter
const mine = await collection.find({ uid: myUid }).toArray();

Sorting and pagination

typescript
const recent = await collection
    .find({})
    .sort({ updated: -1 })
    .limit(20)
    .toArray();

const page = await collection
    .find({ uid: myUid })
    .sort({ time: -1 })
    .skip(pageSize * pageNum)
    .limit(pageSize)
    .toArray();

Lookups across document types

Use $lookup (MongoDB's join) to resolve parent references:

typescript
const withFolders = await db.collection('bookmark').aggregate([
    {
        $lookup: {
            from: 'folder',
            localField: 'parent',
            foreignField: 'hash',
            as: 'folder'
        }
    },
    { $unwind: { path: '$folder', preserveNullAndEmptyArrays: true } },
    { $sort: { time: -1 } },
    { $limit: 50 },
]).toArray();
typescript
// Create a text index
await collection.createIndex({ title: 'text', url: 'text' });

// Search
const results = await collection
    .find({ $text: { $search: 'example' } })
    .toArray();

Aggregation

typescript
// Count by owner
const counts = await collection.aggregate([
    { $group: { _id: '$uid', count: { $sum: 1 } } },
]).toArray();

// Tag frequency
const tags = await collection.aggregate([
    { $unwind: '$tags' },
    { $group: { _id: '$tags', count: { $sum: 1 } } },
    { $sort: { count: -1 } },
]).toArray();

Indexes

Create indexes for your query patterns:

typescript
await collection.createIndex({ updated: -1 });
await collection.createIndex({ uid: 1 });
await collection.createIndex({ parent: 1 });
await collection.createIndex({ uid: 1, updated: -1 }); // compound