共计 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,具体效果见这里。