1、环境
Fedora 20 + Postgresql 9.3.4 + scws 1.2.2 + zhparser2、安装
2.1 scws
- wget -q -O - http://www.xunsearch.com/scws/down/scws-1.2.2.tar.bz2• tar xjf - cd scws-1.2.1
- ./configure --prefix=/usr/local #该路径为scws的安装路径
- make install
2.2 zhparser
- git clone https://github.com/amutu/zhparser.git
- SCWS_HOME=/usr/local/scws make && make install
3、配置
[root@localhost ~]# su postgres
[postgres@localhost ~]$ psql
psql (9.3.4)
Type "help" for help.
postgres=# CREATE EXTENSION ZHPARSER;
postgres=# CREATE TEXT SEARCH CONFIGURATION testzhcfg (PARSER = zhparser);
postgres=# ALTER TEXT SEARCH CONFIGURATION testzhcfg ADD MAPPING FOR n,v,a WITH simple;
[postgres@localhost ~]$ psql
psql (9.3.4)
Type "help" for help.
postgres=# CREATE EXTENSION ZHPARSER;
postgres=# CREATE TEXT SEARCH CONFIGURATION testzhcfg (PARSER = zhparser);
postgres=# ALTER TEXT SEARCH CONFIGURATION testzhcfg ADD MAPPING FOR n,v,a WITH simple;
注:此处的n,v, a为词性(名词、动词、形容词),分词时会只考虑此处指定的词性,其他诸如介词、动名词会被忽略。scws中的所有词性说明:http://www.xunsearch.com/scws/docs.php#attr,并不是所有的scws中的词性都被支持,如ns(地理名词)是不被支持的。
4、测试
postgres=# SELECT * FROM ts_parse('zhparser', 'hello world!我不是为了输赢,我就是认真');
tokid | token
-------+-------
101 | hello
101 | world
117 | !
114 | 我
118 | 不是
112 | 为了
110 | 输赢
117 | ,
114 | 我
110 | 就是
118 | 认真
(11 rows)
tokid | token
-------+-------
101 | hello
101 | world
117 | !
114 | 我
118 | 不是
112 | 为了
110 | 输赢
117 | ,
114 | 我
110 | 就是
118 | 认真
(11 rows)
postgres=# SELECT to_tsvector('testzhcfg', 'hello world!我不是为了输赢,我就是认真') @@to_tsquery('认真');
?column?
----------
t
(1 row)
?column?
----------
t
(1 row)
postgres=# SELECT to_tsvector('testzhcfg', '扁担长,板凳宽,扁担没有板凳宽,板凳没有扁担长') @@to_tsquery('板凳');
?column?
----------
t
(1 row)
?column?
----------
t
(1 row)
postgres=# SELECT to_tsvector('testzhcfg', '扁担长,板凳宽,扁担没有板凳宽,板凳没有扁担长') @@to_tsquery('桌子');
?column?
----------
f
(1 row)
?column?
----------
f
(1 row)
5、自定义词库
5.1 自定义字典
工具:XDB导入导出工具(http://www.xunsearch.com/scws/down/phptool_for_scws_xdb.zip)
需要有PHP环境(可在命令行下运行即可,需要有mbstring库),使用方法请参考压缩包中的readme.txt
scws使用的字典为xdb格式,不可以直接编辑和查看,其对应的文本形式如下:
WORD | TF | IDF | ATTR |
---|---|---|---|
保障房 | 1.00 | 1.00 | n |
WORD | TF | IDF | ATTR |
---|---|---|---|
中央 | 3.00 | 3.00 | n |
地方 | 2.00 | 2.00 | n |
“中央”的权重(TF*IDF)小于“从中”,所以“从中央”一起出现时,就会分为“从中”和“央”。
WORD,TF,IDF,ATTR之间的分割符是tab,其他的会出错
5.2 zhparser使用自定义词典
方法1:修改/usr/share/pgsql/tsearch_data下的dict.utf8.xdb(保证文件名不变),不过这种方式耗时较长
借助SCWS提供的工具,可以很快的将dict.utf8.xdb转为普通的文本文件,但将文本文件转回去的时候大约耗时1h左右
方法2:通过增加字典文件来扩展词库
约定
- 字典文件的命名格式为usr_dict_num.xdb,其中num为序号,从1开始,表示第几个自定义的字典num必须连续,如果出现usr_dict_1.xdb,usr_dict_2.xdb,usr_dict_4.xdb,那么usr_dict_4.xdb及以后num大于4的字典都不会被加载
- 所有自定义的字典要放在/usr/share/pgsql/tsearch_data下面
- 新增的字典只要复合以上约定可自动加载,无须重新安装zhparser,也无须重新create extension
需修改代码:
scws_set_dict(scws,dict_path, SCWS_XDICT_XDB);
修改后代码:
beg=1;
while(1){
snprintf(dict_path, MAXPGPATH, "%s/tsearch_data/usr_dict_%d.xdb",sharepath,beg);
exist=access(dict_path,F_OK);
if(exist==-1)
break;
scws_add_dict(scws, dict_path, SCWS_XDICT_XDB);
beg++;
}
while(1){
snprintf(dict_path, MAXPGPATH, "%s/tsearch_data/usr_dict_%d.xdb",sharepath,beg);
exist=access(dict_path,F_OK);
if(exist==-1)
break;
scws_add_dict(scws, dict_path, SCWS_XDICT_XDB);
beg++;
}
修改zhparser.c,需要增加头文件:#include "unistd.h"
/usr/share/pgsql/tsearch_data该路径视postgresql的安装路径而定
6、建立全文检索的索引
方式1:表达式索引
CREATE INDEX pgweb_idx ON pgweb USING gin(to_tsvector(config_name, body));
CREATE INDEX pgweb_idx ON pgweb USING gin(to_tsvector('english', title || body));
CREATE INDEX pgweb_idx ON pgweb USING gin(to_tsvector('english', title || body));
方式2:分离字段索引
ALTER TABLE pgweb ADD COLUMN textsearchable_index_col tsvector;
UPDATE pgweb SET textsearchable_index_col =
to_tsvector('english', coalesce(title,) || coalesce(body,));
CREATE INDEX textsearch_idx ON pgweb USING gin(textsearchable_index_col);
UPDATE pgweb SET textsearchable_index_col =
to_tsvector('english', coalesce(title,) || coalesce(body,));
CREATE INDEX textsearch_idx ON pgweb USING gin(textsearchable_index_col);
在使用独立字段存储 tsvector 形式的时候,我们需要创建一些触发器以保持 tsvector 字段对 title 和 body 修改的同步。分离字段方式比表达式索引方式的一个优点是它不需要在查询里明确声明文本 搜索的配置就可以使用索引。如上面例子所示,查询可以依赖于default_text_search_config。另 外一个优点是搜索会更快,因为它不需要重新调用 to_tsvector 函数来判断是否和索引匹配。不过, 表达式索引的方法的优点是设置简单,而且它需要的磁盘空间更少,因为 tsvector 表现形式不用明 确存储。
7、实验
数据量:17830826条,约4G
在主键id上执行count查询
postgres=# select count(id) from area_detail;
count
----------
17830826
(1 row)
Time: 25749.020 ms
count
----------
17830826
(1 row)
Time: 25749.020 ms
通过id搜索一条记录
postgres=# select * from area_detail where id = '1601799';
Time: 46.024 ms
Time: 46.024 ms
通过like搜索数据库记录
postgres=# select * from area_detail where localityname like '%豆沙%';
Time: 25822.282 ms(约25秒)
Time: 25822.282 ms(约25秒)
通过未建索引的全文检索搜索数据库记录
postgres=# select * from area_detail where to_tsvector('testzhcfg',localityname) @@to_tsquery('testzhcfg','豆沙');
Time: 621077.779 ms
Time: 621077.779 ms
通过建立了索引(gin)的全文检索搜索数据库记录
postgres=# select * from area_detail where to_tsvector('testzhcfg',localityname) @@to_tsquery('testzhcfg','豆沙');
Time: 158.971 ms(约0.1秒)
Time: 158.971 ms(约0.1秒)
8 参考
- http://www.xunsearch.com/scws/index.php,感谢hightman大神
- https://github.com/amutu/zhparser,感谢 amutu大神
- http://blog.163.com/digoal@126/blog/static/163877040201422410175698/
- http://www.pgsqldb.org:8079/mwiki/index.php/12._%E5%85%A8%E6%96%87%E6%90%9C%E7%B4%A2
- http://my.oschina.net/Kenyon/blog/80904