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.
Files changed (181) hide show
  1. package/NOTICE +9 -0
  2. package/README.md +207 -28
  3. package/SECURITY.md +92 -45
  4. package/dist/THIRD-PARTY-NOTICES.txt +682 -0
  5. package/dist/api/catalog.d.cts +69 -0
  6. package/dist/api/catalog.d.ts +50 -2
  7. package/dist/api/cli-options.d.cts +39 -0
  8. package/dist/api/cli-options.d.ts +39 -0
  9. package/dist/api/client.d.cts +89 -0
  10. package/dist/api/client.d.ts +19 -30
  11. package/dist/api/docs.d.cts +211 -0
  12. package/dist/api/docs.d.ts +211 -0
  13. package/dist/api/guidance.d.cts +81 -0
  14. package/dist/api/guidance.d.ts +81 -0
  15. package/dist/api/index.cjs +6610 -2247
  16. package/dist/api/index.d.cts +13 -0
  17. package/dist/api/index.d.ts +10 -7
  18. package/dist/api/index.js +6610 -2247
  19. package/dist/api/local.cjs +118 -6
  20. package/dist/api/local.d.cts +95 -0
  21. package/dist/api/local.d.ts +70 -6
  22. package/dist/api/local.js +104 -6
  23. package/dist/api/mcp.cjs +17384 -0
  24. package/dist/api/mcp.d.cts +108 -0
  25. package/dist/api/mcp.d.ts +108 -0
  26. package/dist/api/mcp.js +17334 -0
  27. package/dist/api/otlp.d.cts +65 -0
  28. package/dist/api/redact.d.cts +17 -0
  29. package/dist/api/redact.d.ts +17 -0
  30. package/dist/api/requests.d.cts +26 -0
  31. package/dist/api/requests.d.ts +14 -16
  32. package/dist/api/results.d.cts +16 -0
  33. package/dist/api/results.d.ts +2 -1
  34. package/dist/api/tokens.d.cts +7 -0
  35. package/dist/datanotes.cjs +62911 -28409
  36. package/dist/docs/administration/configuration.md +59 -0
  37. package/dist/docs/administration/docker.md +80 -0
  38. package/dist/docs/administration/git-sync.md +71 -0
  39. package/dist/docs/administration/limits.md +69 -0
  40. package/dist/docs/administration/run-the-service.md +219 -0
  41. package/dist/docs/administration/status.md +61 -0
  42. package/dist/docs/administration/use-with-obsidian.md +75 -0
  43. package/dist/docs/api/operations/add_column.md +49 -0
  44. package/dist/docs/api/operations/add_constraint.md +55 -0
  45. package/dist/docs/api/operations/adopt_rows.md +50 -0
  46. package/dist/docs/api/operations/align_relations.md +41 -0
  47. package/dist/docs/api/operations/alter_column.md +46 -0
  48. package/dist/docs/api/operations/alter_table.md +67 -0
  49. package/dist/docs/api/operations/batch.md +48 -0
  50. package/dist/docs/api/operations/changes_stream.md +28 -0
  51. package/dist/docs/api/operations/check_table.md +29 -0
  52. package/dist/docs/api/operations/clone_table.md +48 -0
  53. package/dist/docs/api/operations/comment_on.md +42 -0
  54. package/dist/docs/api/operations/configure_database.md +52 -0
  55. package/dist/docs/api/operations/create_table.md +78 -0
  56. package/dist/docs/api/operations/create_view.md +54 -0
  57. package/dist/docs/api/operations/create_webhook.md +37 -0
  58. package/dist/docs/api/operations/define_base.md +52 -0
  59. package/dist/docs/api/operations/delete_rows.md +43 -0
  60. package/dist/docs/api/operations/delete_webhook.md +31 -0
  61. package/dist/docs/api/operations/describe_database.md +30 -0
  62. package/dist/docs/api/operations/describe_history.md +40 -0
  63. package/dist/docs/api/operations/describe_semantic_index.md +24 -0
  64. package/dist/docs/api/operations/describe_table.md +38 -0
  65. package/dist/docs/api/operations/docs.md +13 -0
  66. package/dist/docs/api/operations/drop_column.md +41 -0
  67. package/dist/docs/api/operations/drop_constraint.md +39 -0
  68. package/dist/docs/api/operations/drop_table.md +43 -0
  69. package/dist/docs/api/operations/drop_view.md +38 -0
  70. package/dist/docs/api/operations/engine_status.md +23 -0
  71. package/dist/docs/api/operations/graph_neighbors.md +32 -0
  72. package/dist/docs/api/operations/graph_path.md +32 -0
  73. package/dist/docs/api/operations/graph_traverse.md +43 -0
  74. package/dist/docs/api/operations/identify_session.md +24 -0
  75. package/dist/docs/api/operations/infer_table.md +43 -0
  76. package/dist/docs/api/operations/insert_row.md +48 -0
  77. package/dist/docs/api/operations/list_changes.md +35 -0
  78. package/dist/docs/api/operations/list_folder.md +28 -0
  79. package/dist/docs/api/operations/list_link_targets.md +44 -0
  80. package/dist/docs/api/operations/list_migrations.md +35 -0
  81. package/dist/docs/api/operations/list_tables.md +32 -0
  82. package/dist/docs/api/operations/list_webhooks.md +23 -0
  83. package/dist/docs/api/operations/manual_index.md +14 -0
  84. package/dist/docs/api/operations/manual_page.md +22 -0
  85. package/dist/docs/api/operations/merge_rows.md +44 -0
  86. package/dist/docs/api/operations/move_rows.md +52 -0
  87. package/dist/docs/api/operations/openapi.md +13 -0
  88. package/dist/docs/api/operations/openapi_post.md +20 -0
  89. package/dist/docs/api/operations/read_note.md +29 -0
  90. package/dist/docs/api/operations/recompute_columns.md +44 -0
  91. package/dist/docs/api/operations/record_tool_call.md +36 -0
  92. package/dist/docs/api/operations/rename_column.md +44 -0
  93. package/dist/docs/api/operations/rename_value.md +45 -0
  94. package/dist/docs/api/operations/restore_database.md +51 -0
  95. package/dist/docs/api/operations/restore_table.md +46 -0
  96. package/dist/docs/api/operations/search_notes.md +28 -0
  97. package/dist/docs/api/operations/select_rows.md +61 -0
  98. package/dist/docs/api/operations/semantic_search.md +34 -0
  99. package/dist/docs/api/operations/semantic_search_docs.md +26 -0
  100. package/dist/docs/api/operations/set_table_bases.md +44 -0
  101. package/dist/docs/api/operations/status.md +13 -0
  102. package/dist/docs/api/operations/stop_engine.md +23 -0
  103. package/dist/docs/api/operations/sync_semantic_index.md +33 -0
  104. package/dist/docs/api/operations/table_changes.md +31 -0
  105. package/dist/docs/api/operations/tags/auxiliary.md +14 -0
  106. package/dist/docs/api/operations/tags/change-feed.md +8 -0
  107. package/dist/docs/api/operations/tags/history.md +17 -0
  108. package/dist/docs/api/operations/tags/queries.md +15 -0
  109. package/dist/docs/api/operations/tags/rows-dml.md +16 -0
  110. package/dist/docs/api/operations/tags/schema-ddl.md +22 -0
  111. package/dist/docs/api/operations/tags/service.md +15 -0
  112. package/dist/docs/api/operations/tags/tokens.md +13 -0
  113. package/dist/docs/api/operations/tokens_create.md +34 -0
  114. package/dist/docs/api/operations/tokens_delete.md +29 -0
  115. package/dist/docs/api/operations/tokens_list.md +21 -0
  116. package/dist/docs/api/operations/tokens_me.md +20 -0
  117. package/dist/docs/api/operations/tokens_rotate.md +29 -0
  118. package/dist/docs/api/operations/tokens_update.md +34 -0
  119. package/dist/docs/api/operations/undo_migration.md +46 -0
  120. package/dist/docs/api/operations/update_row.md +60 -0
  121. package/dist/docs/api/operations/update_rows.md +46 -0
  122. package/dist/docs/api/operations/vacuum_table.md +37 -0
  123. package/dist/docs/api.md +135 -0
  124. package/dist/docs/develop/build-an-app.md +320 -0
  125. package/dist/docs/develop/build-on-datanotes.md +138 -0
  126. package/dist/docs/develop/change-feed-and-webhooks.md +70 -0
  127. package/dist/docs/develop/client.md +116 -0
  128. package/dist/docs/develop/concurrency.md +153 -0
  129. package/dist/docs/develop/http-api.md +79 -0
  130. package/dist/docs/develop/telemetry.md +86 -0
  131. package/dist/docs/guides/agents/docs-offline.md +99 -0
  132. package/dist/docs/guides/agents/how-agents-use-datanotes.md +124 -0
  133. package/dist/docs/guides/agents/prompts.md +495 -0
  134. package/dist/docs/guides/agents/reading-errors.md +112 -0
  135. package/dist/docs/guides/data-quality/check-constraints.md +379 -0
  136. package/dist/docs/guides/data-quality/check-table.md +63 -0
  137. package/dist/docs/guides/data-quality/hand-edits.md +507 -0
  138. package/dist/docs/guides/data-quality/sync-between-devices.md +51 -0
  139. package/dist/docs/guides/data-quality/validation.md +90 -0
  140. package/dist/docs/guides/database-objects/bases.md +480 -0
  141. package/dist/docs/guides/database-objects/catalogs-and-schemas.md +897 -0
  142. package/dist/docs/guides/database-objects/columns.md +1332 -0
  143. package/dist/docs/guides/database-objects/relations.md +1454 -0
  144. package/dist/docs/guides/database-objects/tables.md +1404 -0
  145. package/dist/docs/guides/database-objects/views.md +1003 -0
  146. package/dist/docs/guides/history/back-up.md +195 -0
  147. package/dist/docs/guides/history/change-data-feed.md +247 -0
  148. package/dist/docs/guides/history/journal-and-undo.md +262 -0
  149. package/dist/docs/guides/history/restore.md +341 -0
  150. package/dist/docs/guides/history/table-history.md +657 -0
  151. package/dist/docs/guides/history/vacuum.md +35 -0
  152. package/dist/docs/guides/read-and-write/automatic-links.md +768 -0
  153. package/dist/docs/guides/read-and-write/batch.md +139 -0
  154. package/dist/docs/guides/read-and-write/import-notes.md +896 -0
  155. package/dist/docs/guides/read-and-write/joins-and-aggregates.md +251 -0
  156. package/dist/docs/guides/read-and-write/read-and-filter.md +536 -0
  157. package/dist/docs/guides/read-and-write/write-rows.md +573 -0
  158. package/dist/docs/guides/search/link-graph.md +146 -0
  159. package/dist/docs/guides/search/semantic.md +133 -0
  160. package/dist/docs/guides/search/text.md +142 -0
  161. package/dist/docs/guides/security/security-model.md +110 -0
  162. package/dist/docs/guides/security/tokens.md +60 -0
  163. package/dist/docs/guides/security/who-did-what.md +42 -0
  164. package/dist/docs/index.md +113 -0
  165. package/dist/docs/llms.txt +33 -0
  166. package/dist/docs/reference/cli.md +136 -0
  167. package/dist/docs/reference/compatibility.md +63 -0
  168. package/dist/docs/reference/errors.md +72 -0
  169. package/dist/docs/reference/glossary.md +161 -0
  170. package/dist/docs/reference/operations/auxiliary.md +116 -0
  171. package/dist/docs/reference/operations/history.md +168 -0
  172. package/dist/docs/reference/operations/queries.md +169 -0
  173. package/dist/docs/reference/operations/rows.md +185 -0
  174. package/dist/docs/reference/operations/schema.md +316 -0
  175. package/dist/docs/reference/operations.md +96 -0
  176. package/dist/docs/reference/settings.md +31 -0
  177. package/dist/docs/release-notes.md +67 -0
  178. package/dist/docs/start/connect-agents.md +204 -0
  179. package/dist/docs/start/how-it-works.md +317 -0
  180. package/dist/docs/start/quick-start.md +126 -0
  181. package/package.json +59 -14
