PostgreSQL全文檢索簡介

PostgreSQL自帶有一個簡易的全文檢索引擎,能夠實現小規模數據量的全文檢索功能。本文咱們將引導介紹一下這個功能,對於小數據量的搜索這個功能是足夠使用的,而無需搭建額外的ES等重量級的全文檢索服務器。html

詳細的全文檢索功能請參見官方文檔。感謝PostgreSQL中文社區的翻譯文檔git

PostgreSQL的全文檢索入門

PG的全文檢索操做符是@@,當一個tsvector(文檔)和tsquery(條件)匹配時返回true,而且先後順序無影響:github

SELECT 'a fat cat sat on a mat and ate a fat rat'::tsvector @@ 'cat & rat'::tsquery;
 ?column?
----------
 t

SELECT 'fat & cow'::tsquery @@ 'a fat cat sat on a mat and ate a fat rat'::tsvector;
 ?column?
----------
 f

PostgreSQL 10開始,jsonb也支持全文檢索了。web

和普通的SQL查詢同樣,只要在WHERE條件中使用這個符號就表明使用全文檢索條件篩選文檔了。如:sql

SELECT title
FROM pgweb
WHERE to_tsvector('english', body) @@ to_tsquery('english', 'friend');

@@操做符支持隱式轉換,對於text類型能夠無需強類型轉換(::tsvectorto_tsvector(config_name, text)),因此這個操做符實際支持的參數類型是這樣的:json

tsvector @@ tsquery
tsquery  @@ tsvector
text @@ tsquery
text @@ text

NOTE: 實際使用中,建議使用to_tsvector(config_name, text)to_tsquery(config_name, text)這種顯式強類型轉換的形式。由於若是使用隱式轉換或::tsvector這種默認參數轉換,將使用default_text_search_config配置分詞,該配置默認是pg_catalog.simple,可能對於絕大多數查詢場景並不適用。所以推薦使用to_tsvector()to_tsquery()函數顯式強類型轉換,而且指明分詞字典,已實現更精確的查詢需求。服務器

關於tsquery

tsquery查詢條件並非簡單的正則,而是一組搜索術語,使用而且使用布爾操做符&(AND)、|(OR)和!(NOT)來組合它們,還有短語搜索操做符<->(FOLLOWED BY)。更詳細的語法參見此文檔app

此外,PostgreSQL還提供了兩個相對簡化的版本plainto_tsqueryphraseto_tsquery函數

plainto_tsquery(plainto_tsquery([ config regconfig, ] querytext text) returns tsquery)用戶將未格式化的text通過分詞以後,插入&符號轉爲tsquery:post

SELECT plainto_tsquery('english', 'The Fat Rats');
 plainto_tsquery 
-----------------
 'fat' & 'rat'

phraseto_tsquery(phraseto_tsquery([ config regconfig, ] querytext text) returns tsquery)行爲和plainto_tsquery行爲相似,可是分詞以後不是插入&而是<->(FOLLOWED BY):

SELECT phraseto_tsquery('english', 'The Fat Rats');
 phraseto_tsquery
------------------
 'fat' <-> 'rat'

關於索引

使用索引能夠加快全文檢索的速度。對於全文檢索來講,可選的索引類型是GIN(通用倒排索引)和GIST(通用搜索樹),官方文檔更推薦使用GIN索引。建立一個GIN索引的範例:

CREATE INDEX pgweb_idx ON pgweb USING GIN(to_tsvector('english', body));

也能夠是一個鏈接列:

CREATE INDEX pgweb_idx ON pgweb USING GIN(to_tsvector('english', title || ' ' || body));

還能夠單首創建一個tsvector列,爲這個列建立索引:

ALTER TABLE pgweb ADD COLUMN textsearchable_index_col tsvector;
UPDATE pgweb SET textsearchable_index_col =
     to_tsvector('english', coalesce(title,'') || ' ' || coalesce(body,''));

CREATE INDEX textsearch_idx ON pgweb USING GIN(textsearchable_index_col);

NOTE:

  • 建立一個基於 GIN(通用倒排索引)的索引,column必須是tsvector類型。因此須要對列進行顯式類型轉換。
  • 使用了to_tsvector()函數的雙參數版本指定了全文檢索配置,所以必須使用to_tsvector()相同全文檢索配置才能命中索引。比即,WHERE to_tsvector('english', body) @@ 'a & b' 可使用該索引,但WHERE to_tsvector(body) @@ 'a & b'不能。
  • 在使用一個單獨的列來存儲tsvector表示時,有必要建立一個觸發器在titlebody改變時保證tsvector列爲當前值。詳見文檔
  • GIN 索引只存儲 tsvector值的詞(詞位),而且不存儲它們的權重標籤。所以, 在使用涉及權重的查詢時須要一次在錶行上的從新檢查。

關於排序

