datanotes 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +9 -0
- package/README.md +207 -28
- package/SECURITY.md +92 -45
- package/dist/THIRD-PARTY-NOTICES.txt +682 -0
- package/dist/api/catalog.d.cts +69 -0
- package/dist/api/catalog.d.ts +50 -2
- package/dist/api/cli-options.d.cts +39 -0
- package/dist/api/cli-options.d.ts +39 -0
- package/dist/api/client.d.cts +89 -0
- package/dist/api/client.d.ts +19 -30
- package/dist/api/docs.d.cts +211 -0
- package/dist/api/docs.d.ts +211 -0
- package/dist/api/guidance.d.cts +81 -0
- package/dist/api/guidance.d.ts +81 -0
- package/dist/api/index.cjs +6610 -2247
- package/dist/api/index.d.cts +13 -0
- package/dist/api/index.d.ts +10 -7
- package/dist/api/index.js +6610 -2247
- package/dist/api/local.cjs +118 -6
- package/dist/api/local.d.cts +95 -0
- package/dist/api/local.d.ts +70 -6
- package/dist/api/local.js +104 -6
- package/dist/api/mcp.cjs +17384 -0
- package/dist/api/mcp.d.cts +108 -0
- package/dist/api/mcp.d.ts +108 -0
- package/dist/api/mcp.js +17334 -0
- package/dist/api/otlp.d.cts +65 -0
- package/dist/api/redact.d.cts +17 -0
- package/dist/api/redact.d.ts +17 -0
- package/dist/api/requests.d.cts +26 -0
- package/dist/api/requests.d.ts +14 -16
- package/dist/api/results.d.cts +16 -0
- package/dist/api/results.d.ts +2 -1
- package/dist/api/tokens.d.cts +7 -0
- package/dist/datanotes.cjs +62911 -28409
- package/dist/docs/administration/configuration.md +59 -0
- package/dist/docs/administration/docker.md +80 -0
- package/dist/docs/administration/git-sync.md +71 -0
- package/dist/docs/administration/limits.md +69 -0
- package/dist/docs/administration/run-the-service.md +219 -0
- package/dist/docs/administration/status.md +61 -0
- package/dist/docs/administration/use-with-obsidian.md +75 -0
- package/dist/docs/api/operations/add_column.md +49 -0
- package/dist/docs/api/operations/add_constraint.md +55 -0
- package/dist/docs/api/operations/adopt_rows.md +50 -0
- package/dist/docs/api/operations/align_relations.md +41 -0
- package/dist/docs/api/operations/alter_column.md +46 -0
- package/dist/docs/api/operations/alter_table.md +67 -0
- package/dist/docs/api/operations/batch.md +48 -0
- package/dist/docs/api/operations/changes_stream.md +28 -0
- package/dist/docs/api/operations/check_table.md +29 -0
- package/dist/docs/api/operations/clone_table.md +48 -0
- package/dist/docs/api/operations/comment_on.md +42 -0
- package/dist/docs/api/operations/configure_database.md +52 -0
- package/dist/docs/api/operations/create_table.md +78 -0
- package/dist/docs/api/operations/create_view.md +54 -0
- package/dist/docs/api/operations/create_webhook.md +37 -0
- package/dist/docs/api/operations/define_base.md +52 -0
- package/dist/docs/api/operations/delete_rows.md +43 -0
- package/dist/docs/api/operations/delete_webhook.md +31 -0
- package/dist/docs/api/operations/describe_database.md +30 -0
- package/dist/docs/api/operations/describe_history.md +40 -0
- package/dist/docs/api/operations/describe_semantic_index.md +24 -0
- package/dist/docs/api/operations/describe_table.md +38 -0
- package/dist/docs/api/operations/docs.md +13 -0
- package/dist/docs/api/operations/drop_column.md +41 -0
- package/dist/docs/api/operations/drop_constraint.md +39 -0
- package/dist/docs/api/operations/drop_table.md +43 -0
- package/dist/docs/api/operations/drop_view.md +38 -0
- package/dist/docs/api/operations/engine_status.md +23 -0
- package/dist/docs/api/operations/graph_neighbors.md +32 -0
- package/dist/docs/api/operations/graph_path.md +32 -0
- package/dist/docs/api/operations/graph_traverse.md +43 -0
- package/dist/docs/api/operations/identify_session.md +24 -0
- package/dist/docs/api/operations/infer_table.md +43 -0
- package/dist/docs/api/operations/insert_row.md +48 -0
- package/dist/docs/api/operations/list_changes.md +35 -0
- package/dist/docs/api/operations/list_folder.md +28 -0
- package/dist/docs/api/operations/list_link_targets.md +44 -0
- package/dist/docs/api/operations/list_migrations.md +35 -0
- package/dist/docs/api/operations/list_tables.md +32 -0
- package/dist/docs/api/operations/list_webhooks.md +23 -0
- package/dist/docs/api/operations/manual_index.md +14 -0
- package/dist/docs/api/operations/manual_page.md +22 -0
- package/dist/docs/api/operations/merge_rows.md +44 -0
- package/dist/docs/api/operations/move_rows.md +52 -0
- package/dist/docs/api/operations/openapi.md +13 -0
- package/dist/docs/api/operations/openapi_post.md +20 -0
- package/dist/docs/api/operations/read_note.md +29 -0
- package/dist/docs/api/operations/recompute_columns.md +44 -0
- package/dist/docs/api/operations/record_tool_call.md +36 -0
- package/dist/docs/api/operations/rename_column.md +44 -0
- package/dist/docs/api/operations/rename_value.md +45 -0
- package/dist/docs/api/operations/restore_database.md +51 -0
- package/dist/docs/api/operations/restore_table.md +46 -0
- package/dist/docs/api/operations/search_notes.md +28 -0
- package/dist/docs/api/operations/select_rows.md +61 -0
- package/dist/docs/api/operations/semantic_search.md +34 -0
- package/dist/docs/api/operations/semantic_search_docs.md +26 -0
- package/dist/docs/api/operations/set_table_bases.md +44 -0
- package/dist/docs/api/operations/status.md +13 -0
- package/dist/docs/api/operations/stop_engine.md +23 -0
- package/dist/docs/api/operations/sync_semantic_index.md +33 -0
- package/dist/docs/api/operations/table_changes.md +31 -0
- package/dist/docs/api/operations/tags/auxiliary.md +14 -0
- package/dist/docs/api/operations/tags/change-feed.md +8 -0
- package/dist/docs/api/operations/tags/history.md +17 -0
- package/dist/docs/api/operations/tags/queries.md +15 -0
- package/dist/docs/api/operations/tags/rows-dml.md +16 -0
- package/dist/docs/api/operations/tags/schema-ddl.md +22 -0
- package/dist/docs/api/operations/tags/service.md +15 -0
- package/dist/docs/api/operations/tags/tokens.md +13 -0
- package/dist/docs/api/operations/tokens_create.md +34 -0
- package/dist/docs/api/operations/tokens_delete.md +29 -0
- package/dist/docs/api/operations/tokens_list.md +21 -0
- package/dist/docs/api/operations/tokens_me.md +20 -0
- package/dist/docs/api/operations/tokens_rotate.md +29 -0
- package/dist/docs/api/operations/tokens_update.md +34 -0
- package/dist/docs/api/operations/undo_migration.md +46 -0
- package/dist/docs/api/operations/update_row.md +60 -0
- package/dist/docs/api/operations/update_rows.md +46 -0
- package/dist/docs/api/operations/vacuum_table.md +37 -0
- package/dist/docs/api.md +135 -0
- package/dist/docs/develop/build-an-app.md +320 -0
- package/dist/docs/develop/build-on-datanotes.md +138 -0
- package/dist/docs/develop/change-feed-and-webhooks.md +70 -0
- package/dist/docs/develop/client.md +116 -0
- package/dist/docs/develop/concurrency.md +153 -0
- package/dist/docs/develop/http-api.md +79 -0
- package/dist/docs/develop/telemetry.md +86 -0
- package/dist/docs/guides/agents/docs-offline.md +99 -0
- package/dist/docs/guides/agents/how-agents-use-datanotes.md +124 -0
- package/dist/docs/guides/agents/prompts.md +495 -0
- package/dist/docs/guides/agents/reading-errors.md +112 -0
- package/dist/docs/guides/data-quality/check-constraints.md +379 -0
- package/dist/docs/guides/data-quality/check-table.md +63 -0
- package/dist/docs/guides/data-quality/hand-edits.md +507 -0
- package/dist/docs/guides/data-quality/sync-between-devices.md +51 -0
- package/dist/docs/guides/data-quality/validation.md +90 -0
- package/dist/docs/guides/database-objects/bases.md +480 -0
- package/dist/docs/guides/database-objects/catalogs-and-schemas.md +897 -0
- package/dist/docs/guides/database-objects/columns.md +1332 -0
- package/dist/docs/guides/database-objects/relations.md +1454 -0
- package/dist/docs/guides/database-objects/tables.md +1404 -0
- package/dist/docs/guides/database-objects/views.md +1003 -0
- package/dist/docs/guides/history/back-up.md +195 -0
- package/dist/docs/guides/history/change-data-feed.md +247 -0
- package/dist/docs/guides/history/journal-and-undo.md +262 -0
- package/dist/docs/guides/history/restore.md +341 -0
- package/dist/docs/guides/history/table-history.md +657 -0
- package/dist/docs/guides/history/vacuum.md +35 -0
- package/dist/docs/guides/read-and-write/automatic-links.md +768 -0
- package/dist/docs/guides/read-and-write/batch.md +139 -0
- package/dist/docs/guides/read-and-write/import-notes.md +896 -0
- package/dist/docs/guides/read-and-write/joins-and-aggregates.md +251 -0
- package/dist/docs/guides/read-and-write/read-and-filter.md +536 -0
- package/dist/docs/guides/read-and-write/write-rows.md +573 -0
- package/dist/docs/guides/search/link-graph.md +146 -0
- package/dist/docs/guides/search/semantic.md +133 -0
- package/dist/docs/guides/search/text.md +142 -0
- package/dist/docs/guides/security/security-model.md +110 -0
- package/dist/docs/guides/security/tokens.md +60 -0
- package/dist/docs/guides/security/who-did-what.md +42 -0
- package/dist/docs/index.md +113 -0
- package/dist/docs/llms.txt +33 -0
- package/dist/docs/reference/cli.md +136 -0
- package/dist/docs/reference/compatibility.md +63 -0
- package/dist/docs/reference/errors.md +72 -0
- package/dist/docs/reference/glossary.md +161 -0
- package/dist/docs/reference/operations/auxiliary.md +116 -0
- package/dist/docs/reference/operations/history.md +168 -0
- package/dist/docs/reference/operations/queries.md +169 -0
- package/dist/docs/reference/operations/rows.md +185 -0
- package/dist/docs/reference/operations/schema.md +316 -0
- package/dist/docs/reference/operations.md +96 -0
- package/dist/docs/reference/settings.md +31 -0
- package/dist/docs/release-notes.md +67 -0
- package/dist/docs/start/connect-agents.md +204 -0
- package/dist/docs/start/how-it-works.md +317 -0
- package/dist/docs/start/quick-start.md +126 -0
- package/package.json +59 -14
|
@@ -0,0 +1,1332 @@
|
|
|
1
|
+
# Columns and types
|
|
2
|
+
|
|
3
|
+
> Choose column types and attributes, compute generated columns, name rows after a time, and change the columns of a table that already has rows.
|
|
4
|
+
|
|
5
|
+
You declare columns once, in `metastore.yaml`, and the engine checks every write against them. Changing a column changes every row that has it, in one undoable operation.
|
|
6
|
+
|
|
7
|
+
The examples on this page share one fresh vault, which starts with a `film` table holding Nosferatu and Metropolis.
|
|
8
|
+
|
|
9
|
+
## Add a sparse column
|
|
10
|
+
|
|
11
|
+
A new column is sparse by default: the table knows it, but only the rows that get a value store it. Use sparse columns for things you record sometimes. Every column needs a `description`, and `label` and `aliases` add other words for it. They tell an agent what the column means, so it reuses the column instead of adding a second one. `note` links to a note that documents the column.
|
|
12
|
+
|
|
13
|
+
**`add_column`**
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"table": "film",
|
|
18
|
+
"column": {
|
|
19
|
+
"name": "minutes",
|
|
20
|
+
"type": "integer",
|
|
21
|
+
"min": 1,
|
|
22
|
+
"label": "Runtime",
|
|
23
|
+
"aliases": ["runtime", "length"],
|
|
24
|
+
"description": "Running time in minutes",
|
|
25
|
+
"note": "[[How I count minutes]]"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Answer:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"ok": true,
|
|
35
|
+
"op": "add_column",
|
|
36
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
37
|
+
"commits": [{ "table": "main.public.film", "version": 3, "operation": "ADD COLUMNS" }],
|
|
38
|
+
"migration_id": "20261012T071946-add_column-add-column-minutes-to-film"
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**`update_row`**
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"table": "film",
|
|
47
|
+
"path": "datanotes/main/public/film/Metropolis.md",
|
|
48
|
+
"set": { "minutes": 153 }
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Only Metropolis has a value. The Nosferatu note has no `minutes` key, so it reads as `null`:
|
|
53
|
+
|
|
54
|
+
**`select_rows`**
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{ "table": "film", "columns": ["title", "minutes"] }
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Answer:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"table": "main.public.film",
|
|
65
|
+
"total": 2,
|
|
66
|
+
"rows": [
|
|
67
|
+
{
|
|
68
|
+
"path": "datanotes/main/public/film/Metropolis.md",
|
|
69
|
+
"title": "Metropolis",
|
|
70
|
+
"minutes": 153
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"path": "datanotes/main/public/film/Nosferatu.md",
|
|
74
|
+
"title": "Nosferatu",
|
|
75
|
+
"minutes": null
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Add a dense column to every row
|
|
82
|
+
|
|
83
|
+
`dense: true` writes the column on every row, existing rows included, with its `default`.
|
|
84
|
+
|
|
85
|
+
**`add_column`**
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"table": "film",
|
|
90
|
+
"column": {
|
|
91
|
+
"name": "favourite",
|
|
92
|
+
"type": "boolean",
|
|
93
|
+
"default": false,
|
|
94
|
+
"description": "True for the films I would watch again and again",
|
|
95
|
+
"dense": true
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Answer:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"ok": true,
|
|
105
|
+
"op": "add_column",
|
|
106
|
+
"notes_changed": 2,
|
|
107
|
+
"planned_edits": [
|
|
108
|
+
{
|
|
109
|
+
"path": "datanotes/main/public/film/Metropolis.md",
|
|
110
|
+
"set": { "favourite": false },
|
|
111
|
+
"remove": []
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"path": "datanotes/main/public/film/Nosferatu.md",
|
|
115
|
+
"set": { "favourite": false },
|
|
116
|
+
"remove": []
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
120
|
+
"commits": [{ "table": "main.public.film", "version": 5, "operation": "ADD COLUMNS" }],
|
|
121
|
+
"migration_id": "20261012T071947-add_column-add-column-favourite-to-film"
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**`select_rows`**
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{ "table": "film", "columns": ["title", "favourite"] }
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Answer:
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"table": "main.public.film",
|
|
136
|
+
"total": 2,
|
|
137
|
+
"rows": [
|
|
138
|
+
{
|
|
139
|
+
"path": "datanotes/main/public/film/Metropolis.md",
|
|
140
|
+
"title": "Metropolis",
|
|
141
|
+
"favourite": false
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"path": "datanotes/main/public/film/Nosferatu.md",
|
|
145
|
+
"title": "Nosferatu",
|
|
146
|
+
"favourite": false
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Turn an enum into an ordered scale
|
|
153
|
+
|
|
154
|
+
An `enum` accepts only its `values`. With `ordered: true`, the values form a scale: filters, sorting and averages use each value’s position (1, 2…), while the notes keep the words. Here the last value is `seen`; the next example renames it.
|
|
155
|
+
|
|
156
|
+
**`add_column`**
|
|
157
|
+
|
|
158
|
+
```json
|
|
159
|
+
{
|
|
160
|
+
"table": "film",
|
|
161
|
+
"column": {
|
|
162
|
+
"name": "status",
|
|
163
|
+
"type": "enum",
|
|
164
|
+
"values": ["to watch", "seen"],
|
|
165
|
+
"ordered": true,
|
|
166
|
+
"description": "Where the film is on my list"
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Answer:
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"ok": true,
|
|
176
|
+
"op": "add_column",
|
|
177
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
178
|
+
"commits": [{ "table": "main.public.film", "version": 6, "operation": "ADD COLUMNS" }],
|
|
179
|
+
"migration_id": "20261012T071947-add_column-add-column-status-to-film"
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**`update_row`**
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"table": "film",
|
|
188
|
+
"path": "datanotes/main/public/film/Nosferatu.md",
|
|
189
|
+
"set": { "status": "seen" }
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**`update_row`**
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"table": "film",
|
|
198
|
+
"path": "datanotes/main/public/film/Metropolis.md",
|
|
199
|
+
"set": { "status": "to watch" }
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Sorting follows the scale, furthest along first:
|
|
204
|
+
|
|
205
|
+
**`select_rows`**
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{ "table": "film", "columns": ["title", "status"], "order_by": "-status" }
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Answer:
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"table": "main.public.film",
|
|
216
|
+
"total": 2,
|
|
217
|
+
"rows": [
|
|
218
|
+
{
|
|
219
|
+
"path": "datanotes/main/public/film/Nosferatu.md",
|
|
220
|
+
"title": "Nosferatu",
|
|
221
|
+
"status": "seen"
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"path": "datanotes/main/public/film/Metropolis.md",
|
|
225
|
+
"title": "Metropolis",
|
|
226
|
+
"status": "to watch"
|
|
227
|
+
}
|
|
228
|
+
]
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
So does a filter. `gt: "to watch"` means further along than `to watch`:
|
|
233
|
+
|
|
234
|
+
**`select_rows`**
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"table": "film",
|
|
239
|
+
"columns": ["title", "status"],
|
|
240
|
+
"where": { "status": { "gt": "to watch" } }
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Answer:
|
|
245
|
+
|
|
246
|
+
```json
|
|
247
|
+
{
|
|
248
|
+
"table": "main.public.film",
|
|
249
|
+
"total": 1,
|
|
250
|
+
"rows": [
|
|
251
|
+
{
|
|
252
|
+
"path": "datanotes/main/public/film/Nosferatu.md",
|
|
253
|
+
"title": "Nosferatu",
|
|
254
|
+
"status": "seen"
|
|
255
|
+
}
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Rename an enum value
|
|
261
|
+
|
|
262
|
+
`rename_value` renames one value in the definition and on every row. The value keeps its position on the scale. Here `seen` becomes `watched`.
|
|
263
|
+
|
|
264
|
+
**`rename_value`**
|
|
265
|
+
|
|
266
|
+
```json
|
|
267
|
+
{ "table": "film", "column": "status", "from_value": "seen", "to_value": "watched" }
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Answer:
|
|
271
|
+
|
|
272
|
+
```json
|
|
273
|
+
{
|
|
274
|
+
"ok": true,
|
|
275
|
+
"op": "rename_value",
|
|
276
|
+
"notes_changed": 1,
|
|
277
|
+
"planned_edits": [
|
|
278
|
+
{
|
|
279
|
+
"path": "datanotes/main/public/film/Nosferatu.md",
|
|
280
|
+
"set": { "status": "watched" },
|
|
281
|
+
"remove": []
|
|
282
|
+
}
|
|
283
|
+
],
|
|
284
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
285
|
+
"commits": [{ "table": "main.public.film", "version": 9, "operation": "UPDATE" }],
|
|
286
|
+
"migration_id": "20261012T071947-rename_value-rename-film-status-value-seen-"
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**`select_rows`**
|
|
291
|
+
|
|
292
|
+
```json
|
|
293
|
+
{ "table": "film", "columns": ["title", "status"], "where": { "title": "Nosferatu" } }
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Answer:
|
|
297
|
+
|
|
298
|
+
```json
|
|
299
|
+
{
|
|
300
|
+
"table": "main.public.film",
|
|
301
|
+
"total": 1,
|
|
302
|
+
"rows": [
|
|
303
|
+
{
|
|
304
|
+
"path": "datanotes/main/public/film/Nosferatu.md",
|
|
305
|
+
"title": "Nosferatu",
|
|
306
|
+
"status": "watched"
|
|
307
|
+
}
|
|
308
|
+
]
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Describe what each enum value means
|
|
313
|
+
|
|
314
|
+
`value_descriptions` says when to use each value of an enum. `describe_table` shows the descriptions, and so does the refusal of a wrong value, so an agent picks the right one.
|
|
315
|
+
|
|
316
|
+
**`alter_column`**
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
{
|
|
320
|
+
"table": "film",
|
|
321
|
+
"column": "status",
|
|
322
|
+
"changes": {
|
|
323
|
+
"value_descriptions": {
|
|
324
|
+
"to watch": "on my list, not seen yet",
|
|
325
|
+
"watched": "seen to the end"
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Answer:
|
|
332
|
+
|
|
333
|
+
```json
|
|
334
|
+
{
|
|
335
|
+
"ok": true,
|
|
336
|
+
"op": "alter_column",
|
|
337
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
338
|
+
"commits": [
|
|
339
|
+
{ "table": "main.public.film", "version": 10, "operation": "CHANGE COLUMN" }
|
|
340
|
+
],
|
|
341
|
+
"migration_id": "20261012T071947-alter_column-alter-column-status-in-film"
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
**`update_row`**
|
|
346
|
+
|
|
347
|
+
```json
|
|
348
|
+
{
|
|
349
|
+
"table": "film",
|
|
350
|
+
"path": "datanotes/main/public/film/Nosferatu.md",
|
|
351
|
+
"set": { "status": "finished" }
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Refused:
|
|
356
|
+
|
|
357
|
+
```text
|
|
358
|
+
update_row refused — nothing was written:
|
|
359
|
+
invalid datanotes/main/public/film/Nosferatu.md: status must be one of: to watch (on my list, not seen yet); watched (seen to the end)
|
|
360
|
+
Hint: Use one of the allowed values listed in the message, written exactly; describe_table shows what each one means. To add a value, alter_column the enum. Then call update_row again (dry_run: true checks without writing).
|
|
361
|
+
Docs: https://datanotes.dev/guides/database-objects/columns.md#describe-what-each-enum-value-means (offline: read_docs("guides/database-objects/columns#describe-what-each-enum-value-means"))
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## Change a column’s attributes
|
|
365
|
+
|
|
366
|
+
`alter_column` changes a column’s attributes. If rows would break the new definition, the engine refuses the change, lists those rows and changes nothing.
|
|
367
|
+
|
|
368
|
+
**`alter_column`**
|
|
369
|
+
|
|
370
|
+
```json
|
|
371
|
+
{ "table": "film", "column": "minutes", "changes": { "max": 120 } }
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Refused:
|
|
375
|
+
|
|
376
|
+
```text
|
|
377
|
+
alter_column refused — nothing was written:
|
|
378
|
+
invalid datanotes/main/public/film/Metropolis.md: minutes must be <= 120
|
|
379
|
+
Hint: Give a value inside the column's range (the message names the bound); describe_table shows its min and max. Then call alter_column again (dry_run: true checks without writing).
|
|
380
|
+
Docs: https://datanotes.dev/guides/database-objects/tables.md#describe-a-table-before-writing-to-it (offline: read_docs("guides/database-objects/tables#describe-a-table-before-writing-to-it"))
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
A change that every row satisfies goes through:
|
|
384
|
+
|
|
385
|
+
**`alter_column`**
|
|
386
|
+
|
|
387
|
+
```json
|
|
388
|
+
{
|
|
389
|
+
"table": "film",
|
|
390
|
+
"column": "minutes",
|
|
391
|
+
"changes": { "max": 300, "description": "Running time of the commonly cited version" }
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Answer:
|
|
396
|
+
|
|
397
|
+
```json
|
|
398
|
+
{
|
|
399
|
+
"ok": true,
|
|
400
|
+
"op": "alter_column",
|
|
401
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
402
|
+
"commits": [
|
|
403
|
+
{ "table": "main.public.film", "version": 11, "operation": "CHANGE COLUMN" }
|
|
404
|
+
],
|
|
405
|
+
"migration_id": "20261012T071947-alter_column-alter-column-minutes-in-film"
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
`null` removes an attribute. Without `max`, any runtime is valid again.
|
|
410
|
+
|
|
411
|
+
**`alter_column`**
|
|
412
|
+
|
|
413
|
+
```json
|
|
414
|
+
{ "table": "film", "column": "minutes", "changes": { "max": null } }
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Answer:
|
|
418
|
+
|
|
419
|
+
```json
|
|
420
|
+
{
|
|
421
|
+
"ok": true,
|
|
422
|
+
"op": "alter_column",
|
|
423
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
424
|
+
"commits": [
|
|
425
|
+
{ "table": "main.public.film", "version": 12, "operation": "CHANGE COLUMN" }
|
|
426
|
+
],
|
|
427
|
+
"migration_id": "20261012T071947-alter_column-alter-column-minutes-in-film-2"
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
`dense: true` makes the column dense, so the engine writes it on every row, including Nosferatu, which has no value.
|
|
432
|
+
|
|
433
|
+
**`alter_column`**
|
|
434
|
+
|
|
435
|
+
```json
|
|
436
|
+
{ "table": "film", "column": "minutes", "changes": { "dense": true } }
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Answer:
|
|
440
|
+
|
|
441
|
+
```json
|
|
442
|
+
{
|
|
443
|
+
"ok": true,
|
|
444
|
+
"op": "alter_column",
|
|
445
|
+
"notes_changed": 1,
|
|
446
|
+
"planned_edits": [
|
|
447
|
+
{
|
|
448
|
+
"path": "datanotes/main/public/film/Nosferatu.md",
|
|
449
|
+
"set": { "minutes": null },
|
|
450
|
+
"remove": []
|
|
451
|
+
}
|
|
452
|
+
],
|
|
453
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
454
|
+
"commits": [
|
|
455
|
+
{ "table": "main.public.film", "version": 13, "operation": "CHANGE COLUMN" }
|
|
456
|
+
],
|
|
457
|
+
"migration_id": "20261012T071947-alter_column-alter-column-minutes-in-film-3"
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
`datanotes/main/public/film/Nosferatu.md` now reads:
|
|
462
|
+
|
|
463
|
+
```markdown
|
|
464
|
+
---
|
|
465
|
+
table: main.public.film
|
|
466
|
+
id: 01a12efb-5a12-7e85-a3a1-85a2fe3425ec
|
|
467
|
+
title: Nosferatu
|
|
468
|
+
year: 1922
|
|
469
|
+
favourite: false
|
|
470
|
+
status: watched
|
|
471
|
+
minutes: null
|
|
472
|
+
---
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
`dense: false` makes the column sparse again. Rows written from now on store it only when it has a value, and the empty `minutes` already in Nosferatu stays:
|
|
476
|
+
|
|
477
|
+
**`alter_column`**
|
|
478
|
+
|
|
479
|
+
```json
|
|
480
|
+
{ "table": "film", "column": "minutes", "changes": { "dense": false } }
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
Answer:
|
|
484
|
+
|
|
485
|
+
```json
|
|
486
|
+
{
|
|
487
|
+
"ok": true,
|
|
488
|
+
"op": "alter_column",
|
|
489
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
490
|
+
"commits": [
|
|
491
|
+
{ "table": "main.public.film", "version": 14, "operation": "CHANGE COLUMN" }
|
|
492
|
+
],
|
|
493
|
+
"migration_id": "20261012T071947-alter_column-alter-column-minutes-in-film-4"
|
|
494
|
+
}
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
`datanotes/main/public/film/Nosferatu.md` now reads:
|
|
498
|
+
|
|
499
|
+
```markdown
|
|
500
|
+
---
|
|
501
|
+
table: main.public.film
|
|
502
|
+
id: 01a12efb-5a12-7e85-a3a1-85a2fe3425ec
|
|
503
|
+
title: Nosferatu
|
|
504
|
+
year: 1922
|
|
505
|
+
favourite: false
|
|
506
|
+
status: watched
|
|
507
|
+
minutes: null
|
|
508
|
+
---
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
## Rename a column
|
|
512
|
+
|
|
513
|
+
`rename_column` renames the key in `metastore.yaml` and on every row.
|
|
514
|
+
|
|
515
|
+
**`rename_column`**
|
|
516
|
+
|
|
517
|
+
```json
|
|
518
|
+
{ "table": "film", "from_column": "favourite", "to_column": "loved_it" }
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
Answer:
|
|
522
|
+
|
|
523
|
+
```json
|
|
524
|
+
{
|
|
525
|
+
"ok": true,
|
|
526
|
+
"op": "rename_column",
|
|
527
|
+
"notes_changed": 2,
|
|
528
|
+
"planned_edits": [
|
|
529
|
+
{
|
|
530
|
+
"path": "datanotes/main/public/film/Metropolis.md",
|
|
531
|
+
"set": { "loved_it": false },
|
|
532
|
+
"remove": ["favourite"]
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
"path": "datanotes/main/public/film/Nosferatu.md",
|
|
536
|
+
"set": { "loved_it": false },
|
|
537
|
+
"remove": ["favourite"]
|
|
538
|
+
}
|
|
539
|
+
],
|
|
540
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
541
|
+
"commits": [
|
|
542
|
+
{ "table": "main.public.film", "version": 15, "operation": "RENAME COLUMN" }
|
|
543
|
+
],
|
|
544
|
+
"migration_id": "20261012T071947-rename_column-rename-column-favourite-to-lov"
|
|
545
|
+
}
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
**`select_rows`**
|
|
549
|
+
|
|
550
|
+
```json
|
|
551
|
+
{ "table": "film", "columns": ["title", "loved_it"] }
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
Answer:
|
|
555
|
+
|
|
556
|
+
```json
|
|
557
|
+
{
|
|
558
|
+
"table": "main.public.film",
|
|
559
|
+
"total": 2,
|
|
560
|
+
"rows": [
|
|
561
|
+
{
|
|
562
|
+
"path": "datanotes/main/public/film/Metropolis.md",
|
|
563
|
+
"title": "Metropolis",
|
|
564
|
+
"loved_it": false
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
"path": "datanotes/main/public/film/Nosferatu.md",
|
|
568
|
+
"title": "Nosferatu",
|
|
569
|
+
"loved_it": false
|
|
570
|
+
}
|
|
571
|
+
]
|
|
572
|
+
}
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
## Drop a column and undo the drop
|
|
576
|
+
|
|
577
|
+
`drop_column` removes the column and its values from every row. The journal keeps the values, so `undo_migration` brings them back.
|
|
578
|
+
|
|
579
|
+
**`drop_column`**
|
|
580
|
+
|
|
581
|
+
```json
|
|
582
|
+
{ "table": "film", "column": "loved_it" }
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
Answer:
|
|
586
|
+
|
|
587
|
+
```json
|
|
588
|
+
{
|
|
589
|
+
"ok": true,
|
|
590
|
+
"op": "drop_column",
|
|
591
|
+
"notes_changed": 2,
|
|
592
|
+
"planned_edits": [
|
|
593
|
+
{
|
|
594
|
+
"path": "datanotes/main/public/film/Metropolis.md",
|
|
595
|
+
"set": {},
|
|
596
|
+
"remove": ["loved_it"]
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
"path": "datanotes/main/public/film/Nosferatu.md",
|
|
600
|
+
"set": {},
|
|
601
|
+
"remove": ["loved_it"]
|
|
602
|
+
}
|
|
603
|
+
],
|
|
604
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
605
|
+
"commits": [{ "table": "main.public.film", "version": 16, "operation": "DROP COLUMNS" }],
|
|
606
|
+
"migration_id": "20261012T071947-drop_column-drop-column-loved_it-from-film"
|
|
607
|
+
}
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
**`undo_migration`**
|
|
611
|
+
|
|
612
|
+
```json
|
|
613
|
+
{ "migration_id": "20261012T071947-drop_column-drop-column-loved_it-from-film" }
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
Answer:
|
|
617
|
+
|
|
618
|
+
```json
|
|
619
|
+
{
|
|
620
|
+
"ok": true,
|
|
621
|
+
"op": "undo_migration",
|
|
622
|
+
"undoes": "20261012T071947-drop_column-drop-column-loved_it-from-film",
|
|
623
|
+
"migration_id": "20261012T071947-undo_migration-undo-20261012t071947-drop_colu",
|
|
624
|
+
"conflicts": [],
|
|
625
|
+
"failed": []
|
|
626
|
+
}
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
The values are back:
|
|
630
|
+
|
|
631
|
+
**`select_rows`**
|
|
632
|
+
|
|
633
|
+
```json
|
|
634
|
+
{ "table": "film", "columns": ["title", "loved_it"] }
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
Answer:
|
|
638
|
+
|
|
639
|
+
```json
|
|
640
|
+
{
|
|
641
|
+
"table": "main.public.film",
|
|
642
|
+
"total": 2,
|
|
643
|
+
"rows": [
|
|
644
|
+
{
|
|
645
|
+
"path": "datanotes/main/public/film/Metropolis.md",
|
|
646
|
+
"title": "Metropolis",
|
|
647
|
+
"loved_it": false
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
"path": "datanotes/main/public/film/Nosferatu.md",
|
|
651
|
+
"title": "Nosferatu",
|
|
652
|
+
"loved_it": false
|
|
653
|
+
}
|
|
654
|
+
]
|
|
655
|
+
}
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
## Default to today’s date or the current time
|
|
659
|
+
|
|
660
|
+
As a default, `"{today}"` gives a date and `"{now}"` gives a date-time. The engine evaluates them when it writes the row.
|
|
661
|
+
|
|
662
|
+
**`add_column`**
|
|
663
|
+
|
|
664
|
+
```json
|
|
665
|
+
{
|
|
666
|
+
"table": "film",
|
|
667
|
+
"column": {
|
|
668
|
+
"name": "added",
|
|
669
|
+
"type": "date",
|
|
670
|
+
"default": "{today}",
|
|
671
|
+
"description": "The day I put the film on my list"
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
Answer:
|
|
677
|
+
|
|
678
|
+
```json
|
|
679
|
+
{
|
|
680
|
+
"ok": true,
|
|
681
|
+
"op": "add_column",
|
|
682
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
683
|
+
"commits": [{ "table": "main.public.film", "version": 18, "operation": "ADD COLUMNS" }],
|
|
684
|
+
"migration_id": "20261012T071947-add_column-add-column-added-to-film"
|
|
685
|
+
}
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
**`add_column`**
|
|
689
|
+
|
|
690
|
+
```json
|
|
691
|
+
{
|
|
692
|
+
"table": "film",
|
|
693
|
+
"column": {
|
|
694
|
+
"name": "added_at",
|
|
695
|
+
"type": "datetime",
|
|
696
|
+
"default": "{now}",
|
|
697
|
+
"description": "When I put the film on my list"
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
**`insert_row`**
|
|
703
|
+
|
|
704
|
+
```json
|
|
705
|
+
{ "table": "film", "values": { "title": "Sherlock Jr.", "year": 1924 } }
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
Answer:
|
|
709
|
+
|
|
710
|
+
```json
|
|
711
|
+
{
|
|
712
|
+
"ok": true,
|
|
713
|
+
"op": "insert_row",
|
|
714
|
+
"files_created": 1,
|
|
715
|
+
"planned_creates": ["datanotes/main/public/film/Sherlock Jr..md"],
|
|
716
|
+
"commits": [{ "table": "main.public.film", "version": 20, "operation": "WRITE" }],
|
|
717
|
+
"migration_id": "20261012T071947-insert_row-insert-into-film"
|
|
718
|
+
}
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
`datanotes/main/public/film/Sherlock Jr..md` now reads:
|
|
722
|
+
|
|
723
|
+
```markdown
|
|
724
|
+
---
|
|
725
|
+
table: main.public.film
|
|
726
|
+
id: 01a12efb-5b24-7686-937c-757e0d7b754a
|
|
727
|
+
loved_it: false
|
|
728
|
+
title: Sherlock Jr.
|
|
729
|
+
year: 1924
|
|
730
|
+
added: 2026-10-12
|
|
731
|
+
added_at: 2026-10-12T07:19:47Z
|
|
732
|
+
---
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
## Store lists and structured values
|
|
736
|
+
|
|
737
|
+
A `list` column holds several values of its `items` type, and the engine checks each one. A `json` column stores any YAML Ain’t Markup Language (YAML) value as is, without checks, such as objects or lists of objects. Its name comes from JavaScript Object Notation (JSON).
|
|
738
|
+
|
|
739
|
+
**`add_column`**
|
|
740
|
+
|
|
741
|
+
```json
|
|
742
|
+
{
|
|
743
|
+
"table": "film",
|
|
744
|
+
"column": {
|
|
745
|
+
"name": "seen_at",
|
|
746
|
+
"type": "list",
|
|
747
|
+
"items": "enum",
|
|
748
|
+
"values": ["cinema", "home", "outdoor screening"],
|
|
749
|
+
"description": "Every kind of place I saw the film in"
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
Answer:
|
|
755
|
+
|
|
756
|
+
```json
|
|
757
|
+
{
|
|
758
|
+
"ok": true,
|
|
759
|
+
"op": "add_column",
|
|
760
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
761
|
+
"commits": [{ "table": "main.public.film", "version": 21, "operation": "ADD COLUMNS" }],
|
|
762
|
+
"migration_id": "20261012T071947-add_column-add-column-seen_at-to-film"
|
|
763
|
+
}
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
**`add_column`**
|
|
767
|
+
|
|
768
|
+
```json
|
|
769
|
+
{
|
|
770
|
+
"table": "film",
|
|
771
|
+
"column": {
|
|
772
|
+
"name": "viewings",
|
|
773
|
+
"type": "json",
|
|
774
|
+
"description": "Each viewing: when, where, and a note on it"
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
Answer:
|
|
780
|
+
|
|
781
|
+
```json
|
|
782
|
+
{
|
|
783
|
+
"ok": true,
|
|
784
|
+
"op": "add_column",
|
|
785
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
786
|
+
"commits": [{ "table": "main.public.film", "version": 22, "operation": "ADD COLUMNS" }],
|
|
787
|
+
"migration_id": "20261012T071947-add_column-add-column-viewings-to-film"
|
|
788
|
+
}
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
**`update_row`**
|
|
792
|
+
|
|
793
|
+
```json
|
|
794
|
+
{
|
|
795
|
+
"table": "film",
|
|
796
|
+
"path": "datanotes/main/public/film/Nosferatu.md",
|
|
797
|
+
"set": {
|
|
798
|
+
"seen_at": ["outdoor screening", "home"],
|
|
799
|
+
"viewings": [
|
|
800
|
+
{ "on": "2026-07-18", "where": "outdoor screening", "note": "live piano" },
|
|
801
|
+
{
|
|
802
|
+
"on": "2026-10-02",
|
|
803
|
+
"where": "home",
|
|
804
|
+
"note": "the shadow on the stairs still works"
|
|
805
|
+
}
|
|
806
|
+
]
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
Answer:
|
|
812
|
+
|
|
813
|
+
```json
|
|
814
|
+
{
|
|
815
|
+
"ok": true,
|
|
816
|
+
"op": "update_row",
|
|
817
|
+
"notes_changed": 1,
|
|
818
|
+
"planned_edits": [
|
|
819
|
+
{
|
|
820
|
+
"path": "datanotes/main/public/film/Nosferatu.md",
|
|
821
|
+
"set": {
|
|
822
|
+
"seen_at": ["outdoor screening", "home"],
|
|
823
|
+
"viewings": [
|
|
824
|
+
{ "on": "2026-07-18", "where": "outdoor screening", "note": "live piano" },
|
|
825
|
+
{
|
|
826
|
+
"on": "2026-10-02",
|
|
827
|
+
"where": "home",
|
|
828
|
+
"note": "the shadow on the stairs still works"
|
|
829
|
+
}
|
|
830
|
+
]
|
|
831
|
+
},
|
|
832
|
+
"remove": []
|
|
833
|
+
}
|
|
834
|
+
],
|
|
835
|
+
"commits": [{ "table": "main.public.film", "version": 23, "operation": "UPDATE" }],
|
|
836
|
+
"migration_id": "20261012T071947-update_row-update-film-3"
|
|
837
|
+
}
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
`datanotes/main/public/film/Nosferatu.md` now reads:
|
|
841
|
+
|
|
842
|
+
```markdown
|
|
843
|
+
---
|
|
844
|
+
table: main.public.film
|
|
845
|
+
id: 01a12efb-5a12-7e85-a3a1-85a2fe3425ec
|
|
846
|
+
title: Nosferatu
|
|
847
|
+
year: 1922
|
|
848
|
+
loved_it: false
|
|
849
|
+
status: watched
|
|
850
|
+
minutes: null
|
|
851
|
+
seen_at:
|
|
852
|
+
- outdoor screening
|
|
853
|
+
- home
|
|
854
|
+
viewings:
|
|
855
|
+
- on: 2026-07-18
|
|
856
|
+
where: outdoor screening
|
|
857
|
+
note: live piano
|
|
858
|
+
- on: 2026-10-02
|
|
859
|
+
where: home
|
|
860
|
+
note: the shadow on the stairs still works
|
|
861
|
+
---
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
One wrong item makes the engine refuse the whole write:
|
|
865
|
+
|
|
866
|
+
**`update_row`**
|
|
867
|
+
|
|
868
|
+
```json
|
|
869
|
+
{
|
|
870
|
+
"table": "film",
|
|
871
|
+
"path": "datanotes/main/public/film/Metropolis.md",
|
|
872
|
+
"set": { "seen_at": ["home", "festival"] }
|
|
873
|
+
}
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
Refused:
|
|
877
|
+
|
|
878
|
+
```text
|
|
879
|
+
update_row refused — nothing was written:
|
|
880
|
+
invalid datanotes/main/public/film/Metropolis.md: seen_at[1] must be one of: cinema; home; outdoor screening
|
|
881
|
+
Hint: Use one of the allowed values listed in the message, written exactly; describe_table shows what each one means. To add a value, alter_column the enum. Then call update_row again (dry_run: true checks without writing).
|
|
882
|
+
Docs: https://datanotes.dev/guides/database-objects/columns.md#describe-what-each-enum-value-means (offline: read_docs("guides/database-objects/columns#describe-what-each-enum-value-means"))
|
|
883
|
+
```
|
|
884
|
+
|
|
885
|
+
## Compute a column from other columns
|
|
886
|
+
|
|
887
|
+
The engine computes a `generated` column from other columns on every write, and you can’t set it yourself. The expression takes one of three forms:
|
|
888
|
+
|
|
889
|
+
* **A column name**: the value of that column
|
|
890
|
+
* **`date(col)`**: the date of `col`, with an optional day shift
|
|
891
|
+
* **A template**: text with `{col}` and `{col:FORMAT}` placeholders
|
|
892
|
+
|
|
893
|
+
`min(…)`, `max(…)` and `coalesce(…)` pick among several columns.
|
|
894
|
+
|
|
895
|
+
**`create_table`**
|
|
896
|
+
|
|
897
|
+
```json
|
|
898
|
+
{
|
|
899
|
+
"name": "watch",
|
|
900
|
+
"columns": [
|
|
901
|
+
{
|
|
902
|
+
"name": "film",
|
|
903
|
+
"type": "string",
|
|
904
|
+
"required": true,
|
|
905
|
+
"description": "The film I watched"
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
"name": "occurred_at",
|
|
909
|
+
"type": "datetime",
|
|
910
|
+
"required": true,
|
|
911
|
+
"description": "When the film started"
|
|
912
|
+
},
|
|
913
|
+
{
|
|
914
|
+
"name": "where",
|
|
915
|
+
"type": "enum",
|
|
916
|
+
"values": ["cinema", "home", "outdoor screening"],
|
|
917
|
+
"description": "The kind of place I watched it in"
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
"name": "day",
|
|
921
|
+
"type": "date",
|
|
922
|
+
"generated": "date(occurred_at)",
|
|
923
|
+
"description": "The day of the viewing, for daily notes"
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
"name": "label",
|
|
927
|
+
"type": "string",
|
|
928
|
+
"generated": "{film} ({occurred_at:DD/MM HH:mm}, {where})",
|
|
929
|
+
"description": "A one-line summary of the viewing, for lists"
|
|
930
|
+
}
|
|
931
|
+
],
|
|
932
|
+
"time_column": "occurred_at",
|
|
933
|
+
"description": "Each time I watch a film"
|
|
934
|
+
}
|
|
935
|
+
```
|
|
936
|
+
|
|
937
|
+
Answer:
|
|
938
|
+
|
|
939
|
+
```json
|
|
940
|
+
{
|
|
941
|
+
"ok": true,
|
|
942
|
+
"op": "create_table",
|
|
943
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
944
|
+
"commits": [{ "table": "main.public.watch", "version": 0, "operation": "CREATE TABLE" }],
|
|
945
|
+
"migration_id": "20261012T071947-create_table-create-table-main-public-watch"
|
|
946
|
+
}
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
**`insert_row`**
|
|
950
|
+
|
|
951
|
+
```json
|
|
952
|
+
{
|
|
953
|
+
"table": "watch",
|
|
954
|
+
"values": {
|
|
955
|
+
"film": "His Girl Friday",
|
|
956
|
+
"occurred_at": "2026-10-03T21:30",
|
|
957
|
+
"where": "home"
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
Answer:
|
|
963
|
+
|
|
964
|
+
```json
|
|
965
|
+
{
|
|
966
|
+
"ok": true,
|
|
967
|
+
"op": "insert_row",
|
|
968
|
+
"files_created": 1,
|
|
969
|
+
"planned_creates": ["datanotes/main/public/watch/2026-10-03T2130.md"],
|
|
970
|
+
"commits": [{ "table": "main.public.watch", "version": 1, "operation": "WRITE" }],
|
|
971
|
+
"migration_id": "20261012T071947-insert_row-insert-into-watch"
|
|
972
|
+
}
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
`datanotes/main/public/watch/2026-10-03T2130.md` now reads:
|
|
976
|
+
|
|
977
|
+
```markdown
|
|
978
|
+
---
|
|
979
|
+
table: main.public.watch
|
|
980
|
+
id: 01a12efb-5b61-7abf-9cb9-45a1f5a8f839
|
|
981
|
+
film: His Girl Friday
|
|
982
|
+
occurred_at: 2026-10-03T21:30
|
|
983
|
+
where: home
|
|
984
|
+
day: 2026-10-03
|
|
985
|
+
label: His Girl Friday (03/10 21:30, home)
|
|
986
|
+
---
|
|
987
|
+
```
|
|
988
|
+
|
|
989
|
+
The engine refuses a value for a generated column:
|
|
990
|
+
|
|
991
|
+
**`insert_row`**
|
|
992
|
+
|
|
993
|
+
```json
|
|
994
|
+
{
|
|
995
|
+
"table": "watch",
|
|
996
|
+
"values": {
|
|
997
|
+
"film": "His Girl Friday",
|
|
998
|
+
"occurred_at": "2026-10-04T21:30",
|
|
999
|
+
"day": "2026-10-01"
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
Refused:
|
|
1005
|
+
|
|
1006
|
+
```text
|
|
1007
|
+
"day" is a generated column: its value is computed
|
|
1008
|
+
Hint: Leave generated and identity columns out of the values: the engine computes them (recompute_columns computes them again).
|
|
1009
|
+
Docs: https://datanotes.dev/guides/database-objects/columns.md#compute-a-column-from-other-columns (offline: read_docs("guides/database-objects/columns#compute-a-column-from-other-columns"))
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
`time_column` names each row’s file after that date-time: here, the time the viewing started. The name follows the database’s `filename` format, `YYYY-MM-DD[T]HHmm` by default.
|
|
1013
|
+
|
|
1014
|
+
## Let the engine fill identity columns
|
|
1015
|
+
|
|
1016
|
+
The engine fills an `identity` column on its own. `filename` keeps the note’s file name, and `created` records when the engine created the row and never changes. Every table also has `id`, the identity column that holds the row’s universally unique identifier (UUID). The meaning of `id` and `created` never changes, so the engine writes their descriptions too.
|
|
1017
|
+
|
|
1018
|
+
**`add_column`**
|
|
1019
|
+
|
|
1020
|
+
```json
|
|
1021
|
+
{
|
|
1022
|
+
"table": "film",
|
|
1023
|
+
"column": {
|
|
1024
|
+
"name": "note_name",
|
|
1025
|
+
"type": "string",
|
|
1026
|
+
"identity": "filename",
|
|
1027
|
+
"description": "The name of the film's note, kept as the file is renamed"
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
Answer:
|
|
1033
|
+
|
|
1034
|
+
```json
|
|
1035
|
+
{
|
|
1036
|
+
"ok": true,
|
|
1037
|
+
"op": "add_column",
|
|
1038
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
1039
|
+
"commits": [{ "table": "main.public.film", "version": 24, "operation": "ADD COLUMNS" }],
|
|
1040
|
+
"migration_id": "20261012T071947-add_column-add-column-note_name-to-film"
|
|
1041
|
+
}
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
**`add_column`**
|
|
1045
|
+
|
|
1046
|
+
```json
|
|
1047
|
+
{
|
|
1048
|
+
"table": "film",
|
|
1049
|
+
"column": { "name": "created", "type": "datetime", "identity": "created" }
|
|
1050
|
+
}
|
|
1051
|
+
```
|
|
1052
|
+
|
|
1053
|
+
Answer:
|
|
1054
|
+
|
|
1055
|
+
```json
|
|
1056
|
+
{
|
|
1057
|
+
"ok": true,
|
|
1058
|
+
"op": "add_column",
|
|
1059
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
1060
|
+
"commits": [{ "table": "main.public.film", "version": 25, "operation": "ADD COLUMNS" }],
|
|
1061
|
+
"migration_id": "20261012T071947-add_column-add-column-created-to-film"
|
|
1062
|
+
}
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
**`insert_row`**
|
|
1066
|
+
|
|
1067
|
+
```json
|
|
1068
|
+
{ "table": "film", "values": { "title": "Charade", "year": 1963 } }
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
Answer:
|
|
1072
|
+
|
|
1073
|
+
```json
|
|
1074
|
+
{
|
|
1075
|
+
"ok": true,
|
|
1076
|
+
"op": "insert_row",
|
|
1077
|
+
"files_created": 1,
|
|
1078
|
+
"planned_creates": ["datanotes/main/public/film/Charade.md"],
|
|
1079
|
+
"commits": [{ "table": "main.public.film", "version": 26, "operation": "WRITE" }],
|
|
1080
|
+
"migration_id": "20261012T071947-insert_row-insert-into-film-2"
|
|
1081
|
+
}
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
`datanotes/main/public/film/Charade.md` now reads:
|
|
1085
|
+
|
|
1086
|
+
```markdown
|
|
1087
|
+
---
|
|
1088
|
+
table: main.public.film
|
|
1089
|
+
id: 01a12efb-5b90-78eb-ba11-d2990af01451
|
|
1090
|
+
loved_it: false
|
|
1091
|
+
title: Charade
|
|
1092
|
+
year: 1963
|
|
1093
|
+
added: 2026-10-12
|
|
1094
|
+
added_at: 2026-10-12T07:19:47Z
|
|
1095
|
+
note_name: Charade
|
|
1096
|
+
created: 2026-10-12T07:19:47Z
|
|
1097
|
+
---
|
|
1098
|
+
```
|
|
1099
|
+
|
|
1100
|
+
## Partition rows into folders
|
|
1101
|
+
|
|
1102
|
+
`options.partition` puts rows in subfolders named from their values. A `{column:FORMAT}` placeholder formats a date, so here each actor goes in the folder of their birth year. A table of events could use one folder per year and month, with `{occurred_at:YYYY/MM}`.
|
|
1103
|
+
|
|
1104
|
+
**`create_table`**
|
|
1105
|
+
|
|
1106
|
+
```json
|
|
1107
|
+
{
|
|
1108
|
+
"name": "actor",
|
|
1109
|
+
"description": "Actors of the films I watch",
|
|
1110
|
+
"columns": [
|
|
1111
|
+
{
|
|
1112
|
+
"name": "name",
|
|
1113
|
+
"type": "string",
|
|
1114
|
+
"required": true,
|
|
1115
|
+
"description": "The actor's name, as credited"
|
|
1116
|
+
},
|
|
1117
|
+
{
|
|
1118
|
+
"name": "born",
|
|
1119
|
+
"type": "date",
|
|
1120
|
+
"required": true,
|
|
1121
|
+
"description": "The actor's date of birth"
|
|
1122
|
+
}
|
|
1123
|
+
],
|
|
1124
|
+
"filename": "{name}",
|
|
1125
|
+
"partition": "{born:YYYY}"
|
|
1126
|
+
}
|
|
1127
|
+
```
|
|
1128
|
+
|
|
1129
|
+
Answer:
|
|
1130
|
+
|
|
1131
|
+
```json
|
|
1132
|
+
{
|
|
1133
|
+
"ok": true,
|
|
1134
|
+
"op": "create_table",
|
|
1135
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
1136
|
+
"commits": [{ "table": "main.public.actor", "version": 0, "operation": "CREATE TABLE" }],
|
|
1137
|
+
"migration_id": "20261012T071948-create_table-create-table-main-public-actor"
|
|
1138
|
+
}
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
**`insert_row`**
|
|
1142
|
+
|
|
1143
|
+
```json
|
|
1144
|
+
{ "table": "actor", "values": { "name": "Cary Grant", "born": "1904-01-18" } }
|
|
1145
|
+
```
|
|
1146
|
+
|
|
1147
|
+
Answer:
|
|
1148
|
+
|
|
1149
|
+
```json
|
|
1150
|
+
{
|
|
1151
|
+
"ok": true,
|
|
1152
|
+
"op": "insert_row",
|
|
1153
|
+
"files_created": 1,
|
|
1154
|
+
"planned_creates": ["datanotes/main/public/actor/1904/Cary Grant.md"],
|
|
1155
|
+
"commits": [{ "table": "main.public.actor", "version": 1, "operation": "WRITE" }],
|
|
1156
|
+
"migration_id": "20261012T071948-insert_row-insert-into-actor"
|
|
1157
|
+
}
|
|
1158
|
+
```
|
|
1159
|
+
|
|
1160
|
+
**`insert_row`**
|
|
1161
|
+
|
|
1162
|
+
```json
|
|
1163
|
+
{ "table": "actor", "values": { "name": "Audrey Hepburn", "born": "1929-05-04" } }
|
|
1164
|
+
```
|
|
1165
|
+
|
|
1166
|
+
**`list_folder`**
|
|
1167
|
+
|
|
1168
|
+
```json
|
|
1169
|
+
{ "folder": "datanotes/main/public/actor" }
|
|
1170
|
+
```
|
|
1171
|
+
|
|
1172
|
+
Answer:
|
|
1173
|
+
|
|
1174
|
+
```json
|
|
1175
|
+
{
|
|
1176
|
+
"folder": "datanotes/main/public/actor",
|
|
1177
|
+
"folders": ["datanotes/main/public/actor/1904", "datanotes/main/public/actor/1929"],
|
|
1178
|
+
"files": []
|
|
1179
|
+
}
|
|
1180
|
+
```
|
|
1181
|
+
|
|
1182
|
+
## See the column definitions in `metastore.yaml`
|
|
1183
|
+
|
|
1184
|
+
The engine wrote this file with the columns from this page, in the operations’ words. Two things have a place of their own:
|
|
1185
|
+
|
|
1186
|
+
* **`time_column`**: the table’s `filename` pattern under `options`
|
|
1187
|
+
* **`partition`**: under `options`, next to `filename`
|
|
1188
|
+
|
|
1189
|
+
`datanotes/metastore.yaml` now reads:
|
|
1190
|
+
|
|
1191
|
+
```yaml
|
|
1192
|
+
# Metastore (Datanotes): catalogs, schemas, tables, columns, views.
|
|
1193
|
+
id: 01a12efb-5a06-7f54-8618-acdaa56c69f9
|
|
1194
|
+
config:
|
|
1195
|
+
history: true
|
|
1196
|
+
|
|
1197
|
+
|
|
1198
|
+
catalogs:
|
|
1199
|
+
main:
|
|
1200
|
+
schemas:
|
|
1201
|
+
public:
|
|
1202
|
+
tables:
|
|
1203
|
+
film:
|
|
1204
|
+
description: Classic films I watched or want to watch
|
|
1205
|
+
options:
|
|
1206
|
+
filename: "{title}"
|
|
1207
|
+
archive: _archive
|
|
1208
|
+
columns:
|
|
1209
|
+
- name: id
|
|
1210
|
+
type: string
|
|
1211
|
+
required: true
|
|
1212
|
+
description: "Unique id of the row (UUIDv7): set when the row is created, never changes"
|
|
1213
|
+
unique: true
|
|
1214
|
+
identity: uuid
|
|
1215
|
+
dense: true
|
|
1216
|
+
- name: title
|
|
1217
|
+
type: string
|
|
1218
|
+
required: true
|
|
1219
|
+
description: The film's title, as on its poster
|
|
1220
|
+
- name: year
|
|
1221
|
+
type: integer
|
|
1222
|
+
description: Year the film first came out
|
|
1223
|
+
- name: minutes
|
|
1224
|
+
type: integer
|
|
1225
|
+
min: 1
|
|
1226
|
+
label: Runtime
|
|
1227
|
+
description: Running time of the commonly cited version
|
|
1228
|
+
aliases:
|
|
1229
|
+
- runtime
|
|
1230
|
+
- length
|
|
1231
|
+
note: "[[How I count minutes]]"
|
|
1232
|
+
- name: loved_it
|
|
1233
|
+
type: boolean
|
|
1234
|
+
default: false
|
|
1235
|
+
description: True for the films I would watch again and again
|
|
1236
|
+
dense: true
|
|
1237
|
+
- name: status
|
|
1238
|
+
type: enum
|
|
1239
|
+
values:
|
|
1240
|
+
- to watch
|
|
1241
|
+
- watched
|
|
1242
|
+
value_descriptions:
|
|
1243
|
+
to watch: on my list, not seen yet
|
|
1244
|
+
watched: seen to the end
|
|
1245
|
+
description: Where the film is on my list
|
|
1246
|
+
ordered: true
|
|
1247
|
+
- name: added
|
|
1248
|
+
type: date
|
|
1249
|
+
default: "{today}"
|
|
1250
|
+
description: The day I put the film on my list
|
|
1251
|
+
- name: added_at
|
|
1252
|
+
type: datetime
|
|
1253
|
+
default: "{now}"
|
|
1254
|
+
description: When I put the film on my list
|
|
1255
|
+
- name: seen_at
|
|
1256
|
+
type: list
|
|
1257
|
+
items: enum
|
|
1258
|
+
values:
|
|
1259
|
+
- cinema
|
|
1260
|
+
- home
|
|
1261
|
+
- outdoor screening
|
|
1262
|
+
description: Every kind of place I saw the film in
|
|
1263
|
+
- name: viewings
|
|
1264
|
+
type: json
|
|
1265
|
+
description: "Each viewing: when, where, and a note on it"
|
|
1266
|
+
- name: note_name
|
|
1267
|
+
type: string
|
|
1268
|
+
description: The name of the film's note, kept as the file is renamed
|
|
1269
|
+
identity: filename
|
|
1270
|
+
- name: created
|
|
1271
|
+
type: datetime
|
|
1272
|
+
description: "When the row was created: set by the engine, never changes"
|
|
1273
|
+
identity: created
|
|
1274
|
+
watch:
|
|
1275
|
+
description: Each time I watch a film
|
|
1276
|
+
options:
|
|
1277
|
+
filename: "{occurred_at:YYYY-MM-DD[T]HHmm}"
|
|
1278
|
+
archive: _archive
|
|
1279
|
+
columns:
|
|
1280
|
+
- name: id
|
|
1281
|
+
type: string
|
|
1282
|
+
required: true
|
|
1283
|
+
description: "Unique id of the row (UUIDv7): set when the row is created, never changes"
|
|
1284
|
+
unique: true
|
|
1285
|
+
identity: uuid
|
|
1286
|
+
dense: true
|
|
1287
|
+
- name: film
|
|
1288
|
+
type: string
|
|
1289
|
+
required: true
|
|
1290
|
+
description: The film I watched
|
|
1291
|
+
- name: occurred_at
|
|
1292
|
+
type: datetime
|
|
1293
|
+
required: true
|
|
1294
|
+
description: When the film started
|
|
1295
|
+
- name: where
|
|
1296
|
+
type: enum
|
|
1297
|
+
values:
|
|
1298
|
+
- cinema
|
|
1299
|
+
- home
|
|
1300
|
+
- outdoor screening
|
|
1301
|
+
description: The kind of place I watched it in
|
|
1302
|
+
- name: day
|
|
1303
|
+
type: date
|
|
1304
|
+
description: The day of the viewing, for daily notes
|
|
1305
|
+
generated: date(occurred_at)
|
|
1306
|
+
- name: label
|
|
1307
|
+
type: string
|
|
1308
|
+
description: A one-line summary of the viewing, for lists
|
|
1309
|
+
generated: "{film} ({occurred_at:DD/MM HH:mm}, {where})"
|
|
1310
|
+
actor:
|
|
1311
|
+
description: Actors of the films I watch
|
|
1312
|
+
options:
|
|
1313
|
+
partition: "{born:YYYY}"
|
|
1314
|
+
filename: "{name}"
|
|
1315
|
+
archive: _archive
|
|
1316
|
+
columns:
|
|
1317
|
+
- name: id
|
|
1318
|
+
type: string
|
|
1319
|
+
required: true
|
|
1320
|
+
description: "Unique id of the row (UUIDv7): set when the row is created, never changes"
|
|
1321
|
+
unique: true
|
|
1322
|
+
identity: uuid
|
|
1323
|
+
dense: true
|
|
1324
|
+
- name: name
|
|
1325
|
+
type: string
|
|
1326
|
+
required: true
|
|
1327
|
+
description: The actor's name, as credited
|
|
1328
|
+
- name: born
|
|
1329
|
+
type: date
|
|
1330
|
+
required: true
|
|
1331
|
+
description: The actor's date of birth
|
|
1332
|
+
```
|