# 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]].