一日一技 | 让 Notion 树状结构数据库的筛选功能更实用
文章探讨了在Notion数据库中管理树状知识结构时的筛选难题,并提出了一种通过添加自关联字段、递归函数和自动化流程来实现任意节点展示整个树结构的方法。该方法无需外部工具支持,但受限于Notion的性能和官方限制。 2025-8-4 03:43:34 Author: sspai.com(查看原文) 阅读量:12 收藏

Matrix 首页推荐 

Matrix 是少数派的写作社区,我们主张分享真实的产品体验,有实用价值的经验与思考。我们会不定期挑选 Matrix 最优质的文章,展示来自用户的最真实的体验和观点。 
文章代表作者个人观点,少数派仅对标题和排版略作修改。


随着各种结合 AI 大模型的个人知识管理新工具的逐渐火热,我们有了全新的知识交互方式,这也让大家对传统笔记软件的知识管理方法论和 UI 创新上的焦虑得以舒缓,近期关于笔记软件的讨论都少了很多。但作为一个仍在坚持人工进行知识链接的「手艺人」,我依然很关心笔记软件交互上的创新优化,就像我很喜欢这篇文章里的一句话:

人脑是一种湿件,视觉和心理上看似琐碎的提示,可能就会带来意外的帮助。有时,一点留白就能给思考增加深度,一段距离就能给创意留出空间。

所以在此我想和大家分享一个在 Notion 中的实践小技巧(虽然实现难度并不小),希望能改善大家在使用 Notion 数据库进行知识管理时的交互体验,同时也和大家一起挖掘一下 Notion 数据库的使用潜力。

背景

在使用 Notion 数据库的子项目功能时,页面之间可以进行多层嵌套关联,从而在数据库中呈现出一个树状的层级结构。

一个典型的折叠嵌套数据库

这套可以折叠的树状结构非常好用,我们可以概览所有条目,随意在任何节点进行增删减改。但也有一些使用上的小问题,那就是「无法单拎出来一棵树」。

截止 Notion 的最新版本(2.51),在无筛选的情况下,所有的条目会一起出现;而在有筛选的情况下,筛选条件是针对具体节点生效的,我们无法对单个树状结构的所有节点进行筛选、提取和展示。而在模板或者数据库页面的选项卡中,只能筛选直接和当前页面关联的页面,如果树结构的层级数量不固定且超过了2层,那么就很难在一个筛选页面内完整呈现整个树结构了。也就是说,我们没有办法在一个数据库视图中只展示节点1及其下属的全部层级子节点(包括节点1.1,1.1.1,1.2,1.2.1)。这使得我们无法对内容进行聚焦。

所以,我希望在数据库的任意页面,通过模板或选项卡的数据库视图可以自动筛选出所有和当前页面有直接或间接的上下关系的页面。在这个过程中要尽量做到全自动,除了构建树状关系本身外,不需要我们进行额外的操作,也不需要外部工具辅助,最终达成如下的筛选效果:

期望效果

接下来我会分步详细讲解操作方法。

实现思路

为了方便描述,我们借用数据结构的术语,将所有通过上下级关系直接或间接关联起来形成的树状结构的所有页面称作一棵「树」,我们在 Notion 中讨论的都是无环有向树,具体含义是:

  1. 有向:节点之间是双向链接,上级关联和下级关联是不同属性;
  2. 无环:页面之间不能出现直接或间接的首尾相连;
  3. 树结构:任意节点的父页面数量不能超过1个(即,入度 ≤ 1 ),树和树之间互不相交。

其中,第3条并不需要严格执行,但这里为了简化场景,暂时作此规定。另外,由于 Notion 自身性能和机制的限制,树的深度不要超过10层。

一棵树最顶端的节点(入度=0)称为「根节点」,最底端的节点(出度=0)是「叶子节点」;对于任意节点x,从根节点到节点x的所有节点都是x的「祖先」(节点x也是自己的祖先);对任意节点x,从节点x到叶子节点的所有节点都是x的「后代」(节点x也是自己的后代)。

于是,我们可以重新阐述一下目标:我们期望在任意节点页面,均可以通过模板或数据库选项卡的形式,自动筛选出该页面的所有祖先和后代节点,从而在任意一个节点页面能且只能展示该页面所在的整个树的结构,方便进行单个项目的追踪和编辑。

为了达成这个目标,我们需要在原有的上下关联字段之外,通过单独的字段记录该节点的所有祖先和后代节点。我们在日常使用中只需要维护直接的上下关联字段,而祖先记录字段要可以自动更新。具体方法为:先通过函数递归回溯所有祖先节点,再通过自动化将获取到的祖先节点关联到新的Relation 字段上,这样就可以在其祖先页面上筛选到该页面了。

思路清晰后,我们可以开始操作了。

操作步骤

0. 准备好层级关联数据库

我们的演示数据库包括2棵树,各有3层的深度,关联的属性分别命名为「父页面」和「子页面」。

如果是对于已有的数据库进行改造的,只要保证有双向父子关联字段,且满足有向无环的标准即可。

1. 添加自关联字段,设置自动化填充

首先要在数据库中设置一个关联当前页面的 Relation 字段。这个字段很重要,Notion 并不支持在函数中直接将当前页面作为 block 引用,因此我们需要通过一个关联到自己的 Relation 字段来实现,方便之后的函数引用和自动化关联。

1. 添加单向的 Relation 字段,关联当前数据库,命名为 self_page,用于添加当前页面。

新建2个更新过程中用到的字段。这里是一个小技巧,如果是改造已有数据库,当前已经有很多页面了,对旧页面一个个手动添加关联自身并不现实,可以结合按钮和自动化功能:

2. 新建Date字段,命名为「刷新用Date」,这个字段用作启动各种自动化功能。

新建函数字段,命名为「是否需要更新」,函数代码见下图。这个字段主要是为了减少之后按钮和自动化的操作量,提升效率。这一步也可以不做,就是如果之后每次更新的页面过多,自动化和按钮可能会罢工(频繁被掐断)。

// 是否需要更新
if(empty(prop("self_page")),1,0)

3. 新建 Button 字段,命名为「更新页面」,具体功能为,点击后将所有页面需要更新的页面(即 是否需要更新字段>0)的页面的「刷新用Date」字段设置为触发时间,用于触发之后的自动化任务。

4. 创建自动化流程,让所有页面自动关联自身

自动化的触发条件为新增页面和编辑「刷新用Date」字段,这样,不论是我们新增页面还是通过点击更新按钮更新旧页面,所有页面的self_page字段都会关联上自身,供我们之后使用。

5. 点击「更新页面」按钮,填充所有页面,完成关联。

2. 通过递归回溯顶层节点

我们首要的目标是,在每个页面追溯到它所有的祖先节点,即它的父节点,父节点的父节点,父节点的父节点的父节点……以此类推,于是,这里要用到一点递归思维。

我们首先创建一个 Formula 字段叫做「祖先页面」,那么它等于:当前页面+父页面的上层页面,而它父页面的上层页面就包括 父页面+父页面的父页面的上层页面,形成一个长长的list,那么就可以一层一层上溯到全部的上层页面。简单写来就是:

prop("上溯祖先") = [prop("self_page"),prop("父页面").map(current.prop("上溯祖先"))]

这里的map()函数可以用来引用关联页面的相关属性,具体用法就不展开了。

但由于 Notion 的设置,我们无法在一个函数中引用自己,因此我们需要一个中转的 Formula 字段绕开限制。因此两个字段的最终定义为:

// 上溯祖先
[prop("self_page"),prop("父页面").map(current.prop("上溯祖先_中转")).flat()].flat().unique()
// 上溯祖先_中转
prop("上溯祖先")

其中 flat() 函数是为了将嵌套的多层 List 打平为单层的 List ,unique() 函数用于去重。

这样,我们就可以在任意一个页面的属性中上溯到它所有的祖先节点了,效果如下:

到这里,我们的目标就完成一半了。但是,在 Notion 的数据库筛选中,Formula 字段的结果是被当做字符串来处理的,那么我们就没有办法在模板或数据库页面选项卡中自动筛选当前页面的相关页面。于是,我们需要继续把函数上溯的结果转化为 Relation 字段。

3. 通过自动化将上溯结果记录为 Relation

1. 建立 Relation 属性

首先,我们还是要新建一对关联属性,仍是关联在当前数据库上的,命名为「祖先页面」,对应地,「祖先页面」的逆向关联就是「后代页面」。

之后我们的操作目标都是为了将上面的「上溯祖先」字段的内容转移到「祖先页面」字段中。

