用Python和Postgresql实现中文全文搜索

全文检索的基本原理就是,把文本分词,提取出结构化数据(创建索引),根据用户查询请求,使用索引查找到目标(搜索索引)。这个博客程序使用了PostgreSQL数据库和Peewee ORM,为了实现这个目标,我们需要使用PostgreSQL的全文检索插件,这里使用的是zhparser

PostgreSQL的配置

首先根据系统安装PostgreSQL数据库,建议根据官方文档安装,这里使用 PostgreSQL 14 版本。

因为 zhparser 实际使用的是 SCWS 分词,要安装 SCWS 插件,

wget -q -O - http://www.xunsearch.com/scws/down/scws-1.2.3.tar.bz2 | tar xf -
cd scws-1.2.3 ; ./configure ; make install

然后安装zhparser

git clone --depth=1 https://github.com/amutu/zhparser.git
make && make install

创建extension 打开psql,运行以下脚本,

CREATE EXTENSION zhparser;

这里的配置适用于普遍情况,如有特殊需求,请参考对应网站进行配置, 在2021年的现在,建议一切部署都通过docker实现,这里有一个我的Docker配置,可以快速搭建起一个PostgreSQL实例,包含了全文检索需要的插件

https://github.com/lerry/postgresql-docker

数据库配置

这里假设你已经创建好了你的数据库,和对应的数据表,以本博客程序为例,

CREATE TABLE entry (
    id SERIAL PRIMARY KEY,
    uid integer NOT NULL,
    slug character varying(50) NOT NULL,
    title character varying(50) NOT NULL,
    content text NOT NULL,
    created_at timestamp without time zone NOT NULL,
    updated_at timestamp without time zone NOT NULL,
    status smallint NOT NULL,
    password character varying(50),
    allow_comment smallint NOT NULL,
    view_times integer NOT NULL,
    cover text
);

创建全文检索相关的配置,注意最后两行要根据你实际的数据库字段修改,

CREATE EXTENSION zhparser;
CREATE TEXT SEARCH CONFIGURATION zh (PARSER = zhparser);
ALTER TEXT SEARCH CONFIGURATION zh ADD MAPPING FOR n,v,a,i,e,l WITH simple;
CREATE INDEX fts_idx_title ON entry USING gin(to_tsvector('zh',title));
CREATE INDEX fts_idx_content ON entry USING gin(to_tsvector('zh',content));

查询

用peewee ORM查询

result = Entry.select().where(Entry.status == 1, Match(Entry.title, q, 'zh') | Match(Entry.content, q, 'zh')) \
            .order_by(Entry.created_at.desc()).limit(options.PAGE_LIMIT)

这里的zh对应这里的zh CREATE TEXT SEARCH CONFIGURATION zh (PARSER = zhparser);

对应的SQL,

SELECT  * FROM entry WHERE to_tsvector('zh', title) @@ to_tsquery('zh', 'python') OR to_tsvector('zh', content) @@ to_tsquery('zh', 'python')

这里有个小插曲,最早我用peewee的时候,它的Match函数,第三个参数,语言配置并没有生效,于是我提交了一个PR,修正了这个问题,很快这个PR被合并到代码库了,这也是我第一次参与到实际的开源项目。

PR在这里,https://github.com/coleifer/peewee/pull/1379/files