Skip to content

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 meta

DataviewJS

什么是 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)渲染任意 HTMLdv.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 插件使用,可以实现更强大的自动化工作流!