Dataview 实战
Dataview 是 Obsidian 最强大的数据处理插件,本指南通过实际案例帮助你掌握其用法。
基础查询
LIST 查询
最简单的列表形式查询:
dataview
LIST
FROM "笔记文件夹"
WHERE file.mtime >= date(today) - dur(7 days)
SORT file.mtime DESC参数说明:
LIST:以列表形式显示FROM:数据来源(文件夹/标签)WHERE:筛选条件SORT:排序方式
TABLE 查询
表格形式展示更多字段:
dataview
TABLE
file.ctime AS "创建时间",
file.mtime AS "修改时间",
author AS "作者",
status AS "状态"
FROM "项目"
WHERE status = "进行中"
SORT file.mtime DESC任务查询
查询所有未完成任务:
dataview
TASK
FROM "任务"
WHERE !completed常用查询示例
最近修改的笔记
dataview
TABLE
file.mtime AS "修改时间",
file.folder AS "位置"
FROM ""
WHERE file.mtime >= date(today) - dur(7 days)
SORT file.mtime DESC
LIMIT 10按标签分类
dataview
TABLE
file.ctime AS "创建时间",
tags AS "标签"
FROM "#读书笔记"
SORT file.ctime DESC项目进度追踪
dataview
TABLE
progress AS "进度",
deadline AS "截止日期",
status AS "状态"
FROM "项目"
WHERE status != "已完成"
SORT deadline ASC书籍阅读清单
dataview
TABLE
author AS "作者",
rating AS "评分",
status AS "阅读状态"
FROM "书籍"
WHERE type = "book"
SORT rating DESC高级查询技巧
计算字段
使用表达式创建计算字段:
dataview
TABLE
file.name AS "笔记名",
length(file.content) AS "字数",
round(length(file.content)/1000, 1) AS "千字"
FROM "文章"
SORT length(file.content) DESC链接字段
展示和操作链接:
dataview
TABLE
author AS "作者",
link(file.link, "查看") AS "链接"
FROM "文章"日期计算
计算日期相关值:
dataview
TABLE
file.ctime AS "创建时间",
date(today) - file.ctime AS "距今天数"
FROM "笔记"
WHERE file.ctime <= date(today) - dur(30 days)分组显示
使用 GROUP BY 分组:
dataview
TABLE
rows.file.name AS "笔记"
FROM "项目"
GROUP BY status嵌套字段
访问嵌套的 YAML 字段:
yaml
---
meta:
author: 张三
info:
created: 2024-01-01
updated: 2024-01-15
---dataview
TABLE
meta.author AS "作者",
meta.info.created AS "创建日期"
FROM ""
WHERE metaDataviewJS
什么是 DataviewJS
DataviewJS 允许使用 JavaScript 编写更复杂的查询,提供完全的编程控制能力。适用于 DQL 无法满足的复杂场景。
DQL vs DataviewJS 对比:
| 维度 | DQL(声明式查询) | DataviewJS(编程式查询) |
|---|---|---|
| 学习难度 | ⭐⭐ | ⭐⭐⭐⭐ |
| 灵活性 | 中等 | 极高 |
| 适用场景 | 简单查询、表格 | 复杂逻辑、自定义渲染 |
| 性能 | 较好 | 取决于代码质量 |
| 调试难度 | 低 | 较高 |
dv 对象常用 API
| API | 用途 | 示例 |
|---|---|---|
dv.pages(source) | 获取页面集合 | dv.pages('"笔记"') |
dv.page(path) | 获取单个页面 | dv.page("项目/A") |
dv.table(headers, rows) | 渲染表格 | dv.table(["名","值"], rows) |
dv.list(items) | 渲染列表 | dv.list([1, 2, 3]) |
dv.header(level, text) | 渲染标题 | dv.header(2, "标题") |
dv.paragraph(text) | 渲染段落 | dv.paragraph("文本") |
dv.taskList(tasks) | 渲染任务列表 | dv.taskList(dv.pages().tasks) |
dv.el(tag, text) | 渲染任意 HTML | dv.el("b", "加粗") |
dv.fileLink(path) | 创建文件链接 | dv.fileLink("笔记名") |
dv.date(str) | 解析日期 | dv.date("2024-01-15") |
dv.duration(str) | 解析时间间隔 | dv.duration("7 days") |
动态统计
dataviewjs
// 统计各标签的笔记数量
const tags = dv.pages()
.flatMap(p => p.file.tags)
.groupBy(t => t)
.map(t => ({tag: t.key, count: t.rows.length}))
.sort(t => t.count, 'desc');
dv.table(["标签", "笔记数"], tags.map(t => [t.tag, t.count]));进度条
dataviewjs
// 显示项目进度条
const pages = dv.pages("#project");
for (let page of pages) {
const progress = page.progress || 0;
const bar = "█".repeat(Math.floor(progress/10)) + "░".repeat(10-Math.floor(progress/10));
dv.paragraph(`**${page.file.name}**: ${bar} ${progress}%`);
}最近活动
dataviewjs
// 最近 7 天的活动
const week = dv.pages()
.where(p => p.file.mtime >= dv.date("today") - dur("7 days"))
.sort(p => p.file.mtime, 'desc');
dv.table(
["日期", "笔记", "修改时间"],
week.map(p => [
p.file.mtime.toFormat("MM-dd"),
p.file.link,
p.file.mtime.toFormat("HH:mm")
])
);随机笔记发现
dataviewjs
// 随机推荐 5 篇笔记,帮助发现遗忘的内容
const allPages = dv.pages().where(p => !p.file.path.startsWith("模板"));
const shuffled = allPages.sort(() => Math.random() - 0.5);
const selected = shuffled.limit(5);
dv.header(3, "🎲 随机发现");
dv.table(
["笔记", "创建时间", "修改时间"],
selected.map(p => [
p.file.link,
p.file.ctime.toFormat("yyyy-MM-dd"),
p.file.mtime.toFormat("yyyy-MM-dd")
])
);笔记健康检查
dataviewjs
// 检查笔记健康状况:孤立笔记、空笔记、过旧笔记
const pages = dv.pages().where(p => !p.file.path.startsWith("模板"));
// 孤立笔记(没有入链也没有出链)
const orphaned = pages.where(p => p.file.inlinks.length === 0 && p.file.outlinks.length === 0);
// 空笔记(内容少于 100 字符)
const empty = pages.where(p => p.file.length < 100);
// 超过 90 天未更新
const stale = pages.where(p => p.file.mtime < dv.date("today") - dur("90 days"));
dv.header(3, "🏥 笔记健康报告");
dv.paragraph(`📄 总笔记数:${pages.length}`);
dv.paragraph(`🏝️ 孤立笔记:${orphaned.length} 篇`);
dv.paragraph(`📭 空笔记:${empty.length} 篇`);
dv.paragraph(`💤 超过90天未更新:${stale.length} 篇`);
if (orphaned.length > 0) {
dv.header(4, "孤立笔记");
dv.list(orphaned.limit(10).map(p => p.file.link));
}日记热力图
dataviewjs
// 统计每日笔记数量,生成简易热力图
const dailyNotes = dv.pages('"Daily"');
const dateCount = {};
for (let note of dailyNotes) {
const date = note.file.name; // 假设文件名是日期格式
dateCount[date] = (dateCount[date] || 0) + 1;
}
const maxCount = Math.max(...Object.values(dateCount), 1);
let heatmap = "";
for (let [date, count] of Object.entries(dateCount).sort()) {
const intensity = Math.round((count / maxCount) * 4);
const blocks = ["⬜", "🟨", "🟧", "🟥", "🟪"];
heatmap += `${blocks[intensity]} ${date}: ${count} 篇\n`;
}
dv.paragraph(heatmap || "暂无日记数据");自定义格式化输出
dataviewjs
// 生成格式化的周报
const today = dv.date("today");
const weekStart = today.minus(dur(`${today.weekday - 1} days`));
const weekPages = dv.pages()
.where(p => p.file.ctime >= weekStart)
.sort(p => p.file.ctime, 'desc');
dv.header(2, `📅 周报 ${weekStart.toFormat("MM-dd")} ~ ${today.toFormat("MM-dd")}`);
dv.paragraph(`本周新增笔记:${weekPages.length} 篇`);
// 按文件夹分组
const grouped = weekPages.groupBy(p => p.file.folder);
for (let group of grouped) {
dv.header(3, `${group.key || "根目录"} (${group.rows.length})`);
dv.list(group.rows.map(p => p.file.link));
}条件渲染
dataviewjs
// 根据条件显示不同内容
const overdue = dv.pages("#task")
.where(p => p.due && dv.date(p.due) < dv.date("today") && !p.completed);
if (overdue.length > 0) {
dv.header(3, `⚠️ 逾期任务 (${overdue.length})`);
dv.table(
["任务", "截止日期", "逾期天数"],
overdue.map(p => [
p.file.link,
p.due,
Math.floor(dv.date("today") - dv.date(p.due))
])
);
} else {
dv.paragraph("✅ 没有逾期任务!");
}实战案例
每周回顾
dataview
TABLE
choice(completed, "✅", "⬜") AS "状态",
task AS "任务"
FROM "日记"
WHERE file.ctime >= date(today) - dur(7 days)阅读统计
dataviewjs
// 本月阅读统计
const books = dv.pages("#书籍")
.where(p => p.read_date && p.read_date.month === dv.date("today").month);
dv.paragraph(`📚 本月已读: ${books.length} 本`);
dv.table(
["书名", "作者", "评分"],
books.map(b => [b.file.link, b.author, "⭐".repeat(b.rating || 0)])
);任务看板
dataviewjs
// 简易看板
const tasks = {
"待办": dv.pages().where(p => p.status === "todo"),
"进行中": dv.pages().where(p => p.status === "doing"),
"已完成": dv.pages().where(p => p.status === "done")
};
for (let [status, pages] of Object.entries(tasks)) {
dv.header(3, `${status} (${pages.length})`);
dv.list(pages.map(p => p.file.link));
}知识图谱统计
dataviewjs
// 统计笔记关联
const notes = dv.pages();
const links = notes.flatMap(p => p.file.inlinks);
const mostLinked = links
.groupBy(l => l.path)
.map(g => ({note: g.key, count: g.rows.length}))
.sort(l => l.count, 'desc')
.limit(10);
dv.table(["笔记", "被引用次数"], mostLinked.map(l => [dv.fileLink(l.note), l.count]));性能优化
限制结果
使用 LIMIT 提高性能:
dataview
TABLE file.name
FROM ""
LIMIT 100避免全局搜索
指定具体文件夹:
dataview
-- 推荐
FROM "特定文件夹"
-- 避免全局搜索
FROM ""索引字段
在 frontmatter 中定义索引字段:
yaml
---
tags: [项目, 重要]
status: 进行中
priority: 高
---常见问题
查询不显示
- 检查 YAML 格式是否正确
- 确认字段名称拼写无误
- 查看 Obsidian 控制台错误
中文排序问题
使用自定义排序:
dataviewjs
dv.pages("笔记")
.sort(p => p.file.name, 'asc', 'zh-CN')日期格式化
dataviewjs
// 格式化日期
dv.date("2024-01-15").toFormat("yyyy年MM月dd日")提示
Dataview 的强大在于可以动态展示笔记数据,建议从简单查询开始,逐步学习高级功能!
INFO
结合 Templater 插件使用,可以实现更强大的自动化工作流!