Files
Trilium/docs/Developer Guide/Architecture/Entity-System.md

612 lines
14 KiB
Markdown
Raw Normal View History

# 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
```mermaid
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
```typescript
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
```typescript
// 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
```typescript
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
```typescript
// 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
```typescript
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
```typescript
// 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
```typescript
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
```typescript
// 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
```typescript
class BOption {
name: string; // Option key
value: string; // Option value
isSynced: boolean; // Sync across instances
utcDateModified: string; // Last change
}
```
#### Common Options
```typescript
// 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
```typescript
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
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
// 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
```typescript
// Always use transactions for multiple operations
sql.transactional(() => {
const note = new BNote({...});
note.save();
note.addLabel("status", "draft");
note.addRelation("template", templateId);
});
```
### Entity Updates
```typescript
// 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
```typescript
// 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**
```typescript
// Detect cycles before creating branches
if (!parentNote.hasAncestor(childNote.noteId)) {
childNote.setParent(parentNote.noteId);
}
```
2. **Orphaned Entities**
```typescript
// 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**
```typescript
// Handle duplicate attributes
const existing = note.getAttribute("label", "status");
if (existing) {
existing.value = "new value";
existing.save();
} else {
note.addLabel("status", "new value");
}
```
## Related Documentation
- [Three-Layer Cache System](Three-Layer-Cache-System.md) - Cache architecture
- [Database Schema](../Development%20and%20architecture/Database/notes.md) - Database structure
- [Script API](../../Script%20API/) - Entity API for scripts