Row operations (DML)
Operations that write rows: insert, update, merge, delete, move, adopt and batch. The overview lists every operation by kind. The MCP server, datanotes mcp, offers each operation as an MCP tool of the same name, and engineTools() of datanotes/mcp gives the same definitions to your own MCP server. DatanotesClient.call(name, args) runs one from code. Over HTTP each one is POST /datanotes/operations/<name>/, with its arguments as the JSON body: the HTTP API reference lists them.
Every write is validated. Writes to rows and to the schema are journaled and undone with undo_migration; configure_database, the webhook operations, vacuum_table and sync_semantic_index are not journaled.
insert_row
Section titled “insert_row”Insert Row · write
Insert a row (new note). Values are validated against the columns; the note path comes from
the table’s path/filename patterns (e.g. an event logged today about last night lands in
last night’s day folder when you set occurred_at to last night). body is the note text.
Generated / identity columns are computed (do not pass them); CHECK constraints are enforced.
Returns migration_id (undo) and the created path in planned_creates.
body links the notes it mentions by name, at their first mention (automatic links; off with
autolink: false or the database’s autolink setting). links_added lists the links made;
ambiguous_mentions lists the mentions left as text with the notes they may name: read those,
then link the right one with update_row and autolink: false, or leave the text.
Examples: Catalogs, schemas and the database, Tables, Columns and types, Bases, Relations and hierarchies, Validation, CHECK constraints, Write and move rows, Automatic links, Batch writes, Import existing notes, Journal and undo, Table history and time travel, Change data feed, Restore a table or the database.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
table |
string | yes | ||
values |
object | yes | ||
body |
string \ | null | null |
|
path |
string \ | null | null |
|
dry_run |
boolean | false |
||
autolink |
boolean \ | null | null |
update_row
Section titled “update_row”Update Row · destructive write
Update exactly ONE row, addressed by its id (the system UUID) or its path; refused when
the row does not exist (or id and path disagree). Same validation, journal and history as
update_rows.
set/unset: columns to set or remove.body: replace the whole note body; or edit it keeping the rest:body_sections{"History": "…"}replaces the text under that heading (any level, up to the next heading of the same level; added as## Historywhen missing),body_prepend/body_appendadd text before / after.expect: compare-and-set —{"status": "open"}writes only if the row still has those values (a person may have changed it since you read it); otherwise refused, nothing written.autolink: the text written links the notes it mentions by name, at their first mention (off withautolink: falseor the database’s setting).links_addedlists the links made;ambiguous_mentionsthe mentions left as text with the notes they may name: read those, then link the right one with update_row andautolink: false, or leave the text.
Examples: Columns and types, Bases, Relations and hierarchies, CHECK constraints, Hand edits, Write and move rows, Automatic links, Batch writes, Journal and undo, Table history and time travel, Change data feed, Restore a table or the database.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
table |
string | yes | ||
id |
string \ | null | null |
|
path |
string \ | null | null |
|
set |
object \ | null | null |
|
unset |
string[] \ | null | null |
|
body |
string \ | null | null |
|
body_sections |
object \ | null | null |
|
body_prepend |
string \ | null | null |
|
expect |
object \ | null | null |
|
body_append |
string \ | null | null |
|
dry_run |
boolean | false |
||
autolink |
boolean \ | null | null |
update_rows
Section titled “update_rows”Update Rows · destructive write
Update every row matching where (required: {} is every row). where: keys AND-ed; values are equality or operators
eq, ne, gt, gte, lt, lte, in, contains, icontains (case/accent-insensitive, also inside lists —
use it for names and aliases), exists, within (a hierarchy link: that row and everything below);
combine with or / and / not (nestable); path is a pseudo-column. A dotted key follows a
link column: {"project.area": "[[Work]]"} matches rows whose project links a row with that area. set sets, unset
removes; all rows are validated first.
expect_count: refuse unless exactly that many match. One row, or its text: update_row.
Examples: Write and move rows.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
table |
string | yes | ||
where |
object | yes | The rows to change; {} is every row of the table. | |
set |
object \ | null | null |
|
unset |
string[] \ | null | null |
|
dry_run |
boolean | false |
||
expect_count |
integer \ | null | null |
merge_rows
Section titled “merge_rows”Merge Rows · destructive write
MERGE (upsert) rows into table by the key columns on (e.g. ["occurred_at"] or
["id"]): a row matching an existing one updates it (when_matched: “update” default or
“ignore”), the others are inserted (when_not_matched: “insert” default or “ignore”).
Date-times are compared after completion with the database time zone. A source key matching
several rows, or repeated, is a conflict. One journaled, undoable operation.
Examples: Write and move rows.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
table |
string | yes | ||
on |
string[] | yes | ||
rows |
object[] | yes | ||
when_matched |
"update" \ |
"ignore" \ |
null | |
when_not_matched |
"insert" \ |
"ignore" \ |
null | |
dry_run |
boolean | false |
delete_rows
Section titled “delete_rows”Delete Rows · destructive write
Delete rows matching where. mode: “archive” (default, move to the table’s archive
folder) or “delete” (trash; content journaled for undo). on_referenced: “restrict”
(default, refuse rows other notes link to), “set_null” (clear front matter links to them) or
“ignore” (the links stay, to no row: an archived row is gone as a trashed one, so check_table
reports them as broken_link and new links to it are refused). where is required: {} deletes every row.
Examples: Relations and hierarchies, Write and move rows, Table history and time travel.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
table |
string | yes | ||
where |
object | yes | The rows to change; {} is every row of the table. | |
mode |
"archive" \ |
"delete" \ |
null | |
on_referenced |
"restrict" \ |
"set_null" \ |
"ignore" \ |
null |
dry_run |
boolean | false |
move_rows
Section titled “move_rows”Move Rows · destructive write
Move rows matching where (required: {} is every row) to another table (e.g. split a domain out of log into its own
table): sets each row’s table to the destination, applies rename ({"metric_distance_km": "distance_km"}), set and drop, adds the destination’s missing dense fields, and
validates every row against the destination first. Keys unknown to the destination are
reported, never dropped silently — rename or drop them explicitly. relocate: true also moves
the files to the destination table’s folder (named by its filename pattern); by default rows stay where
they are, as rows already in folders of their own usually do (adopt_rows, for notes that are no rows
yet, moves them by default). Refused while rows
staying behind link to a moved row through a column whose ref is the source table (the
conflicts name them): change those links first. Undo with
undo_migration.
Examples: Write and move rows.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
table |
string | yes | ||
to_table |
string | yes | ||
where |
object | yes | The rows to change; {} is every row of the table. | |
rename |
object \ | null | null |
|
set |
object \ | null | null |
|
drop |
string[] \ | null | null |
|
relocate |
boolean | false |
||
dry_run |
boolean | false |
adopt_rows
Section titled “adopt_rows”Adopt Rows · destructive write
Turn existing notes (not rows of any table yet) into rows of table, keeping their body and
links: rename keys ({"data": "occurred_at"}), set for all, per_row values by
note path ({"Home/x.md": {"zone": "[[Kitchen]]"}}), drop keys the table does not have.
Every note gets table and a system id; with relocate (default true, unlike move_rows:
notes become rows where the table keeps its rows) it moves to the table’s folder, named by its filename pattern (links to it are rewritten). All notes are validated first
(as rows of table, so notes of the same call may link to one another):
unknown keys and bad values are listed and nothing is written. Run with dry_run first; undo
with undo_migration. Many notes: adopt them in calls of about 1,000 paths each, so a
stop or a crash interrupts less (each call is one journal entry).
Examples: Import existing notes.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
table |
string | yes | ||
paths |
string[] | yes | ||
rename |
object \ | null | null |
|
set |
object \ | null | null |
|
per_row |
object \ | null | null |
|
drop |
string[] \ | null | null |
|
relocate |
boolean | true |
||
dry_run |
boolean | false |
Batch · destructive write
Several writes that succeed or fail together. operations: [{"op": ..., ...arguments}] where
op is a row operation (insert_row, update_row, update_rows, delete_rows, merge_rows,
move_rows, adopt_rows, recompute_columns, align_relations, restore_table) or a DDL op
(create_table, add_column, alter_column, …), with the same arguments as the operation (an unknown one refuses the batch) (e.g.
values for insert_row, set for update_row / update_rows). They run in order, each seeing the previous ones; if one is
refused, the ones already applied are undone (rolled_back). Returns steps (per operation, each with its result,
where ambiguous_mentions is as in insert_row) and
migration_id: undo_migration on it reverts the whole batch. Use it when a change spans several
rows (create an event and update the people in it). dry_run checks each operation against the
current data only.
The schema changes only through operations like this one: never edit metastore.yaml by hand (no validation, no undo).
Examples: Views, Relations and hierarchies, CHECK constraints, Batch writes, Restore a table or the database.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
operations |
object[] | yes | ||
summary |
string \ | null | null |
|
dry_run |
boolean | false |
align_relations
Section titled “align_relations”Align Relations · destructive write
Add the reciprocal links missing on a table’s inverse link columns (e.g. Alice lists Carol
in friends but Carol does not list Alice). Run it after declaring inverse on existing data;
check_table reports such rows (code inverse). Single-value links already pointing elsewhere
are conflicts (nothing written). The links it adds are checked like other writes (CHECKs that
follow links, ref_where / ref_by). Writes through the tools already keep both sides in step.
Examples: Relations and hierarchies.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
table |
string | yes | ||
paths |
string[] \ | null | null |
|
dry_run |
boolean | false |
recompute_columns
Section titled “recompute_columns”Recompute Columns · destructive write
Bring generated and identity columns up to date (all rows, or only paths), e.g. after
rows were edited by hand (an editor, git, sync) or a generated expression changed with alter_column.
Also gives rows without an id a new one, and a duplicated note (same id as an older row)
a new id. new_ids=true regenerates every id in scope (new ones on every call): only on the user’s
explicit request.
check_table reports stale values with code generated. No-op when nothing is stale;
otherwise journaled (undo_migration) and a RECOMPUTE version in the table’s history.
Examples: Hand edits.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
table |
string | yes | ||
paths |
string[] \ | null | null |
|
new_ids |
boolean | false |
||
dry_run |
boolean | false |