pg中的全文检索功能十分丰富,例如我们想要搜索包含“速度与激情”的文档,但是我们都知道pg中的全文检索实质上是将语句进行切词,然后按照词组进行匹配查询的。所以这就会出现一个问题:我们直接查询包含“速度”&“激情”的文档会导致匹配到包含“速度”和“激情”两个单独词语的文档。
那么碰到这种情况该怎么办呢?pg全文检索中支持位置匹配,例如“速度与激情”这个词,分词后是有位置信息的,我们期望搜到的是“速度”和“激情”之间间隔一个的情况,提高精准度。
PostgreSQL的搜索距离的语法如下:
select * from tbl where ts @@ '速度 <距离值> 激情'::tsquery;
如
select * from tbl where ts @@ '速度 <1> 激情'::tsquery;
例子:
1、创建测试表
bill=# create table ts_test (id int, info text, ts tsvector);
CREATE TABLE
2、安装中文分词器pg_scws
下载地址:https://github.com/jaiminpan/pg_scws
bill=# create extension pg_scws;
CREATE EXTENSION
3、写入测试数据
bill=# insert into ts_test values (1, '激情,创新,坚持,速度-- 北京北方创业出租汽车公司优质服务小花絮', to_tsvector('scwscfg', '激情,创新,坚持,速度-- 北京北方创业出租汽车公司优质服务小花絮'));
INSERT 0 1
bill=# insert into ts_test values (1, '电影速度与激情8的票房破亿', to_tsvector('scwscfg', '电影速度与激情8的票房破亿'));
INSERT 0 1
4、查看分词结果
bill=# select * from ts_test;
id | info | ts
----+-----------------------------------------------------------------+------------------------------------------------------------------------------------------------------------
1 | 激情,创新,坚持,速度-- 北京北方创业出租汽车公司优质服务小花絮 | '优质服务':9 '公司':8 '出租汽车':7 '创业':6 '创新':2 '北京':5 '坚持':3 '小花':10 '激情':1 '絮':11 '速度':4
1 | 电影速度与激情8的票房破亿 | '激情':3 '电影':1 '破':5 '票房':4 '速度':2
(2 rows)
5、写入更多测试数据
bill=# insert into ts_test select 2, '激情,创新,坚持,速度-- 北京北方创业出租汽车公司优质服务小花絮', to_tsvector('scwscfg', '激情,创新,坚持,速度-- 北京北方创业出租汽车公司优质服务小花絮') from generate_series(1,1000000);
INSERT 0 1000000
6、创建rum索引
bill=# create extension rum;
CREATE EXTENSION
bill=# CREATE INDEX rumidx ON ts_test USING rum (ts rum_tsvector_ops);
CREATE INDEX
7、查询
不带位置信息,则返回全部记录。
bill=# explain (analyze,verbose,timing,costs,buffers) select * from ts_test where ts @@ '速度 & 激情'::tsquery;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
Seq Scan on public.ts_test (cost=0.00..49537.95 rows=999996 width=270) (actual time=0.025..385.005 rows=1000002 loops=1)
Output: id, info, ts
Filter: (ts_test.ts @@ '''速度'' & ''激情'''::tsquery)
Buffers: shared hit=37038
Planning Time: 0.303 ms
Execution Time: 444.955 ms
(6 rows)
带上位置,只匹配了1条记录,也就是我们要的“速度与激情”。
bill=# explain (analyze,verbose,timing,costs,buffers) select * from ts_test where ts @@ '速度 <1> 激情'::tsquery;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
Seq Scan on public.ts_test (cost=0.00..49537.95 rows=999996 width=270) (actual time=0.014..324.677 rows=1 loops=1)
Output: id, info, ts
Filter: (ts_test.ts @@ '''速度'' <-> ''激情'''::tsquery)
Rows Removed by Filter: 1000001
Buffers: shared hit=37038
Planning Time: 0.103 ms
Execution Time: 324.697 ms
(7 rows)