PostgreSQL自帶有一個簡易的全文檢索引擎,能夠實現小規模數據量的全文檢索功能。本文咱們將引導介紹一下這個功能,對於小數據量的搜索這個功能是足夠使用的,而無需搭建額外的ES等重量級的全文檢索服務器。html
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
類型能夠無需強類型轉換(::tsvector
或to_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查詢條件並非簡單的正則,而是一組搜索術語,使用而且使用布爾操做符&
(AND)、|
(OR)和!
(NOT)來組合它們,還有短語搜索操做符<->
(FOLLOWED BY)。更詳細的語法參見此文檔。app
此外,PostgreSQL還提供了兩個相對簡化的版本plainto_tsquery
和phraseto_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
表示時,有必要建立一個觸發器在title
或body
改變時保證tsvector
列爲當前值。詳見文檔。- GIN 索引只存儲
tsvector
值的詞(詞位),而且不存儲它們的權重標籤。所以, 在使用涉及權重的查詢時須要一次在錶行上的從新檢查。
除了普通的ORDER BY
條件以外,PostgreSQL爲全文檢索提供了兩個可選的排序函數ts_rank([ weights float4[], ] vector tsvector, query tsquery [, normalization integer ]) returns float4
和ts_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默認的分詞字典中並不包含中文分詞字典,所以咱們必須手工引入。目前一個比較好的項目是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
這一類的控制分詞結果的選項,根據實際使用酌情開啓。