Long Text Records
Large text storage with Operational Transformation
Overview
Long Text Records are designed for storing and collaboratively editing large text content. They use Operational Transformation (OT) for real-time collaboration, allowing multiple users to edit the same document simultaneously.
When to Use Long Text
Use Long Text Records when you need:
- Large text documents (notes, articles, code)
- Real-time collaborative text editing
- Character-by-character synchronization
Use Key-Value Records instead when:
- You have structured data (JSON objects)
- Text is relatively short and rarely edited collaboratively
- You need to update specific fields independently
Creating Long Text Records
Using createLongText()
const notes = await lr.Record.createLongText(
'Initial content of the document',
[['$it', 'isA', 'Notes']]
);Using create()
const article = await lr.Record.create(
'longText',
'Article content goes here...',
[['$it', 'isA', 'Article']]
);In Blueprint Pattern
const { doc, notes } = await lr.Record.createAll({
doc: {
type: 'KeyValueAttribute',
value: { title: 'My Document', metadata: {} },
facts: [['$it', 'isA', 'Document']],
},
notes: {
type: 'LongTextAttribute',
value: 'Detailed notes for the document...',
facts: [
['$it', 'isA', 'DocumentNotes'],
['$it', 'belongsTo', '{{doc}}'],
],
},
});Reading Content
const notes = await lr.Record.find(notesId);
const content = await notes.getValue();
console.log(content); // Returns the full text contentUpdating Content
Replace Entire Content
const notes = await lr.Record.find(notesId);
await notes.set('Completely new content');Using set() replaces the entire text content. For collaborative editing,
use the real-time editing approach with load().
Real-Time Collaboration
Enabling Real-Time Updates
const notes = await lr.Record.find(notesId);
// The record now syncs with the server
// Changes from other users are automatically merged
// Whenever you call notes.getValue() you will get the most up to date valueEditing with OT
Once loaded, edits are automatically synchronized:
// Get current content
let content = await notes.getValue();
// Make changes (this would typically be done through a text editor)
// The OT system handles merging concurrent editsQuerying Long Text Records
By Type
const { notes } = await lr.Record.findAll({
notes: [
['$it', '$hasDataType', 'LongTextAttribute'],
['$it', 'isA', 'Notes'],
],
});By Relationship
const { docNotes } = await lr.Record.findAll({
docNotes: [
['$it', '$hasDataType', 'LongTextAttribute'],
['$it', 'belongsTo', documentId],
],
});Integration with Text Editors
Long Text Records work well with text editor libraries. Here's a conceptual example:
function CollaborativeEditor({ recordId }) {
const { lr } = useLinkedRecords();
const [content, setContent] = useState('');
const [record, setRecord] = useState(null);
useEffect(() => {
let attr;
async function setupRecord() {
attr = await lr.Record.find(recordId);
if (attr) {
// Subscribe to receive real-time changes from other users
await attr.subscribe(async () => {
setContent(await attr.getValue());
});
setRecord(attr);
setContent(await attr.getValue());
}
}
setupRecord();
return () => {
attr?.unload();
};
}, [recordId, lr]);
const handleChange = async (newContent) => {
setContent(newContent);
await record?.set(newContent);
};
return (
<textarea
value={content}
onChange={(e) => handleChange(e.target.value)}
/>
);
}Common Patterns
Meeting Notes
// Create meeting notes linked to a meeting
const meetingNotes = await lr.Record.createLongText(
`# Meeting Notes - ${new Date().toLocaleDateString()}
## Attendees
-
## Agenda
-
## Discussion
-
## Action Items
- `,
[
['$it', 'isA', 'MeetingNotes'],
['$it', 'belongsTo', meetingId],
[teamId, '$canAccess', '$it'],
]
);Documentation
const { doc, content } = await lr.Record.createAll({
doc: {
type: 'KeyValueAttribute',
value: {
title: 'API Documentation',
version: '1.0.0',
lastUpdated: Date.now(),
},
facts: [['$it', 'isA', 'Documentation']],
},
content: {
type: 'LongTextAttribute',
value: '# API Documentation\n\n## Introduction\n\n...',
facts: [
['$it', 'isA', 'DocumentContent'],
['$it', 'belongsTo', '{{doc}}'],
],
},
});Code Snippets
const snippet = await lr.Record.createLongText(
`function greet(name) {
return \`Hello, \${name}!\`;
}`,
[
['$it', 'isA', 'CodeSnippet'],
['$it', 'language', 'JavaScript'],
['$it', 'belongsTo', projectId],
]
);Permissions
Long Text Records follow the same authorization model as other records:
// Create with team access
const sharedDoc = await lr.Record.createLongText(
'Shared content',
[
['$it', 'isA', 'SharedDocument'],
[teamId, '$canAccess', '$it'], // Team can read and write
]
);
// Create with read-only sharing
const readOnlyDoc = await lr.Record.createLongText(
'Published content',
[
['$it', 'isA', 'PublishedDocument'],
[viewerTeamId, '$canRead', '$it'], // Viewers can only read
]
);Limitations
-
No partial updates - Unlike Key-Value Records, you can't update specific portions through the API (OT handles this internally during real-time)
-
Text only - No binary content, use Blob Records for files
Best Practices
-
Use for prose content - Long Text is ideal for documents, notes, and articles
-
Clean up subscriptions - Always call
unload()when done to free resources -
Combine with Key-Value - Use Key-Value Records for metadata (title, author, tags) and Long Text Records for the content body