除了普通的ORDER BY條件以外,PostgreSQL爲全文檢索提供了兩個可選的排序函數ts_rank([ weights float4[], ] vector tsvector, query tsquery [, normalization integer ]) returns float4ts_rank_cd([ weights float4[], ] vector tsvector, query tsquery [, normalization integer ]) returns float4,以便實現基於權重的排序。

SELECT title, ts_rank_cd(textsearch, query) AS rank
FROM apod, to_tsquery('neutrino|(dark & matter)') query
WHERE query @@ textsearch
ORDER BY rank DESC
LIMIT 10;
                     title                     |   rank
-----------------------------------------------+----------
 Neutrinos in the Sun                          |      3.1
 The Sudbury Neutrino Detector                 |      2.4
 A MACHO View of Galactic Dark Matter          |  2.01317
 Hot Gas and Dark Matter                       |  1.91171
 The Virgo Cluster: Hot Plasma and Dark Matter |  1.90953
 Rafting for Solar Neutrinos                   |      1.9
 NGC 4650A: Strange Galaxy and Dark Matter     |  1.85774
 Hot Gas and Dark Matter                       |   1.6123
 Ice Fishing for Cosmic Neutrinos              |      1.6
 Weak Lensing Distorts the Universe            | 0.818218

此外,對於PostgreSQL 9.6以上的版本還可使用RUM index排序。(注意,這個是擴展,默認不包含)。

參考文檔: http://www.postgres.cn/docs/10/textsearch-controls.html#TEXTSEARCH-RANKING

PostgreSQL中文全文檢索

PostgreSQL默認的分詞字典中並不包含中文分詞字典,所以咱們必須手工引入。目前一個比較好的項目是zhparser,同時這個插件也是阿里雲的RDS默認包含的。安裝和啓用沒什麼好說的。值得一提的是分詞配置參數。

CREATE EXTENSION以後,必須配置分詞參數才能正確進行分詞和查找,不然什麼都查不到。官方文檔提供的一個配置策略是:

CREATE TEXT SEARCH CONFIGURATION testzhcfg (PARSER = zhparser);
ALTER TEXT SEARCH CONFIGURATION testzhcfg ADD MAPPING FOR n,v,a,i,e,l WITH simple;

n,v,a,i,e,l這幾個字母分別表示一種token策略,只啓用了這幾種token mapping,其他則被屏蔽。具體支持的參數和含義能夠用\dFp+ zhparser顯示:

postgres=# \dFp+ zhparser 
      Text search parser "public.zhparser"
     Method      |    Function     | Description 
-----------------+-----------------+-------------
 Start parse     | zhprs_start     | 
 Get next token  | zhprs_getlexeme | 
 End parse       | zhprs_end       | 
 Get headline    | prsd_headline   | (internal)
 Get token types | zhprs_lextype   | 

Token types for parser "public.zhparser"
 Token name |      Description       
------------+------------------------
 a          | adjective,形容詞
 b          | differentiation,區別詞
 c          | conjunction,連詞
 d          | adverb,副詞
 e          | exclamation,感嘆詞
 f          | position,方位詞
 g          | root,詞根
 h          | head,前鏈接成分
 i          | idiom,成語
 j          | abbreviation,簡稱
 k          | tail,後鏈接成分
 l          | tmp,習用語
 m          | numeral,數詞
 n          | noun,名詞
 o          | onomatopoeia,擬聲詞
 p          | prepositional,介詞
 q          | quantity,量詞
 r          | pronoun,代詞
 s          | space,處所詞
 t          | time,時語素
 u          | auxiliary,助詞
 v          | verb,動詞
 w          | punctuation,標點符號
 x          | unknown,未知詞
 y          | modal,語氣詞
 z          | status,狀態詞
(26 rows)

WITH simple表示詞典使用的是內置的simple詞典,即僅作小寫轉換。根據須要能夠靈活定義詞典和token映射,以實現屏蔽詞和同義詞歸併等功能。

好比咱們看下面這個例子:

-- 如下這個全文檢索配置來源於zhparser文檔的範例
ALTER TEXT SEARCH CONFIGURATION zhparser ADD MAPPING FOR n,v,a,i,e,l WITH simple;
postgres=# SELECT to_tsquery('zhparser', '江淮杜伏威');
     to_tsquery     
--------------------
 '杜' & '伏' & '威'
(1 row)

能夠看到江淮這個詞組在查詢的時候被忽略了,咱們啓用j(abbreviation,簡稱)再看看結果:

postgres=# ALTER TEXT SEARCH CONFIGURATION zhparser ADD MAPPING FOR j WITH simple;
ALTER TEXT SEARCH CONFIGURATION
postgres=# SELECT to_tsquery('zhparser', '江淮杜伏威');
         to_tsquery          
-----------------------------
 '江淮' & '杜' & '伏' & '威'
(1 row)

因此實際使用中要設置合理的token types,過少將致使搜索結果不許確,過多將致使性能降低。此外,還有一些諸如短詞複合: zhparser.multi_short = f這一類的控制分詞結果的選項,根據實際使用酌情開啓。

相關文章
相關標籤/搜索