# Dataview Queries & Timelines
This page explains the queries that make dimension pages build themselves. You don't need to understand every line to use the system — the [[05 Templates|templates]] already contain these. But when you want to adapt something, this is the reference.
There are two flavors:
- **`dataview`** blocks — a simple query language. Used for straightforward tables (rosters, submissions, affiliations).
- **`dataviewjs`** blocks — JavaScript with full control. Used for the interactive timelines, filter dropdowns, and clickable task checkboxes. These require **Enable JavaScript Queries** ([[02 Installation|Installation]]).
## The core timeline pattern
Every dimension's timeline is built on the same idea: *find every activity whose link field points back to this note.* In plain Dataview that's:
````md
```dataview
TABLE WITHOUT ID
("[[" + file.name + "]]") AS "Note",
type AS "Type",
date AS "Date",
default(subject, default(topic, default(file.name, ""))) AS "Subject"
FROM "Activities"
WHERE contains(people, this.file.link)
SORT date DESC, time DESC
```
````
- `FROM "Activities"` scans every activity note (recursively through subfolders).
- `WHERE contains(people, this.file.link)` keeps only those whose `people:` list contains *this* page's link. Swap `people` for `projects`, `classes`, or `assignments` to make the same query work on a Project, Class, or Assignment page.
- `default(subject, default(topic, ...))` shows the email's `subject`, or a class session's `topic`, or falls back to the filename — one column that does the right thing for every activity type.
- `SORT date DESC` puts newest first.
That single pattern, with the link field swapped, is the backbone of the whole system.
## The interactive timeline (dataviewjs)
The dimension templates use a richer `dataviewjs` version of that query with two dropdowns — **Type** (All / email / meeting / …) and **Range** (All time / last 7 / 30 / 90 / 365 days) — that re-render a table on change. The shape is:
````md
```dataviewjs
const dim = "people"; // ← the only thing that changes per template
const all = dv.pages('"Activities"')
.where(p => {
const v = p[dim];
if (!v) return false;
const arr = Array.isArray(v) ? v : [v];
return arr.some(l => l && l.path === dv.current().file.path);
})
.sort(p => p.date, "desc");
// ...builds Type + Range <select> controls, then renders a
// filtered <table> of Note / Type / Date / Subject...
```
````
The full code (the control-building and render function) is identical across Person, Student, Class, Assignment, and Project — **only the first line, `const dim = "..."`, differs.** Copy it from any dimension template's source page (e.g. [[Person Template]]). On an Assignment page it also filters out `submission` rows so the Related-activity feed doesn't duplicate the Submissions table.
## Open / Closed task lists with live checkboxes
Dimension pages show the open and closed tasks linked to them, each with a checkbox that actually toggles the task note. The open-tasks block:
````md
```dataviewjs
const dim = "people";
const tasks = dv.pages('"Activities/Tasks"')
.where(p => {
if (p.status !== "open") return false;
const val = p[dim];
if (!val) return false;
const arr = Array.isArray(val) ? val : [val];
return arr.some(d => d && d.path === dv.current().file.path);
})
.sort(p => p.due, "asc");
const rows = tasks.map(p => {
const cb = document.createElement("input");
cb.type = "checkbox";
cb.addEventListener("click", async () => {
const file = app.vault.getAbstractFileByPath(p.file.path);
const content = await app.vault.read(file);
const today = new Date().toISOString().slice(0, 10);
let updated = content.replace(/^status:\s*["']?open["']?\s*$/m, "status: done");
if (/^closed:/m.test(updated)) {
updated = updated.replace(/^closed:.*$/m, `closed: ${today}`);
} else {
updated = updated.replace(/^(---\n[\s\S]*?\n)---\n/, `$1closed: ${today}\n---\n`);
}
if (updated !== content) await app.vault.modify(file, updated);
});
return [cb, p.file.link, p.due, p.date];
});
dv.table(["Done", "Task", "Due", "Created"], rows);
```
````
Ticking the box rewrites the task note's frontmatter: `status: open` → `status: done` and stamps `closed:` with today's date. The closed-tasks block is the mirror image — it filters `status === "done"`, pre-checks the boxes, and un-ticking one flips it back to open and clears `closed:`.
## The "+ New task from this note" button
Most pages carry an Actions block with a button that spins up a linked task:
````md
```dataviewjs
const btn = dv.el("button", "+ New task from this note");
btn.addEventListener("click", async () => {
window._pendingTaskSource = dv.current().file.name;
const templater = app.plugins.plugins["templater-obsidian"];
if (!templater) { new Notice("Templater plugin not enabled"); return; }
const taskTpl = app.vault.getAbstractFileByPath("Templates/Task.md");
if (!taskTpl) { new Notice("Templates/Task.md not found"); return; }
await templater.templater.create_new_note_from_template(taskTpl, "", "", true);
});
```
````
It stashes the current note's name on `window._pendingTaskSource`, then runs the Task template. The Task template reads that value and writes it into the new task's `sources:` field — so the task is automatically linked back to wherever you created it. This is why the Task template and this button work as a pair.
## Affiliation & hierarchy queries
A Person page lists the organizations and departments they belong to with a plain Dataview query that reads the *current page's* affiliation links:
````md
```dataview
TABLE WITHOUT ID
file.link AS "Affiliation",
default(org_type, type) AS "Kind",
email AS "Email",
phone AS "Phone"
FROM "Organizations"
WHERE contains(this.organizations, file.link) OR contains(this.departments, file.link)
SORT type ASC, file.name ASC
```
````
Organization pages run the relationship the other direction in `dataviewjs`: a University finds its Colleges (notes whose `parent_organization` points at it), then the Departments under those Colleges, then the People affiliated with any of them, then the Activities involving those People — walking the hierarchy so a top-level institution rolls up everything beneath it. That code is on the [[Organization Template]] page.
## Aggregate queries
Assignments compute a class average with a grouped query:
````md
```dataview
TABLE WITHOUT ID
length(rows) AS "Count",
round(average(rows.score), 1) AS "Avg",
min(rows.score) AS "Min",
max(rows.score) AS "Max"
FROM "Activities/Submissions"
WHERE contains(assignments, this.file.link) AND score
GROUP BY true
```
````
## Adapting the queries
To add a new dimension (say, "Committees"), you'd add a `committees:` field to your activity templates and build a dimension template that reuses the timeline block with `const dim = "committees"` and `WHERE contains(committees, this.file.link)`. Because every activity query reads `FROM "Activities"` without filtering on type, **new activity types are picked up automatically** — no query edits needed. More on this in [[10 Adapting and Extending|Adapting & Extending]].
Next: [[07 Daily Notes and Dashboard|Daily Notes & Dashboard]].