Skip to content

Queries

  • Where - a fence whose info string is sql query, optionally followed by a name, runs as a query; any other sql fence stays code. A .sql file is a query too: its page shows the query and its result, and a reference to it - {table: queries/busy.sql}, a chart's data.url, or from 'busy.sql' in another query - uses its result.
  • Results - an unnamed query shows its result as a table below its fence, which folds away under Query. #<name> refers to a named query of the same document in an embed, a chart's data.url or a figure - not inside another query - and a part follows its own #: {value: #top#0.city}. A named query shows its result below its fence too.
  • Files - a table named like a path, in quotes, is a data file: it resolves as an embed reference does, against the file holding the query, and 'file#table' names a database's table. read_csv, read_json, read_ndjson, read_parquet and their variants read only files of the folder named in quotes. A name holding * or ? is a pattern - ** crosses folders, * and ? stay in one - and reads every data file it matches, thousands included, stacked by column name as union all by name stacks them, with each row's file in a filename column, its path in the served folder; a matched file's own filename column is kept as filename_1, and a pattern matching nothing is an empty table. A pattern matching a SQLite or DuckDB file fails, naming the file, unless it names a table after a #, which each database it matches is then read by. A reader given a pattern reads every match, stacked by column name - its own filename = true adds each file's full path - and fails when there is none, until a match appears, or when one is a database. In a pattern named as a table, each JSON, JSON Lines and YAML file keeps its own shape: a file of plain values beside files of records adds a json column rather than turning the records into values. Matches are only files inside the served folder: one reached through a symlink leading out is left out, and symlinked folders are not followed. A file matching later, or one removed, is followed as any other file the query reads. range, generate_series and unnest also run; no other table function does.
  • Documents - the folder's documents are tables too, with nothing to declare: folio.documents (path; filename, the file's name without its folder; its one h1 as title, an h1 inside <details> not counted, null with no h1, an empty one or several; and kind), folio.headings (path, line, level, text, anchor - every heading the file's own text draws, in a quote or raw HTML too), folio.links (path; line; url, the address as written; target, the path it leads to; fragment; and context, the text around it), folio.tasks (path, line, done, text), folio.frontmatter (path, key, value, a value that is not text as JSON) and folio.tables (path, line, row, header and cells as lists). Each declared kind is kind.<name>: a row per document of the kind, its path and a column per field, so no field is named path - text and links as text, numbers as doubles, dates as dates, or as timestamps when any holds a time - a time written with an offset moved to UTC - lists as lists. A field two declarations type apart is text, each document's value as its own declaration reads it. Content an include brings into a page - headings, links, tasks, tables, labels and sections - counts in the file holding it, never in the page including it. They join with data files and each other, and show as a table, chart or value like any query.
  • Limits - one SELECT statement only; PIVOT, COPY, ATTACH and anything that would change a file are refused before running. A reference outside the served folder, through a symlink or not, and a web address are refused. A query stops after 10 seconds of running, not counting time spent waiting for its turn, as at most three engine reads run at once - a data page or embed reading through the engine stops after 10 seconds too; a result keeps its first 10,000 rows, and no more than 16 MB of them, and says so; a single value longer than 20,000 characters - fewer when a result has more than ten columns, down to 1,000, and on a data page a nested value only past 100,000 - is cut short with an ellipsis, and a binary value shows as its size. The engine uses at most 1 GB of memory across everything it runs: a sort or join past that spills to disk, and a query needing more for one value fails with DuckDB's out-of-memory error. Each shows its reason in place, and the page never waits for a query.
  • Updates - an embed, chart or query result redraws on every open page when a file it reads - its data file, a .sql file, or any file its query reads - is saved, created or removed, and, when its query reads the folder's documents, when a change reaches a document table it reads; nothing else on the page redraws, and a table keeps its view. A redraw already under way runs once more when it ends, so a file written continuously is followed without a blank moment. An embed above the line being read keeps that line where it is. A file no open embed reads runs nothing.
  • Dependencies - a document's ?model lists, under data, every data and query file its embeds, charts and queries read.