obsidian-dataview
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: valuebetween---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.linkfile.ctime/file.cday,file.mtime/file.mday(created/modified dates)file.size,file.tags,file.etags,file.aliasesfile.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 inyyyy-mm-ddformat)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]].fieldgets field from that page
GROUP BY Behavior
When you use GROUP BY field, each result row has:
key: the grouped field valuerows: 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- Read when: building complex queries with GROUP BY, FLATTEN, or multiple data commands
- Source: Query Structure, Query Types, Data Commands, DQL vs SQL
-
references/metadata-and-types.md— How to add metadata (frontmatter, inline fields), all implicit fields for pages and tasks, field types, emoji shorthands- Read when: user asks about what fields are available, how to annotate notes, or task-specific queries
- Source: Add Metadata, Page Metadata, Task Metadata, Types of Metadata
-
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
- Read when: user needs
-
references/javascript-api.md— DataviewJS codeblock API (dv.*), rendering, querying, Data Arrays, utility functions- Read when: DQL is insufficient and user needs JavaScript-level flexibility, or for
dataviewjsblocks - Source: JS API Intro, Code Reference, Data Array, Code Examples
- Read when: DQL is insufficient and user needs JavaScript-level flexibility, or for
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, checktypeof(due) = "date"to avoid null comparisons returning unexpected results. WHERE fieldchecks existence.WHERE duefilters out pages wheredueis null/undefined.- TASK queries operate at task level. Task implicit fields (
completed,text,tags) are directly available. For other query types, access tasks viafile.tasks. - GROUP BY changes available fields. After grouping, you have
keyandrows— userows.fieldto 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
dataviewjsblocks with thedv.*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.