Skip to content

canvas-ai/canvas-synapsd

Repository files navigation

SynapsD

A very simple, naive implementation of a JSON document store with some bitmap indexes in the mix. Module primarily but not exclusively for use with Canvas (https://github.com/canvas-ai/canvas-server)

Architecture

Components

JSON Document Store

  • Simple LMDB KV store with enforced document schemas (See ./src/schemas for more details)
  • Every data abstraction schema (File, Note, Browser tab, Email etc) defines its own set of indexing options

Index implementation

Hashmaps / Inverted indexes

  • algorithm/checksum | docID
    Example: sha1/4e1243.. => document ID)
  • timestamp | docID
    Example: 20250212082411.1234 => document ID
    We could use composite keys and LMDB range queries instead (timestamp/docID => document) but for now this way is more practical.

Bitmap indexes

The following bitmap index prefixes are enforced to organize and filter documents:

  • internal/ - Internal bitmaps
  • context/ - Context path bitmaps, used internally by Canvas (as context tree nodes, context/uuid)
  • data/abstraction/<schema> - Schema type filters (incl subtrees like data/abstraction/file/ext/json)
  • data/mime/<type>
  • data/content/encoding/<encoding>
  • client/os/<os>
  • client/application/<application>
  • client/device/<device-id>
  • client/network/<network-id> -We support storing documents on multiple backends(StoreD), when a canvas application connects from a certain network, not all backends may be reachable(your home NAS from work for example)
  • user/
  • tag/ - Generic tag bitmaps
  • custom/ - Throw what you need here

API Documentation

API Patterns

SynapsD follows a hybrid API pattern - for better or worse -

Single Document Operations

Single document operations:

try {
    const docId = await db.insertDocument(doc);
    // Success case
} catch (error) {
    // Error handling
}

Available single document operations:

  • insertDocument(doc, contextSpec, featureBitmapArray)
  • updateDocument(docId, updateData, contextSpec, featureBitmapArray)
  • deleteDocument(docId)
  • getDocument(docId)
  • getDocumentById(id)
  • getDocumentByChecksumString(checksumString)

Batch Operations

Batch operations return a result object:

const result = await db.insertDocumentArray(docs);
if (result.failed.length > 0) {
    // Handle partial failures
}
// Access successful operations
const successfulIds = result.successful.map(s => s.id);

// Using findDocuments
const result = await db.findDocuments('/some/path', ['feature1'], ['filter1']);
if (result.error) {
    console.error('Query failed:', result.error);
} else {
    console.log(`Found ${result.count} documents:`, result.data);
}

// Using query
const queryResult = await db.query('some query', ['context1'], ['feature1']);
if (queryResult.error) {
    console.error('Query failed:', queryResult.error);
} else {
    console.log(`Found ${queryResult.count} documents:`, queryResult.data);
}

Result object structure:

interface BatchResult {
    successful: Array<{
        index: number;    // Original array index
        id: number;       // Document ID
    }>;
    failed: Array<{
        index: number;    // Original array index
        error: string;    // Error message
        doc: any;        // Original document
    }>;
    total: number;       // Total number of operations
}

Available batch operations:

  • insertDocumentArray(docs, contextSpec, featureBitmapArray)
  • updateDocumentArray(docs, contextSpec, featureBitmapArray)
  • deleteDocumentArray(docIds)
  • getDocumentsByIdArray(ids)
  • getDocumentsByChecksumStringArray(checksums)

Query Operations

Pagination is supported for all queries. Default page size is 100 documents.

Options:

  • limit (number): page size (default 100)
  • offset (number): starting index (default 0)
  • page (number): 1-based page number (ignored if offset provided)
  • parse (boolean): parse into schema instances (default true)

Usage:

// First page (implicit): limit=100, offset=0
const docs = await db.findDocuments(contextSpec, featureBitmapArray, [], { limit: 100 });
console.log(docs.length, docs.count); // docs has .count metadata

// Second page via page
const page2 = await db.findDocuments(contextSpec, featureBitmapArray, [], { page: 2, limit: 100 });

// Or using offset directly
const next100 = await db.findDocuments(contextSpec, featureBitmapArray, [], { offset: 100, limit: 100 });

Return shape:

type QueryResultArray = Array<any> & { count: number; error: string | null };

Available query operations:

  • findDocuments(contextSpec, featureBitmapArray, filterArray, options)
  • query(query, contextBitmapArray, featureBitmapArray, filterArray)
  • ftsQuery(query, contextBitmapArray, featureBitmapArray, filterArray)

Error Handling

SynapsD uses standard JavaScript Error objects with specific error types:

  • ValidationError: Document validation failed
  • NotFoundError: Document not found
  • DuplicateError: Document already exists
  • DatabaseError: General database errors

Example:

try {
    await db.insertDocument(doc);
} catch (error) {
    if (error instanceof ValidationError) {
        // Handle validation error
    } else if (error instanceof DatabaseError) {
        // Handle database error
    }
}

References

License

Licensed under AGPL-3.0-or-later. See main project LICENSE file.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •