关于文章ID的处理
很多年前开始切换到静态Markdown生成博客的时候,最让我头疼的就是文章的ID了。这种苦恼来自于Markdown是静态的,写一篇新文章的时候不能像传统数据库一样有自增的ID。如果博客本身是用英文标题(Slug)来定位文章的,那么这种ID也许可有可无,最值得纠结的反而是英文标题怎么起。但是我个人对于这种方法感到不太满意,因为文章的标题本身也是可能会有修改的,理论上不太适合作为固定的锚点。尽管一般而论,博客荒废的几率比锚点变换的几率可能还要大,但的确也有一些比较现实的需求,比如使用了Disqus这样的第三方评论系统,就要保证文章路径变化后评论会被继承下来,相较于在Disqus里面调整映射,一开始就有一个固定ID会更简单。以我个人为例,我最纠结这个问题的时间就是从Wordpress正式迁出的时候。
这次设置新博客,又让我重新纠结起ID的问题。起因是我这次计划在路径里面直接使用中文的标题(至于为什么这么决策,希望有机会能在另外一篇文章里面讨论)。中文标题在地址栏里面显示得很好,但是复制之后浏览器自动帮忙做了URI编码,导致路径变得非常长。以这篇日志为例:
因此为了解决这个问题,我首先想到的是短网址服务,在对比了几家之后,Tinyurl的免费方案看起来比较合理。然而我又转念一想,我都已经上了服务端了,这种短网址的事情我其实自己就能做,正好接触到了Upstash这样的便宜大碗的Redis serverless数据库服务,于是一个通过Redis存储短网址ID和日志原标题的新方案就写完了。服务器在第一次打开文章路径的时候,会去查询是否已经注册短网址ID,如果没有注册则会生成一个并且把双向映射关系储存起来。
这个系统试运行了一两天,我复盘了一下发现,好像还是不太优雅,映射关系是独立于文章内容仓库本身的,灭失了就无法找回,那么为了保险起见,可能还要备份数据库,或者下载到仓库里面来。转了一圈我发现还是绕回来了,需要给每篇文章一个ID,而且最好是存在仓库里。
多年以前,我写过一个叫做hexo-incremental-id
的库,原理是当hexo新增了文章,就把所有文章遍历一遍,找出当前最大的id,并给没有id的文章添加id。到了2024年的今天,我发现还是要做类似的事情,虽然ID不用自增了,但是还是要避免重复,全部扫描一遍不可避。
这次新博客使用的是Contentlayer来管理文章,很可惜它没有设置唯一字段的API,虽然有文章处理完毕之后的callback,可以直接在里面获取数据,但是在callback里面对文章做写入并不能触发Contentlayer的重新处理,也没有任何相关的API可以触发重载,因此也不是很理想。最后我还是在改造Contentlayer的难度面前,和多年前的自我妥协了,写了一个NodeJS脚本,手动遍历并且加上ID。考虑到有可能年老健忘,还加入了Git hook来确保提交的时候能尽量挽救一下。至于性能,也许等到我写了成百上千篇文章再考虑也不迟,并且现在都是固态硬盘了,目测也不会有很大的影响,希望如此……