Obsidian 核心插件数据库(Bases)之公式使用教程
数据库(Bases)是 Obsidian 官方推出的核心数据库插件,无需任何第三方工具就能把一批笔记聚合成可排序、可筛选的多种视图布局。
公式属性让数据库具备了真正的计算能力:可以直接在数据库中创建由其他属性派生而来的计算列,把评分和完成度合并成综合指数、把文件修改时间转为「3 天前」这样的相对描述、根据截止日期和进度自动标注逾期状态——这些原本只能靠 Dataview 或手动维护的功能,现在都可以用一行公式搞定。本文将完整覆盖数据库公式的核心用法:从公式属性的创建流程,到属性引用、运算符与函数的具体语法,再到常见的实用示例与进阶技巧。
目录
1. 创建公式属性
公式属性通过数据库文件的工具栏菜单来创建,操作步骤如下。
第一步:打开属性菜单。 在已打开的数据库(.base)文件中,点击顶部工具栏中的「笔记属性」按钮,这会弹出一个属性管理菜单,列出当前数据库中已有的所有属性列。
第二步:添加公式属性。 在属性管理菜单的底部,点击「添加公式」选项,菜单会展开为一个公式编辑区域。
第三步:为公式属性命名。 在名称输入框中为这个公式属性取一个有意义的名称,例如 综合得分、剩余天数、完整书名 等。名称在同一个 Bases 文件中必须唯一,且不能与已有的 frontmatter 属性重名,建议使用清晰的中文或英文短语,方便后续在筛选和排序时辨认。
第四步:输入公式。 在「公式」字段中输入你的计算表达式。公式编辑器会在你输入时提供自动补全,包括已识别的笔记属性名称和可用的函数名称,输入 . 后会自动列出可用的方法,输入函数名的前几个字母后会弹出候选列表。当公式语法正确时,输入框右侧会出现一个绿色勾号;如果语法有错误,则会显示红色提示。
第五步:关闭对话框。 确认公式无误后,关闭属性管理菜单,数据库视图会立即显示新增的公式列,并对每一行笔记计算出对应的结果。
💡 说明 公式属性创建后,可以像其他属性列一样通过拖拽调整它在视图中的列顺序,也可以在视图设置中选择是否显示该列。公式列的值不可手动编辑(单元格呈灰色),因为它的值完全由公式决定。
2. 公式语法:引用属性
公式的核心能力之一是引用当前行笔记的属性值,再对这些值进行计算或组合。Bases 支持引用三类属性。
2.1 笔记属性(frontmatter 属性)
直接写属性名即可引用笔记 frontmatter 中的字段,不需要任何前缀。例如,如果笔记的 frontmatter 中有一个叫 difficulty 的字段,公式中直接写 difficulty 就能取到这个值。
difficulty * importance属性名区分大小写,Difficulty 和 difficulty 是两个不同的属性。如果属性名中包含空格或特殊字符,需要使用方括号语法引用,例如:note["my property"]。
2.2 文件属性(内置属性)
Obsidian 为每个笔记文件内置了一批系统级属性,统一以 file. 为前缀。常用的内置文件属性包括:
file.name— 文件名(不含扩展名)file.path— 文件在库中的完整路径file.size— 文件大小(字节数)file.ctime— 文件创建时间(日期时间对象)file.mtime— 文件最后修改时间(日期时间对象)
示例:将文件名与 frontmatter 中的作者字段拼接成一个组合标识:
file.name + "(" + author + ")"2.3 公式属性(引用其他公式)
如果已经定义了其他公式属性,可以在新公式中引用它们,必须使用 formula. 前缀加上公式属性的名称。
formula.base_score * weight_factor关于公式引用公式的更多细节,见第七节「引用其他公式:派生计算」。
⚠️ 注意(特定设置) 以下属性名仅为示例,请根据你自己的笔记属性替换。
项目 示例属性名 说明 笔记属性 difficulty、importance、author替换为你在 frontmatter 中实际使用的字段名 内置文件属性 file.name、file.mtime这些是固定的内置属性,名称不需要修改 公式属性 formula.base_score替换为你自己已定义的公式属性名称 公式属性名在创建时必须与现有的 frontmatter 属性名不重复,否则 Bases 无法区分两者。
3. 公式语法:运算符
3.1 算术运算符
算术运算符对数字类型的属性进行数学计算,支持标准的四则运算和括号控制运算优先级:
score + bonus— 加法total - deduction— 减法unit_price * quantity— 乘法total_score / item_count— 除法(mastery + fluency) * 0.5— 括号控制运算顺序,此例计算两项均值
除法时注意分母为零的情况,Bases 会在单元格中显示错误,可以通过 if() 函数判断后处理(见 4.1 节)。
3.2 比较运算符
比较运算符对两个值进行比对,返回布尔值(true 或 false),常用于筛选条件或 if() 函数的判断分支:
score > 80— 大于word_count < 500— 小于stage == "已完成"— 等于(注意是两个等号)stage != "已搁置"— 不等于file.mtime > now() - "30d"— 最近 30 天内修改过的文件
3.3 布尔运算符
布尔运算符用于组合多个条件:
!is_archived— 非(对布尔属性取反)score > 60 && attendance > 0.8— 且(两个条件均须满足)is_urgent || is_blocked— 或(任一条件满足即可)
💡 说明 比较运算符的结果为布尔值,可以直接作为公式属性的输出(单元格会显示
true或false),也可以作为if()函数的判断条件。将复杂的布尔表达式封装成一个公式属性,再在其他公式或筛选器中引用,是保持公式简洁的常用做法。
4. 公式语法:函数
函数对属性值执行更复杂的操作,使用时以 属性.函数名() 的链式写法调用(类型函数),或以 函数名(参数) 的形式直接调用(全局函数)。以下按函数类别介绍。
4.1 全局函数
全局函数不依附于特定的值类型,直接调用即可:
if(条件, 满足时的值, 不满足时的值)— 条件判断,第三个参数可省略(省略时默认为空)now()— 返回当前日期时间today()— 返回当前日期(不含时间)date("2026-01-01")— 将字符串解析为日期对象link("路径/文件名")— 返回一个可渲染的链接对象max(a, b, c)— 返回多个数值中的最大值min(a, b, c)— 返回多个数值中的最小值
if() 函数是公式中使用频率最高的全局函数之一,几乎所有需要「根据条件显示不同内容」的场景都会用到它。基本用法:
if(stage == "已发布", "✅ 已上线", "⏳ 进行中")4.2 字符串函数
字符串函数作用于文本类型的值,以链式方式调用:
text.contains("关键词")— 判断文本是否包含某个子字符串,返回布尔值text.replace("旧内容", "新内容")— 替换文本中的指定内容text.split("分隔符")— 按分隔符拆分字符串,返回列表text.lower()— 转为全小写text.upper()— 转为全大写text.title()— 转为标题格式(每个单词首字母大写)
示例:将笔记的文件名统一转为小写,方便与其他字段做不区分大小写的比较:
file.name.lower()4.3 数字函数
数字函数作用于数值类型的属性:
num.round()— 四舍五入到整数num.ceil()— 向上取整num.floor()— 向下取整num.abs()— 取绝对值num.toFixed(2)— 保留指定位数的小数(返回字符串)
示例:将一个浮点数评分格式化为保留一位小数的显示值:
if(score, score.toFixed(1) + " 分", "未评分")4.4 日期函数
日期函数作用于日期/时间类型的值,这类属性通常是通过 file.mtime、file.ctime 或 frontmatter 中的日期字段获取:
date.format("YYYY-MM-DD")— 将日期格式化为指定字符串(使用 Moment.js 格式)date.relative()— 返回相对于当前时间的描述,如「3 天前」、「2 个月后」date.date()— 提取日期部分(去除时间)date.time()— 提取时间部分
示例:将文件修改时间显示为对人友好的相对描述:
file.mtime.relative()示例:将 frontmatter 中的 publish_date 字段格式化输出:
publish_date.format("YYYY 年 MM 月 DD 日")4.5 列表函数
列表函数作用于多值属性(如标签、链接列表等):
list.filter(条件)— 筛选列表中满足条件的元素list.map(表达式)— 对列表中的每个元素应用表达式,返回新列表list.sort()— 对列表排序list.join("分隔符")— 将列表合并为一个字符串list.unique()— 去除列表中的重复值list.length— 返回列表中的元素数量(注意:length是属性,不是函数,不带括号)
示例:计算一篇综述笔记中引用的参考文献数量:
references.length示例:将标签列表中所有包含 project/ 前缀的标签提取出来,用逗号连接成字符串:
tags.filter(value.startsWith("project/")).join("、")💡 说明 列表函数中的
filter()和map()使用value关键字代指当前正在遍历的列表元素,这一点和常见编程语言的 lambda 语法不同,初次使用时容易忘记。记住:凡是在filter()、map()的括号内写条件或表达式时,用value来表示「当前这个元素」。
5. 数据类型详解
公式能处理的值有固定的类型,不同类型支持的操作和函数也不同。了解类型系统有助于避免常见的类型不匹配错误。
字符串(String):文本类型,用单引号或双引号括起来定义字面量,例如 "已完成" 或 'project'。字符串支持拼接(+ 运算符)和所有字符串函数。大多数 frontmatter 文本属性都是字符串类型。
数字(Number):数值类型,直接写数字字面量,如 42、3.14、(10 + 5)。数字支持四则运算和数字函数。frontmatter 中未加引号的纯数字属性通常会被识别为数字类型。
布尔值(Boolean):只有 true 和 false 两个值,是比较运算符和布尔运算符的运算结果,也可以作为 if() 函数的条件参数。frontmatter 中类型为「复选框」的属性是布尔类型。
日期(Date):日期时间类型,由 date()、today()、now() 函数创建,或从 frontmatter 的日期类型属性读取。日期对象支持加减时间段(如 review_date + "14d")和所有日期函数。时间段字面量用字符串表示,支持 d(天)、w(周)、M(月)、h(小时)等单位。
列表(List):多值集合,可以用方括号定义字面量,如 [1, 2, 3],或由 frontmatter 的多值属性自动识别。列表支持 filter()、map()、sort()、join() 等函数,以及通过 [索引] 访问特定位置的元素(索引从 0 开始)。
对象(Object):键值对结构,用花括号定义,如 {"name": "value"}。主要用于处理嵌套属性,如通过 file.properties 访问文件属性对象内的子字段。在一般日常公式中用到较少。
💡 说明 公式的输出类型由表达式的最终返回值决定,不需要显式声明。如果公式返回一个字符串,单元格就显示文本;如果返回一个布尔值,单元格就显示 true/false;如果返回一个日期,单元格就以日期格式渲染。当不同类型混合运算时(例如用
+拼接数字和字符串),通常需要先用.toFixed()或字符串转换函数将非字符串类型显式转为字符串,否则可能出现[object Object]这样的渲染异常。
6. 引用其他公式:派生计算
公式属性可以相互引用,形成多层派生计算的链条。引用其他公式时,必须使用 formula. 前缀加上目标公式属性的名称。
假设已定义了一个名为 weighted_score 的公式:
(knowledge * 0.4) + (skill * 0.6)然后可以在另一个公式中引用它,进一步计算等级标签:
if(formula.weighted_score >= 90, "优秀", if(formula.weighted_score >= 75, "良好", if(formula.weighted_score >= 60, "及格", "待提升")))这种嵌套 if() 的写法实现了类似多分支判断的效果,适合需要把连续数值映射为离散分类的场景。
⚠️ 警告:避免循环引用 公式不能直接或间接地引用自身。例如,公式 A 引用了公式 B,公式 B 又引用了公式 A,就构成循环引用,Bases 会在单元格中显示错误并拒绝计算。设计多层公式时,应确保引用关系是单向的有向无环图(DAG),即每个公式只依赖于其他已存在的、不反过来依赖自己的公式。
派生计算的好处不仅是避免重复写相同的表达式,更重要的是让逻辑分层变得清晰:底层公式计算基础数值,中层公式对基础数值加工,顶层公式负责展示和分类。当底层逻辑需要调整时,只改一处即可,其他公式自动更新。
7. 实用公式示例
以下示例覆盖常见的使用场景,每个示例均可直接参考或按需修改后使用。
7.1 计算复习提醒日期
将笔记的「上次复习日期」加上指定的复习间隔天数,得到下次应当复习的日期:
last_review + "21d"⚠️ 注意(特定设置) 以上公式依赖名为
last_review的日期类型属性。时间段单位"21d"为示例值,请根据你自己的复习周期替换(如"7d"、"1M"等)。
项目 示例值 替换建议 上次复习属性 last_review替换为你实际使用的日期字段名 复习间隔 "21d"替换为你的记忆曲线复习间隔
7.2 标注待复习状态
如果下次复习日期已过且笔记状态不是「已掌握」,则标注为「待复习」,否则显示为空:
if(next_review < now() && mastery_level != "已掌握", "⚠️ 待复习", "")7.3 格式化评分显示
将数值评分属性以带星号符号的格式显示,如果属性为空则显示「暂未评分」:
if(rating, "★ " + rating.toFixed(1), "暂未评分")7.4 统计关联笔记数量
计算一个列表属性中的元素总数,适用于统计参考文献数、关联项目数等:
linked_notes.length7.5 合并系列与卷册信息
将书目数据库中的系列名和卷册编号拼接为完整的显示标题:
series_name + " · 第 " + volume_number + " 卷"7.6 计算综合优先级指数
用重要程度和紧迫程度的乘积除以预计所需精力,得出一个优先级排序依据:
(importance * urgency) / estimated_effort💡 说明(按需调整) 此公式的设计逻辑是:重要且紧急的任务排在前面,但所需精力高的任务会被降权。三个属性均需为数字类型,且建议使用统一的量纲(例如均为 1~5 的整数),否则计算结果的可比性较差。
7.7 计算文件创建至今的天数
用当前时间减去文件创建时间,得到该笔记已创建多少天,适合用于追踪笔记的「年龄」:
(now() - file.ctime) / 86400000💡 说明 两个日期相减的结果是毫秒数,86400000 是一天的毫秒数(24 × 60 × 60 × 1000),除以它即可换算为天数。可在外层套用
.floor()取整:((now() - file.ctime) / 86400000).floor()。
7.8 提取文件名中的日期部分
对于按日期命名的笔记(如 2026-05-03 读书摘要),从文件名中提取前 10 个字符作为日期字符串,适合在表格中显示一个统一的日期列:
file.name.split(" ")[0]⚠️ 注意(特定设置) 以上公式假设文件名的格式为「日期 空格 其他内容」,且日期部分作为第一个空格分隔的片段。如果你的文件命名规则不同(例如日期在文件名末尾、或使用下划线而非空格分隔),需要调整
split()的分隔符和索引值。
7.9 学习卡片综合熟练度
将知识点的「理解程度」和「记忆强度」加权平均,得出一个整体熟练度分数(满分 10 分):
(comprehension * 0.4 + retention * 0.6).toFixed(1)7.10 项目健康度红绿灯
根据进度百分比和截止日期距离天数综合判断项目状态,返回颜色标记:
if(progress >= 1, "🟢 已完成",
if((due_date - now()) / 86400000 < 0, "🔴 已逾期",
if((due_date - now()) / 86400000 < 7 && progress < 0.5, "🟡 有风险", "🟢 正常")))7.11 书单完整书目标签
将书名、作者和出版年份组合为一行标准引用格式,当作者或出版年份缺失时退回到仅显示书名:
if(author && publish_year,
title + "(" + author + "," + publish_year + ")",
title)7.12 距离截止日的剩余天数(取整)
计算今天到截止日期之间的完整天数,负数表示已逾期:
((due_date - now()) / 86400000).floor()7.13 统计有内容的可选属性个数
对于一组非必填属性,统计其中已填写(非空)的数量,用于衡量笔记的「完整度」:
[title, abstract, keywords, source_url].filter(value).length7.14 将文件修改时间格式化为友好显示
根据文件最后修改时间距今的远近,切换显示格式——近期用相对描述,较早用绝对日期:
if(now() - file.mtime < 604800000,
file.mtime.relative(),
file.mtime.format("YYYY-MM-DD"))8. 常见问题与注意事项
8.1 公式单元格显示 [object Object]
这通常发生在用 + 拼接字符串时,右侧的操作数不是字符串类型。例如把一个数字或日期直接拼接到字符串中,会触发此问题。解决方法是在拼接前先将非字符串值转换为字符串:数字类型使用 .toFixed(0) 或 .round().toString(),日期类型使用 .format("YYYY-MM-DD") 转为格式化字符串。
8.2 公式单元格显示为空但没有报错
属性为空(null)时,公式的计算结果可能是空值而非错误,单元格什么都不显示。如果希望在属性为空时也显示一个默认值,可以用 if() 处理空值情况:
if(topic, topic.upper(), "(未分类)")这样,当 topic 属性有值时显示其大写形式,当属性为空时显示「(未分类)」。
8.3 日期加减运算报错
时间段(如 "14d")必须是字符串字面量,且写在日期值的右侧。时间段本身不能直接参与乘除运算,如果需要计算动态的时间段(例如「属性值 × 天」),需要先用 duration() 函数将数字转为时间段对象,并且时间段必须写在乘法的左侧:
review_date + duration(interval_days + "d")这里 interval_days 是一个表示天数的数字属性,通过字符串拼接构成时间段字符串后传入 duration() 解析。
8.4 filter() 或 map() 中报错
确认是否使用了 value 关键字来引用当前遍历的元素。以下是常见的错误写法与正确写法对比:
// 错误:直接写属性名,没有用 value
tags.filter(tags.startsWith("area/"))
// 正确:用 value 代表当前遍历的元素
tags.filter(value.startsWith("area/"))8.5 升级 Obsidian 后公式全部失效
参见本文「版本说明」章节。在 Obsidian 1.9.1 版本中,Bases 对所有内置函数名进行了命名规范调整,snake_case 函数名全部改为 camelCase,例如 to_fixed 变为 toFixed,is_empty 变为 isEmpty。升级后需要手动检查并更新 .base 文件中的所有函数名,否则涉及这些函数的公式将无法运行。
✅ 推荐做法 在 Obsidian 大版本升级前,建议先在测试库中确认功能兼容性,再迁移到主库。如果已有多个 Bases 文件,可以在文本编辑器中批量搜索替换函数名,效率更高。
8.6 公式运算结果保留了过多小数位
数字运算产生浮点数时,显示结果可能出现很多位小数(例如 0.3333333…)。用 .toFixed(n) 可以将其格式化为保留 n 位小数的字符串,或用 .round() 四舍五入为整数:
(completed_count / total_count * 100).toFixed(1) + "%"9. 进阶用法与个性化调整
9.1 在筛选器中使用公式属性
公式属性创建后,不仅可以作为显示列,还可以直接用于视图的筛选条件。例如,先定义一个名为 is_overdue 的布尔型公式:
due_date < now() && completion_rate < 1然后在视图的筛选设置中,添加筛选条件「formula.is_overdue 等于 true」,这个视图就只会显示已逾期且未完成的笔记,无需每次手动输入复杂的日期比较条件。这种「把复杂逻辑封装进公式,再用公式来筛选」的模式是保持视图配置简洁的关键技巧。
9.2 将公式作为排序依据
数值类型的公式属性可以直接作为视图的排序列。例如,定义一个综合热度评分公式:
(view_count * 0.3) + (citation_count * 0.7)再在视图设置中按该公式属性降序排列,就能把「被引用多且浏览量高」的笔记排在最前面,比手动整理高效得多。
9.3 利用 image() 函数在单元格中渲染图片
image() 是一个全局函数,可以将图片路径或 URL 渲染为单元格内的预览图:
image(cover_image)其中 cover_image 是一个存储了图片路径(如 Assets/book-covers/some-cover.jpg)或图片 URL 的 frontmatter 属性。这对构建书单、电影库等有视觉需求的数据库非常有用。
9.4 利用 link() 函数创建动态链接
link() 函数可以基于属性值动态生成一个笔记链接。例如,根据项目代码属性自动生成指向对应项目笔记的链接:
link("Projects/" + project_code)这样在数据库中点击该单元格就会直接跳转到对应的项目笔记,而不需要在 frontmatter 中手动维护一个链接字段。
💡 说明(按需调整)
link()的参数是笔记的路径字符串(相对于库根目录),路径中的"Projects/"前缀为示例,请替换为你实际的文件夹结构。如果目标笔记直接位于库根目录,省略文件夹前缀即可:link(project_code)。
9.5 多条件的复杂逻辑公式
if() 函数可以嵌套以实现多分支逻辑,但层数过多会使公式难以阅读。可以借助布尔运算符简化:
if(priority == "高" && deadline_days < 3, "🔴 紧急",
if(priority == "高" || deadline_days < 7, "🟡 关注", "🟢 正常"))这个公式的逻辑是:如果优先级为高且截止日还有不到 3 天,显示红色紧急;如果优先级高或截止日不到 7 天,显示黄色关注;其他情况显示绿色正常。
10. 与其他插件或功能的联动
10.1 与 Dataview 的关系
Bases 和 Dataview 在功能定位上有一定重叠,但侧重不同。Dataview 更擅长跨库全局查询和输出高度自定义的表格,支持 DQL 和 DataviewJS 两种查询语言,灵活度极高;Bases 更侧重于为特定的笔记集合提供可交互、可直接编辑属性的数据库视图,公式计算是其原生能力。
两者可以并存于同一个库中,互不干扰。如果你已经有大量 Dataview 查询,不需要迁移,Bases 可以作为一种补充:在需要直接编辑数据、或需要原生视图(而非嵌入式查询块)的场景下,Bases 是更合适的工具。
10.2 与 Templater 的联动
Templater 可以在新建笔记时自动填充 frontmatter 属性,而 Bases 的公式计算建立在这些属性之上。两者组合使用的典型工作流是:用 Templater 模板保证每类笔记都有完整、规范的属性集合(字段名统一、类型一致),再在 Bases 中对这些属性建立公式计算层。
如果 frontmatter 字段名不统一(例如有些笔记用 due_date,有些用 dueDate),公式只能识别其中一种,计算结果会出现大量空值。因此,在推广 Bases 之前,建议先用 Templater 或属性重命名工具统一笔记属性的命名规范。
10.3 与 Properties(属性面板)的联动
Obsidian 的内置属性面板支持直接在笔记中编辑 frontmatter,与 Bases 的数据库视图是同一套数据的两个入口。在数据库视图中修改一个笔记的属性值(非公式列),会立即反映到该笔记的 frontmatter 中;反之,在笔记的属性面板中修改属性,数据库视图也会在刷新后更新。公式列不可直接编辑,但其计算结果会随源属性的变更实时更新。