datanotes 2.0.1 → 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 +62511 -28069
- 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 +58 -14
|
@@ -0,0 +1,1454 @@
|
|
|
1
|
+
# Relations and hierarchies
|
|
2
|
+
|
|
3
|
+
> Link rows to rows of other tables, keep two-way links in step, take allowed values from a table, build trees and limit link targets.
|
|
4
|
+
|
|
5
|
+
A link column holds a wikilink to a note, such as `[[Charade]]`, and `ref` names the table that note must be a row of. The link points to a row of the other table, and you reach that row’s columns through the link.
|
|
6
|
+
|
|
7
|
+
The examples on this page share one diary of classic films, with actors, films, viewings, tags and genres.
|
|
8
|
+
|
|
9
|
+
## Point a column to a row of another table
|
|
10
|
+
|
|
11
|
+
A viewing is of one film, so its `film` column is a `link` whose `ref` is the film table. The value must link to a note that is a row of `film`.
|
|
12
|
+
|
|
13
|
+
**`create_table`**
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"name": "watch",
|
|
18
|
+
"description": "Each time I watch a film",
|
|
19
|
+
"columns": [
|
|
20
|
+
{
|
|
21
|
+
"name": "film",
|
|
22
|
+
"type": "link",
|
|
23
|
+
"ref": "main.public.film",
|
|
24
|
+
"required": true,
|
|
25
|
+
"description": "The film I watched"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "where",
|
|
29
|
+
"type": "enum",
|
|
30
|
+
"values": ["cinema", "home", "outdoor screening"],
|
|
31
|
+
"required": true,
|
|
32
|
+
"description": "The kind of place I watched it in"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "place",
|
|
36
|
+
"type": "string",
|
|
37
|
+
"description": "The cinema, park or room, in my words"
|
|
38
|
+
},
|
|
39
|
+
{ "name": "with", "type": "string", "description": "Who watched it with me" },
|
|
40
|
+
{
|
|
41
|
+
"name": "occurred_at",
|
|
42
|
+
"type": "datetime",
|
|
43
|
+
"required": true,
|
|
44
|
+
"description": "When the film started"
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"time_column": "occurred_at"
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Answer:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"ok": true,
|
|
56
|
+
"op": "create_table",
|
|
57
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
58
|
+
"commits": [{ "table": "main.public.watch", "version": 0, "operation": "CREATE TABLE" }],
|
|
59
|
+
"migration_id": "20261012T071949-create_table-create-table-main-public-watch"
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**`insert_row`**
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"table": "watch",
|
|
68
|
+
"values": {
|
|
69
|
+
"film": "[[His Girl Friday]]",
|
|
70
|
+
"where": "cinema",
|
|
71
|
+
"place": "the film club downtown",
|
|
72
|
+
"with": "my sister",
|
|
73
|
+
"occurred_at": "2026-03-14T20:30"
|
|
74
|
+
},
|
|
75
|
+
"body": "A full hall, and everyone laughed at the same lines.\n"
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Answer:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"ok": true,
|
|
84
|
+
"op": "insert_row",
|
|
85
|
+
"files_created": 1,
|
|
86
|
+
"planned_creates": ["datanotes/main/public/watch/2026-03-14T2030.md"],
|
|
87
|
+
"commits": [{ "table": "main.public.watch", "version": 1, "operation": "WRITE" }],
|
|
88
|
+
"migration_id": "20261012T071949-insert_row-insert-into-watch"
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`datanotes/main/public/watch/2026-03-14T2030.md` now reads:
|
|
93
|
+
|
|
94
|
+
```markdown
|
|
95
|
+
---
|
|
96
|
+
table: main.public.watch
|
|
97
|
+
id: 01a12efb-6265-7094-88e9-b7fc57c84b37
|
|
98
|
+
film: "[[His Girl Friday]]"
|
|
99
|
+
where: cinema
|
|
100
|
+
place: the film club downtown
|
|
101
|
+
with: my sister
|
|
102
|
+
occurred_at: 2026-03-14T20:30
|
|
103
|
+
---
|
|
104
|
+
A full hall, and everyone laughed at the same lines.
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**`insert_row`**
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"table": "watch",
|
|
112
|
+
"values": {
|
|
113
|
+
"film": "[[Charade]]",
|
|
114
|
+
"where": "home",
|
|
115
|
+
"place": "my living room",
|
|
116
|
+
"occurred_at": "2026-09-26T21:30"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The engine refuses a link to a note that doesn’t exist (`broken_link`):
|
|
122
|
+
|
|
123
|
+
**`insert_row`**
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"table": "watch",
|
|
128
|
+
"values": { "film": "[[Charde]]", "where": "home", "occurred_at": "2026-09-27T21:00" }
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Refused:
|
|
133
|
+
|
|
134
|
+
```text
|
|
135
|
+
insert_row refused — nothing was written:
|
|
136
|
+
invalid datanotes/main/public/watch/2026-09-27T2100.md: film link does not resolve: Charde
|
|
137
|
+
Hint: Link to a note that exists, as "[[Note name]]": list_link_targets(table, column) lists the rows the column may point to. Then call insert_row again (dry_run: true checks without writing).
|
|
138
|
+
Docs: https://datanotes.dev/guides/database-objects/relations.md#point-a-column-to-a-row-of-another-table (offline: read_docs("guides/database-objects/relations#point-a-column-to-a-row-of-another-table"))
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
It also refuses a link to a note of another table (`ref`), here an actor:
|
|
142
|
+
|
|
143
|
+
**`insert_row`**
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"table": "watch",
|
|
148
|
+
"values": {
|
|
149
|
+
"film": "[[Cary Grant]]",
|
|
150
|
+
"where": "home",
|
|
151
|
+
"occurred_at": "2026-09-27T21:00"
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Refused:
|
|
157
|
+
|
|
158
|
+
```text
|
|
159
|
+
insert_row refused — nothing was written:
|
|
160
|
+
invalid datanotes/main/public/watch/2026-09-27T2100.md: film link must point to a row of table "main.public.film"
|
|
161
|
+
Hint: Link to a row of the table the column points to (its ref) that its ref_where / ref_by allow: list_link_targets(table, column) lists them. Then call insert_row again (dry_run: true checks without writing).
|
|
162
|
+
Docs: https://datanotes.dev/guides/database-objects/relations.md#limit-link-targets-with-ref_by-and-ref_where (offline: read_docs("guides/database-objects/relations#limit-link-targets-with-ref_by-and-ref_where"))
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Link to several rows with a list of links
|
|
166
|
+
|
|
167
|
+
A film has several actors, so `cast` is a `list` of `link` items, and the engine checks each one against `ref`. `inverse: films` names the actor column that links back, and from then on the engine keeps both sides in step. Both columns must exist, and you declare `inverse` on one side only.
|
|
168
|
+
|
|
169
|
+
**`add_column`**
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"table": "actor",
|
|
174
|
+
"column": {
|
|
175
|
+
"name": "films",
|
|
176
|
+
"type": "list",
|
|
177
|
+
"items": "link",
|
|
178
|
+
"ref": "main.public.film",
|
|
179
|
+
"description": "The films the actor plays in"
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Answer:
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"ok": true,
|
|
189
|
+
"op": "add_column",
|
|
190
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
191
|
+
"commits": [{ "table": "main.public.actor", "version": 4, "operation": "ADD COLUMNS" }],
|
|
192
|
+
"migration_id": "20261012T071949-add_column-add-column-films-to-actor"
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**`add_column`**
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{
|
|
200
|
+
"table": "film",
|
|
201
|
+
"column": {
|
|
202
|
+
"name": "cast",
|
|
203
|
+
"type": "list",
|
|
204
|
+
"items": "link",
|
|
205
|
+
"ref": "main.public.actor",
|
|
206
|
+
"inverse": "films",
|
|
207
|
+
"description": "The actors in the film"
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Answer:
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"ok": true,
|
|
217
|
+
"op": "add_column",
|
|
218
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
219
|
+
"commits": [{ "table": "main.public.film", "version": 4, "operation": "ADD COLUMNS" }],
|
|
220
|
+
"migration_id": "20261012T071949-add_column-add-column-cast-to-film"
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**`update_row`**
|
|
225
|
+
|
|
226
|
+
```json
|
|
227
|
+
{
|
|
228
|
+
"table": "film",
|
|
229
|
+
"path": "datanotes/main/public/film/Charade.md",
|
|
230
|
+
"set": { "cast": ["[[Cary Grant]]", "[[Audrey Hepburn]]"] }
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Answer:
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"ok": true,
|
|
239
|
+
"op": "update_row",
|
|
240
|
+
"notes_changed": 3,
|
|
241
|
+
"planned_edits": [
|
|
242
|
+
{
|
|
243
|
+
"path": "datanotes/main/public/film/Charade.md",
|
|
244
|
+
"set": { "cast": ["[[Cary Grant]]", "[[Audrey Hepburn]]"] },
|
|
245
|
+
"remove": []
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"path": "datanotes/main/public/actor/Cary Grant.md",
|
|
249
|
+
"set": { "films": ["[[Charade]]"] },
|
|
250
|
+
"remove": []
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
"path": "datanotes/main/public/actor/Audrey Hepburn.md",
|
|
254
|
+
"set": { "films": ["[[Charade]]"] },
|
|
255
|
+
"remove": []
|
|
256
|
+
}
|
|
257
|
+
],
|
|
258
|
+
"commits": [
|
|
259
|
+
{ "table": "main.public.actor", "version": 5, "operation": "UPDATE" },
|
|
260
|
+
{ "table": "main.public.film", "version": 5, "operation": "UPDATE" }
|
|
261
|
+
],
|
|
262
|
+
"migration_id": "20261012T071949-update_row-update-film"
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
`datanotes/main/public/film/Charade.md` now reads:
|
|
267
|
+
|
|
268
|
+
```markdown
|
|
269
|
+
---
|
|
270
|
+
table: main.public.film
|
|
271
|
+
id: 01a12efb-6245-7d26-b6fb-0820654b699a
|
|
272
|
+
title: Charade
|
|
273
|
+
year: 1963
|
|
274
|
+
minutes: 113
|
|
275
|
+
cast:
|
|
276
|
+
- "[[Cary Grant]]"
|
|
277
|
+
- "[[Audrey Hepburn]]"
|
|
278
|
+
---
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
The same write added the film to both actors, in one operation with one undo:
|
|
282
|
+
|
|
283
|
+
**`read_note`**
|
|
284
|
+
|
|
285
|
+
```json
|
|
286
|
+
{ "path": "datanotes/main/public/actor/Cary Grant.md" }
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Answer:
|
|
290
|
+
|
|
291
|
+
```markdown
|
|
292
|
+
---
|
|
293
|
+
table: main.public.actor
|
|
294
|
+
id: 01a12efb-621d-71b4-a0ec-578ef615f6b0
|
|
295
|
+
name: Cary Grant
|
|
296
|
+
born: 1904-01-18
|
|
297
|
+
films:
|
|
298
|
+
- "[[Charade]]"
|
|
299
|
+
---
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
One wrong item makes the engine refuse the whole list:
|
|
303
|
+
|
|
304
|
+
**`update_row`**
|
|
305
|
+
|
|
306
|
+
```json
|
|
307
|
+
{
|
|
308
|
+
"table": "film",
|
|
309
|
+
"path": "datanotes/main/public/film/His Girl Friday.md",
|
|
310
|
+
"set": { "cast": ["[[Cary Grant]]", "[[Charade]]"] }
|
|
311
|
+
}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Refused:
|
|
315
|
+
|
|
316
|
+
```text
|
|
317
|
+
update_row refused — nothing was written:
|
|
318
|
+
invalid datanotes/main/public/film/His Girl Friday.md: cast[1] link must point to a row of table "main.public.actor"
|
|
319
|
+
Hint: Link to a row of the table the column points to (its ref) that its ref_where / ref_by allow: list_link_targets(table, column) lists them. Then call update_row again (dry_run: true checks without writing).
|
|
320
|
+
Docs: https://datanotes.dev/guides/database-objects/relations.md#limit-link-targets-with-ref_by-and-ref_where (offline: read_docs("guides/database-objects/relations#limit-link-targets-with-ref_by-and-ref_where"))
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**`update_row`**
|
|
324
|
+
|
|
325
|
+
```json
|
|
326
|
+
{
|
|
327
|
+
"table": "film",
|
|
328
|
+
"path": "datanotes/main/public/film/His Girl Friday.md",
|
|
329
|
+
"set": { "cast": ["[[Cary Grant]]", "[[Rosalind Russell]]"] }
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Read linked rows in the same call
|
|
334
|
+
|
|
335
|
+
`expand` replaces each link with the linked row’s columns, so one call returns the films with their actors.
|
|
336
|
+
|
|
337
|
+
**`select_rows`**
|
|
338
|
+
|
|
339
|
+
```json
|
|
340
|
+
{
|
|
341
|
+
"table": "film",
|
|
342
|
+
"columns": ["title", "cast"],
|
|
343
|
+
"expand": ["cast"],
|
|
344
|
+
"where": { "title": "Charade" }
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Answer:
|
|
349
|
+
|
|
350
|
+
```json
|
|
351
|
+
{
|
|
352
|
+
"table": "main.public.film",
|
|
353
|
+
"total": 1,
|
|
354
|
+
"rows": [
|
|
355
|
+
{
|
|
356
|
+
"path": "datanotes/main/public/film/Charade.md",
|
|
357
|
+
"title": "Charade",
|
|
358
|
+
"cast": [
|
|
359
|
+
{
|
|
360
|
+
"path": "datanotes/main/public/actor/Cary Grant.md",
|
|
361
|
+
"table": "main.public.actor",
|
|
362
|
+
"id": "01a12efb-621d-71b4-a0ec-578ef615f6b0",
|
|
363
|
+
"name": "Cary Grant",
|
|
364
|
+
"born": "1904-01-18",
|
|
365
|
+
"films": ["[[Charade]]", "[[His Girl Friday]]"]
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
"path": "datanotes/main/public/actor/Audrey Hepburn.md",
|
|
369
|
+
"table": "main.public.actor",
|
|
370
|
+
"id": "01a12efb-622a-7ee5-b08f-81dd7aa17fb1",
|
|
371
|
+
"name": "Audrey Hepburn",
|
|
372
|
+
"born": "1929-05-04",
|
|
373
|
+
"films": ["[[Charade]]"]
|
|
374
|
+
}
|
|
375
|
+
]
|
|
376
|
+
}
|
|
377
|
+
]
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## Repair two-way links with `align_relations`
|
|
382
|
+
|
|
383
|
+
Writes through the engine keep both sides of an `inverse` link in step, and the engine mirrors links you edit by hand about 2s later. When you declare `inverse` on data that already exists, `align_relations` adds the missing reciprocal links. `check_table` reports the missing ones first, with code `inverse`.
|
|
384
|
+
|
|
385
|
+
**`add_column`**
|
|
386
|
+
|
|
387
|
+
```json
|
|
388
|
+
{
|
|
389
|
+
"table": "film",
|
|
390
|
+
"column": {
|
|
391
|
+
"name": "see_also",
|
|
392
|
+
"type": "list",
|
|
393
|
+
"items": "link",
|
|
394
|
+
"ref": "self",
|
|
395
|
+
"description": "Films to watch next if I liked this one"
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
**`update_row`**
|
|
401
|
+
|
|
402
|
+
```json
|
|
403
|
+
{
|
|
404
|
+
"table": "film",
|
|
405
|
+
"path": "datanotes/main/public/film/Charade.md",
|
|
406
|
+
"set": { "see_also": ["[[His Girl Friday]]"] }
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Charade points to His Girl Friday, but His Girl Friday doesn’t point back. Declare `see_also` symmetric:
|
|
411
|
+
|
|
412
|
+
**`alter_column`**
|
|
413
|
+
|
|
414
|
+
```json
|
|
415
|
+
{ "table": "film", "column": "see_also", "changes": { "inverse": "see_also" } }
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Answer:
|
|
419
|
+
|
|
420
|
+
```json
|
|
421
|
+
{
|
|
422
|
+
"ok": true,
|
|
423
|
+
"op": "alter_column",
|
|
424
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
425
|
+
"commits": [{ "table": "main.public.film", "version": 9, "operation": "CHANGE COLUMN" }],
|
|
426
|
+
"migration_id": "20261012T071949-alter_column-alter-column-see_also-in-film"
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
**`check_table`**
|
|
431
|
+
|
|
432
|
+
```json
|
|
433
|
+
{ "table": "film" }
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
Answer:
|
|
437
|
+
|
|
438
|
+
```json
|
|
439
|
+
{
|
|
440
|
+
"table": "film",
|
|
441
|
+
"rows_checked": 3,
|
|
442
|
+
"total": 1,
|
|
443
|
+
"violations": [
|
|
444
|
+
{
|
|
445
|
+
"path": "datanotes/main/public/film/Charade.md",
|
|
446
|
+
"violations": [
|
|
447
|
+
{
|
|
448
|
+
"column": "see_also",
|
|
449
|
+
"code": "inverse",
|
|
450
|
+
"message": "datanotes/main/public/film/His Girl Friday.md does not link back in \"see_also\" (align_relations adds it)",
|
|
451
|
+
"value": "datanotes/main/public/film/His Girl Friday.md"
|
|
452
|
+
}
|
|
453
|
+
]
|
|
454
|
+
}
|
|
455
|
+
]
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**`align_relations`**
|
|
460
|
+
|
|
461
|
+
```json
|
|
462
|
+
{ "table": "film" }
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
Answer:
|
|
466
|
+
|
|
467
|
+
```json
|
|
468
|
+
{
|
|
469
|
+
"ok": true,
|
|
470
|
+
"op": "align_relations",
|
|
471
|
+
"notes_changed": 1,
|
|
472
|
+
"planned_edits": [
|
|
473
|
+
{
|
|
474
|
+
"path": "datanotes/main/public/film/His Girl Friday.md",
|
|
475
|
+
"set": { "see_also": ["[[Charade]]"] },
|
|
476
|
+
"remove": []
|
|
477
|
+
}
|
|
478
|
+
],
|
|
479
|
+
"commits": [
|
|
480
|
+
{ "table": "main.public.film", "version": 10, "operation": "ALIGN RELATIONS" }
|
|
481
|
+
],
|
|
482
|
+
"migration_id": "20261012T071949-align_relations-align-inverse-links-of-main-pu"
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
**`select_rows`**
|
|
487
|
+
|
|
488
|
+
```json
|
|
489
|
+
{ "table": "film", "columns": ["title", "see_also"] }
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
Answer:
|
|
493
|
+
|
|
494
|
+
```json
|
|
495
|
+
{
|
|
496
|
+
"table": "main.public.film",
|
|
497
|
+
"total": 3,
|
|
498
|
+
"rows": [
|
|
499
|
+
{
|
|
500
|
+
"path": "datanotes/main/public/film/Charade.md",
|
|
501
|
+
"title": "Charade",
|
|
502
|
+
"see_also": ["[[His Girl Friday]]"]
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
"path": "datanotes/main/public/film/His Girl Friday.md",
|
|
506
|
+
"title": "His Girl Friday",
|
|
507
|
+
"see_also": ["[[Charade]]"]
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
"path": "datanotes/main/public/film/Night of the Living Dead.md",
|
|
511
|
+
"title": "Night of the Living Dead",
|
|
512
|
+
"see_also": null
|
|
513
|
+
}
|
|
514
|
+
]
|
|
515
|
+
}
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
## Take allowed values from another table
|
|
519
|
+
|
|
520
|
+
`vocabulary` limits a text column, or a list of text, to the names of another table’s rows, without links. Each value must be the `name` of a row of that table, so adding a row to the vocabulary table adds an allowed value.
|
|
521
|
+
|
|
522
|
+
**`create_table`**
|
|
523
|
+
|
|
524
|
+
```json
|
|
525
|
+
{
|
|
526
|
+
"name": "tag",
|
|
527
|
+
"description": "Words I tag films with",
|
|
528
|
+
"columns": [
|
|
529
|
+
{
|
|
530
|
+
"name": "name",
|
|
531
|
+
"type": "string",
|
|
532
|
+
"required": true,
|
|
533
|
+
"unique": true,
|
|
534
|
+
"description": "The tag, as I write it"
|
|
535
|
+
}
|
|
536
|
+
],
|
|
537
|
+
"filename": "{name}"
|
|
538
|
+
}
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
**`insert_row`**
|
|
542
|
+
|
|
543
|
+
```json
|
|
544
|
+
{ "table": "tag", "values": { "name": "funny" } }
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
**`insert_row`**
|
|
548
|
+
|
|
549
|
+
```json
|
|
550
|
+
{ "table": "tag", "values": { "name": "rewatch" } }
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
**`add_column`**
|
|
554
|
+
|
|
555
|
+
```json
|
|
556
|
+
{
|
|
557
|
+
"table": "film",
|
|
558
|
+
"column": {
|
|
559
|
+
"name": "tags",
|
|
560
|
+
"type": "list",
|
|
561
|
+
"items": "string",
|
|
562
|
+
"vocabulary": "main.public.tag",
|
|
563
|
+
"description": "My tags for the film, from the tag table"
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
Answer:
|
|
569
|
+
|
|
570
|
+
```json
|
|
571
|
+
{
|
|
572
|
+
"ok": true,
|
|
573
|
+
"op": "add_column",
|
|
574
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
575
|
+
"commits": [{ "table": "main.public.film", "version": 11, "operation": "ADD COLUMNS" }],
|
|
576
|
+
"migration_id": "20261012T071949-add_column-add-column-tags-to-film"
|
|
577
|
+
}
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
**`update_row`**
|
|
581
|
+
|
|
582
|
+
```json
|
|
583
|
+
{
|
|
584
|
+
"table": "film",
|
|
585
|
+
"path": "datanotes/main/public/film/His Girl Friday.md",
|
|
586
|
+
"set": { "tags": ["funny", "fast talk"] }
|
|
587
|
+
}
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
Refused:
|
|
591
|
+
|
|
592
|
+
```text
|
|
593
|
+
update_row refused — nothing was written:
|
|
594
|
+
invalid datanotes/main/public/film/His Girl Friday.md: tags[1] "fast talk" is not in main.public.tag (add it there, or use one of: funny, rewatch)
|
|
595
|
+
Hint: Use one of the values listed in the message, or first insert the new value as a row of the vocabulary table. Then call update_row again (dry_run: true checks without writing).
|
|
596
|
+
Docs: https://datanotes.dev/guides/database-objects/relations.md#take-allowed-values-from-another-table (offline: read_docs("guides/database-objects/relations#take-allowed-values-from-another-table"))
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
**`insert_row`**
|
|
600
|
+
|
|
601
|
+
```json
|
|
602
|
+
{ "table": "tag", "values": { "name": "fast talk" } }
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
Once you add `fast talk` to the tag table, the same write goes through:
|
|
606
|
+
|
|
607
|
+
**`update_row`**
|
|
608
|
+
|
|
609
|
+
```json
|
|
610
|
+
{
|
|
611
|
+
"table": "film",
|
|
612
|
+
"path": "datanotes/main/public/film/His Girl Friday.md",
|
|
613
|
+
"set": { "tags": ["funny", "fast talk"] }
|
|
614
|
+
}
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
Answer:
|
|
618
|
+
|
|
619
|
+
```json
|
|
620
|
+
{
|
|
621
|
+
"ok": true,
|
|
622
|
+
"op": "update_row",
|
|
623
|
+
"notes_changed": 1,
|
|
624
|
+
"planned_edits": [
|
|
625
|
+
{
|
|
626
|
+
"path": "datanotes/main/public/film/His Girl Friday.md",
|
|
627
|
+
"set": { "tags": ["funny", "fast talk"] },
|
|
628
|
+
"remove": []
|
|
629
|
+
}
|
|
630
|
+
],
|
|
631
|
+
"commits": [{ "table": "main.public.film", "version": 12, "operation": "UPDATE" }],
|
|
632
|
+
"migration_id": "20261012T071949-update_row-update-film-4"
|
|
633
|
+
}
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
## Build a tree of rows with a parent link
|
|
637
|
+
|
|
638
|
+
`hierarchy: true` makes a link column a parent link to the same table (`ref: self`). A row can never be its own ancestor. The `within` filter on the parent column returns every row below a given one, at any depth. Here the genres form a tree: Cinema holds the sound era, which holds screwball comedy.
|
|
639
|
+
|
|
640
|
+
**`create_table`**
|
|
641
|
+
|
|
642
|
+
```json
|
|
643
|
+
{
|
|
644
|
+
"name": "genre",
|
|
645
|
+
"description": "Eras and genres of cinema, as a tree",
|
|
646
|
+
"columns": [
|
|
647
|
+
{
|
|
648
|
+
"name": "name",
|
|
649
|
+
"type": "string",
|
|
650
|
+
"required": true,
|
|
651
|
+
"description": "The name of the era or genre"
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
"name": "kind",
|
|
655
|
+
"type": "enum",
|
|
656
|
+
"values": ["cinema", "era", "genre"],
|
|
657
|
+
"required": true,
|
|
658
|
+
"description": "The level in the tree: cinema, an era or a genre"
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
"name": "parent",
|
|
662
|
+
"type": "link",
|
|
663
|
+
"ref": "self",
|
|
664
|
+
"hierarchy": true,
|
|
665
|
+
"description": "The era or cinema one level up"
|
|
666
|
+
}
|
|
667
|
+
],
|
|
668
|
+
"filename": "{name}"
|
|
669
|
+
}
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
Answer:
|
|
673
|
+
|
|
674
|
+
```json
|
|
675
|
+
{
|
|
676
|
+
"ok": true,
|
|
677
|
+
"op": "create_table",
|
|
678
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
679
|
+
"commits": [{ "table": "main.public.genre", "version": 0, "operation": "CREATE TABLE" }],
|
|
680
|
+
"migration_id": "20261012T071949-create_table-create-table-main-public-genre"
|
|
681
|
+
}
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
**`insert_row`**
|
|
685
|
+
|
|
686
|
+
```json
|
|
687
|
+
{ "table": "genre", "values": { "name": "Cinema", "kind": "cinema" } }
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
**`insert_row`**
|
|
691
|
+
|
|
692
|
+
```json
|
|
693
|
+
{
|
|
694
|
+
"table": "genre",
|
|
695
|
+
"values": { "name": "Sound film", "kind": "era", "parent": "[[Cinema]]" }
|
|
696
|
+
}
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
**`insert_row`**
|
|
700
|
+
|
|
701
|
+
```json
|
|
702
|
+
{
|
|
703
|
+
"table": "genre",
|
|
704
|
+
"values": { "name": "Screwball comedy", "kind": "genre", "parent": "[[Sound film]]" }
|
|
705
|
+
}
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
**`select_rows`**
|
|
709
|
+
|
|
710
|
+
```json
|
|
711
|
+
{
|
|
712
|
+
"table": "genre",
|
|
713
|
+
"columns": ["name", "parent"],
|
|
714
|
+
"where": { "parent": { "within": "[[Cinema]]" } }
|
|
715
|
+
}
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
Answer:
|
|
719
|
+
|
|
720
|
+
```json
|
|
721
|
+
{
|
|
722
|
+
"table": "main.public.genre",
|
|
723
|
+
"total": 2,
|
|
724
|
+
"rows": [
|
|
725
|
+
{
|
|
726
|
+
"path": "datanotes/main/public/genre/Screwball comedy.md",
|
|
727
|
+
"name": "Screwball comedy",
|
|
728
|
+
"parent": "[[Sound film]]"
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
"path": "datanotes/main/public/genre/Sound film.md",
|
|
732
|
+
"name": "Sound film",
|
|
733
|
+
"parent": "[[Cinema]]"
|
|
734
|
+
}
|
|
735
|
+
]
|
|
736
|
+
}
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
The engine refuses a loop:
|
|
740
|
+
|
|
741
|
+
**`update_row`**
|
|
742
|
+
|
|
743
|
+
```json
|
|
744
|
+
{
|
|
745
|
+
"table": "genre",
|
|
746
|
+
"path": "datanotes/main/public/genre/Cinema.md",
|
|
747
|
+
"set": { "parent": "[[Screwball comedy]]" }
|
|
748
|
+
}
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
Refused:
|
|
752
|
+
|
|
753
|
+
```text
|
|
754
|
+
update_row refused — nothing was written:
|
|
755
|
+
invalid datanotes/main/public/genre/Cinema.md: parent would make "Cinema" its own ancestor (Cinema → Screwball comedy → Sound film → Cinema)
|
|
756
|
+
Hint: A row cannot be its own ancestor: point the hierarchy column to a row that is not below this one. Then call update_row again (dry_run: true checks without writing).
|
|
757
|
+
Docs: https://datanotes.dev/guides/database-objects/relations.md#build-a-tree-of-rows-with-a-parent-link (offline: read_docs("guides/database-objects/relations#build-a-tree-of-rows-with-a-parent-link"))
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
## Limit link targets with `ref_by` and `ref_where`
|
|
761
|
+
|
|
762
|
+
A link column can limit which rows it points to, without a CHECK. `ref_by` reads a column of the row itself. With `levels: [cinema, era, genre]`, Cinema has no parent, an era’s parent must be Cinema, and a genre’s parent must be an era.
|
|
763
|
+
|
|
764
|
+
These are the levels of the CHECK above, which stays. Each refusal below names both rules, and the `ref_by` one also lists the valid parents.
|
|
765
|
+
|
|
766
|
+
`describe_table` shows the expanded `map`. You can also write `map` directly, with `null` for a top level.
|
|
767
|
+
|
|
768
|
+
`ref_where` is a `where` filter that every linked row must match. The engine checks writes as it does for CHECKs that follow links, including the rows that link to a changed row.
|
|
769
|
+
|
|
770
|
+
**`alter_column`**
|
|
771
|
+
|
|
772
|
+
```json
|
|
773
|
+
{
|
|
774
|
+
"table": "genre",
|
|
775
|
+
"column": "parent",
|
|
776
|
+
"changes": { "ref_by": { "column": "kind", "levels": ["cinema", "era", "genre"] } }
|
|
777
|
+
}
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
Answer:
|
|
781
|
+
|
|
782
|
+
```json
|
|
783
|
+
{
|
|
784
|
+
"ok": true,
|
|
785
|
+
"op": "alter_column",
|
|
786
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
787
|
+
"commits": [
|
|
788
|
+
{ "table": "main.public.genre", "version": 10, "operation": "CHANGE COLUMN" }
|
|
789
|
+
],
|
|
790
|
+
"migration_id": "20261012T071951-alter_column-alter-column-parent-in-genre"
|
|
791
|
+
}
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
**`describe_table`**
|
|
795
|
+
|
|
796
|
+
```json
|
|
797
|
+
{ "table": "genre" }
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
Answer:
|
|
801
|
+
|
|
802
|
+
```json
|
|
803
|
+
{
|
|
804
|
+
"name": "parent",
|
|
805
|
+
"type": "link",
|
|
806
|
+
"required": false,
|
|
807
|
+
"ref": "main.public.genre",
|
|
808
|
+
"aliases": [],
|
|
809
|
+
"description": "The era or cinema one level up",
|
|
810
|
+
"hierarchy": true,
|
|
811
|
+
"ref_by": {
|
|
812
|
+
"column": "kind",
|
|
813
|
+
"levels": ["cinema", "era", "genre"],
|
|
814
|
+
"map": { "cinema": null, "era": ["cinema"], "genre": ["era"] }
|
|
815
|
+
},
|
|
816
|
+
"dense": false
|
|
817
|
+
}
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
A genre needs a parent, and the refusal (`required`) says why:
|
|
821
|
+
|
|
822
|
+
**`update_row`**
|
|
823
|
+
|
|
824
|
+
```json
|
|
825
|
+
{
|
|
826
|
+
"table": "genre",
|
|
827
|
+
"path": "datanotes/main/public/genre/Slapstick comedy.md",
|
|
828
|
+
"unset": ["parent"]
|
|
829
|
+
}
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
Refused:
|
|
833
|
+
|
|
834
|
+
```text
|
|
835
|
+
update_row refused — nothing was written:
|
|
836
|
+
invalid datanotes/main/public/genre/Slapstick comedy.md: parent is required for kind "genre": it needs a parent with kind era
|
|
837
|
+
invalid datanotes/main/public/genre/Slapstick comedy.md: parent_by_level violates CHECK parent_by_level: {"or":[{"kind":"cinema","parent":{"exists":false}},{"kind":"era","parent.kind":"cinema"},{"kind":"genre","parent.kind":"era"}]}
|
|
838
|
+
Hint: Give a value to every required column; describe_table marks them (required: true) and lists an enum's values. The row breaks a CHECK constraint of the table (named in the message, with its rule): change the values so the rule holds. describe_table lists the table's constraints. Then call update_row again (dry_run: true checks without writing).
|
|
839
|
+
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"))
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
The engine refuses a parent of the wrong level and lists the valid ones:
|
|
843
|
+
|
|
844
|
+
**`update_row`**
|
|
845
|
+
|
|
846
|
+
```json
|
|
847
|
+
{
|
|
848
|
+
"table": "genre",
|
|
849
|
+
"path": "datanotes/main/public/genre/Slapstick comedy.md",
|
|
850
|
+
"set": { "parent": "[[Cinema]]" }
|
|
851
|
+
}
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
Refused:
|
|
855
|
+
|
|
856
|
+
```text
|
|
857
|
+
update_row refused — nothing was written:
|
|
858
|
+
invalid datanotes/main/public/genre/Slapstick comedy.md: parent [[Cinema]] is not a valid parent for kind "genre" (needs kind era); valid: Silent film, Sound film
|
|
859
|
+
invalid datanotes/main/public/genre/Slapstick comedy.md: parent_by_level violates CHECK parent_by_level: {"or":[{"kind":"cinema","parent":{"exists":false}},{"kind":"era","parent.kind":"cinema"},{"kind":"genre","parent.kind":"era"}]}
|
|
860
|
+
Hint: Link to a row of the table the column points to (its ref) that its ref_where / ref_by allow: list_link_targets(table, column) lists them. The row breaks a CHECK constraint of the table (named in the message, with its rule): change the values so the rule holds. describe_table lists the table's constraints. Then call update_row again (dry_run: true checks without writing).
|
|
861
|
+
Docs: https://datanotes.dev/guides/database-objects/relations.md#limit-link-targets-with-ref_by-and-ref_where (offline: read_docs("guides/database-objects/relations#limit-link-targets-with-ref_by-and-ref_where"))
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
`list_link_targets` returns the valid targets for a row before you write it, so an agent or a picker offers only those:
|
|
865
|
+
|
|
866
|
+
**`list_link_targets`**
|
|
867
|
+
|
|
868
|
+
```json
|
|
869
|
+
{ "table": "genre", "column": "parent", "values": { "kind": "genre" } }
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
Answer:
|
|
873
|
+
|
|
874
|
+
```json
|
|
875
|
+
{
|
|
876
|
+
"table": "main.public.genre",
|
|
877
|
+
"column": "parent",
|
|
878
|
+
"ref": "main.public.genre",
|
|
879
|
+
"required": true,
|
|
880
|
+
"empty": false,
|
|
881
|
+
"reason": "kind \"genre\" needs kind era",
|
|
882
|
+
"rows": [
|
|
883
|
+
{
|
|
884
|
+
"name": "Silent film",
|
|
885
|
+
"link": "[[datanotes/main/public/genre/Silent film|Silent film]]",
|
|
886
|
+
"path": "datanotes/main/public/genre/Silent film.md",
|
|
887
|
+
"kind": "era"
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
"name": "Sound film",
|
|
891
|
+
"link": "[[datanotes/main/public/genre/Sound film|Sound film]]",
|
|
892
|
+
"path": "datanotes/main/public/genre/Sound film.md",
|
|
893
|
+
"kind": "era"
|
|
894
|
+
}
|
|
895
|
+
],
|
|
896
|
+
"total": 2
|
|
897
|
+
}
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
Silent film can’t become a top level while a genre hangs from it:
|
|
901
|
+
|
|
902
|
+
**`update_row`**
|
|
903
|
+
|
|
904
|
+
```json
|
|
905
|
+
{
|
|
906
|
+
"table": "genre",
|
|
907
|
+
"path": "datanotes/main/public/genre/Silent film.md",
|
|
908
|
+
"set": { "kind": "cinema" },
|
|
909
|
+
"unset": ["parent"]
|
|
910
|
+
}
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
Refused:
|
|
914
|
+
|
|
915
|
+
```text
|
|
916
|
+
update_row refused — nothing was written:
|
|
917
|
+
invalid datanotes/main/public/genre/Slapstick comedy.md: parent [[Silent film]] is not a valid parent for kind "genre" (needs kind era); valid: Sound film (after this change to [[Silent film]], a row it links to)
|
|
918
|
+
invalid datanotes/main/public/genre/Slapstick comedy.md: parent_by_level violates CHECK parent_by_level: {"or":[{"kind":"cinema","parent":{"exists":false}},{"kind":"era","parent.kind":"cinema"},{"kind":"genre","parent.kind":"era"}]} (after this change to [[Silent film]], a row it links to)
|
|
919
|
+
Hint: Link to a row of the table the column points to (its ref) that its ref_where / ref_by allow: list_link_targets(table, column) lists them. The row breaks a CHECK constraint of the table (named in the message, with its rule): change the values so the rule holds. describe_table lists the table's constraints. Then call update_row again (dry_run: true checks without writing).
|
|
920
|
+
Docs: https://datanotes.dev/guides/database-objects/relations.md#limit-link-targets-with-ref_by-and-ref_where (offline: read_docs("guides/database-objects/relations#limit-link-targets-with-ref_by-and-ref_where"))
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
A new `genre` column on `film` uses `ref_where`, so a film links to a genre, never to an era or to Cinema:
|
|
924
|
+
|
|
925
|
+
**`add_column`**
|
|
926
|
+
|
|
927
|
+
```json
|
|
928
|
+
{
|
|
929
|
+
"table": "film",
|
|
930
|
+
"column": {
|
|
931
|
+
"name": "genre",
|
|
932
|
+
"type": "link",
|
|
933
|
+
"ref": "main.public.genre",
|
|
934
|
+
"ref_where": { "kind": "genre" },
|
|
935
|
+
"description": "The film's genre"
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
```
|
|
939
|
+
|
|
940
|
+
Answer:
|
|
941
|
+
|
|
942
|
+
```json
|
|
943
|
+
{
|
|
944
|
+
"ok": true,
|
|
945
|
+
"op": "add_column",
|
|
946
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
947
|
+
"commits": [{ "table": "main.public.film", "version": 13, "operation": "ADD COLUMNS" }],
|
|
948
|
+
"migration_id": "20261012T071951-add_column-add-column-genre-to-film"
|
|
949
|
+
}
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
**`list_link_targets`**
|
|
953
|
+
|
|
954
|
+
```json
|
|
955
|
+
{ "table": "film", "column": "genre" }
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
Answer:
|
|
959
|
+
|
|
960
|
+
```json
|
|
961
|
+
{
|
|
962
|
+
"table": "main.public.film",
|
|
963
|
+
"column": "genre",
|
|
964
|
+
"ref": "main.public.genre",
|
|
965
|
+
"required": false,
|
|
966
|
+
"empty": false,
|
|
967
|
+
"reason": "ref_where {\"kind\":\"genre\"}",
|
|
968
|
+
"rows": [
|
|
969
|
+
{
|
|
970
|
+
"name": "Screwball comedy",
|
|
971
|
+
"link": "[[datanotes/main/public/genre/Screwball comedy|Screwball comedy]]",
|
|
972
|
+
"path": "datanotes/main/public/genre/Screwball comedy.md"
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
"name": "Slapstick comedy",
|
|
976
|
+
"link": "[[datanotes/main/public/genre/Slapstick comedy|Slapstick comedy]]",
|
|
977
|
+
"path": "datanotes/main/public/genre/Slapstick comedy.md"
|
|
978
|
+
}
|
|
979
|
+
],
|
|
980
|
+
"total": 2
|
|
981
|
+
}
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
## Compute a link from other columns
|
|
985
|
+
|
|
986
|
+
A generated link column builds its link from a template. `link({occurred_at:YYYY-MM-DD})`, with `ref` set to a table of days, links each viewing to its day’s note. That note may not exist yet; the link resolves once it does.
|
|
987
|
+
|
|
988
|
+
**`create_table`**
|
|
989
|
+
|
|
990
|
+
```json
|
|
991
|
+
{
|
|
992
|
+
"name": "day",
|
|
993
|
+
"description": "One note per day of my diary",
|
|
994
|
+
"columns": [
|
|
995
|
+
{
|
|
996
|
+
"name": "date",
|
|
997
|
+
"type": "date",
|
|
998
|
+
"required": true,
|
|
999
|
+
"description": "The day the note is about"
|
|
1000
|
+
}
|
|
1001
|
+
],
|
|
1002
|
+
"time_column": "date",
|
|
1003
|
+
"filename": "{date:YYYY-MM-DD}"
|
|
1004
|
+
}
|
|
1005
|
+
```
|
|
1006
|
+
|
|
1007
|
+
**`add_column`**
|
|
1008
|
+
|
|
1009
|
+
```json
|
|
1010
|
+
{
|
|
1011
|
+
"table": "watch",
|
|
1012
|
+
"column": {
|
|
1013
|
+
"name": "on_day",
|
|
1014
|
+
"type": "link",
|
|
1015
|
+
"ref": "main.public.day",
|
|
1016
|
+
"generated": "link({occurred_at:YYYY-MM-DD})",
|
|
1017
|
+
"description": "The diary note of the day I watched the film"
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
Answer:
|
|
1023
|
+
|
|
1024
|
+
```json
|
|
1025
|
+
{
|
|
1026
|
+
"ok": true,
|
|
1027
|
+
"op": "add_column",
|
|
1028
|
+
"planned_texts": ["datanotes/metastore.yaml"],
|
|
1029
|
+
"commits": [{ "table": "main.public.watch", "version": 3, "operation": "ADD COLUMNS" }],
|
|
1030
|
+
"migration_id": "20261012T071951-add_column-add-column-on_day-to-watch"
|
|
1031
|
+
}
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
**`insert_row`**
|
|
1035
|
+
|
|
1036
|
+
```json
|
|
1037
|
+
{
|
|
1038
|
+
"table": "watch",
|
|
1039
|
+
"values": {
|
|
1040
|
+
"film": "[[Night of the Living Dead]]",
|
|
1041
|
+
"where": "cinema",
|
|
1042
|
+
"place": "the film club downtown",
|
|
1043
|
+
"with": "my sister",
|
|
1044
|
+
"occurred_at": "2026-10-31T23:00"
|
|
1045
|
+
},
|
|
1046
|
+
"body": "A late show. Half the hall jumped at the same moment.\n"
|
|
1047
|
+
}
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
Answer:
|
|
1051
|
+
|
|
1052
|
+
```json
|
|
1053
|
+
{
|
|
1054
|
+
"ok": true,
|
|
1055
|
+
"op": "insert_row",
|
|
1056
|
+
"files_created": 1,
|
|
1057
|
+
"planned_creates": ["datanotes/main/public/watch/2026-10-31T2300.md"],
|
|
1058
|
+
"commits": [{ "table": "main.public.watch", "version": 4, "operation": "WRITE" }],
|
|
1059
|
+
"migration_id": "20261012T071951-insert_row-insert-into-watch"
|
|
1060
|
+
}
|
|
1061
|
+
```
|
|
1062
|
+
|
|
1063
|
+
`datanotes/main/public/watch/2026-10-31T2300.md` now reads:
|
|
1064
|
+
|
|
1065
|
+
```markdown
|
|
1066
|
+
---
|
|
1067
|
+
table: main.public.watch
|
|
1068
|
+
id: 01a12efb-6be3-7ac0-b188-7489a3e62a71
|
|
1069
|
+
film: "[[Night of the Living Dead]]"
|
|
1070
|
+
where: cinema
|
|
1071
|
+
place: the film club downtown
|
|
1072
|
+
with: my sister
|
|
1073
|
+
occurred_at: 2026-10-31T23:00
|
|
1074
|
+
on_day: "[[datanotes/main/public/day/2026-10-31|2026-10-31]]"
|
|
1075
|
+
---
|
|
1076
|
+
A late show. Half the hall jumped at the same moment.
|
|
1077
|
+
```
|
|
1078
|
+
|
|
1079
|
+
## Delete a row that other notes link to
|
|
1080
|
+
|
|
1081
|
+
`on_referenced` tells `delete_rows` what to do with links to the rows it deletes:
|
|
1082
|
+
|
|
1083
|
+
* **`"restrict"`**: the default; refuses rows that other notes link to
|
|
1084
|
+
* **`"set_null"`**: clears those links, but refuses a required one, like `ON DELETE SET NULL` on a `NOT NULL` column in Structured Query Language (SQL)
|
|
1085
|
+
* **`"ignore"`**: leaves the links dangling
|
|
1086
|
+
|
|
1087
|
+
**`delete_rows`**
|
|
1088
|
+
|
|
1089
|
+
```json
|
|
1090
|
+
{ "table": "film", "where": { "title": "Charade" } }
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
Refused:
|
|
1094
|
+
|
|
1095
|
+
```text
|
|
1096
|
+
delete_rows refused — nothing was written:
|
|
1097
|
+
conflict datanotes/main/public/film/Charade.md: referenced by datanotes/main/public/actor/Audrey Hepburn.md, datanotes/main/public/actor/Cary Grant.md, datanotes/main/public/film/His Girl Friday.md, datanotes/main/public/watch/2026-09-26T2130.md
|
|
1098
|
+
Hint: Other rows link to it: pass on_referenced ("set_null" or "ignore"), or change those rows first.
|
|
1099
|
+
Docs: https://datanotes.dev/guides/database-objects/relations.md#delete-a-row-that-other-notes-link-to (offline: read_docs("guides/database-objects/relations#delete-a-row-that-other-notes-link-to"))
|
|
1100
|
+
```
|
|
1101
|
+
|
|
1102
|
+
Charade’s viewing needs its `film`, so the engine also refuses to clear the link:
|
|
1103
|
+
|
|
1104
|
+
**`delete_rows`**
|
|
1105
|
+
|
|
1106
|
+
```json
|
|
1107
|
+
{ "table": "film", "where": { "title": "Charade" }, "on_referenced": "set_null" }
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
Refused:
|
|
1111
|
+
|
|
1112
|
+
```text
|
|
1113
|
+
delete_rows refused — nothing was written:
|
|
1114
|
+
conflict datanotes/main/public/watch/2026-09-26T2130.md: "film" is required and would be left empty: change or delete this row first, or use on_referenced "ignore"
|
|
1115
|
+
Hint: Other rows link to it: pass on_referenced ("set_null" or "ignore"), or change those rows first.
|
|
1116
|
+
Docs: https://datanotes.dev/guides/database-objects/relations.md#delete-a-row-that-other-notes-link-to (offline: read_docs("guides/database-objects/relations#delete-a-row-that-other-notes-link-to"))
|
|
1117
|
+
```
|
|
1118
|
+
|
|
1119
|
+
Delete the viewing and the film together with `batch`. The viewing goes first, then `set_null` clears Charade from Cary Grant’s `films`. If the engine refused any step, it would apply none of them.
|
|
1120
|
+
|
|
1121
|
+
**`batch`**
|
|
1122
|
+
|
|
1123
|
+
```json
|
|
1124
|
+
{
|
|
1125
|
+
"summary": "remove Charade and its viewing",
|
|
1126
|
+
"operations": [
|
|
1127
|
+
{ "op": "delete_rows", "table": "watch", "where": { "film": "[[Charade]]" } },
|
|
1128
|
+
{
|
|
1129
|
+
"op": "delete_rows",
|
|
1130
|
+
"table": "film",
|
|
1131
|
+
"where": { "title": "Charade" },
|
|
1132
|
+
"on_referenced": "set_null"
|
|
1133
|
+
}
|
|
1134
|
+
]
|
|
1135
|
+
}
|
|
1136
|
+
```
|
|
1137
|
+
|
|
1138
|
+
Answer (72 lines)
|
|
1139
|
+
|
|
1140
|
+
```json
|
|
1141
|
+
{
|
|
1142
|
+
"ok": true,
|
|
1143
|
+
"op": "batch",
|
|
1144
|
+
"dry_run": false,
|
|
1145
|
+
"migration_id": "20261012T071951-batch-remove-charade-and-its-viewing",
|
|
1146
|
+
"steps": [
|
|
1147
|
+
{
|
|
1148
|
+
"index": 0,
|
|
1149
|
+
"op": "delete_rows",
|
|
1150
|
+
"ok": true,
|
|
1151
|
+
"migration_id": "20261012T071951-delete_rows-archive-1-rows-of-watch",
|
|
1152
|
+
"result": {
|
|
1153
|
+
"ok": true,
|
|
1154
|
+
"op": "delete_rows",
|
|
1155
|
+
"files_moved": 1,
|
|
1156
|
+
"planned_moves": [
|
|
1157
|
+
{
|
|
1158
|
+
"from": "datanotes/main/public/watch/2026-09-26T2130.md",
|
|
1159
|
+
"to": "datanotes/main/public/watch/_archive/2026-09-26T2130.md"
|
|
1160
|
+
}
|
|
1161
|
+
],
|
|
1162
|
+
"commits": [{ "table": "main.public.watch", "version": 5, "operation": "DELETE" }],
|
|
1163
|
+
"migration_id": "20261012T071951-delete_rows-archive-1-rows-of-watch"
|
|
1164
|
+
}
|
|
1165
|
+
},
|
|
1166
|
+
{
|
|
1167
|
+
"index": 1,
|
|
1168
|
+
"op": "delete_rows",
|
|
1169
|
+
"ok": true,
|
|
1170
|
+
"migration_id": "20261012T071951-delete_rows-archive-1-rows-of-film",
|
|
1171
|
+
"result": {
|
|
1172
|
+
"ok": true,
|
|
1173
|
+
"op": "delete_rows",
|
|
1174
|
+
"notes_changed": 4,
|
|
1175
|
+
"files_moved": 1,
|
|
1176
|
+
"planned_edits": [
|
|
1177
|
+
{
|
|
1178
|
+
"path": "datanotes/main/public/actor/Audrey Hepburn.md",
|
|
1179
|
+
"set": { "films": [] },
|
|
1180
|
+
"remove": []
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
"path": "datanotes/main/public/actor/Cary Grant.md",
|
|
1184
|
+
"set": { "films": ["[[His Girl Friday]]"] },
|
|
1185
|
+
"remove": []
|
|
1186
|
+
},
|
|
1187
|
+
{
|
|
1188
|
+
"path": "datanotes/main/public/film/His Girl Friday.md",
|
|
1189
|
+
"set": { "see_also": [] },
|
|
1190
|
+
"remove": []
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
"path": "datanotes/main/public/watch/_archive/2026-09-26T2130.md",
|
|
1194
|
+
"set": { "film": null },
|
|
1195
|
+
"remove": []
|
|
1196
|
+
}
|
|
1197
|
+
],
|
|
1198
|
+
"planned_moves": [
|
|
1199
|
+
{
|
|
1200
|
+
"from": "datanotes/main/public/film/Charade.md",
|
|
1201
|
+
"to": "datanotes/main/public/film/_archive/Charade.md"
|
|
1202
|
+
}
|
|
1203
|
+
],
|
|
1204
|
+
"commits": [
|
|
1205
|
+
{ "table": "main.public.actor", "version": 7, "operation": "DELETE" },
|
|
1206
|
+
{ "table": "main.public.film", "version": 14, "operation": "DELETE" }
|
|
1207
|
+
],
|
|
1208
|
+
"migration_id": "20261012T071951-delete_rows-archive-1-rows-of-film"
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
]
|
|
1212
|
+
}
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
**`read_note`**
|
|
1216
|
+
|
|
1217
|
+
```json
|
|
1218
|
+
{ "path": "datanotes/main/public/actor/Cary Grant.md" }
|
|
1219
|
+
```
|
|
1220
|
+
|
|
1221
|
+
Answer:
|
|
1222
|
+
|
|
1223
|
+
```markdown
|
|
1224
|
+
---
|
|
1225
|
+
table: main.public.actor
|
|
1226
|
+
id: 01a12efb-621d-71b4-a0ec-578ef615f6b0
|
|
1227
|
+
name: Cary Grant
|
|
1228
|
+
born: 1904-01-18
|
|
1229
|
+
films:
|
|
1230
|
+
- "[[His Girl Friday]]"
|
|
1231
|
+
---
|
|
1232
|
+
```
|
|
1233
|
+
|
|
1234
|
+
## See the relations in `metastore.yaml`
|
|
1235
|
+
|
|
1236
|
+
The engine wrote this file with the relations from this page:
|
|
1237
|
+
|
|
1238
|
+
* **Link columns**: each with its `ref`, and `self` kept as written
|
|
1239
|
+
* **`inverse`**: on one side only
|
|
1240
|
+
* **`vocabulary`, `hierarchy` and `ref_where`**: as declared
|
|
1241
|
+
* **`ref_by`**: with the `levels` shortcut kept as written
|
|
1242
|
+
* **Generated link**: the `on_day` column with its template
|
|
1243
|
+
|
|
1244
|
+
`datanotes/metastore.yaml` now reads:
|
|
1245
|
+
|
|
1246
|
+
```yaml
|
|
1247
|
+
# Metastore (Datanotes): catalogs, schemas, tables, columns, views.
|
|
1248
|
+
id: 01a12efb-6211-77a6-a9b2-1c2a593b92c9
|
|
1249
|
+
config:
|
|
1250
|
+
history: true
|
|
1251
|
+
|
|
1252
|
+
|
|
1253
|
+
catalogs:
|
|
1254
|
+
main:
|
|
1255
|
+
schemas:
|
|
1256
|
+
public:
|
|
1257
|
+
tables:
|
|
1258
|
+
actor:
|
|
1259
|
+
description: Actors of the films I watch
|
|
1260
|
+
options:
|
|
1261
|
+
filename: "{name}"
|
|
1262
|
+
archive: _archive
|
|
1263
|
+
columns:
|
|
1264
|
+
- name: id
|
|
1265
|
+
type: string
|
|
1266
|
+
required: true
|
|
1267
|
+
description: "Unique id of the row (UUIDv7): set when the row is created, never changes"
|
|
1268
|
+
unique: true
|
|
1269
|
+
identity: uuid
|
|
1270
|
+
dense: true
|
|
1271
|
+
- name: name
|
|
1272
|
+
type: string
|
|
1273
|
+
required: true
|
|
1274
|
+
description: The actor's name, as credited
|
|
1275
|
+
unique: true
|
|
1276
|
+
- name: born
|
|
1277
|
+
type: date
|
|
1278
|
+
description: The actor's date of birth
|
|
1279
|
+
- name: films
|
|
1280
|
+
type: list
|
|
1281
|
+
items: link
|
|
1282
|
+
ref: main.public.film
|
|
1283
|
+
description: The films the actor plays in
|
|
1284
|
+
film:
|
|
1285
|
+
description: Classic films I watched or want to watch
|
|
1286
|
+
options:
|
|
1287
|
+
filename: "{title}"
|
|
1288
|
+
archive: _archive
|
|
1289
|
+
columns:
|
|
1290
|
+
- name: id
|
|
1291
|
+
type: string
|
|
1292
|
+
required: true
|
|
1293
|
+
description: "Unique id of the row (UUIDv7): set when the row is created, never changes"
|
|
1294
|
+
unique: true
|
|
1295
|
+
identity: uuid
|
|
1296
|
+
dense: true
|
|
1297
|
+
- name: title
|
|
1298
|
+
type: string
|
|
1299
|
+
required: true
|
|
1300
|
+
description: The film's title, as on its poster
|
|
1301
|
+
unique: true
|
|
1302
|
+
- name: year
|
|
1303
|
+
type: integer
|
|
1304
|
+
description: Year the film first came out
|
|
1305
|
+
- name: minutes
|
|
1306
|
+
type: integer
|
|
1307
|
+
min: 1
|
|
1308
|
+
description: Running time in minutes
|
|
1309
|
+
- name: cast
|
|
1310
|
+
type: list
|
|
1311
|
+
items: link
|
|
1312
|
+
ref: main.public.actor
|
|
1313
|
+
description: The actors in the film
|
|
1314
|
+
inverse: films
|
|
1315
|
+
- name: see_also
|
|
1316
|
+
type: list
|
|
1317
|
+
items: link
|
|
1318
|
+
ref: self
|
|
1319
|
+
description: Films to watch next if I liked this one
|
|
1320
|
+
inverse: see_also
|
|
1321
|
+
- name: tags
|
|
1322
|
+
type: list
|
|
1323
|
+
items: string
|
|
1324
|
+
description: My tags for the film, from the tag table
|
|
1325
|
+
vocabulary: main.public.tag
|
|
1326
|
+
- name: genre
|
|
1327
|
+
type: link
|
|
1328
|
+
ref: main.public.genre
|
|
1329
|
+
description: The film's genre
|
|
1330
|
+
ref_where:
|
|
1331
|
+
kind: genre
|
|
1332
|
+
watch:
|
|
1333
|
+
description: Each time I watch a film
|
|
1334
|
+
options:
|
|
1335
|
+
filename: "{occurred_at:YYYY-MM-DD[T]HHmm}"
|
|
1336
|
+
archive: _archive
|
|
1337
|
+
columns:
|
|
1338
|
+
- name: id
|
|
1339
|
+
type: string
|
|
1340
|
+
required: true
|
|
1341
|
+
description: "Unique id of the row (UUIDv7): set when the row is created, never changes"
|
|
1342
|
+
unique: true
|
|
1343
|
+
identity: uuid
|
|
1344
|
+
dense: true
|
|
1345
|
+
- name: film
|
|
1346
|
+
type: link
|
|
1347
|
+
required: true
|
|
1348
|
+
ref: main.public.film
|
|
1349
|
+
description: The film I watched
|
|
1350
|
+
- name: where
|
|
1351
|
+
type: enum
|
|
1352
|
+
required: true
|
|
1353
|
+
values:
|
|
1354
|
+
- cinema
|
|
1355
|
+
- home
|
|
1356
|
+
- outdoor screening
|
|
1357
|
+
description: The kind of place I watched it in
|
|
1358
|
+
- name: place
|
|
1359
|
+
type: string
|
|
1360
|
+
description: The cinema, park or room, in my words
|
|
1361
|
+
- name: with
|
|
1362
|
+
type: string
|
|
1363
|
+
description: Who watched it with me
|
|
1364
|
+
- name: occurred_at
|
|
1365
|
+
type: datetime
|
|
1366
|
+
required: true
|
|
1367
|
+
description: When the film started
|
|
1368
|
+
- name: on_day
|
|
1369
|
+
type: link
|
|
1370
|
+
ref: main.public.day
|
|
1371
|
+
description: The diary note of the day I watched the film
|
|
1372
|
+
generated: link({occurred_at:YYYY-MM-DD})
|
|
1373
|
+
tag:
|
|
1374
|
+
description: Words I tag films with
|
|
1375
|
+
options:
|
|
1376
|
+
filename: "{name}"
|
|
1377
|
+
archive: _archive
|
|
1378
|
+
columns:
|
|
1379
|
+
- name: id
|
|
1380
|
+
type: string
|
|
1381
|
+
required: true
|
|
1382
|
+
description: "Unique id of the row (UUIDv7): set when the row is created, never changes"
|
|
1383
|
+
unique: true
|
|
1384
|
+
identity: uuid
|
|
1385
|
+
dense: true
|
|
1386
|
+
- name: name
|
|
1387
|
+
type: string
|
|
1388
|
+
required: true
|
|
1389
|
+
description: The tag, as I write it
|
|
1390
|
+
unique: true
|
|
1391
|
+
genre:
|
|
1392
|
+
description: Eras and genres of cinema, as a tree
|
|
1393
|
+
constraints:
|
|
1394
|
+
parent_by_level:
|
|
1395
|
+
or:
|
|
1396
|
+
- kind: cinema
|
|
1397
|
+
parent:
|
|
1398
|
+
exists: false
|
|
1399
|
+
- kind: era
|
|
1400
|
+
parent.kind: cinema
|
|
1401
|
+
- kind: genre
|
|
1402
|
+
parent.kind: era
|
|
1403
|
+
options:
|
|
1404
|
+
filename: "{name}"
|
|
1405
|
+
archive: _archive
|
|
1406
|
+
columns:
|
|
1407
|
+
- name: id
|
|
1408
|
+
type: string
|
|
1409
|
+
required: true
|
|
1410
|
+
description: "Unique id of the row (UUIDv7): set when the row is created, never changes"
|
|
1411
|
+
unique: true
|
|
1412
|
+
identity: uuid
|
|
1413
|
+
dense: true
|
|
1414
|
+
- name: name
|
|
1415
|
+
type: string
|
|
1416
|
+
required: true
|
|
1417
|
+
description: The name of the era or genre
|
|
1418
|
+
- name: kind
|
|
1419
|
+
type: enum
|
|
1420
|
+
required: true
|
|
1421
|
+
values:
|
|
1422
|
+
- cinema
|
|
1423
|
+
- era
|
|
1424
|
+
- genre
|
|
1425
|
+
description: "The level in the tree: cinema, an era or a genre"
|
|
1426
|
+
- name: parent
|
|
1427
|
+
type: link
|
|
1428
|
+
ref: self
|
|
1429
|
+
description: The era or cinema one level up
|
|
1430
|
+
hierarchy: true
|
|
1431
|
+
ref_by:
|
|
1432
|
+
column: kind
|
|
1433
|
+
levels:
|
|
1434
|
+
- cinema
|
|
1435
|
+
- era
|
|
1436
|
+
- genre
|
|
1437
|
+
day:
|
|
1438
|
+
description: One note per day of my diary
|
|
1439
|
+
options:
|
|
1440
|
+
filename: "{date:YYYY-MM-DD}"
|
|
1441
|
+
archive: _archive
|
|
1442
|
+
columns:
|
|
1443
|
+
- name: id
|
|
1444
|
+
type: string
|
|
1445
|
+
required: true
|
|
1446
|
+
description: "Unique id of the row (UUIDv7): set when the row is created, never changes"
|
|
1447
|
+
unique: true
|
|
1448
|
+
identity: uuid
|
|
1449
|
+
dense: true
|
|
1450
|
+
- name: date
|
|
1451
|
+
type: date
|
|
1452
|
+
required: true
|
|
1453
|
+
description: The day the note is about
|
|
1454
|
+
```
|