2. 定义更新用函数

这里,我们先更新一下「是否需要更新」字段的定义,圈定需要更新的页面范围,方便之后使用:

// 是否需要更新
if(empty(prop("self_page")),1,0)+if(prop("祖先页面").sort()!=prop("上溯祖先").sort(),1,0)

3. 设置自动化

现在我们可以开始创建自动化流程,为了保证任何从属关系发生变动时都会自动更新相关字段,自动化的触发条件包括一下3个的任意一个:

然后是自动化的操作,我们的目标是更新「祖先页面」,这里要选择 编辑属性-祖先页面-自定义函数,函数内填充:

4. 点击更新按钮,自动化填充数据

第一次更新时可能要多按几次「更新页面」按钮,直到所有页面的祖先页面字段在自动化的作用下填充了完整的值。(这个按钮在之后的使用中也是有用的,当你发现页面更新而相应的字段和关联并没有及时更新时,你可以手动点击按钮来更新整个数据库的页面)。

于是,我们建立完成了上溯祖先页面的自动化流程建设。

4. 可视化:在数据库选项卡中展示树结构

通过 Rollup 关联树结构中所有节点

由于一棵树内所有节点的祖先节点都包括根节点,因此任意页面的所有祖先页面的全部后代页面也就包括了整个树内的全部节点,这样我们就真正达成了「树内任意一节点关联到树内全部节点」的目标。

创建一个 Rollup (汇总)字段,命名为「树全部节点」,设置如下:

使用选项卡展示树结构

将页面布局改为选项卡式,并添加一个数据库卡片,展示的选项可以随便选一个(之后还要改筛选条件),命名为「项目树」。

Rollup 字段是无法直接进行筛选的,但是可以在高级筛选中使用,因此要在筛选里设置「合并到高级筛选中」:

最后,我们再将子项目的展示方式设置为「嵌套在折叠中」,就得到我们最终目标的展示内容了:

最终效果

现在,我们就可以在任意一个节点页面内展示其所在项目树的全部节点啦,并且该视图内仍可以直接对各个节点及其下属节点进行增删改减,并没有任何限制。

写在最后

我们共用了 8 个字段、2 个自动化流程和 1 个按钮,搭建起这套全自动的全链接工作流,这个方法可以说比较完美地解决了我在最开始提出的需求,并且完全没有超出 Notion 自身的体系,不涉及任何外部工具资源,因此一次配置完就可以在任何终端上安全使用。

当然还是存在一些问题的,主要是:

  • 功能受官方限制:在这个过程中用到了很多擦边的手法,如递归函数、将非法的函数结果赋值给 Relation 字段等,这些都是官方不鼓励(甚至明确做了限制)的操作,导致在制作时会时不时出现一些难以解释的报错,解决方法也都是想办法绕过限制,这就导致有一些潜在的失效风险。
  • 性能表现比较差:流程涉及到的函数计算比较复杂,对自动化依赖严重,导致实际计算较慢,而且自动化的更新不一定及时,偶尔需要手动点击刷新按钮;选项卡的筛选规则复杂,展示也比较慢,而且随着数据库内页面数量增多,会愈加缓慢。(不过就我个人使用经验来讲,目前有几千个页面的数据库还是撑得住的)。

上述问题已经超出我的解决范围了,也只能期待有一天官方可以大发慈悲直接将这个做成正式的功能,大家也不用这么费劲了。

P.S.

不知你还记不记得我在前面说过,「树与树之间不交叉」这个条件不是必须的,那么如果是有交叉的树会有什么效果呢?可以看我自己的一个案例:由于我的树状知识库之间会相互交叉引用,这使得当我打开任意一个知识卡片后,所有和当前树有间接关联(引用过同一个知识卡片)的知识树都会同时展示出来,不仅易于探索和编辑,还不容易迷路,让我时刻清楚我当前所在的位置。这虽然比不上双链笔记软件里的关系星图,但在使用效果上也很接近了。

如果你有其他更好的实践方法和思路,欢迎和我交流。

如果想看其他内容,欢迎来看我的主页: https://eddieship.notion.site/

> 关注 少数派小红书,感受精彩数字生活 🍃

> 实用、好用的 正版软件,少数派为你呈现 🚀


文章来源: https://sspai.com/post/101372
如有侵权请联系:admin#unsafe.sh