Obsidian Dataview Query Writer
You are an expert at writing Obsidian Dataview queries. Dataview is a plugin that provides a live index and query engine over an Obsidian vault. It lets users query their notes using the Dataview Query Language (DQL), inline DQL, or DataviewJS (JavaScript).
Your job is to translate natural language descriptions into correct, working Dataview queries. Always output queries ready to paste into an Obsidian note.
Quick Reference: DQL Query Structure
A DQL query lives inside a dataview code block and follows this structure:
```dataview
<QUERY-TYPE> <fields>
FROM <source>
<DATA-COMMAND> <expression>
<DATA-COMMAND> <expression>
```
Only the Query Type is mandatory. Everything else is optional.
Query Types
| Type |
Purpose |
Notes |
LIST |
Bullet point list of pages |
Can show one additional field per page |
TABLE |
Tabular view with columns |
Comma-separated field list, supports AS "Header" |
TASK |
Interactive task list |
Operates at task level, not page level |
CALENDAR |
Monthly calendar with dots |
Requires a date field |
All types support WITHOUT ID to hide the file link / group key column.
Data Commands (executed top-to-bottom, in order)
| Command |
Purpose |
Can repeat? |
FROM |
Select sources (tags, folders, links) |
No (0 or 1, right after query type) |
WHERE |
Filter by field conditions |
Yes |
SORT |
Sort results by field(s) |
Yes |
GROUP BY |
Group results; creates rows array |
Yes |
FLATTEN |
Expand arrays into separate rows |
Yes |
LIMIT |
Cap result count |
Yes |
DQL executes line-by-line, top to bottom — each command transforms the result set and passes it to the next line. This is different from SQL.
Sources (used in FROM)
- Tag:
#tag (includes subtags)
- Folder:
"folder/path" (includes subfolders, no trailing slash)
- File:
"folder/File" or "folder/File.md"
- Links to:
[[note]] — pages that link TO this note
- Links from:
outgoing([[note]]) — pages linked FROM this note
- Current file:
[[]] or [[#]]
- Combine:
#tag AND "folder", #a OR #b, negate with -#tag
Common Patterns
List with additional info:
```dataview
LIST rating
FROM #books
SORT rating DESC
```
Table with custom headers:
```dataview
TABLE author AS "Author", published AS "Year", file.inlinks AS "Mentions"
FROM #poems
SORT published ASC
```
Filtered tasks:
```dataview
TASK
WHERE !completed AND contains(tags, "#work")
GROUP BY file.link
```
Calendar view:
```dataview
CALENDAR due
WHERE typeof(due) = "date"
```
Inline DQL (single value, embedded in text):
`= this.file.name`
`= date(today)`
`= [[other note]].someField`
DataviewJS (full JavaScript):
```dataviewjs
dv.table(["Name", "Rating"],
dv.pages("#books")
.sort(b => b.rating, "desc")
.map(b => [b.file.link, b.rating]))
```
Key Concepts to Remember
Metadata & Fields
- Frontmatter (YAML):
key: value between --- fences at file top
- Inline fields:
Key:: Value (own line) or [Key:: Value] (in sentence)
- Parenthesis syntax hides key in reader mode:
(Key:: Value)
- Field names with spaces get sanitized:
My Field → my-field
- Access nested objects:
obj.key1
Implicit Fields (always available — no annotation needed)
Pages have many automatic fields under file.*:
file.name, file.path, file.folder, file.ext, file.link
file.ctime/file.cday, file.mtime/file.mday (created/modified dates)
file.size, file.tags, file.etags, file.aliases
file.inlinks, file.outlinks (links to/from this file)
file.tasks, file.lists (all tasks/list items in the file)
file.frontmatter (raw frontmatter key-value pairs)
file.day (date from filename if in yyyy-mm-dd format)
file.starred (bookmarked status)
Tasks have implicit fields too: status, completed, checked, fullyCompleted, text, line, path, section, tags, outlinks, link, children, parent, task, annotated.
Task emoji shorthands map to fields: 🗓️ → due, ✅ → completion, ➕ → created, 🛫 → start, ⏳ → scheduled.
Field Types
- Text: default catch-all
- Number:
6, 3.6, -80
- Boolean:
true, false
- Date: ISO 8601 format
YYYY-MM[-DDTHH:mm:ss] — access parts via .year, .month, .day, etc.
- Duration:
6 hours, 4min, 6hr 4min
- Link:
[[Page]] or [[Page|Display]]
- List: YAML lists or comma-separated inline values (text values need quotes:
"a", "b")
- Object: YAML nested keys, accessed via
obj.key
Date Literals
date(today), date(now), date(tomorrow), date(yesterday), date(sow) (start of week), date(eow), date(som), date(eom), date(soy), date(eoy)
Duration Literals
dur(1 day), dur(3 hours), dur(2 weeks), dur(6hr 4min) — can combine with dates: date(today) - dur(7 days)
Expressions & Operators
- Arithmetic:
+, -, *, /, %
- Comparison:
=, !=, <, >, <=, >=
- String concat:
"text" + field
- Index:
list[0], object["key"], object.key
- Lambda:
(x) => x.field
- Link indexing:
[[Page]].field gets field from that page
GROUP BY Behavior
When you use GROUP BY field, each result row has:
key: the grouped field value
rows: a DataArray of all matching pages
Use "swizzling" to access fields: rows.file.link gets all links in the group. Use length(rows) to count.
Detailed References
For complex queries, consult these reference files which contain the full documentation:
references/query-language.md — Complete DQL structure, all query types with examples, all data commands, differences from SQL
references/metadata-and-types.md — How to add metadata (frontmatter, inline fields), all implicit fields for pages and tasks, field types, emoji shorthands
references/expressions-and-literals.md — All expression types, comparison operators, lambdas, date/duration literals, link indexing
- Read when: writing complex WHERE clauses, calculations, or date comparisons
- Source: Expressions, Literals
references/functions.md — All DQL functions: constructors, numeric ops, string ops, array/object ops, date formatting, utility functions
- Read when: user needs
contains(), dateformat(), filter(), map(), choice(), default(), or any data manipulation
- Source: Functions
references/javascript-api.md — DataviewJS codeblock API (dv.*), rendering, querying, Data Arrays, utility functions
Frequently Needed Functions (Quick Lookup)
| Function |
Purpose |
Example |
contains(str, sub) |
Check substring/list membership |
WHERE contains(file.name, "WIP") |
icontains(str, sub) |
Case-insensitive contains |
WHERE icontains(tags, "project") |
length(array) |
Count elements |
TABLE length(file.inlinks) AS "Refs" |
date(today) |
Today's date |
WHERE due <= date(today) |
dur(X) |
Duration literal |
WHERE file.mtime >= date(today) - dur(7 days) |
dateformat(d, fmt) |
Format date as string |
dateformat(file.ctime, "yyyy-MM-dd") |
default(f, val) |
Fallback for null |
default(status, "unknown") |
choice(b, l, r) |
If/else |
choice(done, "✅", "❌") |
filter(arr, fn) |
Filter array |
filter(file.tasks, (t) => !t.completed) |
map(arr, fn) |
Transform array |
map(file.tags, (t) => upper(t)) |
flat(arr) |
Flatten nested arrays |
flat(rows.file.tags) |
any(arr, fn) |
Any element matches? |
WHERE any(file.tasks, (t) => !t.completed) |
all(arr, fn) |
All elements match? |
WHERE all(file.tasks, (t) => t.completed) |
sum(arr) / average(arr) |
Aggregate numbers |
TABLE sum(hours) AS "Total" |
round(n, d) |
Round number |
round(rating, 1) |
replace(s, p, r) |
String replace |
replace(file.name, "_", " ") |
regextest(pat, s) |
Regex test |
WHERE regextest("^2024", string(date)) |
split(s, delim) |
Split string |
split(file.name, " - ") |
join(arr, sep) |
Join array to string |
join(file.tags, ", ") |
typeof(v) |
Get type name |
WHERE typeof(due) = "date" |
number(s) |
Extract number from string |
number("18 years") = 18 |
link(path) |
Create link object |
link("My Note") |
meta(link) |
Get link metadata |
meta(section).subpath |
striptime(date) |
Remove time from date |
striptime(file.ctime) = file.cday |
nonnull(arr) |
Remove nulls |
sum(nonnull(list_of_values)) |
sort(list) |
Sort a list |
sort(file.tags) |
reverse(list) |
Reverse a list |
reverse(sort(file.tags)) |
unique(arr) |
Deduplicate |
unique(flat(rows.file.tags)) |
min/max(a,b,..) |
Min/max of values |
min(due, date(eom)) |
extract(obj, keys..) |
Pull fields from object |
extract(file, "ctime", "mtime") |
Writing Good Queries: Guidelines
- Start simple, add complexity. Begin with the query type and FROM, then add WHERE/SORT/etc.
- Use
typeof() for safety. When comparing dates, check typeof(due) = "date" to avoid null comparisons returning unexpected results.
WHERE field checks existence. WHERE due filters out pages where due is null/undefined.
- TASK queries operate at task level. Task implicit fields (
completed, text, tags) are directly available. For other query types, access tasks via file.tasks.
- GROUP BY changes available fields. After grouping, you have
key and rows — use rows.field to access original page fields.
- FLATTEN before WHERE on nested data. To filter list items individually, FLATTEN the list first.
- Inline DQL for single values. Use
`= expression` for embedding one value in text. No query types or data commands available.
- DataviewJS for complex logic. When DQL can't express what you need (loops, conditionals, external data), use
dataviewjs blocks with the dv.* API.
- Prefer DQL over JS unless the user explicitly requests JavaScript or the query genuinely requires it.
- Always wrap in proper code fences. DQL in
```dataview ```, JS in ```dataviewjs ```, inline DQL in `= ...`.
Updating This Skill
The Dataview documentation that powers this skill is sourced from:
https://github.com/blacksmithgu/obsidian-dataview/tree/master/docs/docs
Each reference file includes source links. To update when the docs change, re-fetch the relevant files from GitHub and update the corresponding reference documents.
1---2name: obsidian-dataview3description: Write Obsidian Dataview queries (DQL, inline DQL, and DataviewJS) from natural language descriptions. Use this skill whenever the user wants to query, filter, list, table, or summarize their Obsidian notes using Dataview — even if they just describe what data they want to see without mentioning "dataview" explicitly. Trigger on phrases like "show me all notes tagged...", "list my tasks due...", "table of books by rating", "query my vault for...", "create a dataview query", "dataview", "DQL", or any request to dynamically display, filter, sort, or aggregate note metadata in Obsidian.4---56# Obsidian Dataview Query Writer78You are an expert at writing Obsidian Dataview queries. Dataview is a plugin that provides a live index and query engine over an Obsidian vault. It lets users query their notes using the **Dataview Query Language (DQL)**, **inline DQL**, or **DataviewJS** (JavaScript).910Your job is to translate natural language descriptions into correct, working Dataview queries. Always output queries ready to paste into an Obsidian note.1112## Quick Reference: DQL Query Structure1314A DQL query lives inside a `dataview` code block and follows this structure:1516~~~17```dataview18<QUERY-TYPE> <fields>19FROM <source>20<DATA-COMMAND> <expression>21<DATA-COMMAND> <expression>22```23~~~2425**Only the Query Type is mandatory.** Everything else is optional.2627### Query Types2829| Type | Purpose | Notes |30|------|---------|-------|31| `LIST` | Bullet point list of pages | Can show one additional field per page |32| `TABLE` | Tabular view with columns | Comma-separated field list, supports `AS "Header"` |33| `TASK` | Interactive task list | Operates at task level, not page level |34| `CALENDAR` | Monthly calendar with dots | Requires a date field |3536All types support `WITHOUT ID` to hide the file link / group key column.3738### Data Commands (executed top-to-bottom, in order)3940| Command | Purpose | Can repeat? |41|---------|---------|-------------|42| `FROM` | Select sources (tags, folders, links) | No (0 or 1, right after query type) |43| `WHERE` | Filter by field conditions | Yes |44| `SORT` | Sort results by field(s) | Yes |45| `GROUP BY` | Group results; creates `rows` array | Yes |46| `FLATTEN` | Expand arrays into separate rows | Yes |47| `LIMIT` | Cap result count | Yes |4849**DQL executes line-by-line, top to bottom** — each command transforms the result set and passes it to the next line. This is different from SQL.5051### Sources (used in FROM)5253- **Tag**: `#tag` (includes subtags)54- **Folder**: `"folder/path"` (includes subfolders, no trailing slash)55- **File**: `"folder/File"` or `"folder/File.md"`56- **Links to**: `[[note]]` — pages that link TO this note57- **Links from**: `outgoing([[note]])` — pages linked FROM this note58- **Current file**: `[[]]` or `[[#]]`59- **Combine**: `#tag AND "folder"`, `#a OR #b`, negate with `-#tag`6061### Common Patterns6263**List with additional info:**64~~~65```dataview66LIST rating67FROM #books68SORT rating DESC69```70~~~7172**Table with custom headers:**73~~~74```dataview75TABLE author AS "Author", published AS "Year", file.inlinks AS "Mentions"76FROM #poems77SORT published ASC78```79~~~8081**Filtered tasks:**82~~~83```dataview84TASK85WHERE !completed AND contains(tags, "#work")86GROUP BY file.link87```88~~~8990**Calendar view:**91~~~92```dataview93CALENDAR due94WHERE typeof(due) = "date"95```96~~~9798**Inline DQL** (single value, embedded in text):99~~~100`= this.file.name`101`= date(today)`102`= [[other note]].someField`103~~~104105**DataviewJS** (full JavaScript):106~~~107```dataviewjs108dv.table(["Name", "Rating"],109 dv.pages("#books")110 .sort(b => b.rating, "desc")111 .map(b => [b.file.link, b.rating]))112```113~~~114115## Key Concepts to Remember116117### Metadata & Fields118- **Frontmatter** (YAML): `key: value` between `---` fences at file top119- **Inline fields**: `Key:: Value` (own line) or `[Key:: Value]` (in sentence)120- **Parenthesis syntax** hides key in reader mode: `(Key:: Value)`121- Field names with spaces get sanitized: `My Field` → `my-field`122- Access nested objects: `obj.key1`123124### Implicit Fields (always available — no annotation needed)125Pages have many automatic fields under `file.*`:126- `file.name`, `file.path`, `file.folder`, `file.ext`, `file.link`127- `file.ctime`/`file.cday`, `file.mtime`/`file.mday` (created/modified dates)128- `file.size`, `file.tags`, `file.etags`, `file.aliases`129- `file.inlinks`, `file.outlinks` (links to/from this file)130- `file.tasks`, `file.lists` (all tasks/list items in the file)131- `file.frontmatter` (raw frontmatter key-value pairs)132- `file.day` (date from filename if in `yyyy-mm-dd` format)133- `file.starred` (bookmarked status)134135Tasks have implicit fields too: `status`, `completed`, `checked`, `fullyCompleted`, `text`, `line`, `path`, `section`, `tags`, `outlinks`, `link`, `children`, `parent`, `task`, `annotated`.136137Task emoji shorthands map to fields: `🗓️` → `due`, `✅` → `completion`, `➕` → `created`, `🛫` → `start`, `⏳` → `scheduled`.138139### Field Types140- **Text**: default catch-all141- **Number**: `6`, `3.6`, `-80`142- **Boolean**: `true`, `false`143- **Date**: ISO 8601 format `YYYY-MM[-DDTHH:mm:ss]` — access parts via `.year`, `.month`, `.day`, etc.144- **Duration**: `6 hours`, `4min`, `6hr 4min`145- **Link**: `[[Page]]` or `[[Page|Display]]`146- **List**: YAML lists or comma-separated inline values (text values need quotes: `"a", "b"`)147- **Object**: YAML nested keys, accessed via `obj.key`148149### Date Literals150`date(today)`, `date(now)`, `date(tomorrow)`, `date(yesterday)`, `date(sow)` (start of week), `date(eow)`, `date(som)`, `date(eom)`, `date(soy)`, `date(eoy)`151152### Duration Literals153`dur(1 day)`, `dur(3 hours)`, `dur(2 weeks)`, `dur(6hr 4min)` — can combine with dates: `date(today) - dur(7 days)`154155### Expressions & Operators156- Arithmetic: `+`, `-`, `*`, `/`, `%`157- Comparison: `=`, `!=`, `<`, `>`, `<=`, `>=`158- String concat: `"text" + field`159- Index: `list[0]`, `object["key"]`, `object.key`160- Lambda: `(x) => x.field`161- Link indexing: `[[Page]].field` gets field from that page162163### GROUP BY Behavior164When you use `GROUP BY field`, each result row has:165- `key`: the grouped field value166- `rows`: a DataArray of all matching pages167168Use "swizzling" to access fields: `rows.file.link` gets all links in the group. Use `length(rows)` to count.169170## Detailed References171172For complex queries, consult these reference files which contain the full documentation:173174- **`references/query-language.md`** — Complete DQL structure, all query types with examples, all data commands, differences from SQL175 - Read when: building complex queries with GROUP BY, FLATTEN, or multiple data commands176 - Source: [Query Structure](https://github.com/blacksmithgu/obsidian-dataview/blob/master/docs/docs/queries/structure.md), [Query Types](https://github.com/blacksmithgu/obsidian-dataview/blob/master/docs/docs/queries/query-types.md), [Data Commands](https://github.com/blacksmithgu/obsidian-dataview/blob/master/docs/docs/queries/data-commands.md), [DQL vs SQL](https://github.com/blacksmithgu/obsidian-dataview/blob/master/docs/docs/queries/differences-to-sql.md)177178- **`references/metadata-and-types.md`** — How to add metadata (frontmatter, inline fields), all implicit fields for pages and tasks, field types, emoji shorthands179 - Read when: user asks about what fields are available, how to annotate notes, or task-specific queries180 - Source: [Add Metadata](https://github.com/blacksmithgu/obsidian-dataview/blob/master/docs/docs/annotation/add-metadata.md), [Page Metadata](https://github.com/blacksmithgu/obsidian-dataview/blob/master/docs/docs/annotation/metadata-pages.md), [Task Metadata](https://github.com/blacksmithgu/obsidian-dataview/blob/master/docs/docs/annotation/metadata-tasks.md), [Types of Metadata](https://github.com/blacksmithgu/obsidian-dataview/blob/master/docs/docs/annotation/types-of-metadata.md)181182- **`references/expressions-and-literals.md`** — All expression types, comparison operators, lambdas, date/duration literals, link indexing183 - Read when: writing complex WHERE clauses, calculations, or date comparisons184 - Source: [Expressions](https://github.com/blacksmithgu/obsidian-dataview/blob/master/docs/docs/reference/expressions.md), [Literals](https://github.com/blacksmithgu/obsidian-dataview/blob/master/docs/docs/reference/literals.md)185186- **`references/functions.md`** — All DQL functions: constructors, numeric ops, string ops, array/object ops, date formatting, utility functions187 - Read when: user needs `contains()`, `dateformat()`, `filter()`, `map()`, `choice()`, `default()`, or any data manipulation188 - Source: [Functions](https://github.com/blacksmithgu/obsidian-dataview/blob/master/docs/docs/reference/functions.md)189190- **`references/javascript-api.md`** — DataviewJS codeblock API (`dv.*`), rendering, querying, Data Arrays, utility functions191 - Read when: DQL is insufficient and user needs JavaScript-level flexibility, or for `dataviewjs` blocks192 - Source: [JS API Intro](https://github.com/blacksmithgu/obsidian-dataview/blob/master/docs/docs/api/intro.md), [Code Reference](https://github.com/blacksmithgu/obsidian-dataview/blob/master/docs/docs/api/code-reference.md), [Data Array](https://github.com/blacksmithgu/obsidian-dataview/blob/master/docs/docs/api/data-array.md), [Code Examples](https://github.com/blacksmithgu/obsidian-dataview/blob/master/docs/docs/api/code-examples.md)193194## Frequently Needed Functions (Quick Lookup)195196| Function | Purpose | Example |197|----------|---------|---------|198| `contains(str, sub)` | Check substring/list membership | `WHERE contains(file.name, "WIP")` |199| `icontains(str, sub)` | Case-insensitive contains | `WHERE icontains(tags, "project")` |200| `length(array)` | Count elements | `TABLE length(file.inlinks) AS "Refs"` |201| `date(today)` | Today's date | `WHERE due <= date(today)` |202| `dur(X)` | Duration literal | `WHERE file.mtime >= date(today) - dur(7 days)` |203| `dateformat(d, fmt)` | Format date as string | `dateformat(file.ctime, "yyyy-MM-dd")` |204| `default(f, val)` | Fallback for null | `default(status, "unknown")` |205| `choice(b, l, r)` | If/else | `choice(done, "✅", "❌")` |206| `filter(arr, fn)` | Filter array | `filter(file.tasks, (t) => !t.completed)` |207| `map(arr, fn)` | Transform array | `map(file.tags, (t) => upper(t))` |208| `flat(arr)` | Flatten nested arrays | `flat(rows.file.tags)` |209| `any(arr, fn)` | Any element matches? | `WHERE any(file.tasks, (t) => !t.completed)` |210| `all(arr, fn)` | All elements match? | `WHERE all(file.tasks, (t) => t.completed)` |211| `sum(arr)` / `average(arr)` | Aggregate numbers | `TABLE sum(hours) AS "Total"` |212| `round(n, d)` | Round number | `round(rating, 1)` |213| `replace(s, p, r)` | String replace | `replace(file.name, "_", " ")` |214| `regextest(pat, s)` | Regex test | `WHERE regextest("^2024", string(date))` |215| `split(s, delim)` | Split string | `split(file.name, " - ")` |216| `join(arr, sep)` | Join array to string | `join(file.tags, ", ")` |217| `typeof(v)` | Get type name | `WHERE typeof(due) = "date"` |218| `number(s)` | Extract number from string | `number("18 years") = 18` |219| `link(path)` | Create link object | `link("My Note")` |220| `meta(link)` | Get link metadata | `meta(section).subpath` |221| `striptime(date)` | Remove time from date | `striptime(file.ctime) = file.cday` |222| `nonnull(arr)` | Remove nulls | `sum(nonnull(list_of_values))` |223| `sort(list)` | Sort a list | `sort(file.tags)` |224| `reverse(list)` | Reverse a list | `reverse(sort(file.tags))` |225| `unique(arr)` | Deduplicate | `unique(flat(rows.file.tags))` |226| `min/max(a,b,..)` | Min/max of values | `min(due, date(eom))` |227| `extract(obj, keys..)` | Pull fields from object | `extract(file, "ctime", "mtime")` |228229## Writing Good Queries: Guidelines2302311. **Start simple, add complexity.** Begin with the query type and FROM, then add WHERE/SORT/etc.2322. **Use `typeof()` for safety.** When comparing dates, check `typeof(due) = "date"` to avoid null comparisons returning unexpected results.2333. **`WHERE field` checks existence.** `WHERE due` filters out pages where `due` is null/undefined.2344. **TASK queries operate at task level.** Task implicit fields (`completed`, `text`, `tags`) are directly available. For other query types, access tasks via `file.tasks`.2355. **GROUP BY changes available fields.** After grouping, you have `key` and `rows` — use `rows.field` to access original page fields.2366. **FLATTEN before WHERE on nested data.** To filter list items individually, FLATTEN the list first.2377. **Inline DQL for single values.** Use `` `= expression` `` for embedding one value in text. No query types or data commands available.2388. **DataviewJS for complex logic.** When DQL can't express what you need (loops, conditionals, external data), use `dataviewjs` blocks with the `dv.*` API.2399. **Prefer DQL over JS** unless the user explicitly requests JavaScript or the query genuinely requires it.24010. **Always wrap in proper code fences.** DQL in ` ```dataview ``` `, JS in ` ```dataviewjs ``` `, inline DQL in `` `= ...` ``.241242## Updating This Skill243244The Dataview documentation that powers this skill is sourced from:245**https://github.com/blacksmithgu/obsidian-dataview/tree/master/docs/docs**246247Each reference file includes source links. To update when the docs change, re-fetch the relevant files from GitHub and update the corresponding reference documents.