Skip to main content
DQL (Dataview Query Language) treats your frontmatter as a table. You ask for pages by folder, tag, or field value and get rows back. It is modeled on Obsidian Dataview but runs on the server against the SQLite index, so agents, scripts, and the web UI all get the same answer.
DQL needs the default sqlite search engine. With --search grep, query endpoints return 501.

Run a query

Terminal
Flags: --format (table, list, json, count), --limit, --sort, --order, --root.

Query types

The first word picks the shape of the result. Add WITHOUT ID after TABLE or LIST to drop the page path column.

Clauses

Clauses can appear in any order after the type. WHERE and SORT can repeat; repeated WHERE clauses are joined with AND.

Fields

A bare name reads a frontmatter key: status reads status:. Use dots for nested keys (owner.team) and backticks for names with spaces or other characters (`due date`). Hyphens are allowed in bare names, so source-of-truth and missing-rate work as written.

Implicit fields

Every page has these, with or without frontmatter:

Computed fields

At index time, KiwiFS adds these keys to each page’s indexed frontmatter. They are queryable like any other field and never written to your files.

Expressions

WHERE clauses and TABLE columns accept expressions. Keywords are case-insensitive. DQL has no arithmetic operators; use custom computed fields when you need a derived number.

Functions

Function names are case-insensitive.
sum(f), average(f), min(f), max(f), and join(f, separator) combine values across rows.
rollup(link_field, expr) evaluates expr on every page the row links to and returns the results as an array.
The first argument names a typed link field listed in [links] typed_fields. Use links or outlinks to follow every outbound wiki link instead. Traversal is one hop, and a page with no matching links gets an empty array.

Records and claims

Frontmatter holds one set of values per page. When a page holds a table of values, put it in a kiwi-data block and query each record as its own row.
datasets/housing.md
Field names resolve against the record first, then fall back to the page’s frontmatter. Force one side with record.field or page.field. Record rows add _kind, _block (which block on the page), and _record (position in the block). FROM CLAIMS works the same way over :::claim directives, one row per claim, with fields such as text, evidence, confidence, and source. The evidence class is optional:
See Widgets for the kiwi-data and :::claim syntax.

Aggregation

Group pages by a field and compute summaries without writing a query.
Calculations are count, avg:field, sum:field, min:field, and max:field. Narrow the input with a DQL where filter. Over MCP, call kiwi_aggregate.

Computed views

A computed view is a markdown file whose body is generated from a query. Set kiwi-view: true and kiwi-query in its frontmatter:
reports/drafts.md
KiwiFS renders the results between <!-- kiwi:auto --> and <!-- /kiwi-view --> markers and leaves the rest of the body alone. The file is rewritten only when the output changes. kiwi-format accepts table (default), list, count, distinct, json, and task. Views regenerate when you read them through the API, and on demand:
Terminal
Over REST, use POST /api/kiwi/view/refresh with {"path": "reports/drafts.md"}. Over MCP, call kiwi_view_refresh.

Custom computed fields

Define your own derived fields in config. They are evaluated at index time from frontmatter, stored in the index alongside it, and queryable in DQL.
.kiwi/config.toml
Expressions support +, -, *, /, comparisons, &&, ||, and the functions len, days_since, min, max, and abs. Run kiwifs reindex after you change them. Your source files are never modified.

Metadata API

Query, aggregate, and view refresh endpoints.

Bases views

Saved table and board views built on queries.

Search

Find pages by text instead of fields.

Schemas

Keep the frontmatter you query consistent.
Last modified on October 19, 2026