共计 6597 个字符,预计需要花费 17 分钟才能阅读完成。
如果说 Obsidian 有高中低等几种级别的玩法,那么 Dataview 必然会是高级玩法中的必备组件之一。Dataview 为 Obsidian 带来了动态查询数据和展示的能力,比如官方列的一些应用场景:
这是一个简单但强大的想法:
- 在每日笔记中记录睡眠时间表和习惯来跟踪它们,并自动创建睡眠时间表的每周报表。
- 自动收集笔记中书籍的链接,并按评分对它们排序。
- 自动收集带有指定日期注释的页面,将它们显示在每日笔记或其他地方中。
- 查看没有后续标记的页面,或显示特定标记的页面。
- 创建即将到来的事件的动态视图,并附注注释。
注:本文示例文档见 obsidian-roam-guide。
组件
Dataview 主要由 annotation 和 querying 两大组件构成。下面说下这两类组件的用法。
Annotation
Annotation 用来标注字段信息,经过查询后展示字段值,目前 Annotation 有三类设置方式。
Front Matter
除了 Obsidian 默认的几个 Key 外,Dataview 支持在 Front Matter 自定义 Annotation 来设置 Key,如下:
---
name: Obsidian 漫游指南
url: https://qileq.com/series/obsidian/
priority: High
status: In Progressing
---
该方式设置的 Annotation 在阅读模式不可见。
行内字段
若要在文件内容中设置 Annotation,可使用如下三种方式:
Key:: Value
:单独一行设置 Annotation,该方式在阅读模式原样展示。[Key:: Value]
:如果要插入在一行中则使用本方式,该方式在阅读模式中仅显示 Key 和 Value。(Key:: Value)
:和[Key:: Value]
类似,但在阅读模式中仅显示 Value。
隐式字段
Dataview 默认在每个页面、任务和列表中都添加了如下 Annotation 元信息,如下:
页面 Annotation 元信息
页面 Annotation 元信息如下:
file.name
: 文件名file.folder
: 文件所属的文件夹file.path
: 文件路径file.ext
: 文件扩展属性,如为.md
file.link
: 文件链接file.size
: 文件大小file.ctime
: 文件创建时间(日期 + 时间)file.cday
: 文件创建日期(日期)file.mtime
: 文件最后修改时间(日期 + 时间)file.mday
: 文件最后修改日期(日期)file.tags
: 文件 tags,多级 tags 会被分开存储,如#Tag/1/A
会被存储为[#Tag, #Tag/1, #Tag/1/A]
file.etags
: 文件显式指定的 tags,不会像file.tags
一样拆分 tagsfile.inlinks
: 所有引用该文件的文件(即入链)file.outlinks
: 该文件引用的所有文件(即出链)file.aliases
: 文件 aliases 数组file.tasks
: 文件中所有的任务列表(任务形式为- [ ]
或- [x]
)file.lists
: 文件中的所有列表(包括任务列表)file.frontmatter
: Front Matter 中所有信息,主要用于检查原始 Front Matter 或动态展示 Front Matter 值file.day
: 如果文件名中有形如yyyy-mm-dd
或yyyymmdd
的日期信息,可通过本字段获取file.starred
: 文件是否标星(若是会在 Starred Files 中显示)
任务 Annotation 元信息
任务 Annotation 元信息如下:
status
: 任务状态,任务由标记,默认为空,表示未完成;即
表示任务未完成,若为
[X]
表示任务已完成checked
: 任务是否已被检查(如状态是否完成)completed
: 标记任务是否已完成,若状态为X
则为完成,不考虑该任务下子任务的状态fullyCompleted
: 是否所有子任务均已完成text
: 任务文本line
: 任务显示行lineCount
: 任务行数path
: 任务路径section
: 任务所属块tags
: 任务中的 tagsoutlinks
: 任务中定义的链接link
: 离此任务最近的链接children
: 任务的子任务列表task
: 若为true
则表示为任务,否则表示常规列表completion
: 任务完成时间,可通过[completion:: ...]
或Completed Date: ✅YYYYY-MM-DD
(如 Completed last saturday ✅2022-12-21 )设置due
: 任务截至日期,可通过[due:: ...]
或Due Date: 🗓️YYYY-MM-DD
(如 Due this saturday 🗓️2021-08-29)设置created
: 任务创建日期,可通过[created:: ...]
或Created Date: ➕YYYY-MM-DD
(如 I made this on ➕1990-06-14)设置start
: 任务开始日期,可通过[start:: ...]
或Start Date: 🛫YYYY-MM-DD
(如 Task I can start this weekend 🛫2021-08-29)设置scheduled
: 任务计划日期,可通过[scheduled:: ...]
或Scheduled Date: ⏳YYYY-MM-DD
(如 Task I finished ahead of schedule ⏳2021-08-29 ✅2021-08-22)设置annotated
: 若有任务自定义 annotations 则为true
,否则为false
parent
: 此任务上方任务的行号(如果存在);若是根任务,则该属性为null
blockId
: 任务/列表元素的 blockId
下图展示了这几种用法:
效果如下:
Querying
Querying 根据条件查询 Annotation 字段并进行动态展示。目前可以通过如下四种方式查询。
Dataview 动态查询语言
Dataview 动态查询语言简称 DQL,提供了基础查询能力,如:
TABLE file.name AS "File", rating AS "Rating" FROM #book
行内表达式
如果想在行内显示 Dataview 的查询结果,可使用 = expression
语法,如插入当前文件名 = this.file.name
,插入日期 = date(today)
,更多表达式参考 Expressions。
Dataview JavaScript API
Dataview JavaScript API(下文简称为 Dataview JS)提供了类似 JavaScript API 来访问 Dataview 数据的能力。如:
dv.taskList(dv.pages().file.tasks.where(t => !t.completed));
行内 Dataview JS 表达式
和行内表达式一样在行中插入表达式值,语法为 $= expression
,如下:
文件最后更新时间为 `$= dv.current().file.mtime`.
下面详细介绍下 DQL 和 Dataview JS。
DQL
DQL 提供了类似 SQL 的语法,其通用语法如下:
<QueryType> <field> [AS "Column Name"], <field>, ..., <field>
FROM <source>
WHERE <expression>
SORT <expression> [ASC/DESC]
... other data commands
如示例仓库中查找所有书籍信息,并展示书名、封面、作者、评分和阅读进查找所有书箱信息,并展示书名、封面、作者、评分和阅读进度:
Table
WITHOUT ID
file.name AS 书名,
embed(link(cover)) AS 封面,
author AS 作者,
rating AS 评分,
status AS 进度
FROM #book AND -"templates"
SORT status DESC
效果如下:
QueryType
除了 QueryType 为必需字段外,其它字段均可以省略,此时表示仓库中所有的文件。QueryTable 表示视图类型,有如下几个值:
TABLE
:以表格形式展示视图LIST
:以列表形式展示视图TASK
:以任务形式展示视图CALENDAR
:以日历形式展示视图,文件以.
的形式展示在日历中指定日期下方。由于显示方式较特殊,所以语法和其他几类视图不同:CALENDAR <date> FROM <source>
field
即展示的字段,即 Annotation 信息。可以原样输出字段,也可以对字段进行修改、计算后再输出。字段可以通过 AS
指定别名,在展示时将显示别名。
默认会展示 ID 字段,由于目前 Dataview 设置中 ID 字段会以 File 展示,所以首列为 File,表示文件,如果想使用其它名称,可在 Dataview 设置中修改。对于 Table/List 两种类型来说,如果不想展示 ID 信息,可以在查询时加上 WITHOUT ID
。
FROM
表示从哪里查找文件,目前可设置如下几种类型:
- Tags:从指定 tag(及其所有子 tag)中查找,语法为
FROM #tag
- 文件夹:从指定文件夹(及其所有子文件夹)查找,语法为
FROM "folder"
- 单个文件:指定从具体某个文件查找,语法为
FROM "path/file"
- 文件链接:如果要查找引向某文件的所有文件,使用
FROM [[note]]
;如果要查找某文件中的所有文件链接,使用FROM outgoing([[note]]
这些类型可以通过谓词组合使用,如下:
and
:同时满足多个条件,如FROM #tag and "folder"
表示查找文件夹 folder 中有 tag 标签的所有文件。or
:满足条件之一,如FROM #tag or "folder"
表示查找文件夹 folder 或有 tag 标签的所有文件。-
:表示排除,如FROM -#tag and "folder"
表示查找文件夹 folder 中不含有 tag 标签的所有文件。
多个条件还可以使用 ()
指定优先级,如 FROM #tag and ("folder" or #other-tag)
表示从文件夹 folder 或有 other-tag 标签的文件中再查找有 tag 标签的文件。
WHERE
筛选条件,用来过滤查询结果中 Annotation 不符合条件的文件。如返回一天内有过修改的文件:LIST WHERE file.mtime >= date(today) - dur(1 day)
。
SORT
对结果排序,有两种排序方式:
- ASCENDING/ASC:升序,默认排序方式
- DESCENDING/DESC:降序
可以对多个字段同时排序,优先级从左到右,如 SORT priority DESC, status ASC
表示优先使用 priority 升序排序,如果 priority 相同,再使用 status 降序排序。
GROUP BY
按字段分组展示。
FLATTEN
将结果打开,如书籍信息中有多名作者,若使用 FLATTEN
打平作者,此时该书籍信息会形成多条记录,每条记录的作者只有一位。FLATTEN
一般用于数组或层级列表中。
LIMIT
限制返回结果数,如 LIMIT 10
表示最多返回 10 条记录。
Dataview JS
Dataview JS 允许通过访问数据视图索引和查询引擎来执行任意的 JavaScript 代码,这种方式对复杂视图或与其他插件的交互很有用。相比 Dataview 指定代码类型为 dataview
而言,Dataivew JS 需指定代码类型为 dataviewjs
,如下:
dv.pages("#thing")...
其中的 dv
(或 dataview
)为 Dataview JS 隐式提供的变量,用来查询信息、渲染 HTML 和配置视图。
数据数组
Dataview JS 主要是操作数组 DataArray,数据数组有一些常用的方法,如下:
- where:同 DQL 中的 WHERE。
- filter:是 where 的别名,两者等同。
- sort:同 DQL 中的 SORT,同样支持
"asc"
和"desc"
两种排序方式。 - groupBy:同 DQL 中的 GROUP BY。
- limit:同 DQL 中的 LIMIT。
- map:遍历数组并应用函数。
- find:查找符合条件的元素。
- join:拼接字符串并返回。
那么如何使用 Dataview JS,如何获取数据数组呢?这里先解释下 Dataview JS 的一些语法。
查询
dv.pages(source)
类似于 Dataview 的 From,可以通过dv.pages(source)
来查询指定条件的文件,返回文件数组,如dv.pages("#book")
表示查询所有 tag 为 book 的文件,dv.pages('"folder" or #tag')
表示是查询 folder 目录或标签名为 tag 的所有文件,可以看到其用法大致和 Dataview 的 From 相似。dv.pagePaths(source)
返回指定条件的文件路径,返回文件路径数组。dv.page(path)
返回指定路径的文件。dv.current()
表示当前文件。
渲染
dv.el(html-element, text)
根据指定的 HTML 元素来渲染 text,如dv.el("b", "qileq.com:分享简单的成长");
。dv.header(level, text)
根据指定标题级别渲染 text,如dv.header(1, "Obsidian 漫游指南");
。dv.paragraph(text)
将 text 渲染为段落,如dv.paragraph("Dataview 插件概览");
。dv.span(text)
将 text 渲染为行内文本,如dv.span("Dataview JS 语法");
。dv.execute(source)
执行 DQL 查询并插入到当前文件中,如dv.execute("LIST FROM #tag");
。dv.executeJs(source)
执行 Dataivew JS 查询并插入到当前文件中,如dv.executeJs("dv.list([1, 2, 3])");
。
Dataviews
dv.list(elements)
以列表形式展示数组,同 DQL 中的LIST
视图类型,如dv.list(dv.pages(#book).file.link)
。dv.taskList(tasks, groupByFile)
以任务列表的形式展示任务数组,同 DQL 中的TASK
视图类型。dv.table(headers, elements)
以表格形式展示数组,并指定表格头,同 DQL 中的TABLE
视图类型,如下是示例仓库 用 Dataview JS 来展示的语句:dv.table(["书名", "封面", "作者", "评分", "进度"], dv.pages('#book AND -"templates"') .sort(b => b.status, 'desc') .map(b => [b.file.name, dv.fileLink(b.cover, true), b.author, b.rating, b.status]))
工具方法
Dataview JS 提供了一些工具方法。
dv.array(value)
根据指定值构成数据数组,如dv.array([1, 2, 3])
返回值为[1, 2, 3]
的数组。dv.fileLink(path, [embed?], [display-name])
根据指定路径创建文件链接。如dv.fileLink("测试", false, "测试别名")
表示创建名为“测试别名”的文件链接,其指向文件“测试”。dv.sectionLink(path, section, [embed?], [display?])
创建段落链接,如dv.sectionLink("Note", "Header2")
表示[[Note#Header2]]
,该语法可参考Obsidian 使用技巧。dv.blockLink(path, blockId, [embed?], [display?])
创建块链接,如dv.blockLink("Note", "12gdhjg3")
表示[[Note#^12gdhjg3]]
。dv.date(text)
插入日期,如dv.date("2023-01-01")
。
以上就是 Dataview JS 的语法了。
如果想看参考案例,可点击这里。如果要优化展示效果的话,还可以将 Dataview 与其它插件组合使用,如 Admonition,具体效果见这里。