Obsidian 插件(一):DataView 的使用

DataView 的使用

一、 环境配置

首先,我们需要安装 Obsidian,同时,在这里 Obsidian 的基本使用就不会去介绍了。

Dataview 是一个覆盖OB知识库的实时索引和查询引擎。可以将数据(如标签、日期、代码段、数字等)与笔记相关联,然后查询(如筛选、排序、转换)数据。用一种数据库处理的形式,通过查找字段并筛选,进而使用列表、表格的形式展现出来,也支持JavaScript的高级查询形式。

安装方式:

或者,我们也可以使用 GitHub 来进行安装,我们将文件下载下来,解压到<vault>/.obsidian/plugins/

二、 入门介绍

1、 快速开始

官方文档:https://blacksmithgu.github.io/obsidian-dataview/

Dataview 是一个在你的知识库中生成数据的动态视图的高级查询引擎/索引。你可以通过使用任意和页面相关联的值,如标签(tag),文件夹(folder), 内容(content),或者字段(field)来生成视图。

我们可以使用 ::来生成 dataview 的数据

或者,这些信息放在 frontmatter,如:

那我们查询的话,

```dataview
LIST "<br>**电影名**:" + Movie + "<br>**简介**:" + Brief + "<br>**评分**:" + Score + "<br>**年份**:" + Year + "<br>**类型**:" + Type + " " + Location 
FROM #entertain/movie 
WHERE contains(file.folder, "record_2023")
SORT Date desc
```

那么,我们就可以生成这样的一个页面:

2、 页面和字段

