Computed fields
Computed fields derive frontmatter values from the rest of the collection:
- Formula evaluates fields on the same record.
- Lookup follows an outgoing Relation and copies a target field.
- Rollup gathers related values and reduces them with a formula.
Definitions live in .markdownvdb.schema.yml. Successful results are materialized into the owning Markdown file’s frontmatter, so they can be filtered, sorted, searched, exported, and read by tools that know nothing about Markdown VDB.
Formula and Lookup/Rollup are built-in modules. They run during full and incremental ingestion, while watching, after relevant schema changes, or when requested explicitly.
Formula
This definition computes an invoice total:
scopes:
invoices:
fields:
total:
field_type: formula
formula: quantity * unit_price
result_type: number
Given:
---
quantity: 4
unit_price: 125
---
# Invoice 1042
ingestion writes total: 500 into the same frontmatter.
Validate an expression before adopting it:
mdvdb modules validate formula \
--formula 'quantity * unit_price' \
--result-type number
Run Formula for one scope:
mdvdb modules run formula --path invoices --json
Supported result types are string, number, boolean, date, datetime, list, and JSON. Expressions use a deterministic, sandboxed JavaScript-like expression language. They cannot access the filesystem, network, or host JavaScript runtime.
Lookup
Lookup follows a Relation on the current record and returns an exact top-level field from the target:
scopes:
projects:
fields:
client:
field_type: relation
target: clients
client_domain:
field_type: lookup
relation_field: client
target_field: domain
With this project:
client: clients/acme.md
and clients/acme.md containing:
domain: acme.example
the project receives client_domain: acme.example.
A scalar Relation produces a scalar value or null. A Relation list produces an ordered list. Lookup is outgoing only and does not accept a formula or result type.
Rollup
An incoming Rollup finds records in a source folder whose selected Relation points to the current record. This client field totals all related invoices:
scopes:
clients:
fields:
invoice_total:
field_type: rollup
relation_direction: incoming
relation_scope: invoices
relation_field: client
target_field: total
formula: values.reduce((sum, value) => sum + Number(value), 0)
result_type: number
Here relation_scope is the Collection-relative folder to scan, relation_field is the ordinary Relation on each invoice, and target_field is the invoice value to collect. The formula reduces the collected values array.
Validate the reducer:
mdvdb modules validate lookup_rollup \
--formula 'values.reduce((sum, value) => sum + Number(value), 0)' \
--result-type number
Then run the module:
mdvdb modules run lookup_rollup --path clients --json
An outgoing Rollup instead follows a Relation on the owner. It omits relation_direction and relation_scope:
scopes:
portfolios:
fields:
projects:
field_type: relation
target: projects
total_budget:
field_type: rollup
relation_field: projects
target_field: budget
formula: values.reduce((sum, value) => sum + Number(value), 0)
result_type: number
Execution and dependencies
Computed values are module-owned output:
- Formula fields run before Lookup and Rollup.
- Lookup and Rollup definitions are dependency-ordered when one computed field consumes another.
- All successful writes for a module run are applied atomically.
- Failures produce diagnostics rather than a partial aggregate.
- A stale owned output is removed when its definition no longer produces a valid value.
Inspect built-in modules and their latest state with:
mdvdb modules list --json
mdvdb modules status formula --path invoices --json
mdvdb modules status lookup_rollup --path clients --json
The Markdown frontmatter is authoritative after materialization; computed results are not only an index cache. Rewriting a computed value preserves unrelated YAML and the body, and an unchanged body does not need to be re-embedded.
Design safely
- Treat computed output fields as read-only. The next module run may replace a manual edit.
- Choose new field names carefully. Adding a computed definition with the same name as an authored field adopts that field as module-owned output.
- A Formula reads local fields only.
- Lookup and Rollup target exact top-level fields, not arbitrary nested selectors.
- Lookup cannot traverse incoming Relations.
- An incoming Rollup requires both relation_scope and an ordinary relation_field in that scope.
- Computed strings that look like Markdown links do not create Relation graph edges.
Use schema to inspect the resolved definitions and Relations to verify the fields used for traversal.
Query computed values
Once materialized, computed fields behave like other frontmatter fields:
mdvdb collection invoices \
--recursive \
--sort total \
--order desc \
--json
This is part of Markdown VDB’s SQL-like structured-data model: Formula, Lookup, and Rollup values act like computed columns, but there is no SQL expression or JOIN interface.
Tesseract