xmdhs's blog

简单的弄一个站内搜索

xmdhs

按照惯常的博客玩法,写几篇关于折腾博客的博文,然后就再也没有更新了。

不过我这里并不是这样的,我这写的是之前折腾某个论坛的站内搜索,而不是博客的站内搜索,毕竟 blogger 就已经提供了一个很棒的站内搜索了。

起因

这个论坛因为一些原因,导致了其中的问答版块的帖子需要登录后才能查看。而论坛本身的搜索功能很差,需要登录后才能查看就导致了搜索引擎无法爬取到其中的内容。这让我有些不爽。

所以,我就自己闲着无聊用 golang 通过 discuz 的 api 爬取了问答版的所有帖子,再用 sqlite 储存,搜索。

其中还是踩了一些坑吧,或者说不算坑,只能算自己菜。

最开始的构思

一开始看 golang 的 sqlite 驱动时,看到了 fts5,然后看到了 sqlite_icu,想着是不是可以用这个来实现一个简单的站内搜索。

可是测试的时候,创建数据库却总说找不到 icu 分词器,折腾了老半天,最后翻了下 sqlite 的官方文档才发现 fts5 不能用 icu,但是这个驱动却似乎只能用 fts5,实在有点搞不懂。

解决的办法

但是我又不想换成 mysql,毕竟多装一个软件还是挺麻烦的。网上找了找关于全文搜索的文章,发现还可以事先分词,然后弄全文索引。这样即使用不支持中文的全文索引也能搜索到想要的内容。

知道了办法那就好办了,又到网上找了找,golang 中用来分词的用的多的有两个。一个是用 cgo 调用 c++ 的代码,另一个是用的纯 go 编写。

肯定用 go 的,cgo 毕竟太麻烦。

我爬取问答版中帖子的数据,是先储存在 sqlite 的,所以需要做的工作仅仅是创建一个 fts5 的虚拟表,然后分词再存入就行了。

一开始的工作还是挺顺畅的,不过没过多久,cpu 的占用就上了 100% 左右,或许应该算正常,不过我修改了下代码,让其显示进度,结果发现卡在了一个位置上。不知道是这个分词项目的什么 bug,我也懒得去分析。然后就换成了 c++ 实现的分词。

c++ 的分词速度都要快了不少,不过上传到服务器运行程序时却报错 so 的版本过低。

(./upsql: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by ./upsql))

想想也很正常,服务器用的是 centos7.3,之前不知道听了谁的说 centos 稳定,稳定的代价就是这样了。

换系统就更麻烦,于是按照网上的教程一套下来,也解决了这个问题。现在回想一下,或许在另一台机子上,用 ldd 命令,查看程序的依赖库的路径,然后复制过来,或许就可以解决问题了。

搜索的实现

分词则是把一段文字变成用 / 分隔的文本,类似/这样/。然后存入 sqlite 中。

这样如果用某个词语,就能搜索到这段语句。

一开始的实现就是直接用用户输入的短语去搜索,如果用了一句完整的话,就搜索不到任何内容了。

所以我之后,把用户用来搜索的短句,也进行分词。然后根据 sqlite 的搜索语法,加上引号。

比如使用 崩溃报告 实际就会用 SELECT key,subject,source FROM qafts5 WHERE qafts5 MATCH "崩溃 报告" ORDER BY rank LIMIT 20 OFFSET ? 去进行搜索。

sqlite 的搜索语法认为引号包着的是短语,就可以搜索到“崩溃/报告”和“崩溃啊报告”之类的内容。但是不会搜索到“报告崩溃”。

之后又实现了几个简单的搜索语法,这就不需要多说了。

网站界面的实现

网站界面是直接偷 github 的 css,mit 开源的,也就是不算偷。然后用 markdown 转 html,再到其中加上一个输入框,就大功告成。

具体的内容也没什么好说的,想看的可以自己去看源码。

总结

sqlite 确实还是挺好用的,除了写入很差,读取的性能和全文搜索的速度还是非常快的。反正我弄了这个站内搜索,应该不会有什么人用,我自己一个人够用就行了,不会碰到性能问题的。更何况我也只租的起一核的小鸡。

开源地址

https://github.com/xmdhs/searchqanda

网站地址则是 https://files.xmdhs.top/search

xmdhs