PostgreSQL + SCWS + zhparser + Rails4 + pg_search 实现中文全文检索

前言:

  1. 全文搜索的实现方式

    Web项目中如果要实现全文检索,最主流的做法是Web项目以外配置一个独立的搜索引擎,然后web程序通过API接口调用这些搜索引擎,实现建立索引以及搜索全文。目前常用的几个开源搜索引擎:ElasticsearchSolrsphinx

    云服务大行其道的今天,当然也少不了调用云服务类型搜索引擎。使用有实力的云搜索引擎服务,应该也是一个不错的选择。例如:Amazon CloudSearch

    还有一种简易做法是直接调用谷歌或者百度的索引结果。(使用site=mydomain,将搜索结果限制在自己的网站域名中)。这种比较适用于实时性要求不高的文章类网站,使用情景有限。

    最后一种是原始的数据库查询方式like '%keywords%'。这种方式对于text文章类的字段来说效率低下,一般不要考虑用来做全文检索。

  2. 中文的全文搜索

    英文、西班牙文等这些字母语言,对于实现全文搜索来说有一个巨大的先天优势:人家不用去考虑分词的问题。英文文章中的单词本来就是用空格及标点符号分隔的。但是像中文和日文这种,一句话中所有的字符都是沾在一起的情况,要实现全文检索,首先要做到的是能够正确地将一句话正确拆分成词语。要达到这个目的,一是需要一个高效的分词处理算法,二是需要一个尽可能包含所有词语的词典。由于语言的进化,这个词典还需要随着时代不停的去更新。

一、为什么选择PostgreSQL进行全文搜索

在用Rails4+PostgreSQL开发一个公司内部使用小系统时,对于全文搜索功能的实现让我陷入了选择的泥坑。

首先,配置一个Solr这样的第三方搜索引擎,看来是全文检索(主要是中文)的不二之选。但是我实在不甘心为了一个只有几千篇文章、用户数只有几百人的内部Web应用、让系统变得复杂化。这时候,这篇文章《Postgres full-text search is Good Enough!》映入了我的眼帘。看起来PostgreSQL数据库在全文搜索方面的性能足以满足小规模系统的需求了。另外,pg_search模块,为Rails应用提供了调用PostgreSQL搜索功能的优秀接口。如此看来,PostgreSQL+Rails4+pg_search能实现我的全文搜索功能。

但是经过一番测试,我发现这个方案默认只会对英语等那些字母语言有用,没有办法对中文进行全文检索。重新审视这个解决方案,原来我忘记了一个重要环节:那就是PostgreSQL默认没有中文分词功能。

通过网上的查找,发现了很多人都推荐的一个中文分词解决方案:SCWS,以及实现调用这个分词引擎的PostgreSQL的扩展插件:zhparser。经过反复测试,终于实现了Rails4+pg_search来进行中文全文检索。

以下是CentOS6下SCWS+zhparser+rails4+pg_search+PostgreSQL的安装配置和开发的要点总结。另外,我也提供了一个完整的样例代码,以供参考

二、安装SCWS

前提:正确安装PostgreSQL数据库。请参照CentOS6下最新版PostgreSQL的安装及设置

  1. 确保Linux系统中已经安装了gcc编译环境,如果没有安装,CentOS的话可以使用yum工具安装需要的编译环境

    # yum -y install gcc gcc-c++ make automake zlib zlib-devel openssl openssl-devel 
    
  2. 安装scws中文分词引擎

    下载最新版本(目前最新版本是1.2.3),解压后执行进行编译和安装

    # cd /opt
    # wget http://www.xunsearch.com/scws/down/scws-1.2.3.tar.bz2
    # tar xvjf scws-1.2.3.tar.bz2
    # cd scws-1.2.3
    # ./configure
    # make 
    # make install
    

    默认安装路径为:

    • 可执行scws所在文件:/usr/local/bin/
    • 库文件所在目录: /usr/local/lib/
    • 配置文件所在目录: /usr/local/etc/

    查看安装后的可执行文件:

    # scws -v
    
  3. 安装中文分词字典

    根据自己的实际编码字符集需求,下载相应编码的最新简体中文字典文件。(推荐数据库使用utf8字符集)

    # cd /opt/
    # wget http://www.xunsearch.com/scws/down/scws-dict-chs-utf8.tar.bz2
    # tar xvjf scws-dict-chs-utf8.tar.bz2
    # mv dict.utf8.xdb /usr/local/etc/
    

    使用命令行测试一下分词:

    # scws -c utf8  -d /usr/local/etc/dict.utf8.xdb  -r /usr/local/etc/rules.utf8.ini -M 9 "证券公司发布重要信息披露" 
    

    结果:

    证券公司 证券 公司 证 券 公 司 发布 发 布 重要 重 要 信息 信 息 披露 披 露 
    +--[scws(scws-cli/1.2.3)]----------+
    | TextLen:   36                  |
    | Prepare:   0.0019    (sec)     |
    | Segment:   0.0002    (sec)     |
    +--------------------------------+
    

