PgSQL · 特性介绍 · 全文搜索介绍

简介: 背景 在日常的数据处理中,我们经常会有这样的需求:从一个文本中寻找某个字符串(比如某个单词)。 对这个需求,我们可以用类似这样的SQL完成:SELECT * FROM tbl WHERE text LIKE ‘%rds PostgreSQL%’;(找到含有“rds PostgreSQL”的文本)

背景

在日常的数据处理中,我们经常会有这样的需求:从一个文本中寻找某个字符串(比如某个单词)。

对这个需求,我们可以用类似这样的SQL完成:SELECT * FROM tbl WHERE text LIKE ‘%rds PostgreSQL%’;(找到含有“rds PostgreSQL”的文本)。

现在我们考虑一些特殊的情形:

  1. 需要查找的文本特别多,特别大;

  2. 不做单纯的字符串匹配,而是考虑自然语言的一些特性,比如匹配某一类字符串(域名、人名)或者匹配单词的所有形式(不考虑它的词性及变化,比如have,has,had都匹配出来);

  3. 对中文自然语言特性的支持。

那么此时再用以上的 “SELECT … LIKE …” 就不明智了,因为对数据库来说,这样的SQL必然走的是全表扫描,那么当文本特别多、特别大的时候,查找效率就会很低。

另外,这样的SQL也不会智能到可以处理自然语言的特性。

怎么办呢?PostgreSQL(以下简称PG)提供了强大的全文搜索功能可以满足这样的需求。

对文本的预处理

全文搜索首先需要对文本预处理,包括3步:

  1. 将文本分解成一个个token,这些token可以是数字、单词、域名、人名、email的格式等等。在PG中可以定义一个parser来做这个工作。

  2. 将第一步分解成的token标准化,所谓的标准化就是利用一些规则将token分好类(比如人名是一类、域名是一类等等)。标准化后的token我们称之为lexeme。在PG中是通过定义一个词典来做这个工作。PG里最简单的词典simple的标准化过程就是将大写字母转成小写字母。

  3. 对文本打分,优化查找过程,比如对于待查找的词,文本1匹配的数量大于文本2匹配的数量,那么在这个查找过程,文本1的优先级大于文本2的优先级。

在PG中,以上对文本的预处理可以通过一个函数to_tsvector来完成,函数的返回值是tsvector这个数据类型。

另外,对于待查找的单词,我们也要用to_tsquery这个函数包装起来,函数的返回值是tsquery这个数据类型。

一个简单的例子见下面,to_tsquery里的参数可以使用运算符(&:与、|:或、!:非):

SELECT to_tsvector('fat cats ate fat rats') @@ to_tsquery('fat & rat');
 ?column?
----------
 t

Quick Start

在了解了这些概念之后,我们用实际的例子来玩一玩PG的全文搜索。

我们在client端输入以下命令,\dFp显示的是所有的parser,这里只有一个默认parser(default)。

\dFp+ default 显示默认parser(default)的详细信息:parse的过程(5个函数),parse的Token类型(asciihword, asciiword…)。

sbtest=# \dFp
        List of text search parsers
   Schema   |  Name   |     Description
------------+---------+---------------------
 pg_catalog | default | default word parser
(1 row)

sbtest=# \dFp+ default
    Text search parser "pg_catalog.default"
     Method      |    Function    | Description
-----------------+----------------+-------------
 Start parse     | prsd_start     | (internal)
 Get next token  | prsd_nexttoken | (internal)
 End parse       | prsd_end       | (internal)
 Get headline    | prsd_headline  | (internal)
 Get token types | prsd_lextype   | (internal)

        Token types for parser "pg_catalog.default"
   Token name    |               Description
-----------------+------------------------------------------
 asciihword      | Hyphenated word, all ASCII
 asciiword       | Word, all ASCII
 blank           | Space symbols
 email           | Email address
 entity          | XML entity
 file            | File or path name
 float           | Decimal notation
 host            | Host
 hword           | Hyphenated word, all letters
 hword_asciipart | Hyphenated word part, all ASCII
 hword_numpart   | Hyphenated word part, letters and digits
 hword_part      | Hyphenated word part, all letters
 int             | Signed integer
 numhword        | Hyphenated word, letters and digits
 numword         | Word, letters and digits
 protocol        | Protocol head
 sfloat          | Scientific notation
 tag             | XML tag
 uint            | Unsigned integer
 url             | URL
 url_path        | URL path
 version         | Version number
 word            | Word, all letters
