Rails Postgres Full Text Search with Docker Compose

hiveer · 2022年01月02日 · 最后由 kansyoukyou 回复于 2022年01月05日 · 846 次阅读

最近需要做一个搜索的功能,数据量不大,本来想着尽量简单,就用 Ruby code 去做了一个模糊搜索的功能。但是折腾完了,发现不行,这怎么支持分页功能呢?哈哈,偷鸡不成蚀把米!所以还是乖乖的回到了 Postgres Full Text Search 来。

参考了 @hooopo 大神的帖子: https://ruby-china.org/topics/38153

以及 zhparser 的文档: https://github.com/amutu/zhparser/

然后使用 gem 'pg_search' 在本地(Mac)环境下先做了一个实验(成功了):

brew install scws
scws -v
git clone https://github.com/amutu/zhparser.git
cd zhparser/
make && make install
psql -d <database>
CREATE EXTENSION zhparser;
CREATE TEXT SEARCH CONFIGURATION chinese_zh (PARSER = zhparser);
ALTER TEXT SEARCH CONFIGURATION chinese_zh
ADD MAPPING FOR a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z
WITH simple;

SELECT to_tsvector('chinese_zh', '人生苦短,我用 Python');
                      to_tsvector
--------------------------------------------------------
 'python':7 '人生':1 '我':5 '用':6 '短':3 '苦':2 ',':4
include PgSearch::Model
pg_search_scope :full_text_search,
                against: %i[company_name company_description address contact_person],
                using: { tsearch: { dictionary: 'chinese_zh' } }

没有遇到什么大的问题,接下来就是把这套逻辑如何迁移到通过 docker,docker-compose 部署的服务器上了。 在服务器上,postgres 是通过 docker-compose service 的形式启动的,使用的是官方的默认 'postgres' image. 所以为了安装这个 zhparser 的扩展,我必须要改造下这个默认的 image,以它为基础,再包装一层,把 zhparser 的扩展装上去。所以有了下面的 Dockerfile:

FROM postgres:14
SHELL ["/bin/bash", "-c"]
RUN apt-get update \
  && apt-get install -y --no-install-recommends \
      bzip2 \
      gcc \
      make \
      libc-dev \
      postgresql-server-dev-14 \
      wget \
      unzip \
      ca-certificates \
      openssl \
  && rm -rf /var/lib/apt/lists/* \
  && wget -q -O - "http://www.xunsearch.com/scws/down/scws-1.2.3.tar.bz2" | tar xjf - \
  && wget -O zhparser.zip "https://github.com/amutu/zhparser/archive/master.zip" \
  && unzip zhparser.zip \
  && cd scws-1.2.3 \
  && ./configure \
  && make install \
  && cd /zhparser-master \
  && SCWS_HOME=/usr/local make && make install \
  && echo "CREATE EXTENSION pg_trgm; \n\
CREATE EXTENSION zhparser; \n\
CREATE TEXT SEARCH CONFIGURATION chinese_zh (PARSER = zhparser); \n\
ALTER TEXT SEARCH CONFIGURATION chinese_zh ADD MAPPING FOR n,v,a,i,e,l,t WITH simple;" \
> /docker-entrypoint-initdb.d/init-zhparser.sql \
  && apt-get purge -y gcc make libc-dev postgresql-server-dev-14 \
  && apt-get autoremove -y \
  && rm -rf \
    /zhparser-master \
    /zhparser.zip \
    /scws-1.2.3

在这里,我们不仅仅把 zhparer 安装了,还把配置 sql 写进了数据库启动文件中,在数据库启动的时候就会去运行,然后把数据库配置好。

接下来就是 image build 和 push,这里依然是利用了 极狐 GitLab 自带的 container registry,轻松完成了 image 的打包和部署。

然后在服务器上,更新 docker-compose.yml 文件:

db:
  image: registry.gitlab.cn/<your project repo>/zhparser

然后

docker-compose -f docker-compose.yml pull 
docker-compose -f docker-compose.yml up -d db

满怀期待的试了试,输入关键字,seach…… 结果,结果,不对!什么玩意儿?

通过数据库日志发现了 root cause,原来是 zhparser 的配置没有生效,也就是我们在 Dockerfile 写进 init sql 的代码没有运行,原因是我们这是一个已经运行的项目,数据库挂载了 volume:

volumes:
  - pgdata:/var/lib/postgresql/data

postgres 会认为已经存在数据库,不再执行初始化 sql:

PostgreSQL Database directory appears to contain a database; Skipping initialization

这种情况,我们需要做的就是 psql 连接数据库,并且手动配置 zhparser,这个是一次性的动作,之后不再需要:

CREATE EXTENSION pg_trgm; 
CREATE EXTENSION zhparser; 
CREATE TEXT SEARCH CONFIGURATION chinese_zh (PARSER = zhparser); 
ALTER TEXT SEARCH CONFIGURATION chinese_zh ADD MAPPING FOR n,v,a,i,e,l,t WITH simple;

然后就可以开心 search 了。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号