三、安装zhparser

  1. 下载并编译安装zhparser

    # cd /opt/
    # git clone https://github.com/amutu/zhparser.git
    # cd zhparser
    # SCWS_HOME=/usr/local make && make install
    
    

    如果出现以下错误:make: pg_config: Command not found,说明需要增加root账号环境变量里关于postgresql的路径。例如9.4版本,默认安装路径的情况下:

    # echo 'export PATH=$PATH:/usr/pgsql-9.4/bin' >> /root/.bash_profile
    # source /root/.bash_profile 
    
  2. 在需要进行全文检索的PostgreSQL数据库中创建扩展及检索配置

    例如我的数据库名称为:db_fts_development,

    # su - postgres
    $ psql -U postgres db_fts_development
    

    在psql命令行状态下,执行创建扩展及检索配置的命令

    db_fts_development=# CREATE EXTENSION zhparser;
    db_fts_development=# CREATE TEXT SEARCH CONFIGURATION zhcnsearch (PARSER = zhparser);
    db_fts_development=# ALTER TEXT SEARCH CONFIGURATION zhcnsearch ADD MAPPING FOR n,v,a,i,e,l,j WITH simple;
    

    最后一行设置分词的参数,表示token映射中指定名词(n)、动词(v)、形容词(a)、成语(i)、叹词(e)、习用语(l)和简略词(j),这7种以外的token全部被屏蔽。词典使用的是内置的simple词典,即仅做小写转换。根据需要可以灵活定义词典和token映射,以实现屏蔽词和同义词归并等功能。

    可以根据需求不同,同一数据库下创建多个搜索配置,如zhcnsearch, twcnsearch1等等。

    记住创建的zhcnsearch这个搜索配置的名称,无论是在psql状态下还是以后在rails程序中使用中文全文检索时,都需要指定该名称。

  3. 使用PostgreSQL SQL命令,验证分词功能是否已经正确安装

    select to_tsvector('zhcnsearch','ruby程序员需要不停地学习新知识,努力提高自己的技术水平。') ;
    

    显示分词结果:

    ----------------------------------------------------------------------------------------------------
    'ruby':1 '不停':4 '努力':9 '地':5 '学习':6 '技术':11 '提高':10 '新知':7 '水平':12 '程序员':2 '识':8 '需要':3
    (1 row)
    

    不知道为什么,新知识一词,被拆分成了新知,也许是参数设置问题?也许是字典需要改进?也许是SCWS的算法需要改进?这里先不做深究了。

四、Rails4下pg_search的使用

  1. 安装Gemfiles中必不可少的的gem包:

    gem 'pg'
    gem 'pg_search'
    
  2. 在Model中设置使用pg_search。例如Article这个表中需要对title(string类型)和content(text类型)这两个字段进行中文全文检索。

    class Article < ActiveRecord::Base
    include PgSearch
    pg_search_scope :chinese_search,
                  :against => [:title, :content],
                  :using => {
                      tsearch: {
                          dictionary: 'zhcnsearch' # 在数据库中设置好的“搜索配置”的名称
                      }
                  }
    end
    
  3. 在Controller的方法中,调用Model的搜索方法。

    class ArticlesController < ApplicationController
    ...
    
    def index
      # 获取页面传过来的检索关键词 (多个检索关键词时用半角空格隔开)
      content_include = params[:content_include]
    
      @articles = Article.all
      # 调用model里定义的pg_search_scope,和scope的用法相同
      @articles = Article.chinese_search(content_include) if content_include.present? 
    end
    ....
    end  
    

五、一个完整的pg_search中文全文检索的样例

对程序员来说可以运行的代码是最好的参考文档。这里我开发了一个使用pg_search中文全文检索的样例。 源代码地址:https://github.com/racksam/pg_search_chinese_japanese_demo

注意: 这套代码能够运行的前提是按照以上的文档,正确安装并配置好PostgreSQL + SCWS + zhparser。

在线演示:Full-text Searching Demo [Chinese/English/Japanese]

六、TODOs

性能测试

参考资源

Share Comments
comments powered by Disqus