dataview的核心数据抽象是页面(page) ,指在你的库中包含字段(field) 的markdwon页面。字段 是一段任意命名的数据 ——文本,日期,时间段,链接。 这些可被dataview理解,展示,筛选。字段可以通过三种方式定义:

  1. 扉页(Frontmatter): 所有的YAML 扉页内容都将自动的转换成dataview字段。

    ---
    tags: daily_node study/obsidian entertain/movie
    aliases: 
    describe: Obsidian 中 DataView 的使用
    Date: 2023-01-20
    Time: 09:26:28
    Author: Steve Anthony
    Email: 3500515050@qq.com
    ---
    
  2. 内联字段(inline field): 一行格式为<Name>:: <Value>的内容将自动的被dataview解析为一个字段,请注意,你可以对<Name>使用标准的Markdown格式,但以后将不再支持。

    Movie:: [猎屠](https://v.qq.com/x/cover/mzc00200e7w52db/b0044upikcw.html)
    Brief:: 影片讲述某地发生电信诈骗事件,一名警员潜伏到中缅边境,深入电信诈骗中心,与诈骗犯罪集团斗智斗勇的故事,是一部反电信诈骗题材的院线片。
    Score:: 7.6
    Year:: 2022
    Type:: 犯罪 动作 剧情
    Location:: 内地
    
  3. 隐含字段(implicit): dataview自带大量的元数据对页面进行注释,如文件的创建日期、任何相关的日期、文件中的链接、标签等。

    如果文件的标题内有一个日期(格式为yyyy-mm-dd或yyyymmdd),或者有一个Date字段/inline字段,它也有以下属性:

    • file.day: 一个该文件的隐含日期。

字段类型:

dataview支持数种不同的字段类型:

  • 文本(Text): 全局默认为文本。如果一个字段不匹配其它具体的类型,默认为一段纯文本。
  • 数字(Number): 数字类似于’6’ 和 ‘3.6’。
  • 布尔值(Boolean): true/false, 就像编程中的概念。
  • 日期(Date): ISO8601 标准定义的通用日期格式 YYYY-MM[-DDTHH:mm:ss]. 月份后面的内容都是可选的。
  • 时间段(Duration): 时间段的格式为 <time> <unit>, 就像 6 hours 或者 4 minutes。支持常见的英文缩写如6hrs 或者 2m
  • 链接(Link): 普通的Obsidian 链接如 [[Page]] 或者 [[Page|Page Display]]
  • 列表(List): YAML中,其它dataview字段组成的列表将作为普通的YAML列表定义;对于内联字段,它们就只是逗号分隔的列表。
  • 对象(Object):名称(name)到dataview字段的映射。这仅能在YAML扉页中利用通用的YANML对象语法进行定义。 对象语法: field: value1: 1 value2: 2 ...

不同的字段类型非常重要。这能确保dataview理解怎样合理的对值进行比较和排序,并提供不同的操作。

3、 创建查询

一旦你给相关的页面添加了有用的数据,你就可以在某一个地方展示它或者操作它。dataview通过dataview代码块建立内联查询,写下查询代码,将会动态运行并在笔记的预览窗口展示。写这样的查询,有三种方式:

  1. dataview的查询语言是一个用于快速创建视图,简化的,类SQL的语言。它支持基本的算术和比较操作,对基础应用很友好。
  2. 查询语言也提供内联查询,允许你直接在一个页面内嵌入单个值——通过= date(tody)创建今天的日期,或者通过= [[Page]].value来嵌入另一个页面的字段。
  3. dataview JavaScript API为你提供了 JavaScript 的全部功能,并为拉取 Dataview 数据和执行查询提供了 DSL ,允许你创建任意复杂的查询和视图。

与JavaScript API相比,查询语言的功能往往比较滞后,主要是因为JavaScript API更接近实际代码;相反,查询语言更稳定,在Dataview的重大更新中不太可能出现故障。

还可以创建日历:

CALENDAR Date
FROM #daily_node
WHERE contains(file.folder, "record_2023")

4、 系统字段

  1. FROM

    FROM语句决定了哪些页面在初始被收集并传递给其他命令进行进一步的筛选。你可以从任何来源中选择,来源可选择文件夹,标签,内链和外链。

    • 标签(Tags): 从标签(包含子标签)中选择,使用FROM #tag
    • 文件夹(Folders): 从文件夹(包含子文件夹)中选择,使用 FROM "folder"
    • 链接(Links): 你可以选择一个链接到该文件的链接,也可以选择该文件链接到其它页面的链接:
    • 获得链接到[[note]]的所有页面,使用FROM [[note]]
    • 获得从[[note]]链接的所有页面(如,文件中的所有链接),使用FROM outgoing([[note]])

    你可以对过滤器进行组合,以便使用 "and "和 "or "获得更高级的来源。

    举个例子

    • #tag and "folder"将返回在folder中和包含#tag的所有页面。
    • [[Food]] or [[Exercise]] 将给出任何链接到[[Food]][[Exercise]]的页面。
  2. WHERE: 笔记进行过滤,聚合条件

  3. SORT:根据什么条件进行排序

    你可以给出多个字段来进行排序。排序将在第一个字段的基础上进行。接着,如果出现相等,第二个字段将被用来对相等的字段进行排序。如果仍然有相等,将用第三个字段进行排序,以此类推。

    SORT field1 [ASCENDING/DESCENDING/ASC/DESC], ..., fieldN [ASC/DESC]
    
  4. GROUP BY:

    对一个字段的所有结果进行分组。每个唯一的字段值产生一行,它有两个属性:

    • 一个对应于被分组的字段
    • 一个是rows数组字段,包含所有匹配的页面。
  5. LIMIT: 限制输出多少条结果

  6. FLATTEN: 根据字段或计算将一个结果拆分为多个结果。

    FLATTEN field
    FLATTEN (computed_field) AS name
    

三、 接口讲解

1、 表达式

1.1 概述

Dataview查询语言表达式 可以是任何能产生一个值的量,所有字段都是表达式,字面值如6,已计算的值如field - 9都是一个表达式,做一个更具体的总结:

# 常规
field               (directly refer to a field)
simple-field        (refer to fields with spaces/punctuation in them like "Simple Field!")
a.b                 (if a is an object, retrieve field named 'b')
a[expr]             (if a is an object or array, retrieve field with name specified by expression 'expr')
f(a, b, ...)        (call a function called `f` on arguments a, b, ...)

# 算术运算
a + b               (addition)
a - b               (subtraction)
a * b               (multiplication)
a / b               (division)

# 比较运算
a > b               (check if a is greater than b)
a < b               (check if a is less than b)
a = b               (check if a equals b)
a != b              (check if a does not equal b)
a <= b              (check if a is less than or equal to b)
a >= b              (check if a is greater than or equal to b)

# 特殊操作
[[Link]].value      (fetch `value` from page `Link`)
1.2 表达式类型

比较运算符:

你可以使用各种比较运算符来比较大多数数值。<, >, <=, >=, =, !=. 这产生了一个布尔的真或假值,可以在查询中的`WHERE’块中使用。

对象获数组:

你可以通过索引操作符array[<index>]从数组中索引数据,其中<index>是任何已计算的表达式。 数组是以0为索引的,所以第一个元素是索引0,第二个元素是索引1,以此类推。 例如,list(1, 2, 3)[0] = 1.

你也可以使用索引操作符从对象(将文本映射到数据值)中检索数据,此时的索引是字符串/文本而不是数字。你也可以使用快捷方式object.<name>,其中<name>是值的索引。例如object("yes", 1).yes = 1

函数的调用

Dataview支持各种用于操作数据的函数,这些函数在函数文档中有完整描述。它们的一般语法是function(arg1, arg2, ...) - 即lower("yes")regexmatch("text", ".+")

1.3 特定类型的交互

大多数dataview类型与运算符有特殊的相互作用,或者有额外的字段可以使用索引操作符索引。

日期:

你可以通过索引来检索一个日期的不同组成部分:date.yeardate.monthdate.daydate.hourdate.minute, date.second, date.week。你也可以将时间段添加到日期中以获得新的日期。

时间段:

时间段可以相互添加,也可以添加到日期。你可以通过索引来检索一个时间段的各种组成部分。duration.years, duration.months, duration.days, duration.hours, duration.minutes, duration.seconds.

链接:

你可以 "通过索引 "一个链接来获得相应页面上的值。例如,[[Link]].value将获得来自Link页面上的value值。

2、 函数

2.1 构造器

构造器创建值

object(key1, value1, ...)

用给定的键和值创建一个新的对象。在调用中,键和值应该交替出现,键应该总是字符串/文本。

object() => empty object
object("a", 6) => object which maps "a" to 6
object("a", 4, "c", "yes") => object which maps a to 4, and c to "yes"

list(value1, value2, ...)

用给定的值创建一个新的列表。

list() => empty list
list(1, 2, 3) => list with 1, 2, and 3
list("a", "b", "c") => list with "a", "b", and "c"

date(any)

从提供的字符串、日期或链接对象中解析一个日期,解析不出返回null。

date("2020-04-18") = <date object representing April 18th, 2020>
date([[2021-04-16]]) = <date object for the given page, refering to file.day>

number(string)

从给定的字符串中抽出第一个数字,并返回该数字。如果字符串中没有数字,则返回null。

number("18 years") = 18
number(34) = 34
number("hmm") = null

link(path, [display])

从给定的文件路径或名称构建一个链接对象。如果有两个参数,第二个参数是链接的显示名称。

link("Hello") => link to page named 'Hello'
link("Hello", "Goodbye") => link to page named 'Hello', displays as 'Goodbye'

elink(url, [display])

构建一个指向外部网址的链接(如www.google.com)。如果有两个参数,第二个参数是该链接的显示名称。

elink("www.google.com") => link element to google.com
elink("www.google.com", "Google") => link element to google.com, displays as "Google"
2.2 常用函数

数值操作

round(number, [digits])

将一个数字四舍五入到指定的位数。如果没有指定第二个参数,则舍入到最接近的整数。 否则,四舍五入到给定的位数。

round(16.555555) = 17
round(16.555555, 2) = 16.56

对象,数组和字符串操作

对容器对象内部的值进行操作的操作。

contains(object|list|string, value)

检查给定的容器类型中是否有给定的值。这个函数的行为稍有不同,它基于第一个参数是一个对象,一个列表,还是一个字符串。

  • 对于对象,检查该对象是否有一个给定名称的键。如: contains(file, "ctime") = true contains(file, "day") = true (if file has a date in its title, false otherwise)
  • 对于列表,检查数组中是否有元素等于给定的值。如: contains(list(1, 2, 3), 3) = true contains(list(), 1) = false
  • 对于字符串,检查给定的值是否是字符串的子串。 contains("hello", "lo") = true contains("yes", "no") = false

extract(object, key1, key2, ...)

从一个对象中抽出多个字段,创建一个抽出字段的新对象。

extract(file, "ctime", "mtime") = object("ctime", file.ctime, "mtime", file.mtime)
extract(object("test", 1)) = object()

sort(list)

排序列表,返回一个排序好的新列表。

sort(list(3, 2, 1)) = list(1, 2, 3)
sort(list("a", "b", "aa")) = list("a", "aa", "b")

reverse(list)

反转列表,返回一个反转好的新列表。

reverse(list(1, 2, 3)) = list(3, 2, 1)
reverse(list("a", "b", "c")) = list("c", "b", "a")

length(object|array)

返回一个对象中的字段数量,或一个数组中的元素数量。

length(list()) = 0
length(list(1, 2, 3)) = 3
length(object("hello", 1, "goodbye", 2)) = 2

sum(array)

数组中数值元素求和。

sum(list(1, 2, 3)) = 6

all(array)

只有当数组中的所有值都为真,才会返回 “true”。你也可以给这个函数传递多个参数,只有当所有的参数都为真时,它才会返回`true’。

all(list(1, 2, 3)) = true
all(list(true, false)) = false
all(true, false) = false
all(true, true, true) = true

any(array)

只要数组中有值为真,便返回true。也可以给这个函数传递多个参数,只要有参数为真,便返回true

any(list(1, 2, 3)) = true
any(list(true, false)) = true
any(list(false, false, false)) = false
all(true, false) = true
all(false, false) = false

none(array)

如果数组中没有元素,返回none

join(array)

将一个数组中的元素连接成一个字符串(即在同一行呈现所有的元素)。如果有第二个参数,那么每个元素将被给定的分隔符分开。

join(list(1, 2, 3)) = "1, 2, 3"
join(list(1, 2, 3), " ") = "1 2 3"
join(6) = "6"
join(list()) = ""

字符串操作

regexmatch(pattern, string)

检查给定的字符串是否与给定的模式相匹配(使用JavaScript regex引擎)。

regexmatch("w+", "hello") = true
regexmatch(".", "a") = true
regexmatch("yes|no", "maybe") = false

regexreplace(string, pattern, replacement)

用 "replacement "替换所有在 "string "中匹配regex pattern的实例。这使用了JavaScript的替换方法,所以你可以使用特殊字符如$1来指代第一个捕获组,以此类推。

regexreplace("yes", "[ys]", "a") = "aea"
regexreplace("Suite 1000", "d+", "-") = "Suite -"

replace(string, pattern, replacement)

replacement替换string中的所有pattern实例。

replace("what", "wh", "h") = "hat"
replace("The big dog chased the big cat.", "big", "small") = "The small dog chased the small cat."
replace("test", "test", "no") = "no"

lower(string)

将一个字符串所有字符转换为小写字符。

lower("Test") = "test"
lower("TEST") = "test"

upper(string)

将一个字符串所有字符转换为大写字符。

upper("Test") = "TEST"
upper("test") = "TEST"
2.3 工具函数

default(field, value)

如果field为空,返回value;否则返回field。对于用默认值替换空值很有用。例如,要显示尚未完成的项目,使用"incomplete"作为其默认值。

default(dateCompleted, "incomplete")

默认值在两个参数中都是矢量;如果你需要在一个列表参数中明确使用默认值,请使用ldefault,它与默认值相同,但没有被矢量化。

default(list(1, 2, null), 3) = list(1, 2, 3)
ldefault(list(1, 2, null), 3) = list(1, 2, null)

choice(bool, left, right)

一个原始的if语句–如果第一个参数为真,则返回第二个参数的内容;否则,返回第三个参数的内容。

choice(true, "yes", "no") = "yes"
choice(false, "yes", "no") = "no"
choice(x > 4, y, z) = y if x > 4, else z

striptime(date)

剥离日期中的时间部分,只留下年、月、日。如果你在比较日期的时候不在乎时间,这种方式挺好。

striptime(file.ctime) = file.cday
striptime(file.mtime) = file.mday