Files
Trilium/docs/Developer Guide/Architecture/Entity-System.md
2025-08-21 15:55:44 +00:00

14 KiB
Vendored

Entity System Architecture

The Entity System forms the core data model of Trilium Notes, providing a flexible and powerful structure for organizing information. This document details the entities, their relationships, and usage patterns.

Core Entities Overview

erDiagram
    Note ||--o{ Branch : "parent-child"
    Note ||--o{ Attribute : "has"
    Note ||--o{ Revision : "history"
    Note ||--o{ Attachment : "contains"
    Attachment ||--|| Blob : "stores in"
    Revision ||--|| Blob : "stores in"
    Note }o--o{ Note : "relates via Attribute"
    
    Note {
        string noteId PK
        string title
        string type
        string content
        boolean isProtected
        string dateCreated
        string dateModified
    }
    
    Branch {
        string branchId PK
        string noteId FK
        string parentNoteId FK
        integer notePosition
        string prefix
        boolean isExpanded
    }
    
    Attribute {
        string attributeId PK
        string noteId FK
        string type "label or relation"
        string name
        string value
        integer position
        boolean isInheritable
    }
    
    Revision {
        string revisionId PK
        string noteId FK
        string title
        string type
        boolean isProtected
        string dateCreated
    }
    
    Attachment {
        string attachmentId PK
        string ownerId FK
        string role
        string mime
        string title
        string blobId FK
    }
    
    Blob {
        string blobId PK
        binary content
        string dateModified
    }
    
    Option {
        string name PK
        string value
        boolean isSynced
    }

Entity Definitions

BNote - Notes with Content and Metadata

Location: /apps/server/src/becca/entities/bnote.ts

Notes are the fundamental unit of information in Trilium. Each note can contain different types of content and maintain relationships with other notes.

Properties

class BNote {
    noteId: string;           // Unique identifier
    title: string;            // Display title
    type: string;             // Content type (text, code, file, etc.)
    mime: string;             // MIME type for content
    isProtected: boolean;     // Encryption flag
    dateCreated: string;      // Creation timestamp
    dateModified: string;     // Last modification
    utcDateCreated: string;   // UTC creation
    utcDateModified: string;  // UTC modification
    
    // Relationships
    parentBranches: BBranch[];  // Parent connections
    children: BBranch[];        // Child connections
    attributes: BAttribute[];   // Metadata
    
    // Content
    content?: string | Buffer;  // Note content (lazy loaded)
    
    // Computed
    isDecrypted: boolean;       // Decryption status
}

Note Types

  • text: Rich text content with HTML formatting
  • code: Source code with syntax highlighting
  • file: Binary file attachment
  • image: Image with preview capabilities
  • search: Saved search query
  • book: Container for hierarchical documentation
  • relationMap: Visual relationship diagram
  • canvas: Drawing canvas (Excalidraw)
  • mermaid: Mermaid diagram
  • mindMap: Mind mapping visualization
  • webView: Embedded web content
  • noteMap: Tree visualization

Usage Examples

// Create a new note
const note = new BNote({
    noteId: generateNoteId(),
    title: "My Note",
    type: "text",
    mime: "text/html",
    content: "<p>Note content</p>"
});
note.save();

// Get note with content
const note = becca.getNote(noteId);
await note.loadContent();

// Update note
note.title = "Updated Title";
note.save();

// Protect note
note.isProtected = true;
note.encrypt();
note.save();

BBranch - Hierarchical Relationships

Location: /apps/server/src/becca/entities/bbranch.ts

Branches define the parent-child relationships between notes, allowing a note to have multiple parents (cloning).

Properties

class BBranch {
    branchId: string;        // Unique identifier
    noteId: string;          // Child note ID
    parentNoteId: string;    // Parent note ID
    notePosition: number;    // Order among siblings
    prefix: string;          // Optional prefix label
    isExpanded: boolean;     // Tree UI state
    
    // Computed
    childNote: BNote;        // Reference to child
    parentNote: BNote;       // Reference to parent
}

Key Features

  • Multiple Parents: Notes can appear in multiple locations
  • Ordering: Explicit positioning among siblings
  • Prefixes: Optional labels for context (e.g., "Chapter 1:")
  • UI State: Expansion state persisted per branch

Usage Examples

// Create parent-child relationship
const branch = new BBranch({
    noteId: childNote.noteId,
    parentNoteId: parentNote.noteId,
    notePosition: 10
});
branch.save();

// Clone note to another parent
const cloneBranch = childNote.cloneTo(otherParent.noteId);

// Reorder children
parentNote.sortChildren((a, b) => 
    a.title.localeCompare(b.title)
);

// Add prefix
branch.prefix = "Important: ";
branch.save();

BAttribute - Key-Value Metadata

Location: /apps/server/src/becca/entities/battribute.ts

Attributes provide flexible metadata and relationships between notes.

Types

  1. Labels: Key-value pairs for metadata
  2. Relations: References to other notes

Properties

class BAttribute {
    attributeId: string;     // Unique identifier
    noteId: string;          // Owning note
    type: 'label' | 'relation';
    name: string;            // Attribute name
    value: string;           // Value or target noteId
    position: number;        // Display order
    isInheritable: boolean;  // Inherited by children
    
    // Computed
    note: BNote;             // Owner note
    targetNote?: BNote;      // For relations
}

Common Patterns

// Add label
note.addLabel("status", "active");
note.addLabel("priority", "high");

// Add relation
note.addRelation("template", templateNoteId);
note.addRelation("renderNote", renderNoteId);

// Query by attributes
const todos = becca.findAttributes("label", "todoItem");
const templates = becca.findAttributes("label", "template");

// Inheritable attributes
note.addLabel("workspace", "project", true); // Children inherit

System Attributes

Special attributes with system behavior:

  • #hidePromotedAttributes: Hide promoted attributes in UI
  • #readOnly: Prevent note editing
  • #autoReadOnlyDisabled: Disable auto read-only
  • #hideChildrenOverview: Hide children count
  • ~template: Note template relation
  • ~renderNote: Custom rendering relation

BRevision - Version History

Location: /apps/server/src/becca/entities/brevision.ts

Revisions provide version history and recovery capabilities.

Properties

class BRevision {
    revisionId: string;      // Unique identifier
    noteId: string;          // Parent note
    type: string;            // Content type
    mime: string;            // MIME type
    title: string;           // Historical title
    isProtected: boolean;    // Encryption flag
    dateCreated: string;     // Creation time
    utcDateCreated: string;  // UTC time
    dateModified: string;    // Content modification
    blobId: string;          // Content storage
    
    // Methods
    getContent(): string | Buffer;
    restore(): void;
}

Revision Strategy

  • Created automatically on significant changes
  • Configurable retention period
  • Day/week/month/year retention rules
  • Protected note revisions are encrypted

Usage Examples

// Get note revisions
const revisions = note.getRevisions();

// Restore revision
const revision = becca.getRevision(revisionId);
revision.restore();

// Manual revision creation
note.saveRevision();

// Compare revisions
const diff = revision1.getContent() !== revision2.getContent();

BOption - Application Configuration

Location: /apps/server/src/becca/entities/boption.ts

Options store application and user preferences.

Properties

class BOption {
    name: string;            // Option key
    value: string;           // Option value
    isSynced: boolean;       // Sync across instances
    utcDateModified: string; // Last change
}

Common Options

// Theme settings
setOption("theme", "dark");

// Protected session timeout
setOption("protectedSessionTimeout", "600");

// Sync settings
setOption("syncServerHost", "https://sync.server");

// Note settings
setOption("defaultNoteType", "text");

BAttachment - File Attachments

Location: /apps/server/src/becca/entities/battachment.ts

Attachments link binary content to notes.

Properties

class BAttachment {
    attachmentId: string;    // Unique identifier
    ownerId: string;         // Parent note ID
    role: string;            // Attachment role
    mime: string;            // MIME type
    title: string;           // Display title
    blobId: string;          // Content reference
    utcDateScheduledForDeletion: string;
    
    // Methods
    getContent(): Buffer;
    getBlob(): BBlob;
}

Usage Patterns

// Add attachment to note
const attachment = note.addAttachment({
    role: "file",
    mime: "application/pdf",
    title: "document.pdf",
    content: buffer
});

// Get attachments
const attachments = note.getAttachments();

// Download attachment
const content = attachment.getContent();

Entity Relationships

Parent-Child Hierarchy

// Single parent
childNote.setParent(parentNote.noteId);

// Multiple parents (cloning)
childNote.cloneTo(parent1.noteId);
childNote.cloneTo(parent2.noteId);

// Get parents
const parents = childNote.getParentNotes();

// Get children
const children = parentNote.getChildNotes();

// Get subtree
const subtree = parentNote.getSubtreeNotes();

Attribute Relationships

// Direct relations
note.addRelation("author", authorNote.noteId);

// Bidirectional relations
note1.addRelation("related", note2.noteId);
note2.addRelation("related", note1.noteId);

// Get related notes
const related = note.getRelations("related");

// Get notes relating to this one
const targetRelations = note.getTargetRelations();

Entity Lifecycle

Creation

// Note creation
const note = new BNote({
    noteId: generateNoteId(),
    title: "New Note",
    type: "text"
});
note.save();

// With parent
const child = parentNote.addChild({
    title: "Child Note",
    type: "text",
    content: "Content"
});

Updates

// Atomic updates
note.title = "New Title";
note.save();

// Batch updates
sql.transactional(() => {
    note1.title = "Title 1";
    note1.save();
    
    note2.content = "Content 2";
    note2.save();
});

Deletion

// Soft delete (move to trash)
note.deleteNote();

// Mark for deletion
note.isDeleted = true;
note.save();

// Permanent deletion (after grace period)
note.eraseNote();

Performance Considerations

Lazy Loading

// Note content loaded on demand
const note = becca.getNote(noteId); // Metadata only
await note.loadContent(); // Load content when needed

// Revisions loaded on demand
const revisions = note.getRevisions(); // Database query

Batch Operations

// Efficient bulk loading
const notes = becca.getNotes(noteIds);

// Batch attribute queries
const attributes = sql.getRows(`
    SELECT * FROM attributes 
    WHERE noteId IN (???) 
    AND name = ?
`, [noteIds, 'label']);

Indexing

// Attribute index for fast lookups
const labels = becca.findAttributes("label", "important");

// Branch index for relationship queries
const branch = becca.getBranchFromChildAndParent(childId, parentId);

Best Practices

Entity Creation

// Always use transactions for multiple operations
sql.transactional(() => {
    const note = new BNote({...});
    note.save();
    
    note.addLabel("status", "draft");
    note.addRelation("template", templateId);
});

Entity Updates

// Check existence before update
const note = becca.getNote(noteId);
if (note) {
    note.title = "Updated";
    note.save();
}

// Use proper error handling
try {
    const note = becca.getNoteOrThrow(noteId);
    note.save();
} catch (e) {
    log.error(`Note ${noteId} not found`);
}

Querying

// Use indexed queries
const attrs = becca.findAttributes("label", "task");

// Avoid N+1 queries
const noteIds = [...];
const notes = becca.getNotes(noteIds); // Single batch

// Use SQL for complex queries
const results = sql.getRows(`
    SELECT n.noteId, n.title, COUNT(b.branchId) as childCount
    FROM notes n
    LEFT JOIN branches b ON b.parentNoteId = n.noteId
    GROUP BY n.noteId
`);

Troubleshooting

Common Issues

  1. Circular References

    // Detect cycles before creating branches
    if (!parentNote.hasAncestor(childNote.noteId)) {
        childNote.setParent(parentNote.noteId);
    }
    
  2. Orphaned Entities

    // Find orphaned notes
    const orphans = sql.getRows(`
        SELECT noteId FROM notes
        WHERE noteId != 'root'
        AND noteId NOT IN (SELECT noteId FROM branches)
    `);
    
  3. Attribute Conflicts

    // Handle duplicate attributes
    const existing = note.getAttribute("label", "status");
    if (existing) {
        existing.value = "new value";
        existing.save();
    } else {
        note.addLabel("status", "new value");
    }