📋 摘要
  • Dataview 把整个 Obsidian 库当作可查询的数据库,数据来自 frontmatter、内联字段(Key:: Value)与隐式字段(file.*)。
  • 四种查询方式:DQLdataview 代码块)、内联 DQL`= ...`)、DataviewJSdataviewjs 代码块)、内联 JS`$= ...`)。
  • DQL 查询 = 一个查询类型(必填)+ 可选 FROM(选来源)+ 可选数据命令(WHERE/SORT/GROUP BY/FLATTEN/LIMIT)。
  • 常用查询类型:LIST(列表)、TABLE(表格)、TASK(任务)、CALENDAR(日历)。
  • 安全:DQL 是沙箱化的,无法修改库;dataviewjs 拥有与插件同级的权限(可读写文件、发网络请求),只运行可信脚本。

一、什么是 Dataview

Dataview(仓库 github.com/blacksmithgu/obsidian-dataview)把 Obsidian 库当作一个实时索引 + 查询引擎:给笔记打上元数据,再用查询语言把它们列出来、过滤、排序、分组。它只负责展示与计算,不负责编辑笔记。

数据来自三个来源:

Markdown
---
rating: 8            # 1. YAML frontmatter
reviewed: false
---

基本字段:: 值         # 2. 内联字段(Inline Field)
**加粗字段**:: 也行   #    支持 Markdown 强调包裹键名

第 3 类来源是隐式字段file.*),由 Dataview 自动维护,无需手动书写(见下文)。

二、数据来源(Metadata)

1. Frontmatter

笔记顶部 --- 包裹的 YAML。支持对象、列表等嵌套结构:

YAML
---
thoughts:
  rating: 8
  reviewable: false
tags: [one, two]
---

2. 内联字段

正文中直接写 键:: 值

Markdown
Basic Field:: Value
**Bold Field**:: Nice!
[mood:: okay]
[length:: 2 hours]

规则:内联字段的文本值遇到换行即结束;同一文件内重复的键会自动合并为列表。

3. 隐式字段(file.*,全部可用)