(23 rows)

输入\dF+ english,给出标准化各类英语token时所用到的dictionary:

sbtest=# \dF+ english
Text search configuration "pg_catalog.english"
Parser: "pg_catalog.default"
      Token      | Dictionaries
-----------------+--------------
 asciihword      | english_stem
 asciiword       | english_stem
 email           | simple
 file            | simple
 float           | simple
 host            | simple
 hword           | english_stem
 hword_asciipart | english_stem
 hword_numpart   | simple
 hword_part      | english_stem
 int             | simple
 numhword        | simple
 numword         | simple
 sfloat          | simple
 uint            | simple
 url             | simple
 url_path        | simple
 version         | simple
 word            | english_stem

创建以default为parser的配置defcfg,并增加token映射,这里我们只关心email, url, host:

sbtest=# CREATE TEXT SEARCH CONFIGURATION defcfg (PARSER = default);
CREATE TEXT SEARCH CONFIGURATION
sbtest=# ALTER TEXT SEARCH CONFIGURATION defcfg ADD MAPPING FOR email,url,host WITH simple;
ALTER TEXT SEARCH CONFIGURATION

建好配置defcfg后,我们看看利用defcfg对文本进行处理的结果。这里使用to_tsvector函数,可以看到email,url,host都被识别出来了:

sbtest=# select to_tsvector('defcfg','xxx yyy xxx@taobao.com yyy@sina.com http://google.com/123 12345 ');
                              to_tsvector
-----------------------------------------------------------------------
 'google.com':4 'google.com/123':3 'xxx@taobao.com':1 'yyy@sina.com':2
(1 row)

在实际对表内的文本做全文搜索时,一般对目标列建立gin索引(也就是倒排索引,详情见官方文档),这样可以加快查询效率,具体操作如下:

sbtest=# CREATE TABLE t1(c1 text);
CREATE TABLE
sbtest=# CREATE INDEX c1_idx ON t1 USING gin(to_tsvector('defcfg', c1));
CREATE INDEX
sbtest=# \d t1
     Table "public.t1"
 Column | Type | Modifiers
--------+------+-----------
 c1     | text |
Indexes:
    "c1_idx" gin (to_tsvector('defcfg'::regconfig, c1))

这里我们插入2条文本,并做一些匹配:

sbtest=# INSERT INTO t1 VALUES('xxx yyy xxx@taobao.com yyy@sina.com http://google.com 12345');
INSERT 0 1
sbtest=# INSERT INTO t1 VALUES('xxx yyy xxx@gmail.com yyy@sina.com http://google.com 12345');
INSERT 0 1
sbtest=# select * from t1;
                             c1
-------------------------------------------------------------
 xxx yyy xxx@taobao.com yyy@sina.com http://google.com 12345
 xxx yyy xxx@gmail.com yyy@sina.com http://google.com 12345
(2 rows)

sbtest=# select * from t1 where to_tsvector('defcfg',c1) @@ 'google.com';
                             c1
-------------------------------------------------------------
 xxx yyy xxx@taobao.com yyy@sina.com http://google.com 12345
 xxx yyy xxx@gmail.com yyy@sina.com http://google.com 12345
(2 rows)

sbtest=# select * from t1 where to_tsvector('defcfg',c1) @@ to_tsquery('google.com & yyy@sina.com');
                             c1
-------------------------------------------------------------
 xxx yyy xxx@taobao.com yyy@sina.com http://google.com 12345
 xxx yyy xxx@gmail.com yyy@sina.com http://google.com 12345
(2 rows)

sbtest=# select * from t1 where to_tsvector('defcfg',c1) @@ to_tsquery('google.com & xxx@gmail.com');
                             c1
------------------------------------------------------------
 xxx yyy xxx@gmail.com yyy@sina.com http://google.com 12345