@@ -0,0 +1,1003 @@
1
+ # Views
2
+
3
+ > Store a query as a view, change it, join and combine tables in it, put it in a schema, and read the note that shows it.
4
+
5
+ A view is a `select_rows` query stored under a name in `metastore.yaml`. You read it like a table, narrow it with more filters, and agents read it instead of rebuilding the filter, so the answer follows your definition.
6
+
7
+ The examples query a diary of classic films with three tables: `film`, `actor` for the cast, and `watch` with one row per viewing from the last few weeks.
8
+
9
+ ## Store a query as a view
10
+
11
+ `create_view` stores a query in `metastore.yaml` under a name; give it the `vw_` prefix. You select a view like a table and can narrow it further, and it writes no note.
12
+
13
+ Agents find a view by its `description` and `aliases`. `labels` are the column headings a client shows when it renders the view as a table in a note; `select_rows` still answers with the column names.
14
+
15
+ **`create_view`**
16
+
17
+ ```json
18
+ {
19
+ "name": "vw_recent_watches",
20
+ "query": {
21
+ "table": "watch",
22
+ "columns": ["film", "occurred_at", "where", "place"],
23
+ "where": { "occurred_at": { "gte": "{today-14d}" } },
24
+ "order_by": "-occurred_at"
25
+ },
26
+ "description": "Films watched in the last two weeks",
27
+ "aliases": ["what I watched lately"],
28
+ "labels": {
29
+ "film": "Film",
30
+ "occurred_at": "Watched",
31
+ "where": "Where",
32
+ "place": "Place"
33
+ }
34
+ }
35
+ ```
36
+
37
+ Answer:
38
+
39
+ ```json
40
+ {
41
+ "ok": true,
42
+ "op": "create_view",
43
+ "planned_texts": ["datanotes/metastore.yaml"],
44
+ "migration_id": "20261012T071956-create_view-create-view-main-public-vw_rec"
45
+ }
46
+ ```
47
+
48
+ A `where` in `select_rows` narrows the view. It combines with the view’s own filter this way:
49
+
50
+ * **A column the view doesn’t filter**: the engine adds the condition to the view’s filter
51
+ * **A column the view filters**: the engine replaces an operator the view already has and adds a new one
52
+ * **`or`, `and` or `not` on either side**: both filters apply
53
+
54
+ **`select_rows`**
55
+
56
+ ```json
57
+ { "table": "vw_recent_watches", "where": { "where": "home" } }
58
+ ```
59
+
60
+ Answer:
61
+
62
+ ```json
63
+ {
64
+ "table": "main.public.watch",
65
+ "total": 2,
66
+ "rows": [
67
+ {
68
+ "path": "datanotes/main/public/watch/2026-10-10T2030.md",
69
+ "film": "[[The Gold Rush]]",
70
+ "occurred_at": "2026-10-10T20:30",
71
+ "where": "home",
72
+ "place": "my living room"
73
+ },
74
+ {
75
+ "path": "datanotes/main/public/watch/2026-10-07T2115.md",
76
+ "film": "[[Charade]]",
77
+ "occurred_at": "2026-10-07T21:15",
78
+ "where": "home",
79
+ "place": "my living room"
80
+ }
81
+ ],
82
+ "view": "main.public.vw_recent_watches"
83
+ }
84
+ ```
85
+
86
+ `list_tables` lists the views next to the tables:
87
+
88
+ **`list_tables`** (no arguments)
89
+
90
+ Answer:
91
+
92
+ ```json
93
+ {
94
+ "views": [
95
+ {
96
+ "name": "vw_recent_watches",
97
+ "catalog": "main",
98
+ "schema": "public",
99
+ "qualified": "main.public.vw_recent_watches",
100
+ "description": "Films watched in the last two weeks",
101
+ "aliases": ["what I watched lately"]
102
+ }
103
+ ]
104
+ }
105
+ ```
106
+
107
+ ## Change an existing view
108
+
109
+ Once a view exists, `create_view` refuses its name. `replace: true` replaces the whole view, like `CREATE OR REPLACE VIEW` in Structured Query Language (SQL), so pass its `description`, `aliases` and `labels` again.
110
+
111
+ **`create_view`**
112
+
113
+ ```json
114
+ {
115
+ "name": "vw_recent_watches",
116
+ "query": { "table": "watch", "where": { "occurred_at": { "gte": "{today-30d}" } } }
117
+ }
118
+ ```
119
+
120
+ Refused:
121
+
122
+ ```text
123
+ create_view refused — nothing was written:
124
+ conflict main.public.vw_recent_watches: main.public.vw_recent_watches already exists (pass replace)
125
+ Hint: The name is taken: choose another one, or pass replace: true to replace a view.
126
+ Docs: https://datanotes.dev/guides/database-objects/catalogs-and-schemas.md#place-tables-in-catalogs-and-schemas (offline: read_docs("guides/database-objects/catalogs-and-schemas#place-tables-in-catalogs-and-schemas"))
127
+ ```
128
+
129
+ **`create_view`**
130
+
131
+ ```json
132
+ {
133
+ "name": "vw_recent_watches",
134
+ "replace": true,
135
+ "query": {
136
+ "table": "watch",
137
+ "columns": ["film", "occurred_at", "where", "place"],
138
+ "where": { "occurred_at": { "gte": "{today-30d}" } },
139
+ "order_by": "-occurred_at"
140
+ },
141
+ "description": "Films watched in the last month",
142
+ "aliases": ["what I watched lately"],
143
+ "labels": {
144
+ "film": "Film",
145
+ "occurred_at": "Watched",
146
+ "where": "Where",
147
+ "place": "Place"
148
+ }
149
+ }
150
+ ```
151
+
152
+ Answer:
153
+
154
+ ```json
155
+ {
156
+ "ok": true,
157
+ "op": "create_view",
158
+ "planned_texts": ["datanotes/metastore.yaml"],
159
+ "migration_id": "20261012T071956-create_view-create-view-main-public-vw_rec-2"
160
+ }
161
+ ```
162
+
163
+ ## Join and expand rows in a view
164
+
165
+ A view stores any query. This one keeps the last three films watched (`order_by`, `limit`) and puts the notes of each film’s cast inline (`expand`). A `left` join attaches the viewing of the `watched_on` day, so a film with no viewing logged that day gets an empty list.
166
+
167
+ **`create_view`**
168
+
169
+ ```json
170
+ {
171
+ "name": "vw_last_watched",
172
+ "query": {
173
+ "table": "film",
174
+ "columns": ["title", "watched_on", "cast"],
175
+ "where": { "status": "watched" },
176
+ "expand": ["cast"],
177
+ "join": {
178
+ "table": "watch",
179
+ "on": { "watched_on": "date(occurred_at)" },
180
+ "as": "viewing",
181
+ "type": "left",
182
+ "columns": ["where"]
183
+ },
184
+ "order_by": "-watched_on",
185
+ "limit": 3
186
+ }
187
+ }
188
+ ```
189
+
190
+ Answer:
191
+
192
+ ```json
193
+ {
194
+ "ok": true,
195
+ "op": "create_view",
196
+ "planned_texts": ["datanotes/metastore.yaml"],
197
+ "migration_id": "20261012T071956-create_view-create-view-main-public-vw_las"
198
+ }
199
+ ```
200
+
201
+ **`select_rows`**
202
+
203
+ ```json
204
+ { "table": "vw_last_watched" }
205
+ ```
206
+
207
+ Answer (65 lines)
208
+
209
+ ```json
210
+ {
211
+ "table": "main.public.film",
212
+ "total": 6,
213
+ "rows": [
214
+ {
215
+ "path": "datanotes/main/public/film/Night of the Living Dead.md",
216
+ "title": "Night of the Living Dead",
217
+ "watched_on": "2026-10-11",
218
+ "cast": [
219
+ {
220
+ "path": "datanotes/main/public/actor/Duane Jones.md",
221
+ "table": "main.public.actor",
222
+ "id": "01a12efb-7fd1-74b0-ad94-c0ee904521ad",
223
+ "name": "Duane Jones",
224
+ "born": "1937-04-02"
225
+ }
226
+ ],
227
+ "viewing": [
228
+ { "path": "datanotes/main/public/watch/2026-10-11T2200.md", "where": "cinema" }
229
+ ]
230
+ },
231
+ {
232
+ "path": "datanotes/main/public/film/The Gold Rush.md",
233
+ "title": "The Gold Rush",
234
+ "watched_on": "2026-10-10",
235
+ "cast": [
236
+ {
237
+ "path": "datanotes/main/public/actor/Charlie Chaplin.md",
238
+ "table": "main.public.actor",
239
+ "id": "01a12efb-7fb2-7ee5-99d8-282dff2a79e5",
240
+ "name": "Charlie Chaplin",
241
+ "born": "1889-04-16"
242
+ }
243
+ ],
244
+ "viewing": [
245
+ { "path": "datanotes/main/public/watch/2026-10-10T2030.md", "where": "home" }
246
+ ]
247
+ },
248
+ {
249
+ "path": "datanotes/main/public/film/Charade.md",
250
+ "title": "Charade",
251
+ "watched_on": "2026-10-07",
252
+ "cast": [
253
+ {
254
+ "path": "datanotes/main/public/actor/Cary Grant.md",
255
+ "table": "main.public.actor",
256
+ "id": "01a12efb-7fbf-77c3-888b-698d82923a6f",
257
+ "name": "Cary Grant",
258
+ "born": "1904-01-18"
259
+ },
260
+ {
261
+ "path": "datanotes/main/public/actor/Audrey Hepburn.md",
262
+ "table": "main.public.actor",
263
+ "id": "01a12efb-7fca-74dd-b41c-fed458b65dc9",
264
+ "name": "Audrey Hepburn",
265
+ "born": "1929-05-04"
266
+ }
267
+ ],
268
+ "viewing": [
269
+ { "path": "datanotes/main/public/watch/2026-10-07T2115.md", "where": "home" }
270
+ ]
271
+ }
272
+ ],
273
+ "view": "main.public.vw_last_watched"
274
+ }
275
+ ```
276
+
277
+ ## Combine several tables in one view
278
+
279
+ A view with `union` stacks the rows of several queries, like SQL’s `UNION ALL`, on one table or on several. It tags each row with `_source`, and each branch takes `table`, `columns` and `where`. Next to `union`, these keys apply to the whole view:
280
+
281
+ * **`columns`**: the columns of every branch that gives none
282
+ * **`where`**: a filter on every branch, on top of its own
283
+ * **`expand`**: the links to expand in every branch
284
+ * **`order_by`** and **`limit`**: the order and the number of the stacked rows
285
+
286
+ This view stacks the films made before 1960 and the actors. Its `where` keeps the notes that at least two other notes link to: the films and actors your diary mentions most.
287
+
288
+ **`create_view`**
289
+
290
+ ```json
291
+ {
292
+ "name": "vw_most_linked",
293
+ "query": {
294
+ "union": [
295
+ {
296
+ "table": "film",
297
+ "columns": ["title", "year"],
298
+ "where": { "year": { "lt": 1960 } }
299
+ },
300
+ { "table": "actor" }
301
+ ],
302
+ "columns": ["name"],
303
+ "where": { "file_backlink_count": { "gte": 2 } },
304
+ "order_by": "path"
305
+ }
306
+ }
307
+ ```
308
+
309
+ Answer:
310
+
311
+ ```json
312
+ {
313
+ "ok": true,
314
+ "op": "create_view",
315
+ "planned_texts": ["datanotes/metastore.yaml"],
316
+ "migration_id": "20261012T071956-create_view-create-view-main-public-vw_mos"
317
+ }
318
+ ```
319
+
320
+ **`select_rows`**
321
+
322
+ ```json
323
+ { "table": "vw_most_linked" }
324
+ ```
325
+
326
+ Answer:
327
+
328
+ ```json
329
+ {
330
+ "table": "union",
331
+ "total": 3,
332
+ "rows": [
333
+ {
334
+ "path": "datanotes/main/public/actor/Cary Grant.md",
335
+ "name": "Cary Grant",
336
+ "_source": "actor"
337
+ },
338
+ {
339
+ "path": "datanotes/main/public/film/His Girl Friday.md",
340
+ "title": "His Girl Friday",
341
+ "year": 1940,
342
+ "_source": "film"
343
+ },
344
+ {
345
+ "path": "datanotes/main/public/film/The Gold Rush.md",
346
+ "title": "The Gold Rush",
347
+ "year": 1925,
348
+ "_source": "film"
349
+ }
350
+ ],
351
+ "view": "main.public.vw_most_linked"
352
+ }
353
+ ```
354
+
355
+ The branches can read the same table. This view stacks every trip to the cinema and the last week’s viewings at home. It keeps the three latest and puts each film’s note inline:
356
+
357
+ **`create_view`**
358
+
359
+ ```json
360
+ {
361
+ "name": "vw_latest_watches",
362
+ "query": {
363
+ "union": [
364
+ { "table": "watch", "where": { "where": "cinema" } },
365
+ {
366
+ "table": "watch",
367
+ "where": { "where": "home", "occurred_at": { "gte": "{today-7d}" } }
368
+ }
369
+ ],
370
+ "columns": ["film", "occurred_at", "where"],
371
+ "expand": ["film"],
372
+ "order_by": "-occurred_at",
373
+ "limit": 3
374
+ }
375
+ }
376
+ ```
377
+
378
+ Answer:
379
+
380
+ ```json
381
+ {
382
+ "ok": true,
383
+ "op": "create_view",
384
+ "planned_texts": ["datanotes/metastore.yaml"],
385
+ "migration_id": "20261012T071956-create_view-create-view-main-public-vw_lat"
386
+ }
387
+ ```
388
+
389
+ **`select_rows`**
390
+
391
+ ```json
392
+ { "table": "vw_latest_watches" }
393
+ ```
394
+
395
+ Answer (64 lines)
396
+
397
+ ```json
398
+ {
399
+ "table": "union",
400
+ "total": 4,
401
+ "rows": [
402
+ {
403
+ "path": "datanotes/main/public/watch/2026-10-11T2200.md",
404
+ "film": {
405
+ "path": "datanotes/main/public/film/Night of the Living Dead.md",
406
+ "table": "main.public.film",
407
+ "id": "01a12efb-801a-78dc-83af-7ff7ecf09dd4",
408
+ "title": "Night of the Living Dead",
409
+ "year": 1968,
410
+ "minutes": 96,
411
+ "status": "watched",
412
+ "rating": 4,
413
+ "watched_on": "2026-10-11",
414
+ "director": "George A. Romero",
415
+ "cast": ["[[Duane Jones]]"]
416
+ },
417
+ "occurred_at": "2026-10-11T22:00",
418
+ "where": "cinema",
419
+ "_source": "watch"
420
+ },
421
+ {
422
+ "path": "datanotes/main/public/watch/2026-10-10T2030.md",
423
+ "film": {
424
+ "path": "datanotes/main/public/film/The Gold Rush.md",
425
+ "table": "main.public.film",
426
+ "id": "01a12efb-7ffa-7aa8-988d-eba752b6277e",
427
+ "title": "The Gold Rush",
428
+ "year": 1925,
429
+ "minutes": 95,
430
+ "status": "watched",
431
+ "rating": 5,
432
+ "watched_on": "2026-10-10",
433
+ "director": "Charlie Chaplin",
434
+ "cast": ["[[Charlie Chaplin]]"]
435
+ },
436
+ "occurred_at": "2026-10-10T20:30",
437
+ "where": "home",
438
+ "_source": "watch"
439
+ },
440
+ {
441
+ "path": "datanotes/main/public/watch/2026-10-07T2115.md",
442
+ "film": {
443
+ "path": "datanotes/main/public/film/Charade.md",
444
+ "table": "main.public.film",
445
+ "id": "01a12efb-8012-7876-bcf6-ea1a58b2cdec",
446
+ "title": "Charade",
447
+ "year": 1963,
448
+ "minutes": 113,
449
+ "status": "watched",
450
+ "rating": 4,
451
+ "watched_on": "2026-10-07",
452
+ "director": "Stanley Donen",
453
+ "cast": ["[[Cary Grant]]", "[[Audrey Hepburn]]"]
454
+ },
455
+ "occurred_at": "2026-10-07T21:15",
456
+ "where": "home",
457
+ "_source": "watch"
458
+ }
459
+ ],
460
+ "view": "main.public.vw_latest_watches"
461
+ }
462
+ ```
463
+
464
+ `drop_view` removes a view, and `undo_migration` puts it back, as it does for every schema change:
465
+
466
+ **`drop_view`**
467
+
468
+ ```json
469
+ { "view": "vw_most_linked" }
470
+ ```
471
+
472
+ Answer:
473
+
474
+ ```json
475
+ {
476
+ "ok": true,
477
+ "op": "drop_view",
478
+ "planned_texts": ["datanotes/metastore.yaml"],
479
+ "migration_id": "20261012T071956-drop_view-drop-view-main-public-vw_most_"
480
+ }
481
+ ```
482
+
483
+ **`undo_migration`**
484
+
485
+ ```json
486
+ { "migration_id": "20261012T071956-drop_view-drop-view-main-public-vw_most_" }
487
+ ```
488
+
489
+ Answer:
490
+
491
+ ```json
492
+ {
493
+ "ok": true,
494
+ "op": "undo_migration",
495
+ "undoes": "20261012T071956-drop_view-drop-view-main-public-vw_most_",
496
+ "migration_id": "20261012T071956-undo_migration-undo-20261012t071956-drop_view",
497
+ "conflicts": [],
498
+ "failed": []
499
+ }
500
+ ```
501
+
502
+ ## Put a view in a schema of its own
503
+
504
+ `catalog` and `schema` place a view the way they place a table, and default to the database’s default catalog and schema. A schema can hold views only. The query can read the tables of any schema, named here in full.
505
+
506
+ **`create_view`**
507
+
508
+ ```json
509
+ {
510
+ "catalog": "main",
511
+ "schema": "reports",
512
+ "name": "vw_watches_by_place",
513
+ "query": {
514
+ "table": "main.public.watch",
515
+ "group_by": "where",
516
+ "aggregates": { "viewings": "count" }
517
+ },
518
+ "description": "Viewings at the cinema, at home and outdoors"
519
+ }
520
+ ```
521
+
522
+ Answer:
523
+
524
+ ```json
525
+ {
526
+ "ok": true,
527
+ "op": "create_view",
528
+ "planned_texts": ["datanotes/metastore.yaml"],
529
+ "migration_id": "20261012T071956-create_view-create-view-main-reports-vw_wa"
530
+ }
531
+ ```
532
+
533
+ **`select_rows`**
534
+
535
+ ```json
536
+ { "table": "reports.vw_watches_by_place" }
537
+ ```
538
+
539
+ Answer:
540
+
541
+ ```json
542
+ {
543
+ "table": "main.public.watch",
544
+ "total": 3,
545
+ "rows": [
546
+ { "where": "cinema", "viewings": 2 },
547
+ { "where": "home", "viewings": 4 },
548
+ { "where": "outdoor screening", "viewings": 1 }
549
+ ],
550
+ "view": "main.reports.vw_watches_by_place"
551
+ }
552
+ ```
553
+
554
+ ## Read the note that shows a view with `{this.*}`
555
+
556
+ A view can read the note that shows it, so one view serves every note of a kind. These are the tokens:
557
+
558
+ * **`{this.<property>}`**: a front matter key of that note, such as `{this.watched_on}`; `{this.watched_on+1d}` is the day after and `{this.watched_on-1w}` a week before
559
+ * **`{this.link}`**: a link to the note
560
+ * **`{this.name}`** and **`{this.path}`**: the note’s name and vault path
561
+
562
+ The engine stores the tokens as written. It fills them in when `select_rows` names the note with `this_note`, which a client passes when it shows the view inside a note. This `batch` creates four views:
563
+
564
+ * **`vw_watches_that_day`**: the viewings on a film’s `watched_on` day
565
+ * **`vw_film_watches`**: every viewing of a film
566
+ * **`vw_same_status`**: the other films with the film’s status
567
+ * **`vw_films_with`**: the films of an actor’s note
568
+
569
+ **`batch`**
570
+
571
+ ```json
572
+ {
573
+ "summary": "views for film and actor notes",
574
+ "operations": [
575
+ {
576
+ "op": "create_view",
577
+ "name": "vw_watches_that_day",
578
+ "query": {
579
+ "table": "watch",
580
+ "where": {
581
+ "occurred_at": { "gte": "{this.watched_on}", "lt": "{this.watched_on+1d}" }
582
+ },
583
+ "order_by": "occurred_at"
584
+ }
585
+ },
586
+ {
587
+ "op": "create_view",
588
+ "name": "vw_film_watches",
589
+ "query": {
590
+ "table": "watch",
591
+ "columns": ["occurred_at", "where"],
592
+ "where": { "film": "{this.link}" }
593
+ }
594
+ },
595
+ {
596
+ "op": "create_view",
597
+ "name": "vw_same_status",
598
+ "query": {
599
+ "table": "film",
600
+ "columns": ["title"],
601
+ "where": { "status": "{this.status}", "path": { "ne": "{this.path}" } }
602
+ }
603
+ },
604
+ {
605
+ "op": "create_view",
606
+ "name": "vw_films_with",
607
+ "query": {
608
+ "table": "film",
609
+ "columns": ["title", "year"],
610
+ "where": { "cast": { "contains": "[[{this.name}]]" } }
611
+ }
612
+ }
613
+ ]
614
+ }
615
+ ```
616
+
617
+ Answer:
618
+
619
+ ```json
620
+ {
621
+ "ok": true,
622
+ "migration_id": "20261012T071957-batch-views-for-film-and-actor-notes",
623
+ "steps": [
624
+ { "op": "create_view", "ok": true },
625
+ { "op": "create_view", "ok": true },
626
+ { "op": "create_view", "ok": true },
627
+ { "op": "create_view", "ok": true }
628
+ ]
629
+ }
630
+ ```
631
+
632
+ `this_note` takes the vault path of the note the result is for, and the tokens read that note. These are the viewings of The Gold Rush, as its note shows them:
633
+
634
+ **`select_rows`**
635
+
636
+ ```json
637
+ { "table": "vw_film_watches", "this_note": "datanotes/main/public/film/The Gold Rush.md" }
638
+ ```
639
+
640
+ Answer:
641
+
642
+ ```json
643
+ {
644
+ "table": "main.public.watch",
645
+ "total": 2,
646
+ "rows": [
647
+ {
648
+ "path": "datanotes/main/public/watch/2026-09-23T2100.md",
649
+ "occurred_at": "2026-09-23T21:00",
650
+ "where": "home"
651
+ },
652
+ {
653
+ "path": "datanotes/main/public/watch/2026-10-10T2030.md",
654
+ "occurred_at": "2026-10-10T20:30",
655
+ "where": "home"
656
+ }
657
+ ],
658
+ "view": "main.public.vw_film_watches"
659
+ }
660
+ ```
661
+
662
+ For the same note, `{this.watched_on}` is the day you last watched The Gold Rush:
663
+
664
+ **`select_rows`**
665
+
666
+ ```json
667
+ {
668
+ "table": "vw_watches_that_day",
669
+ "columns": ["film", "occurred_at", "where"],
670
+ "this_note": "datanotes/main/public/film/The Gold Rush.md"
671
+ }
672
+ ```
673
+
674
+ Answer:
675
+
676
+ ```json
677
+ {
678
+ "table": "main.public.watch",
679
+ "total": 1,
680
+ "rows": [
681
+ {
682
+ "path": "datanotes/main/public/watch/2026-10-10T2030.md",
683
+ "film": "[[The Gold Rush]]",
684
+ "occurred_at": "2026-10-10T20:30",
685
+ "where": "home"
686
+ }
687
+ ],
688
+ "view": "main.public.vw_watches_that_day"
689
+ }
690
+ ```
691
+
692
+ For Metropolis’s note, `{this.status}` is its `status` and `{this.path}` leaves Metropolis itself out. This is the other film still to watch:
693
+
694
+ **`select_rows`**
695
+
696
+ ```json
697
+ { "table": "vw_same_status", "this_note": "datanotes/main/public/film/Metropolis.md" }
698
+ ```
699
+
700
+ Answer:
701
+
702
+ ```json
703
+ {
704
+ "table": "main.public.film",
705
+ "total": 1,
706
+ "rows": [
707
+ {
708
+ "path": "datanotes/main/public/film/Battleship Potemkin.md",
709
+ "title": "Battleship Potemkin"
710
+ }
711
+ ],
712
+ "view": "main.public.vw_same_status"
713
+ }
714
+ ```
715
+
716
+ For Cary Grant’s note, `[[{this.name}]]` is a link to him:
717
+
718
+ **`select_rows`**
719
+
720
+ ```json
721
+ { "table": "vw_films_with", "this_note": "datanotes/main/public/actor/Cary Grant.md" }
722
+ ```
723
+
724
+ Answer:
725
+
726
+ ```json
727
+ {
728
+ "table": "main.public.film",
729
+ "total": 2,
730
+ "rows": [
731
+ { "path": "datanotes/main/public/film/Charade.md", "title": "Charade", "year": 1963 },
732
+ {
733
+ "path": "datanotes/main/public/film/His Girl Friday.md",
734
+ "title": "His Girl Friday",
735
+ "year": 1940
736
+ }
737
+ ],
738
+ "view": "main.public.vw_films_with"
739
+ }
740
+ ```
741
+
742
+ Without `this_note`, a token stays as written and matches no row, whatever its operator. That holds for `film: "{this.link}"` here, for a `ne` like the `path` one, and for `within`.
743
+
744
+ **`select_rows`**
745
+
746
+ ```json
747
+ { "table": "vw_film_watches" }
748
+ ```
749
+
750
+ Answer:
751
+
752
+ ```json
753
+ {
754
+ "table": "main.public.watch",
755
+ "total": 0,
756
+ "rows": [],
757
+ "view": "main.public.vw_film_watches"
758
+ }
759
+ ```
760
+
761
+ ## See the views in `metastore.yaml`
762
+
763
+ The engine wrote this page’s views under `views:` in their schema, next to the tables. It keeps each query as you gave it, with its relative dates and `{this.*}` tokens.
764
+
765
+ `datanotes/metastore.yaml` now reads:
766
+
767
+ ```yaml
768
+ # Metastore (Datanotes): catalogs, schemas, tables, columns, views.
769
+ id: 01a12efb-7f9b-7cd3-a1ae-d70e8a8d4ef4
770
+ config:
771
+ history: true
772
+
773
+
774
+ catalogs:
775
+ main:
776
+ schemas:
777
+ public:
778
+ tables:
779
+ actor:
780
+ description: Actors of the films I watch
781
+ options:
782
+ filename: "{name}"
783
+ archive: _archive
784
+ columns:
785
+ - name: id
786
+ type: string
787
+ required: true
788
+ description: "Unique id of the row (UUIDv7): set when the row is created, never changes"
789
+ unique: true
790
+ identity: uuid
791
+ dense: true
792
+ - name: name
793
+ type: string
794
+ required: true
795
+ description: The actor's name, as credited
796
+ - name: born
797
+ type: date
798
+ description: The actor's date of birth
799
+ film:
800
+ description: Classic films I watched or want to watch
801
+ options:
802
+ filename: "{title}"
803
+ archive: _archive
804
+ columns:
805
+ - name: id
806
+ type: string
807
+ required: true
808
+ description: "Unique id of the row (UUIDv7): set when the row is created, never changes"
809
+ unique: true
810
+ identity: uuid
811
+ dense: true
812
+ - name: title
813
+ type: string
814
+ required: true
815
+ description: The film's title, as on its poster
816
+ unique: true
817
+ - name: year
818
+ type: integer
819
+ description: Year the film first came out
820
+ - name: minutes
821
+ type: integer
822
+ min: 1
823
+ description: Running time in minutes
824
+ - name: status
825
+ type: enum
826
+ values:
827
+ - to watch
828
+ - watched
829
+ description: Where the film is on my list
830
+ ordered: true
831
+ - name: rating
832
+ type: integer
833
+ min: 1
834
+ max: 5
835
+ description: My rating, from 1 (poor) to 5 (a masterpiece)
836
+ - name: watched_on
837
+ type: date
838
+ description: The day I last watched it
839
+ - name: director
840
+ type: string
841
+ description: Who directed the film
842
+ - name: cast
843
+ type: list
844
+ items: link
845
+ ref: main.public.actor
846
+ description: The actors in the film
847
+ watch:
848
+ description: Each time I watch a film
849
+ options:
850
+ filename: "{occurred_at:YYYY-MM-DD[T]HHmm}"
851
+ archive: _archive
852
+ columns:
853
+ - name: id
854
+ type: string
855
+ required: true
856
+ description: "Unique id of the row (UUIDv7): set when the row is created, never changes"
857
+ unique: true
858
+ identity: uuid
859
+ dense: true
860
+ - name: film
861
+ type: link
862
+ required: true
863
+ ref: main.public.film
864
+ description: The film I watched
865
+ - name: occurred_at
866
+ type: datetime
867
+ required: true
868
+ description: When the film started
869
+ - name: where
870
+ type: enum
871
+ values:
872
+ - cinema
873
+ - home
874
+ - outdoor screening
875
+ description: The kind of place I watched it in
876
+ - name: place
877
+ type: string
878
+ description: The cinema, park or room, in my words
879
+ - name: with
880
+ type: string
881
+ description: Who watched it with me
882
+ views:
883
+ vw_recent_watches:
884
+ description: Films watched in the last month
885
+ aliases:
886
+ - what I watched lately
887
+ labels:
888
+ film: Film
889
+ occurred_at: Watched
890
+ where: Where
891
+ place: Place
892
+ query:
893
+ table: watch
894
+ columns:
895
+ - film
896
+ - occurred_at
897
+ - where
898
+ - place
899
+ where:
900
+ occurred_at:
901
+ gte: "{today-30d}"
902
+ order_by: -occurred_at
903
+ vw_last_watched:
904
+ query:
905
+ table: film
906
+ columns:
907
+ - title
908
+ - watched_on
909
+ - cast
910
+ where:
911
+ status: watched
912
+ expand:
913
+ - cast
914
+ join:
915
+ table: watch
916
+ on:
917
+ watched_on: date(occurred_at)
918
+ as: viewing
919
+ type: left
920
+ columns:
921
+ - where
922
+ order_by: -watched_on
923
+ limit: 3
924
+ vw_most_linked:
925
+ query:
926
+ union:
927
+ - table: film
928
+ columns:
929
+ - title
930
+ - year
931
+ where:
932
+ year:
933
+ lt: 1960
934
+ - table: actor
935
+ columns:
936
+ - name
937
+ where:
938
+ file_backlink_count:
939
+ gte: 2
940
+ order_by: path
941
+ vw_latest_watches:
942
+ query:
943
+ union:
944
+ - table: watch
945
+ where:
946
+ where: cinema
947
+ - table: watch
948
+ where:
949
+ where: home
950
+ occurred_at:
951
+ gte: "{today-7d}"
952
+ columns:
953
+ - film
954
+ - occurred_at
955
+ - where
956
+ expand:
957
+ - film
958
+ order_by: -occurred_at
959
+ limit: 3
960
+ vw_watches_that_day:
961
+ query:
962
+ table: watch
963
+ where:
964
+ occurred_at:
965
+ gte: "{this.watched_on}"
966
+ lt: "{this.watched_on+1d}"
967
+ order_by: occurred_at
968
+ vw_film_watches:
969
+ query:
970
+ table: watch
971
+ columns:
972
+ - occurred_at
973
+ - where
974
+ where:
975
+ film: "{this.link}"
976
+ vw_same_status:
977
+ query:
978
+ table: film
979
+ columns:
980
+ - title
981
+ where:
982
+ status: "{this.status}"
983
+ path:
984
+ ne: "{this.path}"
985
+ vw_films_with:
986
+ query:
987
+ table: film
988
+ columns:
989
+ - title
990
+ - year
991
+ where:
992
+ cast:
993
+ contains: "[[{this.name}]]"
994
+ reports:
995
+ views:
996
+ vw_watches_by_place:
997
+ description: Viewings at the cinema, at home and outdoors
998
+ query:
999
+ table: main.public.watch
1000
+ group_by: where
1001
+ aggregates:
1002
+ viewings: count
1003
+ ```