postgresql具有full text search(FTS),可以实现全文检索,也有GIN(倒排索引),可以实现比较简单的搜索场景(如标签搜索,文本搜索)。
相比like等老旧的搜索功能,full text search 有如下优势:
- 单词归一化
- 具有排序功能
- 速度快,不需要遍历所有文档
成本比较
4核32G,80G存储,带运维服务
ES集群:5万/年
PG集群实例:5千/年
性能比较
PG插入速度 2万条/秒
PG搜索速度 15ms(1000万行,limit 100)
ES插入速度 1万条/秒
ES搜索速度 250ms ~ 1500ms(1000万行,limit 100)(时间波动比较大)
数据库SQL实现
tsvector, tsquery的ts代表text search
FTS搜索
SELECT * FROM public.object_storage where tags @@ 'address_fixer & asr'
上述等价于
SELECT * FROM public.object_storage where tags @@ 'address_fixer & asr'::tsquery
SELECT * FROM public.object_storage where tags @@ tsquery('address_fixer & asr') ----- 代码实现要使用这种
但不等于
SELECT * FROM public.object_storage where tags @@ to_tsquery('address_fixer & asr');
tsquery 不会对单词归一化,但是to_tsquery 会对单词归一化。
在标签场景,不建议做归一化。
tags列的数据类型为tsvector
@@等同于ES的match功能。
'address_fixer & asr' 代表必须匹配上 address_fixer 和 asr 2个词。 可以加入或 | 组成更复杂的逻辑组合。
加入排序
排序细节参考:PostgreSQL: Documentation: 15: 12.3. Controlling Text Search
ts_rank(tags, query, 1) 的1(1 divides the rank by 1 + the logarithm of the document length),默认是0(不考虑文档长度,排序效果不佳)
SELECT * FROM object_storage, tsquery(:tag) query, ts_rank(tags, query, 1) rank_tags" + " WHERE bucket = :bucket AND tags @@ query ORDER BY rank_tags DESC
插入数据
insert into object_storage(bucket, object_name, tags) values(?, ?, tsvector(?))
标签搜索场景
虽然pg默认不支持中文分词,但输入的标签(不管中文还是英文)采用空格分割即可,默认按空格切分。
建立倒排索引提高查询速度
GIN是general inverted index 倒排索引的意思,可以提高搜索速度
CREATE INDEX tags_gin_index ON public.object_storage USING GIN (tags);
(GIN和GiST的索引选型,参考:GiST和GIN索引类型 )
TODO:tsquery的搜索权重
spring data jpa的代码实现
如何在spring data jpa中定义pg的tsvector类型?
spring data jpa是一个通用框架,有一些其他但又常用的类型,需要使用另一个库:
里面有不同的文件夹,52代表java 8(55代表java11等)
如果公司只支持java8,所以需要选择52,版本号要尽量高,否则低版本会没有search的PostgreSQLTSVectorType类型
<dependency> <groupId>com.vladmihalcea</groupId> <artifactId>hibernate-types-52</artifactId> <version>2.16.0</version> </dependency>
使用方法参见hibernate-types的github主页(6x系列和5x系列的使用方法不一样)
@Type(type = "com.vladmihalcea.hibernate.type.search.PostgreSQLTSVectorType") @Column(columnDefinition = "tsvector") private String tags;
如何查询?
需要基于自定义query实现。
注意:虽然pgadmin可以直接 tags @@ 'address_fixer & asr'
但是下面的是不能写为 tags @@ :tag 或 tags && ':tag' ,需要加to_tsquery()包裹起来
但是to_tsquery有一个弊端,就是会做归一化等处理,这在标签搜索,反而是弊端,举例address_fixer变成了address 和 fixer 2个独立的词,改变了输入。
@Query(value = "SELECT * FROM object_storage WHERE tags @@ to_tsquery(:tag)", nativeQuery = true) List<ObjectStorageEntity> searchTags(@Param("tag") String tag);
minio的sdk
Java Client API Reference — MinIO Object Storage for Linux
参考文章
PostgreSQL: Documentation: 15: 8.11. Text Search Types 搜索数据类型:
PostgreSQL: Documentation: 15: 9.13. Text Search Functions and Operators 函数及操作符
PostgreSQL: Documentation: 15: 12.1. Introduction
GiST和GIN索引类型 索引选择(GIN, GiST)PostgreSQL实现全文检索 - 知乎 pg实现全文搜索