PostgreSQL全文檢索指北

在項目中如果要實現(xiàn)全文檢索,最普通的方法就是通過數(shù)據(jù)庫查詢語句like '%keywords%',但是這種方法在數(shù)據(jù)量多的情況下效率很低。目前最主流的方法是集成一個搜索引擎,通過調(diào)用相關(guān)API來實現(xiàn)創(chuàng)建索引以及搜索的功能。目前比較常見的開源搜索軟件:Lucene、Solr、ElasticSearch、Sphinx、CoreSeek。

對于一個比較大型的項目來說,集成一個搜索引擎是必須的。但是對于一些比較小型的項目,不想使用like語句,集成一個搜索引擎又需要很大的成本。如果該項目的數(shù)據(jù)庫是PostgreSQL,那么就可以使用PostgreSQL的全文檢索功能。不過PostgreSQL的全文檢索功能效率是比不上那些開源搜索軟件的。

遺憾的是,PostgreSQL默認(rèn)沒有中文分詞功能,因此要實現(xiàn)PostgreSQL的中文分詞功能必須使用擴展插件。我通過百度找到了兩種中文分詞插件,pg_jieba和Zhparser。pg_jieba是基于結(jié)巴中文分詞的,zhparser是基于SCWS。

pg_jieba:https://github.com/jaiminpan/pg_jieba

Zhparser:https://github.com/amutu/zhparser

測試過程中發(fā)現(xiàn)pg_jieba和Zhparser都能夠比較方便的實現(xiàn)全文檢索的功能。

不過碰到特殊的分詞會出現(xiàn)問題,比如數(shù)據(jù)庫里的公司地址字段會出現(xiàn)浙江省余杭市,浙江省余杭區(qū),浙江省余杭這種情況,分詞的結(jié)果分別是“余杭市”、“余杭區(qū)”以及“余杭”。搜索“余杭”關(guān)鍵字只會出現(xiàn)浙江省余杭的相關(guān)數(shù)據(jù),余杭市和余杭區(qū)的數(shù)據(jù)并不會出現(xiàn)。這種情況,pg_jieba可以通過使用自定義的分詞詞典來解決,Zhparser也有相關(guān)功能。但是構(gòu)建分詞詞典比較麻煩,有好多分詞結(jié)果需要處理。我只是想實現(xiàn)一個簡單的全文檢索功能,幸好Zhparser有相關(guān)的參數(shù)進行配置。

另外個問題就是標(biāo)點符號對分詞結(jié)果的影響。pg_jieba的分詞結(jié)果是不受標(biāo)點符號影響的,而Zhparser會受到嚴(yán)重影響。

德哥 PostgreSQL 如何高效解決 按任意字段分詞檢索的問題 - case1

德哥的這篇文章中有寫關(guān)于SCWS分詞的問題,逗號會對分詞結(jié)果產(chǎn)生影響,文中采用的方法是使用replace函數(shù)將逗號替換成空格。實際測試中,我發(fā)現(xiàn)+ - . \ ;等符號也會對分詞結(jié)果產(chǎn)生影響。由于公司有的表的詳情字段是通過富文本編輯器進行編輯的,難免會出現(xiàn)各種標(biāo)點符號,所以這種替換的方法就不可取了,幸好Zhparser有相關(guān)參數(shù)配置能夠忽略標(biāo)點符號的影響。

再加上Zhparser的star數(shù)比pg_jieba的要高(從眾心理不可取),所以我最后選用了Zhparser來進行中文分詞。

集成Zhparser中文分詞插件

1.安裝中文分詞插件SCWS和Zhparser

SCWS:https://github.com/hightman/scws

Zhparser:https://github.com/amutu/zhparser

2.安裝完成之后,執(zhí)行以下三條sql語句來啟用中文分詞,生成一個叫testzhcfg的解釋器。

CREATE EXTENSION zhparser;

CREATE TEXT SEARCH CONFIGURATION testzhcfg(PARSER=zhparser);

ALTER TEXT SEARCH CONFIGURATION testzhcfg ADD MAPPING FOR n,v,a,i,e,l WITH simple;

