Tim*_*mmm 12 sql postgresql full-text-search ranking
继这个答案后,我想知道使用PostgreSQL的内置全文搜索的最佳方法是,如果我想按等级排序,并限制只匹配查询.
让我们假设一个非常简单的表.
CREATE TABLE pictures (
id SERIAL PRIMARY KEY,
title varchar(300),
...
)
Run Code Online (Sandbox Code Playgroud)
管他呢.现在我想搜索该title字段.首先我创建一个索引:
CREATE INDEX pictures_title ON pictures
USING gin(to_tsvector('english', title));
Run Code Online (Sandbox Code Playgroud)
现在我想搜索'small dog'.这有效:
SELECT pictures.id,
ts_rank_cd(
to_tsvector('english', pictures.title), 'small dog'
) AS score
FROM pictures
ORDER BY score DESC
Run Code Online (Sandbox Code Playgroud)
但我真正想要的是:
SELECT pictures.id,
ts_rank_cd(
to_tsvector('english', pictures.title), to_tsquery('small dog')
) AS score
FROM pictures
WHERE to_tsvector('english', pictures.title) @@ to_tsquery('small dog')
ORDER BY score DESC
Run Code Online (Sandbox Code Playgroud)
或者这个(不起作用 - 不能score在WHERE条款中使用):
SELECT pictures.id,
ts_rank_cd(
to_tsvector('english', pictures.title), to_tsquery('small dog')
) AS score
FROM pictures WHERE score > 0
ORDER BY score DESC
Run Code Online (Sandbox Code Playgroud)
最好的方法是什么?我的问题很多:
to_tsvector(...)将它调用两次,或者它是否足够智能以某种方式缓存结果?to_ts...函数调用的情况下执行此操作?score在WHERE条款中使用?score > 0或使用它会更好@@吗?isa*_*pir 12
接受的答案是完全错误的,所以我不得不插话:
使用@@运算符将使用全文GIN索引,而测试则score > 0不会.
我在问题中创建了一个表,但添加了一个名为的列title_tsv:
CREATE TABLE test_pictures (
id BIGSERIAL,
title text,
title_tsv tsvector
);
CREATE INDEX ix_pictures_title_tsv ON test_pictures
USING gin(title_tsv);
Run Code Online (Sandbox Code Playgroud)
我用一些测试数据填充了表格:
INSERT INTO test_pictures(title, title_tsv)
SELECT T.data, to_tsvector(T.data)
FROM some_table T;
Run Code Online (Sandbox Code Playgroud)
然后我运行了Accepted解决方案explain analyze:
EXPLAIN ANALYZE
SELECT score, id, title
FROM (
SELECT ts_rank_cd(P.title_tsv, to_tsquery('address & shipping')) AS score
,P.id
,P.title
FROM test_pictures as P
) S
WHERE score > 0
ORDER BY score DESC;
Run Code Online (Sandbox Code Playgroud)
得到以下内容.请注意执行时间为5,015 ms
QUERY PLAN |
----------------------------------------------------------------------------------------------------------------------------------------------|
Gather Merge (cost=274895.48..323298.03 rows=414850 width=60) (actual time=5010.844..5011.330 rows=1477 loops=1) |
Workers Planned: 2 |
Workers Launched: 2 |
-> Sort (cost=273895.46..274414.02 rows=207425 width=60) (actual time=4994.539..4994.555 rows=492 loops=3) |
Sort Key: (ts_rank_cd(p.title_tsv, to_tsquery('address & shipping'::text))) DESC |
Sort Method: quicksort Memory: 131kB |
-> Parallel Seq Scan on test_pictures p (cost=0.00..247776.02 rows=207425 width=60) (actual time=17.672..4993.997 rows=492 loops=3) |
Filter: (ts_rank_cd(title_tsv, to_tsquery('address & shipping'::text)) > '0'::double precision) |
Rows Removed by Filter: 497296 |
Planning time: 0.159 ms |
Execution time: 5015.664 ms |
Run Code Online (Sandbox Code Playgroud)
现在将其与@@运营商进行比较:
EXPLAIN ANALYZE
SELECT ts_rank_cd(to_tsvector(P.title), to_tsquery('address & shipping')) AS score
,P.id
,P.title
FROM test_pictures as P
WHERE P.title_tsv @@ to_tsquery('address & shipping')
ORDER BY score DESC;
Run Code Online (Sandbox Code Playgroud)
结果的执行时间约为29毫秒:
QUERY PLAN |
-------------------------------------------------------------------------------------------------------------------------------------------------|
Gather Merge (cost=13884.42..14288.35 rows=3462 width=60) (actual time=26.472..26.942 rows=1477 loops=1) |
Workers Planned: 2 |
Workers Launched: 2 |
-> Sort (cost=12884.40..12888.73 rows=1731 width=60) (actual time=17.507..17.524 rows=492 loops=3) |
Sort Key: (ts_rank_cd(to_tsvector(title), to_tsquery('address & shipping'::text))) DESC |
Sort Method: quicksort Memory: 171kB |
-> Parallel Bitmap Heap Scan on test_pictures p (cost=72.45..12791.29 rows=1731 width=60) (actual time=1.781..17.268 rows=492 loops=3) |
Recheck Cond: (title_tsv @@ to_tsquery('address & shipping'::text)) |
Heap Blocks: exact=625 |
-> Bitmap Index Scan on ix_pictures_title_tsv (cost=0.00..71.41 rows=4155 width=0) (actual time=3.765..3.765 rows=1477 loops=1) |
Index Cond: (title_tsv @@ to_tsquery('address & shipping'::text)) |
Planning time: 0.214 ms |
Execution time: 28.995 ms |
Run Code Online (Sandbox Code Playgroud)
正如您在执行计划中所看到的,索引ix_pictures_title_tsv在第二个查询中使用,但在第一个查询中没有使用,使得与@@运算符的查询速度提高了172倍!
select *
from (
SELECT
pictures.id,
ts_rank_cd(to_tsvector('english', pictures.title),
to_tsquery('small dog')) AS score
FROM pictures
) s
WHERE score > 0
ORDER BY score DESC
Run Code Online (Sandbox Code Playgroud)
如果我使用重复的to_tsvector(...)版本,它会调用两次,还是足够智能以某种方式缓存结果?
注意这些事情的最好方法是做一个简单的解释,尽管那些很难阅读.
简而言之,是的,PostgreSQL足够聪明,可以重用计算结果.
有没有办法在不重复to_ts ...函数调用的情况下执行此操作?
我通常做的是添加一个tsv文本搜索向量列.如果使用触发器进行此自动更新,它会立即为您提供易于访问的向量,但它还允许您通过选择性触发来有选择地更新搜索索引.
有没有办法在WHERE子句中使用得分?
是的,但不是那个名字.或者你可以创建一个子查询,但我个人只是重复它.
如果有的话,按分数> 0过滤或使用@@东西会更好吗?
我能想到的最简单的版本是这样的:
SELECT *
FROM pictures
WHERE 'small dog' @@ text_search_vector
Run Code Online (Sandbox Code Playgroud)
该text_search_vector可以明显地喜欢的东西所取代to_tsvector('english', pictures.title)
| 归档时间: |
|
| 查看次数: |
10364 次 |
| 最近记录: |