一、 pg_jieba 方案
安装
brew install cmake
mkdir ~/tmp && cd ~/tmp && git clone https://github.com/jaiminpan/pg_jieba && cd pg_jieba
git submodule update --init --recursive
mkdir build && cd build
cmake -DCMAKE_PREFIX_PATH=/usr/local/opt/postgres ..
make install
测试
$ psql -d vapordb
psql (12.2)
Type "help" for help.
@vapordb=# CREATE EXTENSION pg_jieba;
CREATE EXTENSION
@vapordb=# SELECT * FROM to_tsvector('jiebacfg', '小明硕士毕业于中国科学院计算所,后在日本京都大学深造');
to_tsvector
----------------------------------------------------------------------------------
'中国科学院':5 '小明':1 '日本京都大学':10 '毕业':3 '深造':11 '硕士':2 '计算所':6
(1 row)
@vapordb=# \quit
在测试时,可以感觉到 jieba 的第一次分词有明显的延迟和卡顿,可以通过 Postgresq 预加载 jieba 的动态库和配置文件改善(/usr/local/var/postgres/postgresql.conf)。
#------------------------------------------------------------------------------
# CUSTOMIZED OPTIONS
#------------------------------------------------------------------------------
# Add settings for extensions here
# pg_jieba
shared_preload_libraries = 'pg_jieba.so' # (change requires restart)
# default_text_search_config='pg_catalog.simple'; default value
default_text_search_config='jiebacfg'; uncomment to make 'jiebacfg' as default
二、 zhparser 方案
mac 安装 scws
brew install scws
scws -v
- intel 安装在/usr/local 下
- M1安装在/opt/homebrew 下,M1需要把相关文件拷贝给/usr/local
下载词典文件
mkdir -p /usr/local/etc/scws
curl "http://www.xunsearch.com/scws/down/scws-dict-chs-utf8.tar.bz2" | tar xvjf -
mv dict.utf8.xdb /usr/local/etc/scws/ #需要装在 /usr/local 下
测试效果
scws -c utf8 -d /usr/local/etc/scws/dict.utf8.xdb -r /usr/local/opt/scws/etc/rules.utf8.ini -M 9 "PostgreSQL 自带有一个简易的全文检索引擎"
PostgreSQL 自带 自 带 有 一个 一 个 简易 简 易 的 全文检索 全文 检索 全 文 检 索 引擎 引 擎
+--[scws(scws-cli/1.2.3)]----------+
| TextLen: 52 |
| Prepare: 0.0007 (sec) |
| Segment: 0.0002 (sec) |
+--------------------------------+
安装 zhparser
mkdir ~/tmp && cd ~/tmp
git clone https://github.com/amutu/zhparser.git && cd zhparser
which pg_config
/usr/local/opt/libpq/bin/pg_config
PG_CONFIG=/usr/local/opt/libpq/bin/pg_config make && make install
测试 zhparser
$ psql -d vapordb
psql (12.2)
Type "help" for help.
CREATE EXTENSION zhparser;
#CREATE EXTENSION
CREATE TEXT SEARCH CONFIGURATION zhcfg (PARSER = zhparser);
#CREATE TEXT SEARCH CONFIGURATION
# 添加名词(n)、动词(v)、形容词(a)、成语(i)、叹词(e)、习用语(l)和简称(j)七种分词策略:
ALTER TEXT SEARCH CONFIGURATION zhcfg ADD MAPPING FOR n,v,a,i,e,l,j WITH simple;
#ALTER TEXT SEARCH CONFIGURATION
SELECT to_tsvector('zhcfg', '人生苦短,我用 Python');
to_tsvector
------------------------------------------
'python':5 '人生':1 '用':4 '短':3 '苦':2
(1 row)
@vapordb=# \quit
- 高亮
Post.select("content, ts_headline('zhcfg', content, '父亲', 'StartSel=<mark>, StopSel=</mark>, HighlightAll=true, MaxWords=35, MinWords=3, ShortWord=3, MaxFragments=0, FragmentDelimiter======') mark_content").where("to_tsvector('zhcfg', content) @@ plainto_tsquery('zhcfg', ?)", '父亲')
ts_headline
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
昏黄的路灯,照着他的<mark>父亲</mark>,他偎在那个墙角,身下垫着不知从哪里拣来的破纸箱。此刻,他正把身上的棉衣裹了又裹,而自己高中时围过的围巾,紧紧地缠在<mark>父亲</mark>头上。
(1 row)
- StartSel, StopSel:该字符串分隔文档中出现的查询词,以区别于其他摘录词。 如果它们含有空格或逗号,你必须用双引号字符串。
- MaxWords, MinWords:这些数字决定最长和最短的标题输出。
- ShortWord:这个长度或更短的词在标题的开始和结束被丢弃。三个默认值消除了常见英语文章。
- HighlightAll:布尔标志;如果为真,整个文档将作为标题,忽略了前面的三个参数。
- MaxFragments:要显示的文本摘录或片段的最大数量。默认值零选择非片段标题的生成方法。 一个大于零的值选择基于片段的标题生成。此方法查找文本片段与尽可能多的查询词并在查询词周围延伸这些片段。 作为查询词的结果接近每一片段中间,每边都有词。每个片段至多是MaxWords ,并且长度为ShortWord或更短的词在每一个片段开始和结束被丢弃。 如果不是所有的查询词在文档中找到,则文档中开头的MinWords单片段将被显示。
- FragmentDelimiter:当一个以上的片段显示时,通过字符串分隔这些片段。
- 默认:StartSel=<b>, StopSel=</b>, MaxWords=35, MinWords=15, ShortWord=3, HighlightAll=FALSE, MaxFragments=0, FragmentDelimiter=" … "
- zhcfg的一些配置
# 查看安装的解析器
select * from pg_ts_parser;
# 查看安装的全文搜索插件
select * from pg_ts_config;
# 查看zhparser支持的字典类型
select * from pg_catalog.ts_token_type('zhparser');
# 查看已配置的字典映射
\dF+ zhcfg
select * from pg_ts_config_map where mapcfg=(select oid from pg_ts_config where cfgname='zhcfg');
# 查看用到的字典类型
select ts_debug('zhcfg','三一') ;
可以查看到分词的token类型 如果不在之前的mapping内的话是不会被分词的
# 添加字典映射
ALTER TEXT SEARCH CONFIGURATION zhcfg ADD MAPPING FOR n,v,a,i,e,l,j WITH simple; #常用
ALTER TEXT SEARCH CONFIGURATION zhcfg 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; #全部添加
# 字典对应意思
(97, a, "adjective, 形容词")
(98, b, "differentiation, 区别词")
(99, c, "conjunction, 连词")
(100, d, "adverb, 副词")
(101, e, "exclamation, 感叹词")
(102, f, "position, 方位词")
(103, g, "root, 词根")
(104, h, "head, 前连接成分")
(105, i, "idiom, 成语")
(106, j, "abbreviation, 简称")
(107, k, "tail, 后连接成分")
(108, l, "tmp, 习用语")
(109, m, "numeral, 数词")
(110, n, "noun, 名词")
(111, o, "onomatopoeia, 拟声词")
(112, p, "prepositional, 介词")
(113, q, "quantity, 量词")
(114, r, "pronoun, 代词")
(115, s, "space, 处所词")
(116, t, "time, 时语素")
(117, u, "auxiliary, 助词")
(118, v, "verb, 动词")
(119, w, "punctuation, 标点符号")
(120, x, "unknown, 未知词")
(121, y, "modal, 语气词")
(122, z, "status, 状态词")
# 删除字典映射
ALTER TEXT SEARCH CONFIGURATION zhcfg DROP MAPPING FOR m;
# 忽略所有的标点等特殊符号
set zhparser.punctuation_ignore = on;
# 全部单字复合
set zhparser.multi_zall = on;
# 散字二元复合
set zhparser.multi_duality = on;
# 闲散文字自动以二字分词法聚合
set zhparser.seg_with_duality = on;
# 短词复合
set zhparser.multi_short = on;
# 重要单字复合
set zhparser.multi_zmain = on;
- 自定义词库
所有的自定义词都放在了 zhprs_custom_word 表里面,默认添加的自定义词的词性是 (120, x, “unknown, 未知词”),可能用到的 sql 包含但不局限于如下:
# 添加自定义词
insert into zhparser.zhprs_custom_word values('资金压力');
# 自定义词库也支持停止词功能,例如我们不希望词语'这是'单独作为一个分词,同样可以在自定义词库中插入对应的词语和控制符停止特定分词:
insert into zhparser.zhprs_custom_word(word, attr) values('这是','!');
# 删除自定义词
delete from zhparser.zhprs_custom_word where word = '安全高效';
# 添加/删除自定义分词之后需要执行以下命令才能使词库生效
select sync_zhprs_custom_word();
# 查询已存在的自定义词库
select * from zhparser.zhprs_custom_word;
- 阿里云自定义词库
-- 初始的分词结果
SELECT to_tsquery('testzhcfg', '保障房资金压力');
-- 往自定义分词词典里面插入新的分词
insert into pg_ts_custom_word values ('保障房资');
-- 删除分词
delete from pg_ts_custom_word where word = '非金镶';
-- 使新的分词生效
select zhprs_sync_dict_xdb();
-- 退出此连接
\c
-- 重新查询,可以得到新的分词结果
SELECT to_tsquery('testzhcfg', '保障房资金压力');
阿里云rds自带pg_jieba和zhparser扩展,使用前需要先按要求设置参数shared_preload_libraries,然后创建扩展就行 阿里云rds中文分词链接
三、对比
两种方案效果上差不多.
$ psql -d vapordb
psql (12.2)
Type "help" for help.
@vapordb=# SELECT * FROM to_tsvector('jiebacfg', '小明硕士毕业于中国科学院计算所,后在日本京都大学深造');
to_tsvector
----------------------------------------------------------------------------------
'中国科学院':5 '小明':1 '日本京都大学':10 '毕业':3 '深造':11 '硕士':2 '计算所':6
(1 row)
@vapordb=# SELECT * FROM to_tsvector('zhcfg', '小明硕士毕业于中国科学院计算所,后在日本京都大学深造');
to_tsvector
---------------------------------------------------------------------------
'中国科学院计算所':4 '小明':1 '日本京都大学':5 '毕业':3 '深造':6 '硕士':2
(1 row)
@vapordb=# \quit
四、如何使用
对于全文检索,有两种使用方式,大家可以权衡自己的内容进行选择。
- 在搜索的时候进行分词,然后搜索对应的字段。
- 提前把表中需要检索的字段进行分词,保存到一个新的字段中,再在这个字段上建立索引进行搜。
两种方案就是时间和空间的取舍:第一种方式创建索引简单,存储空间少,但是比较慢。第二种方案由于预先进行了分词并存储,浪费了空间,但是时间上肯定用得少。创建索引也有两种方案:gin 索引和 rum 索引。
第一种
创建索引:
CREATE INDEX idx_xxxx ON xxxx_table USING gin(to_tsvector('jiebacfg',
COALESCE(xx_field, '') || COALESCE(xxx_field, '')));
查询:
EXPLAIN ANALYSE SELECT * FROM xxxx_table
WHERE to_tsvector('jiebacfg', COALESCE(xx_field, '') || COALESCE(xxx_field, '')) @@
to_tsquery('jiebacfg', '关键字或者句子');
第二种
创建 tsv 字段和索引
ALTER TABLE xxxx_table ADD COLUMN tsv tsvector;
UPDATE xxxx_table SET tsv_field = to_tsvector('jiebacfg', COALESCE(xx_field, '') || COALESCE(xxx_field, ''));
CREATE INDEX idx_xxxx ON xxxx_table USING gin(tsv_field);
查询:
EXPLAIN ANALYSE SELECT * FROM xxxx_table WHERE tsv_field @@ to_tsquery('jiebacfg', '关键词或者句子');
当然因为是预先分词保存,所以需要在 update 的时候藉由 触发器
来更新 tsv 字段,。
CREATE TRIGGER tsvector_update BEFORE INSERT OR UPDATE
ON xxxx_table FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger('tsv_field', 'jiebacfg', 'xx_field', 'xxx_field');
rum 索引
使用 rum 索引类似, 但是 rum 引擎默认是没有安装的,需要自己编译,暂时先不用了。文章来源:https://www.toymoban.com/news/detail-579479.html
CREATE INDEX idx_xxxx ON xxxx_table USING rum(tsv_field rum_tsvector_ops);
另外 rum 还支持相似度的查询:文章来源地址https://www.toymoban.com/news/detail-579479.html
SELECT * FROM to_tsvector('jiebacfg', '小明硕士毕业于中国科学院计算所,后在日本京都大学深造');
SELECT * FROM rum_ts_distance(to_tsvector('jiebacfg', '小明硕士毕业于中国科学院计算所,后在日本京都大学深造') , to_tsquery('计算所'));
到了这里,关于[ruby on rails] postgresql分词搜索 pg_jieba 和 zhparser 方案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!