Understanding Deletes in Append-Only Storage
OnDB performs soft deletes . When you delete a document, a new record with deleted: true is appended. The original data is preserved but excluded from query results by default.
Delete Pattern
Soft delete a document by finding it and storing a new version with deleted: true.
// Find the document
const user = await client . queryBuilder ()
. collection ( 'users' )
. whereField ( 'email' ). equals ( 'alice@example.com' )
. executeUnique ();
if ( user ) {
// Store soft-deleted version
await client . store (
{ collection: 'users' , data: [{ ... user , deleted: true , deletedAt: new Date (). toISOString () }] },
async ( quote ) => {
const txHash = await processPayment ( quote );
return { txHash , network: quote . network , sender: walletAddress , chainType: quote . chainType , paymentMethod: 'native' };
},
true
);
console . log ( 'Deleted user' );
} else {
console . log ( 'User not found' );
}
How Deletes Work
Original Record (Block 100):
{ id: "user_1", name: "Alice", email: "alice@example.com", deleted: false }
After Delete (Block 101):
{ id: "user_1", deleted: true, deletedAt: "2024-01-02", updatedAt: "2024-01-02" }
Original record is preserved, but queries exclude deleted records
Examples
Delete by ID
const post = await client . queryBuilder ()
. collection ( 'posts' )
. whereField ( 'id' ). equals ( 'post_123' )
. executeUnique ();
if ( post ) {
await client . store (
{ collection: 'posts' , data: [{ ... post , deleted: true , deletedAt: new Date (). toISOString () }] },
paymentCallback ,
true
);
console . log ( 'Post deleted successfully' );
} else {
console . log ( 'Post not found' );
}
Conditional Delete Pattern
// First verify ownership or permissions
const post = await client . queryBuilder ()
. collection ( 'posts' )
. whereField ( 'id' ). equals ( 'post_123' )
. executeUnique ();
if ( post && post . author === currentUser . id ) {
await client . store (
{ collection: 'posts' , data: [{ ... post , deleted: true , deletedAt: new Date (). toISOString () }] },
paymentCallback ,
true
);
console . log ( 'Post deleted' );
} else {
console . log ( 'Unauthorized or not found' );
}
Verifying Deletion
After deletion, the document won’t appear in normal queries:
// Delete a user
const user = await client . queryBuilder ()
. collection ( 'users' )
. whereField ( 'id' ). equals ( 'user_123' )
. executeUnique ();
if ( user ) {
await client . store (
{ collection: 'users' , data: [{ ... user , deleted: true , deletedAt: new Date (). toISOString () }] },
paymentCallback ,
true
);
}
// executeUnique returns null for deleted documents
const deleted = await client . queryBuilder ()
. collection ( 'users' )
. whereField ( 'id' ). equals ( 'user_123' )
. executeUnique ();
console . log ( deleted ); // null
Data Retention
Since OnDB uses append-only storage:
Aspect Behavior Original data Remains on blockchain permanently Query results Exclude deleted records by default Audit trail Full history is preserved Recovery Possible by querying historical records
Bulk Delete Pattern
For deleting multiple documents, query and delete each:
const result = await client . queryBuilder ()
. collection ( 'users' )
. whereField ( 'active' ). isFalse ()
. execute ();
for ( const user of result . records ) {
await client . store (
{ collection: 'users' , data: [{ ... user , deleted: true , deletedAt: new Date (). toISOString () }] },
paymentCallback ,
true
);
}
Next Steps
CRUD Overview Back to CRUD overview
Query Builder Complex queries