Build query pages around five kinds of practical questions, showing the full grouping and flattening process and explaining time boundaries, task states, and missing fields.
Start a dashboard with questions and field conventions#
Filling a page with queries does not automatically produce a useful dashboard. First decide what actions the page should support: selecting active projects, finding recent changes, checking due tasks, or reviewing a month of reading. Answer each question with its own query so that you can check the fields and conditions individually.
The examples still use the fictional Studio vault. Project pages use type, status, priority, and archived; reading pages use type: reading, date_finished, and topics. The allowed status values are active, paused, and done; a larger priority number means higher priority, and archived must be a Boolean. Put all queries in Markdown code blocks marked dataview. The web page shows examples of the syntax, not results from a personal vault.
---
type: reading
date_finished: 2030-04-18
topics:
- search
- systems
archived: false
---
# 雾岛索引手册
- [ ] 补写索引结构的比较 [due:: 2030-05-10]
- [x] 整理术语 [completion:: 2030-04-18]Write dates in ISO format. If you have finished reading something but have not recorded a date, the monthly statistics can only report a missing date. Do not infer the completion month from the file's last modification time. The project and reading directories are example scopes; replace them with directories that actually exist in your vault.
Active projects: give missing values an explicit place#
```dataview
TABLE priority AS "优先级", status AS "状态", due AS "到期日"
FROM "Studio/Projects"
WHERE type = "project" AND status = "active" AND archived = false
SORT priority DESC, due ASC
```This table answers, “What can I move forward now?” Using status = "active" is more precise than status != "done": the latter can include paused projects, misspelled values, and even records with no status. Define the set of states before writing the query. When you add a state later, it will also be easier to decide whether it belongs in the results.
Keep priority numeric. If missing values are allowed, you can list projects that need this field separately. Not every project necessarily has a due field, so this example displays its original value. If the actual question is “Which active projects are due soonest?”, require due != null and put due ASC before the priority sort. Column order controls presentation; SORT order determines the order of work. They need not be the same.[1]
Recent changes: distinguish the seven-day window from what a modification means#
```dataview
LIST file.mtime
FROM "Studio"
WHERE file.mtime >= date(now) - dur(7 days)
SORT file.mtime DESC
LIMIT 10
```This example uses date(now), so the window starts seven days before the current moment. If you use date(today) instead, the start is midnight today minus seven days. That works for a review based on calendar-day boundaries, but it is not exactly the last 168 hours. Align the start of the date window with the question, rather than labeling the view “the last seven days” while using a different boundary.
The LIST includes modification times so that you can tell why an entry appears. It sorts newest first and then takes ten entries, so the page is only an entry point for review; it does not mean there were only ten notes in the window. Remove LIMIT to see everything. To exclude categories, add explicit field conditions or source restrictions. Synchronization, filling in properties, and formatting changes all affect modification time, so this table cannot measure writing time or learning outcomes.
Aggregating tasks: completion belongs to the task itself#
```dataview
TASK
FROM "Studio/Projects"
WHERE !completed
GROUP BY file.link
```Group unfinished tasks by their note so that you can see the tasks and return to their context. In a TASK query, completed refers to the task's own checkbox state, not the project page's status. Tasks also inherit fields from their parent page, but they have their own implicit fields, including status. Distinguishing project filters from task states helps avoid misreading fields with the same name.[2]
```dataview
TASK
FROM "Studio/Projects"
WHERE !completed AND due != null AND due <= date(today)
SORT due ASC
GROUP BY file.link
```The second query narrows the scope to tasks due today or already overdue. A task's due value should come from a recognized annotation on that task, such as [due:: date]. Tasks without dates remain in the unfinished list for you to fill in separately. Normalize custom due values that contain a time to dates before comparing them with date(today).
completed only checks whether this task is marked complete with x; it does not check all its subtasks. If the question is “Are the parent task and all its subtasks complete?”, use fullyCompleted. checked only means that the checkbox contains a nonempty status, so custom states such as [-] also return true. These three tests are not interchangeable. Checking a box in Dataview's task view updates the source task; a query view is not always read-only.[5]
There is another display rule: when a parent task matches the query, its subtasks appear as part of it, even if a subtask does not independently meet the conditions. An unfinished-task list can therefore include completed subtasks, and a due-task list can include subtasks without that due date. Do not treat every displayed item as an independent match. For strict item-by-item counts, first flatten file.tasks, then filter on each task's fields and state what you are counting.
After grouping by month, one row no longer means one note#
```dataview
TABLE rows.file.link AS "阅读记录", length(rows) AS "数量"
FROM "Studio/Library"
WHERE type = "reading" AND date_finished != null
GROUP BY dateformat(date_finished, "yyyy-MM") AS month
SORT month DESC
```First filter for reading records with a completion date, then format that date as a month and group by it. The yyyy-MM format retains the year and zero-pads the month, so text sorting still orders months correctly across years. dateformat returns text, so month here is for grouping and display. To compare actual dates, compare date_finished before formatting it.[3]
GROUP BY changes the shape of the results. Each row originally represented a page; now each row represents a monthly group, with the original records stored in the rows array. rows.file.link extracts the links in the group, and length(rows) counts its entries. The grouping alias month holds the group's value; the default identifier in a list or table also displays group information.[1]
Similarly, to view all projects by a property, use GROUP BY status and display the members with rows.file.link. After grouping, do not continue treating file.link as an original record: it no longer represents the data unit of the current row. Conversely, place any filter that applies to the original pages before grouping so that it does not mistakenly test whole groups.
Multiple topics per record: flatten before counting#
```dataview
TABLE rows.file.link AS "记录", length(rows) AS "主题关联数"
FROM "Studio/Library"
WHERE type = "reading" AND topics != null
FLATTEN topics AS topic
GROUP BY topic
SORT topic ASC
```Suppose a reading record has topics set to [search, systems]. FLATTEN expands it into two rows, which then enter the search and systems groups separately. Flattening before grouping answers “Which records are associated with each topic?” Using GROUP BY topics directly groups by the entire list value, rather than splitting it into individual topics.
The counting unit here is a record–topic association. A record with several topics appears in several groups, so the sum of all group counts may exceed the number of reading records. This follows from the query's definition. If a list repeats a topic, the duplicate values are flattened too. To count distinct pages, normalize the lists first or explicitly deduplicate with length(unique(rows.file.link)).
List flattening also works with file.etags. Using file.tags expands parent tags as well, changing the scope of the count. After FLATTEN, WHERE topic = "search" keeps only that topic; this condition must come after topic is created. Design the order of flattening, filtering, and grouping around the data unit, rather than memorizing the command names as a fixed template.
Completeness checks: check for missing values before checking their content#
```dataview
TABLE type AS "类型", status AS "状态", priority AS "优先级"
FROM "Studio/Projects"
WHERE type = null OR status = null OR priority = null
```Testing the fields listed here against null is closer to asking “Was this filled in?” than WHERE !priority. The number 0 and Boolean false are also falsy, but they are not necessarily missing. Empty strings, wrong types, and invalid enum values are separate issues; this query does not automatically prove that a record is complete. Check a field in three stages: it exists, its type is correct, and its value follows the convention. Do not collapse all three into a vague “has a property” test.
```dataview
LIST
FROM "Studio/Projects"
WHERE type = "project" AND status != null
AND !contains(list("active", "paused", "done"), status)
```The second query specifically finds values outside the allowed status set, helping reveal typos and old conventions. When maintaining fields, inspect the original record before correcting it. A query identifies candidates; it does not decide which status is correct. Using default for display can make a table look cleaner, but preserve the original values in audits, or fallback text can easily hide missing states.
Choose Inline DQL when you need a value within a sentence#
当前记录:`= this.file.name`
主题数量:`= length(this.topics)`Inline code with the default = prefix is Inline DQL. It displays the result of an expression and can access the current page through this, as well as linked pages. You cannot put FROM, WHERE, or a whole TABLE in that position. It suits a single status or calculated value; keep cross-page lists in dataview code blocks.[4]
dataviewjs code blocks and inline expressions with the default $= prefix are DataviewJS and use the dv API. They require the corresponding JavaScript query settings and have broader access capabilities. Do not call $= dv.pages(...) ordinary Inline DQL. Add this programming and maintenance overhead only when DQL cannot express the required logic clearly.
Finish by checking each block against its question: do active projects exclude paused ones; does the recent-change window match its title; are tasks without due still accessible; do months use the actual completion date; and do multi-topic counts explain overlapping membership? Finally, check whether the field-audit page is empty. An empty table means no data matched the current conditions. Interpret that alongside the sources and conventions; it does not automatically mean the whole vault is correct.
References & further reading
- Dataview: data commands and execution order ↗
- Dataview: task and list fields ↗
- Dataview: function reference ↗
- Dataview: DQL, JavaScript, and inline queries ↗
- Dataview: task views and subtasks ↗
Adapted from technical notes. Examples use generic names.