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的中文分詞功能必須使用擴(kuò)展插件。我通過百度找到了兩種中文分詞插件匾七,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ù)進(jìn)行配置坞淮。

另外個問題就是標(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)生影響谋币。由于公司有的表的詳情字段是通過富文本編輯器進(jìn)行編輯的症概,難免會出現(xiàn)各種標(biāo)點符號,所以這種替換的方法就不可取了诅蝶,幸好Zhparser有相關(guān)參數(shù)配置能夠忽略標(biāo)點符號的影響募壕。

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

集成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í)行時針對長詞進(jìn)行復(fù)合切分符隙,比如余杭區(qū)分詞時會被分成“余杭”和“余杭區(qū)”,不設(shè)置這條語句則分詞結(jié)果只有“余杭區(qū)”拱绑。

alter role all set zhparser.multi_short=on;

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

alter role all set zhparser.punctuation_ignore=on;

有兩種方法進(jìn)行全文檢索,第一種是在搜索的時候進(jìn)行分詞操作红省。第二種是將分詞的結(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字段進(jìn)行索引慎恒,也可以擴(kuò)展別的字段融柬。

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ù)庫表,里面加了將進(jìn)1000萬條相同的浙江某公司測試數(shù)據(jù)烙懦,只有幾十萬條的數(shù)據(jù)是正式數(shù)據(jù)赤炒,分詞的結(jié)果反而比like還要慢亏较,正式的數(shù)據(jù)也不大可能會出現(xiàn)1000萬條分詞相同的數(shù)據(jù)掩缓。如果真的出現(xiàn)了,我想總數(shù)據(jù)量應(yīng)該都上億了巡通,這種時候不能再在代碼層面上進(jìn)行優(yōu)化了舍哄,應(yīng)該對數(shù)據(jù)庫進(jìn)行分表分庫等。

like浙江
中文檢索浙江

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

第二種方法檢索浙江

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市捏顺,隨后出現(xiàn)的幾起案子纬黎,更是在濱河造成了極大的恐慌,老刑警劉巖拆座,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冠息,死亡現(xiàn)場離奇詭異,居然都是意外死亡躏碳,警方通過查閱死者的電腦和手機(jī)散怖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咬最,“玉大人,你說我怎么就攤上這事惑申∶猓” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵碗脊,是天一觀的道長橄妆。 經(jīng)常有香客問我,道長矢劲,這世上最難降的妖魔是什么慌随? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮丸逸,結(jié)果婚禮上剃袍,老公的妹妹穿的比我還像新娘。我一直安慰自己憔维,他們只是感情好畏邢,可當(dāng)我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著凶赁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪致板。 梳的紋絲不亂的頭發(fā)上咏窿,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機(jī)與錄音萝挤,去河邊找鬼根欧。 笑死,一個胖子當(dāng)著我的面吹牛酥泛,可吹牛的內(nèi)容都是我干的嫌拣。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼捶索,長吁一口氣:“原來是場噩夢啊……” “哼灰瞻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起酝润,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤袍祖,失蹤者是張志新(化名)和其女友劉穎谢揪,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凳鬓,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡患民,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了仅孩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡京腥,死狀恐怖公浪,靈堂內(nèi)的尸體忽然破棺而出船侧,到底是詐尸還是另有隱情,我是刑警寧澤镜撩,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布琐鲁,位于F島的核電站,受9級特大地震影響围段,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜适贸,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一涝桅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蕊肥,春花似錦蛤肌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爪膊。三九已至砸王,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間处硬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留疮方,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓疆栏,卻偏偏與公主長得像惫谤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子溜歪,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,864評論 2 354

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