(1 row)

以上的操作都是针对英文,实际上对中文也是支持的,不过会稍微麻烦点,因为中文的token必须通过分词才能产生,所以需要先装分词的组件scws和zhparser,具体可以参考这篇博文

结语

本文对PG的全文搜索做了一个入门级的介绍,方便用户快速上手,如果需要对全文搜索作更深入的研究,建议阅读官方文档第12章

目录
相关文章
|
SQL 缓存 运维
PostgreSQL 事务号回卷分析
## XID 定义 xid 是个啥东西?xid 就是 PostgreSQL 里面的事务号,每个事物都会分配一个 xid。PostgreSQL 数据中每个元组头部都会保存着 插入 或者 删除 这条元组的事务号,即 xid,然后内核通过这个 xid 进行元组的可见性判断。简单理解,比如有两个事务,xid1=200,xid2=201,那么 xid1 中只能看到 t_xmin 200 的元组。 ```c
|
Java Spring
如何优雅的关闭线程池?
使用自创建的线程跟使用线程池有啥区别?提到线程池是不是脑海中闪现了创建线程池的那几个核心参数、工作流程、线程池的复用、拒绝机制、缓冲机制等,这些理论知识点想必也牢记许久了。虽然线程池支持在虚拟机进程接受到退出命令后可以进行shutdown。那么shutdown跟线程中断又有什么区别?在运行中的线程能否直接kill掉?我们能否监听关闭事件进行补偿?
2952 1
如何优雅的关闭线程池?
|
存储 SQL 算法
Mysql进阶索引篇02——InnoDB存储引擎的数据存储结构(一)
前面我们已经剖析了mysql中InnoDB与MyISAM索引的数据结构,了解了B+树的设计思想、原理,并且介绍了B+树与Hash结构、平衡二叉树、AVL树、B树等的区别和实际应用场景。 页和页之间并不一定在物理上相连,只是在逻辑上使用双向链表关联。指针、记录究竟是如何存储的呢?其实这就需要联系我们之前提到的行格式了。数据查找在页目录中二分法快速定位到槽,上面的过程都与页的内部结构相关,本文将详细的阐述。
Mysql进阶索引篇02——InnoDB存储引擎的数据存储结构(一)
|
Unix Linux iOS开发
Elasticsearch如何配置日志
Elasticsearch如何配置日志
|
网络协议 JavaScript 安全
第十一篇 前沿趋势与展望:深入探索GraphQL、RESTful API、WebSocket、SSE及QUIC与HTTP/3
第十一篇 前沿趋势与展望:深入探索GraphQL、RESTful API、WebSocket、SSE及QUIC与HTTP/3
436 1
|
关系型数据库 数据挖掘 数据库
PostgreSQL 模糊查询最佳实践 - (含单字、双字、多字模糊查询方法)
PostgreSQL 模糊查询最佳实践 - (含单字、双字、多字模糊查询方法)https://github.com/digoal/blog/blob/master/201704/20170426_01.md
14629 0
|
数据可视化 数据库
OpenSearch如何可视化分析数据?
OpenSearch如何可视化分析数据
506 0
|
Dubbo Java 应用服务中间件
maven 打包,打包依赖,并只打某个包下的依赖
1、先导出一个个maven项目下的某些包的源文件到target下的dubbo目录 注意:maven-jar-plugin插件智能打包本项目下的代码(自己写的代码) <plugin> <artifactId>maven-jar-plugin</artifactId> <executions> <execut
4604 0
|
存储 弹性计算 安全
OSS的网盘之路,最小化实现网盘分享
其实很多用户都知道OSS是非常适合用于作为网盘、附件系统的底层设施的,但是很多企业用户在沟通的过程中,其实没有很好的系统来支持OSS作为一个网盘去分发一些文件,例如企业像将介绍视频通过微信发布给有购买记录的客户,但是视频比较大也不方便直接通过微信发送视频,也不方便放到B站、优酷等视频平台中。 那么本文就介绍利用最小化资源实现OSS的网盘分享,让 ECS + OSS = 网盘。
1688 1
OSS的网盘之路,最小化实现网盘分享