3.通過下面這條語句設(shè)定分詞執(zhí)行時針對長詞進行復(fù)合切分,比如余杭區(qū)分詞時會被分成“余杭”和“余杭區(qū)”,不設(shè)置這條語句則分詞結(jié)果只有“余杭區(qū)”。

alter role all set zhparser.multi_short=on;

通過下面這條語句來使分詞時忽略標(biāo)點符號的影響。

alter role all set zhparser.punctuation_ignore=on;

有兩種方法進行全文檢索,第一種是在搜索的時候進行分詞操作。第二種是將分詞的結(jié)果存儲到新增的列當(dāng)中,通過觸發(fā)器更新這個字段,然后在該字段上建索引。第一種方法的優(yōu)點是創(chuàng)建索引簡單,占用空間少,缺點是每次執(zhí)行查詢都需要調(diào)用to_tsvector函數(shù)來確保索引值關(guān)聯(lián)。第二種方法的優(yōu)點是查詢的速度快(無需每次調(diào)用to_tsvector函數(shù)),缺點是需要新增一個列,消耗更多的存儲空間。

以zl_company表為例

第一種方法

create index idx_zl_company on zl_company using gin(to_tsvector('testzhcfg',coalesce(name,'')||coalesce(address,'')));//創(chuàng)建gin索引

使用函數(shù)coalesce來確保字段為NULL的也可以建立索引。該語句對name和address字段進行索引,也可以擴展別的字段。

explain analyse select * from zl_company where to_tsvector('testzhcfg',coalesce(name,'')||coalesce(address,'')) @@ to_tsquery('testzhcfg','余杭');

通過這條語句就可以查詢“余杭”關(guān)鍵字對應(yīng)的結(jié)果,如果有多個關(guān)鍵字,比如要查詢余杭的公司可以通過to_tsquery('testzhcfg','余杭&公司')。

第二種方法

alter table zl_company add column tsv tsvector;//新建字段類型是tsvector

update zl_company set tsv = to_tsvector('testzhcfg',coalesce(name,'')||coalesce(address,''));//更新該字段

create index idx_zl_company on zl_company using gin(tsv);//創(chuàng)建gin索引

explain analyse select * from zl_company where tsv @@ to_tsquery('testzhcfg','余杭');

還需要創(chuàng)建一個觸發(fā)器來更新tsv字段的值

create trigger tsvectorupdate before insert or update

on zl_company for each row execute procedure

tsvector_update_trigger(tsv, 'testzhcfg', name, address);

與like的效率對比

以1000萬條數(shù)據(jù)為例

like余杭
中文檢索余杭

測試之后發(fā)現(xiàn)不管搜索的關(guān)鍵字是什么,like的搜索速度比較穩(wěn)定,而PostgreSQL中文檢索的搜索速度跟該搜索關(guān)鍵字的數(shù)據(jù)量有很大的關(guān)系。有個很尷尬的情況,這是我從正式庫里復(fù)制的一個數(shù)據(jù)庫表,里面加了將進1000萬條相同的浙江某公司測試數(shù)據(jù),只有幾十萬條的數(shù)據(jù)是正式數(shù)據(jù),分詞的結(jié)果反而比like還要慢,正式的數(shù)據(jù)也不大可能會出現(xiàn)1000萬條分詞相同的數(shù)據(jù)。如果真的出現(xiàn)了,我想總數(shù)據(jù)量應(yīng)該都上億了,這種時候不能再在代碼層面上進行優(yōu)化了,應(yīng)該對數(shù)據(jù)庫進行分表分庫等。

like浙江
中文檢索浙江

采用第二種方法后速度有明顯提升。

第二種方法檢索浙江

如果PostgreSQL的版本為9.6以上,還能采用rum索引,速度比gin索引更快。詳情看:PostgreSQL 全文檢索加速 快到?jīng)]有朋友 - RUM索引接口(潘多拉魔盒)

為Zhparser添加自定義中文分詞詞典的可以看:如何使用中文分詞和自定義中文分詞詞典

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內(nèi)容