前言:
-
全文搜索的实现方式
Web项目中如果要实现全文检索,最主流的做法是Web项目以外配置一个独立的搜索引擎,然后web程序通过API接口调用这些搜索引擎,实现建立索引以及搜索全文。目前常用的几个开源搜索引擎:Elasticsearch、Solr、sphinx。
云服务大行其道的今天,当然也少不了调用云服务类型搜索引擎。使用有实力的云搜索引擎服务,应该也是一个不错的选择。例如:Amazon CloudSearch
还有一种简易做法是直接调用谷歌或者百度的索引结果。(使用
site=mydomain
,将搜索结果限制在自己的网站域名中)。这种比较适用于实时性要求不高的文章类网站,使用情景有限。最后一种是原始的数据库查询方式
like '%keywords%'
。这种方式对于text文章类的字段来说效率低下,一般不要考虑用来做全文检索。 -
中文的全文搜索
英文、西班牙文等这些字母语言,对于实现全文搜索来说有一个巨大的先天优势:人家不用去考虑分词的问题。英文文章中的单词本来就是用空格及标点符号分隔的。但是像中文和日文这种,一句话中所有的字符都是沾在一起的情况,要实现全文检索,首先要做到的是能够正确地将一句话正确拆分成词语。要达到这个目的,一是需要一个高效的分词处理算法,二是需要一个尽可能包含所有词语的词典。由于语言的进化,这个词典还需要随着时代不停的去更新。
一、为什么选择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的安装及设置
-
确保Linux系统中已经安装了gcc编译环境,如果没有安装,CentOS的话可以使用yum工具安装需要的编译环境
# yum -y install gcc gcc-c++ make automake zlib zlib-devel openssl openssl-devel
-
安装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
- 可执行scws所在文件:
-
安装中文分词字典
根据自己的实际编码字符集需求,下载相应编码的最新简体中文字典文件。(推荐数据库使用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
-
下载并编译安装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
-
在需要进行全文检索的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程序中使用中文全文检索时,都需要指定该名称。 -
使用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的使用
-
安装Gemfiles中必不可少的的gem包:
gem 'pg' gem 'pg_search'
-
在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
-
在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
性能测试
参考资源
- scws官网: http://www.xunsearch.com/scws/
- zhparser官网:https://github.com/amutu/zhparser
- pg_search官网:https://github.com/Casecommons/pg_search
- Postgres full-text search is Good Enough!
- 使用PostgreSQL zhparser时不可不知的几个参数
- PostgreSQL的全文检索插件zhparser的中文分词效果
- CentOS6下最新版PostgreSQL的安装及设置
- Centos6 下安装rbenv + ruby + rails + puma + ngnix