前言
最近将笔记从notion迁移到obsidian,有些展示的功能需要使用dataview才能实现
花费了很多时间使用这个插件,一开始有一些不太了解的东西,后面就熟悉起来了
想着把这次的过程记录一下吧
背景
现在有一些记录了背诵内容的笔记,有这些属性:
datetime当前完成轮次
我要做的是按照艾宾浩斯记忆法的轮次完成情况检索这些记录,最终目标如下:

艾宾浩斯记忆法的轮次有这些:
- 第十轮180d
- 第九轮90d
- 第八轮30d
- 第七轮15d
- 第六轮7d
- 第五轮4d
- 第四轮2d
- 第三轮1d
- 第二轮12h
- 第一轮30m
- 未开始
比方说,如果某个背诵笔记距date+time已经过了30分钟,那就进入了第一轮,需要背一遍了
当前轮开始时就是刚一进入当前轮的时间,目前进度就是笔记的当前完成轮次属性
实现
其实在notion中倒简单,只需要设置计算属性就行,就有点像vue3的computed:

我一开始的思路是,一步步实现dataview的DQL语法
首先先弄框架:
table without id
file.link as "背诵任务",
1 as "当前轮次",
1 as "当前轮开始时",
1 as "背了吗",
当前完成轮次 as "目前进度"
from "背诵"
SORT date
其中table表明查询结果放到表格中, without id出去前面的序号, file.link从page的文件中获取链接形式(点击后可跳转), 当前完成轮次即为page属性了
计算当前轮次首先需要计算文档时间:
date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))
date(now)获取当前时间regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes")是正则替换, time的格式是形如17:25, 我将它格式化为17 hours 25 minutesdur()是将格式化的持续时间时间转化为date格式的持续时间
还要用到choice(), 用法和if()是一模一样的, 第一个参数为条件, true则返回第二个参数, 否则返回第三个参数
通过嵌套choice可以完成当前轮次这一列的计算了:
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(180 day),"第十轮180d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(90 day),"第九轮90d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 day),"第八轮30d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(15 day),"第七轮15d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(7 day),"第六轮7d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(4 day),"第五轮4d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(2 day),"第四轮2d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(1 day),"第三轮1d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(12 hours),"第二轮12h",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 minutes),"第一轮30m",
"未开始")))))))))) as "当前轮次"
那么背了吗只需要当前轮次这一列的结果等于当前完成轮次这一page属性就行了:
choice(当前完成轮次=
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(180 day),"第十轮180d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(90 day),"第九轮90d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 day),"第八轮30d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(15 day),"第七轮15d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(7 day),"第六轮7d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(4 day),"第五轮4d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(2 day),"第四轮2d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(1 day),"第三轮1d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(12 hours),"第二轮12h",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 minutes),"第一轮30m",
"未开始")))))))))),"*完成*","==未完成==") as "背了吗"
当前轮开始时这一列只需要判断一下当前的轮, 然后加上每个轮对应的时间就行, 使用dateformat格式化一下:
dateformat(
date
+dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))
+choice(当前轮次 -> 对应的dur(...)),
"yyyy年M月d日HH:mm"
)
因为我想要不同的颜色来直观显示背没背,所以加一个choice:choice(当前轮=当前完成轮次, "*"+dateformat(...)+"*", "=="+dateformat(...)+"==") as "当前轮开始时"
把已有的逻辑硬塞进去是这样的:
(太长了默认收起来)
choice(当前完成轮次=
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(180 day),"第十轮180d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(90 day),"第九轮90d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 day),"第八轮30d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(15 day),"第七轮15d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(7 day),"第六轮7d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(4 day),"第五轮4d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(2 day),"第四轮2d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(1 day),"第三轮1d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(12 hours),"第二轮12h",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 minutes),"第一轮30m",
"未开始")))))))))),
"*"+dateformat(date+dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))+choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(180 day),dur(180 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(90 day),dur(90 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 day),dur(30 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(15 day),dur(15 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(7 day),dur(7 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(4 day),dur(4 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(2 day),dur(2 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(1 day),dur(1 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(12 hours),dur(12 hours),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 minutes),dur(30 minutes),
dur(0 minutes))))))))))),"yyyy年M月d日HH:mm")+"*",
"=="+dateformat(date+dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))+choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(180 day),dur(180 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(90 day),dur(90 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 day),dur(30 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(15 day),dur(15 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(7 day),dur(7 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(4 day),dur(4 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(2 day),dur(2 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(1 day),dur(1 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(12 hours),dur(12 hours),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 minutes),dur(30 minutes),
dur(0 minutes))))))))))),"yyyy年M月d日HH:mm")+"==") as "当前轮开始时"
简直是嵌套地狱, 完整版如下:
(太长了默认收起来)
table without id
file.link as "背诵任务",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(180 day),"第十轮180d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(90 day),"第九轮90d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 day),"第八轮30d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(15 day),"第七轮15d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(7 day),"第六轮7d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(4 day),"第五轮4d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(2 day),"第四轮2d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(1 day),"第三轮1d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(12 hours),"第二轮12h",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 minutes),"第一轮30m",
"未开始")))))))))) as "当前轮次",
choice(当前完成轮次=
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(180 day),"第十轮180d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(90 day),"第九轮90d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 day),"第八轮30d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(15 day),"第七轮15d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(7 day),"第六轮7d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(4 day),"第五轮4d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(2 day),"第四轮2d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(1 day),"第三轮1d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(12 hours),"第二轮12h",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 minutes),"第一轮30m",
"未开始")))))))))),
"*"+dateformat(date+dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))+choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(180 day),dur(180 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(90 day),dur(90 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 day),dur(30 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(15 day),dur(15 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(7 day),dur(7 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(4 day),dur(4 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(2 day),dur(2 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(1 day),dur(1 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(12 hours),dur(12 hours),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 minutes),dur(30 minutes),
dur(0 minutes))))))))))),"yyyy年M月d日HH:mm")+"*",
"=="+dateformat(date+dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))+choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(180 day),dur(180 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(90 day),dur(90 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 day),dur(30 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(15 day),dur(15 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(7 day),dur(7 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(4 day),dur(4 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(2 day),dur(2 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(1 day),dur(1 day),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(12 hours),dur(12 hours),
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 minutes),dur(30 minutes),
dur(0 minutes))))))))))),"yyyy年M月d日HH:mm")+"==") as "当前轮开始时",
choice(当前完成轮次=
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(180 day),"第十轮180d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(90 day),"第九轮90d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 day),"第八轮30d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(15 day),"第七轮15d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(7 day),"第六轮7d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(4 day),"第五轮4d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(2 day),"第四轮2d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(1 day),"第三轮1d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(12 hours),"第二轮12h",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 minutes),"第一轮30m",
"未开始")))))))))),"*完成*","==未完成==") as "背了吗",
当前完成轮次 as "目前进度"
from "背诵"
where 当前完成轮次!=
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(180 day),"第十轮180d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(90 day),"第九轮90d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 day),"第八轮30d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(15 day),"第七轮15d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(7 day),"第六轮7d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(4 day),"第五轮4d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(2 day),"第四轮2d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(1 day),"第三轮1d",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(12 hours),"第二轮12h",
choice(date(now)-dur(regexreplace(time,"(\d*):(\d*)","$1 hours $2 minutes"))-date>dur(30 minutes),"第一轮30m",
"未开始"))))))))))
SORT date
改进一 使用dataviewjs
后面了解到还有dataviewjs语法, 相当于用js扩展了dataview, 于是乎开始研究
js代码就清晰一点了, 刚好在学vue, 就当练练基本功:
let pagesFolder = '"背诵"'
function getIndex(a) {
return Number(a.slice(0, 3))
}
let pages = dv.pages(pagesFolder).sort(i => getIndex(i.file.name))
先获取pages, 按照文件名排序, 我的文件名字带序号, 都是001-这样的开头dv是dataview提供的类, dv.pages根据条件获取一批笔记
我这里获取背诵文件夹下的所有笔记, 然后排序
然后处理一下, 生成一个列表, 每一项都是一行的数据:
// 轮次配置数组 [轮次名称, 毫秒持续时间]
const roundConfigs = [
["第十轮180d", 180 * 24 * 60 * 60 * 1000],
["第九轮90d", 90 * 24 * 60 * 60 * 1000],
["第八轮30d", 30 * 24 * 60 * 60 * 1000],
["第七轮15d", 15 * 24 * 60 * 60 * 1000],
["第六轮7d", 7 * 24 * 60 * 60 * 1000],
["第五轮4d", 4 * 24 * 60 * 60 * 1000],
["第四轮2d", 2 * 24 * 60 * 60 * 1000],
["第三轮1d", 1 * 24 * 60 * 60 * 1000],
["第二轮12h", 12 * 60 * 60 * 1000],
["第一轮30m", 30 * 60 * 1000],
["未开始", 0]
];
// 单次遍历处理所有数据
const tableContent = pages.map(page => {
// 合并日期时间
const [h, m] = page.time.split(':').map(Number)
// 计算文档时间戳(毫秒)
const date = new Date(page.date)
date.setHours(h, m, 0, 0)
const timestamp = date.getTime()
// 计算当前轮次
const diffMs = now - timestamp
let currentRound = "未开始"
let durationMs = 0
for (const [round, ms] of roundConfigs) {
if (ms === 0 || diffMs > ms) {
currentRound = round
durationMs = ms
break
}
}
// 判断是否完成
const isCompleted = currentRound === page.当前完成轮次
if (!isCompleted) return []
// 计算当前轮开始时间
const roundStartTime = new Date(timestamp + durationMs)
const formattedTime = (() => {
const year = roundStartTime.getFullYear()
const month = roundStartTime.getMonth() + 1
const day = roundStartTime.getDate()
const hours = String(roundStartTime.getHours()).padStart(2, '0')
const minutes = String(roundStartTime.getMinutes()).padStart(2, '0')
if(!isCompleted)
return `==${year}年${month}月${day}日${hours}:${minutes}==`
else return `*${year}年${month}月${day}日${hours}:${minutes}*`
})()
return [
page.file.link, // 背诵任务
currentRound, // 当前轮次
formattedTime, // 当前轮开始时
isCompleted ? "*完成*" : "==未完成==", // 背了吗
page.当前完成轮次 // 目前进度
]
})
前端这种写法, 一开始不太适应, 但后面还是觉得很舒服:
const 变量 = (()=>{
...
return result
})()
这种创建后立刻调用然后赋值的风格, 其实就相当于包了个代码块, 我们并不关心在里面的有一堆临时变量的实现
最后使用dv.table把表格渲染出来即可:
let pagesFolder = '"背诵"'
function getIndex(a) {
return Number(a.slice(0, 3))
}
let pages = dv.pages(pagesFolder).sort(i => getIndex(i.file.name))
let now = Date.now()
// 轮次配置数组 [轮次名称, 毫秒持续时间]
const roundConfigs = [
["第十轮180d", 180 * 24 * 60 * 60 * 1000],
["第九轮90d", 90 * 24 * 60 * 60 * 1000],
["第八轮30d", 30 * 24 * 60 * 60 * 1000],
["第七轮15d", 15 * 24 * 60 * 60 * 1000],
["第六轮7d", 7 * 24 * 60 * 60 * 1000],
["第五轮4d", 4 * 24 * 60 * 60 * 1000],
["第四轮2d", 2 * 24 * 60 * 60 * 1000],
["第三轮1d", 1 * 24 * 60 * 60 * 1000],
["第二轮12h", 12 * 60 * 60 * 1000],
["第一轮30m", 30 * 60 * 1000],
["未开始", 0]
];
// 单次遍历处理所有数据
const tableContent = pages.map(page => {
// 合并日期时间
const [h, m] = page.time.split(':').map(Number)
// 计算文档时间戳(毫秒)
const date = new Date(page.date)
date.setHours(h, m, 0, 0)
const timestamp = date.getTime()
// 计算当前轮次
const diffMs = now - timestamp
let currentRound = "未开始"
let durationMs = 0
for (const [round, ms] of roundConfigs) {
if (ms === 0 || diffMs > ms) {
currentRound = round
durationMs = ms
break
}
}
// 判断是否完成
const isCompleted = currentRound === page.当前完成轮次
if (!isCompleted) return []
// 计算当前轮开始时间
const roundStartTime = new Date(timestamp + durationMs)
const formattedTime = (() => {
const year = roundStartTime.getFullYear()
const month = roundStartTime.getMonth() + 1
const day = roundStartTime.getDate()
const hours = String(roundStartTime.getHours()).padStart(2, '0')
const minutes = String(roundStartTime.getMinutes()).padStart(2, '0')
if(!isCompleted)
return `==${year}年${month}月${day}日${hours}:${minutes}==`
else return `*${year}年${month}月${day}日${hours}:${minutes}*`
})()
return [
page.file.link, // 背诵任务
currentRound, // 当前轮次
formattedTime, // 当前轮开始时
isCompleted ? "*完成*" : "==未完成==", // 背了吗
page.当前完成轮次 // 目前进度
]
})
// 渲染表格
dv.table(
["背诵任务", "当前轮次", "当前轮开始时", "背了吗", "目前进度"],
tableContent
)
改进二 使用flatten
我在一开始的嵌套地狱那块,就在想能不能在DQL语句内部复制变量,但是我也没有查到 。后来我发现使用flatten即可
用法是FLATTEN <value> [AS <name>], 支持多个FLATTEN
这样一来事情就很简单了
定义一下现在的时间到文档时间的时间差:
FLATTEN dur(regexreplace(time, "(\d*):(\d*)", "$1 hours $2 minutes")) AS timeDur
FLATTEN date(now) - date - timeDur AS timeDiff
然后匹配一下轮次即可:
TABLE WITHOUT ID
file.link AS "背诵任务",
currentRound AS "当前轮次",
currentRoundStarted AS "当前轮开始时间",
choice(当前完成轮次 = currentRound, "*完成*", "==未完成==") AS "背了吗",
当前完成轮次 AS "目前进度"
FROM "背诵"
FLATTEN dur(regexreplace(time, "(\d*):(\d*)", "$1 hours $2 minutes")) AS timeDur
FLATTEN date(now) - date - timeDur AS timeDiff
FLATTEN choice(
timeDiff > dur(180 day), "第十轮180d",
choice(timeDiff > dur(90 day), "第九轮90d",
choice(timeDiff > dur(30 day), "第八轮30d",
choice(timeDiff > dur(15 day), "第七轮15d",
choice(timeDiff > dur(7 day), "第六轮7d",
choice(timeDiff > dur(4 day), "第五轮4d",
choice(timeDiff > dur(2 day), "第四轮2d",
choice(timeDiff > dur(1 day), "第三轮1d",
choice(timeDiff > dur(12 hours), "第二轮12h",
choice(timeDiff > dur(30 minutes), "第一轮30m",
"未开始")))))))))
) AS currentRound
FLATTEN date + timeDur + choice(
currentRound = "第十轮180d", dur(180 day),
choice(currentRound = "第九轮90d", dur(90 day),
choice(currentRound = "第八轮30d", dur(30 day),
choice(currentRound = "第七轮15d", dur(15 day),
choice(currentRound = "第六轮7d", dur(7 day),
choice(currentRound = "第五轮4d", dur(4 day),
choice(currentRound = "第四轮2d", dur(2 day),
choice(currentRound = "第三轮1d", dur(1 day),
choice(currentRound = "第二轮12h", dur(12 hours),
choice(currentRound = "第一轮30m", dur(30 minutes),
dur(0 minutes))))))))))) AS startTime
FLATTEN choice(当前完成轮次 = currentRound,
"*" + dateformat(startTime, "yyyy年M月d日HH:mm") + "*",
"==" + dateformat(startTime, "yyyy年M月d日HH:mm") + "=="
) AS currentRoundStarted
WHERE 当前完成轮次 != currentRound
SORT date
这样就完成了,比dataviewjs要简单的多了
至此已完成了这个功能,再看看:

杂谈
中文互联网的环境
虽然早就吐槽过了,但是到处转载搬屎的环境还是见一次难受一次
感谢一下高质量的文章以及官方文档:
- obsidian文档咖啡豆版 – dataviewjs-函数集合
- PKMer – Dataview 基本语法学习指南
- 知乎作者ENTPS – Obsidian DataView 入门保姆级引导手册
- 知乎作者jenemy – Obsidian 达人成长之路 #1:使用终极工具 Dataview 释放笔记库的潜力 · DQL查询语言
- 知乎作者性别只有一个字 – Obsidian-DATAVIEW 官方文档 中文版 05 Javascript API JS接口
- obsidian-dataview官方文档
为什么notion不行?
其数据库不支持中文的文章内容检索,无语
一开始是看中其创建文章数据库足够方便简单,但现在还是算了,果断放弃了