字段类型说明
file.nameText文件名(侧边栏显示名)
file.folderText所在文件夹路径
file.pathText完整路径(含文件名)
file.extText扩展名,通常 md
file.linkLink指向该文件的链接
file.sizeNumber文件大小(字节)
file.ctimeDate+Time创建时间
file.cdayDate创建日期(仅日期)
file.mtimeDate+Time最后修改时间
file.mdayDate最后修改日期(仅日期)
file.tagsList全部标签,子标签逐级展开(#Tag/1/A[#Tag, #Tag/1, #Tag/1/A]
file.etagsList显式标签,不展开子标签
file.inlinksList指向本文件的所有入链
file.outlinksList本文件的所有出链
file.aliasesListYAML 中的别名
file.tasksList文件内所有任务(- [ ]
file.listsList文件内所有列表元素(含任务)
file.frontmatterList原始 frontmatter 的键值文本
file.dayDate仅当文件名含日期(yyyy-mm-ddyyyymmdd)或有 Date 字段时存在
file.starredBoolean是否在「书签」核心插件中

三、数据类型

类型说明字面量示例
Text默认兜底类型"hello"
Number数字6-803.6
Boolean布尔true / false
Date日期,匹配 ISO8601(YYYY-MM[-DDTHH:mm:ss.nnn+ZZ]date(2021-04-18)
Duration时长 <时间> <单位>,可用缩写、可复合dur(8 minutes)dur(6hr7min)
LinkObsidian 链接[[Page]][[Page|显示名]]
List列表(= 数组),逗号分隔或 YAML 数组[1, 2, 3]
Object对象(仅 YAML 可定义),. 访问子字段obj.key1
Null空值null

要点:

  • 日期可用 .year/.month/.week/.weekday/.day/.hour/.minute/.second 等属性;日期 + 时长 = 新日期日期 - 日期 = 时长
  • 列表:内联文本值要带引号才是列表("a", "b");yes, or, no 是纯文本。
  • 链接写在 YAML frontmatter 里必须加引号(key: "[[Link]]"),否则 YAML 非法;引号链接 Dataview 认,但 Obsidian 自身不认(无出链、不随重命名更新)。

四、四种查询方式

方式语法定位
DQL```dataview ...```主流方式,管道式、类 SQL,够用
内联 DQL`= 表达式`在正文任意位置显示单个值
DataviewJS```dataviewjs ...```任意 JavaScript,全 API 访问
内联 JS`$= js表达式`正文内执行任意 JS
Markdown
```dataview
table time-played, length, rating
from "games"
sort rating desc
Text

```markdown
我们正在看 `= this.file.name` 这一页。
Text
dv.taskList(dv.pages().file.tasks.where(t => !t.completed));
Markdown
本页最后修改于 `$= dv.current().file.mtime`

五、DQL 查询结构

Text
QUERY-TYPE <字段> FROM <来源> <数据命令> <表达式> <数据命令> <表达式> ...

只有查询类型是必填,其余可选。

查询类型

类型输出
TABLE表格,每行一条结果,可多列
LIST要点列表,可在链接旁附带一个字段
TASK匹配任务的可交互列表
CALENDAR日历视图,每条命中显示为对应日期的圆点
Text
TABLE due, file.tags AS "tags", average(working-hours)
Text
CALENDAR file.cday

数据命令

命令作用关键点
FROM选来源(标签/文件夹/链接)至多一个,紧跟查询类型之后
WHERE按字段值过滤类型不匹配可能出意外结果,建议加 typeof() 检查
SORT排序SORT 字段 ASC/DESC
GROUP BY分组,每组一行组内用 rows 访问被分组的行
FLATTEN一行拆多行(处理多值/数组字段)常用于列表字段展开
LIMIT限制结果数量

FROM 外,其余命令可多次使用、任意顺序,按书写顺序执行。

Text
LIST WHERE due AND due < date(today)
Text
LIST FROM #status/open SORT file.ctime DESC LIMIT 10
Text
TASK WHERE !completed SORT created ASC LIMIT 10 GROUP BY file.link SORT rows.file.ctime ASC
Text
TABLE L.text AS "My lists" FROM "dailys" FLATTEN file.lists AS L WHERE contains(L.author, "Surname")

类型安全示例:字段为 null 时比较会产生反直觉结果(null <= date(today)true),应写成 WHERE typeof(due) = "date" AND due <= date(today)

六、来源(Sources)

用于 FROM,或 JS 中 dv.pages(source)

来源语法示例
标签#tagnameLIST FROM #homework
文件夹"folder/path"不带末尾斜杠,匹配含子文件夹)FROM "projects/brainstorming"
特定文件"folder/File"(同名冲突时加 .mdFROM "30 Hobbies/Games/Dashboard"
指向某笔记的页面[[note]]LIST FROM [[]](当前文件)
某笔记的出链outgoing([[note]])LIST FROM outgoing([[Dashboard]])

组合与取反:and / or(或 AND/OR)组合,- 前缀取反,括号分组:

Text
LIST FROM #food and -#fastfood
Text
LIST FROM #tag and ("folder" or #other-tag)

⚠️ 不写 FROM 会扫描整个库,大库可能卡住 Obsidian,尽量限定来源。

七、表达式(Expressions)

运算符

类别运算符
算术+(加法/字符串拼接)、-*(乘法/字符串重复)、/%(取模)
比较><=!=<=>=
逻辑ANDand)、ORor)、NOT/!(取反)

访问

  • 列表索引(0 起):list("A", "B", "C")[0]"A"
  • 对象字段:object.fieldobject["field"]
  • 关键字字段名用下标:row["where"]
  • 跟随链接:[[Link]].value 取目标页字段(若已有 Class:: [[Math]] 字段,用 Class.timetable 而非 [[Class]].timetable

Lambda

Text
CALENDAR file.day
FLATTEN all(map(file.tasks, (x) => x.completed)) AS "allCompleted"
WHERE !allCompleted

语法 (arg1, arg2) => 表达式,常用于 map/filter/minby/maxby 等。

八、函数速查

Dataview 内置函数大多支持向量化(对列表逐元素应用)。

构造函数

object(k,v,...) · list(...)(别名 array)· date(text[, format]) · dur(text) · number(text) · string(any) · link(path[, display]) · embed(link) · elink(url[, display]) · typeof(any)

数值

round(n[, digits]) · trunc(n) · floor(n) · ceil(n) · min(...) · max(...) · sum(array) · product(array) · average(array) · reduce(array, 运算)+/-/*///&/|)· minby(array, fn) · maxby(array, fn)

列表 / 对象 / 数组

contains(容器, 值) · icontains(忽略大小写)· econtains(精确)· containsword · extract(obj, k1, k2...) · sort(list) · reverse(list) · length(x) · nonnull(array) · firstvalue(array) · all(array[, fn]) · any(array[, fn]) · none(array[, fn]) · join(array[, delim]) · filter(array, fn) · unique(array) · map(array, fn) · flat(array[, depth]) · slice(array[, start[, end]])

字符串

regextest(pat, s) · regexmatch(pat, s)(整串匹配)· regexreplace(s, pat, rep) · replace(s, pat, rep) · lower(s) · upper(s) · split(s, delim[, limit]) · startswith(s, pre) · endswith(s, suf) · padleft / padright · substring(s, start[, end]) · truncate(s, len[, suffix])

工具

default(field, value)(null 兜底)· ldefault(非向量化版)· display(any) · choice(bool, left, right)(三元表达式)· hash(seed[, text[, variant]]) · striptime(date) · dateformat(date, fmt) · durationformat(dur, fmt) · currencyformat(n[, code]) · localtime(date) · meta(link)

dateformat() 返回字符串(Luxon token 如 yyyy-MM-dd),不能直接与 date() 结果比较;sum([])/average([]) 空数组返回 null

九、DataviewJS API 速查

dataviewjs 代码块内隐式变量 dv(或 dataview)。⌛ 表示异步(需 await)。

查询

JavaScript
dv.pages()              // 全库页面
dv.pages("#books")      // 带标签的页面
dv.pages('"folder"')    // 某文件夹(注意:文件夹在字符串内也要加引号)
dv.pagePaths("#books")  // 只返回路径
dv.page("Index")        // 按路径/链接取单页
dv.current()            // 当前脚本所在页面

渲染

JavaScript
dv.header(1, "标题");
dv.paragraph("段落文本");
dv.span("无内边距的 span");
dv.el("b", "加粗文本", { cls: "my-class", attr: { alt: "x" } });

dv.list(dv.pages().file.link);
dv.taskList(dv.pages("#project").file.tasks.where(t => !t.completed));
dv.taskList(dv.pages("#project").file.tasks, false); // 第二参 false = 不按文件分组
dv.table(["File", "Rating"],
  dv.pages("#book").sort(b => b.rating).map(b => [b.file.link, b.rating]));

返回 Markdown 的变体:dv.markdownTable() · dv.markdownList() · dv.markdownTaskList()(配合 dv.paragraph() 渲染)。

执行

JavaScript
dv.execute("LIST FROM #tag");     // 内嵌执行 DQL
dv.executeJs("dv.list([1,2,3])"); // 内嵌执行 JS
await dv.view("views/custom", { arg: 1 }); // 加载外部视图脚本(路径从库根算起)

工具

dv.array() · dv.isArray() · dv.fileLink() · dv.sectionLink() · dv.blockLink() · dv.date() · dv.duration() · dv.compare(a,b) · dv.equal(a,b) · dv.clone() · dv.parse()

查询求值

JavaScript
await dv.query("LIST FROM #tag");        // 结构化结果
await dv.tryQuery("LIST FROM #tag");     // 失败抛异常
await dv.queryMarkdown("LIST FROM #tag");// 返回 Markdown
dv.evaluate("2 + 2");                    // Result 对象(.successful / .value / .error)
dv.tryEvaluate("2 + 2");                 // 直接返回值,失败抛异常

文件 I/O(dv.io,⌛ 异步)

JavaScript
await dv.io.csv("data.csv");       // 读 CSV → 对象数组
await dv.io.load("File");          // 读文件内容字符串
dv.io.normalize("Test");           // 相对路径 → 绝对路径(同步)

十、安全须知

  • DQL / 内联表达式是沙箱化的,无法对库做破坏性操作。
  • dataviewjs / 内联 JS 拥有与插件同级的权限:可以改写、创建、删除文件,也能发起网络请求。只运行你信任的脚本。

来源