PostgreSQL 中文全文檢索 (使用zhparser)


前言:
PostgreSQL默認分詞是按照空格及各種標點符號來分詞,但是對於國內更多的是中文文章,按照默認分詞方式不符合中文的分詞方式。檢索了網上很多文章,發現使用最多的是zhparser,並且是開源的,完成能夠滿足檢索需求。

前置:
centOS7
PostgreSQL11
SCWS(下載地址:http://www.xunsearch.com/scws/down/scws-1.2.2.tar.bz2)
zhparser(GitHub地址:https://github.com/amutu/zhparser)
postgresql-devel

zhparser支持PostgreSQL 9.2及以上版本,請確保你的PG版本符合要求。 對於REDHAT/CentOS Linux系統,
請確保安裝了相關的庫和頭文件,一般它們在postgresql-devel軟件包中。
  • 1
  • 2

安裝:
1、首先需要安裝postgresql-devel

yum install postgresql-devel 或者 yum install postgresql11-devel (我這里使用的第一種,沒有指定版本號)
  • 1

在這里插入圖片描述
如果不安裝此軟件包的話,在編譯zhparser文件時會報pg_config: Command not found錯誤
在這里插入圖片描述
2、安裝SCWS
因為zhparser是基於SCWS(簡易中文分詞系統)開發的。所以必須首先安裝SCWS。

·2.1 創建一個文件夾(這里不詳細寫創建步驟,這里我統一將SCWS和zhparser放在了/home/pgsql/下面)
·2.2 進入創建的文件夾
在這里插入圖片描述
·2.3 下載SCWS

	wget http://www.xunsearch.com/scws/down/scws-1.2.3.tar.bz2
  • 1

在這里插入圖片描述
·2.3 安裝

[root@pg1 opt]#  tar -jxvf scws-1.2.3.tar.bz2
[root@ecs-f49b-0003 pgsql]# cd scws-1.2.3 ; ./configure ; make install

注意:在FreeBSD release 10及以上版本上運行configure時,需要增加--with-pic選項。
如果是從github上下載的scws源碼需要先運行以下命令生成configure文件: 
touch README;aclocal;autoconf;autoheader;libtoolize;automake --add-missing
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3、安裝zhparser

·3.1 這里可以參考GitHub上的步驟,https://github.com/amutu/zhparser ,忽略創建extension步驟,這里需要按照下面步驟檢查和創建extension ,與GitHub上寫的略有不同(建議編譯和安裝zhparser時采用指定PG_CONFIG 的方式指定版本編譯擴展,避免直接使用make && make install 編譯結果與安裝的數據庫版本不一致)。

ps:因為在安裝PostgreSQL時,我是用YUM方式安裝的,clang並沒有被加入,所以在指定 PG_CONFIG 
版本編譯擴展時可能會報--新增插件異常(clang: Command not found),或者直接編譯不通過。
這里可以參考(https://yq.aliyun.com/articles/698119 ),關閉PostgreSQL新增插件的bc編譯項來解決:

vim	 /usr/pgsql-11/lib/pgxs/src/makefiles/pgxs.mk
注釋所有的with_llvm相關
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在這里插入圖片描述
下面是我編譯后的結果,編譯的文件沒有直接放到pgsql中,下面圖片中標注中的是最后編輯后文件的放置路徑。(如果未出現相同的問題,那么就可以跳過文件整理步驟直接去創建extension。)
在這里插入圖片描述
因為編譯后的文件不在應該在的文件夾中,這里就需要手動去將文件移動至指定目錄:
/usr/lib64/pgsql/zhparser.so → /usr/pgsql-11/lib (/usr/pgsql-11/是PostgreSQL安裝后路徑,如果PostgreSQL是使用的YUM安裝方式,路徑應該是一樣)
在這里插入圖片描述
/usr/share/pgsql/extension/ → /usr/pgsql-11/share/extension
在這里插入圖片描述
/usr/share/pgsql/tsearch_data/ → /usr/pgsql-11/share/tsearch_data
在這里插入圖片描述

·3.2 創建extension
整理好這些文件后,就可以繼續創建extension了。
·3.2.1 切換到postgres賬戶

	su - postgres
  • 1

在這里插入圖片描述
·3.2.2 安裝擴展(每新建一個數據庫都需要執行這一步;這里我直接使用的已有的一個測試數據庫)

#查詢已有的解析器
knowledge=# \dFp
  • 1
  • 2

在這里插入圖片描述
這里可以看到就只有一個默認的解析器。下面開始創建擴展:

knowledge=# create extension zhparser;
  • 1

在這里插入圖片描述
這里再來看一次已有的解析器:
在這里插入圖片描述
這樣解析器就添加到當前數據庫了。但是此時還是不能用,還需要創建使用zhparser作為解析器的全文搜索的配置,也就是需要給zhparser解析器取一個在sql里面可以使用的名字。這里測試取的是(‘zh’)。

在這里插入圖片描述
再繼續向全文搜索配置中增加token映射:

knowledge=# ALTER TEXT SEARCH CONFIGURATION zh ADD MAPPING FOR n,v,a,i,e,l,j WITH simple;
  • 1

在這里插入圖片描述
這樣就算是安裝配置完成了。下面來測試一下效果:
在這里插入圖片描述

追加問題解決

最近使用中遇到一個查詢時的問題:
例如一篇文章中有“…合江縣…”,分詞分出來的就是合江縣,如果查詢的時候輸入的是合江,那么是匹配不到的,所以這里需要在繼續設置一下全文檢索的復合等級;在命令行中使用上一節中介紹的 scws 命令測試分詞配置,如我認為復合等級為 7 時分詞結果最好,則我在 postgresql.conf添加配置:

zhparser.multi_short = true #短詞復合: 1
zhparser.multi_duality = true  #散字二元復合: 2
zhparser.multi_zmain = true  #重要單字復合: 4
zhparser.multi_zall = false  #全部單字復合: 8
  • 1
  • 2
  • 3
  • 4

在這里插入圖片描述
在這里插入圖片描述

然后重啟數據庫,將之前的分詞重新全部生成一遍,在來查詢,問題解決。

OK!!!這樣就可以滿足當前需求了,完工!!!

 

SQL

查詢中我們可以使用最簡單的 SELECT * FROM table WHERE to_tsvector('parser_name', field) @@ 'word' 來查詢 field 字段分詞中帶有 word 一詞的數據;

使用 to_tsquery() 方法將句子解析成各個詞的組合向量,如 國家大劇院 的返回結果為 '國家' & '大劇院' & '大劇' & '劇院' ,當然我們也可以使用 & | 符號拼接自己需要的向量;在查詢 長句 時,可以使用 SELECT * FROM table WHERE to_tsvector('parser_name', field) @@ to_tsquery('parser_name','words')

有時候我們想像 MySQL 的 SQL_CALC_FOUND_ROWS 語句一樣同步返回結果條數,則可以使用 SELECT COUNT(*) OVER() AS score FROM table WHERE ...,PgSQL 會在每一行數據添加 score 字段存儲查詢到的總結果條數;

到這里,普通的全文檢索需求已經實現了。


優化

我們接着對分詞效果和效率進行優化:

存儲分詞結果

我們可以使用一個字段來存儲分詞向量,並在此字段上創建索引來更優地使用分詞索引:

ALTER TABLE table ADD COLUMN tsv_column tsvector;           // 添加一個分詞字段
UPDATE table SET tsv_column = to_tsvector('parser_name', coalesce(field,''));   // 將字段的分詞向量更新到新字段中
CREATE INDEX idx_gin_zhcn ON table USING GIN(tsv_column);   // 在新字段上創建索引
CREATE TRIGGER trigger_name BEFORE INSERT OR UPDATE  ON table FOR EACH ROW EXECUTE PROCEDURE
tsvector_update_trigger(tsv_column, 'parser_name', field); // 創建一個更新分詞觸發器

這樣,再進行查詢時就可以直接使用 SELECT * FROM table WHERE tsv_column @@ 'keyword' 了。

這里需要注意,這時候在往表內插入數據的時候,可能會報錯,提示指定 parser_name 的 schema, 這時候可以使用 \dF 命令查看所有 text search configuration 的參數:

               List of text search configurations
   Schema   |    Name    |              Description
------------+------------+---------------------------------------
 pg_catalog | english    | configuration for english language
 public     | myparser   |

注意 schema 參數,在創建 trigger 時需要指定 schema, 如上面,就需要使用 public.myparser

添加自定義詞典

我們可以在網上下載 xdb 格式的詞庫來替代默認詞典,詞庫放在 share/tsearch_data/ 文件夾下才能被 PgSQL 讀取到,默認使用的詞庫是 dict.utf8.xdb。要使用自定義詞庫,可以將詞庫放在詞庫文件夾后,在 postgresql.conf 配置 zhparser.extra_dict="mydict.xdb" 參數;

當我們只有 txt 的詞庫,想把這個詞庫作為默認詞庫該怎么辦呢?使用 scws 帶的scwe-gen-dict 工具或網上找的腳本生成 xdb 后放入詞庫文件夾后,在 PgSQL 中分詞一直報錯,讀取詞庫文件失敗。我經過多次實驗,總結出了一套制作一個詞典文件的方法:

  1. 准備詞庫源文件 mydict.txt:詞庫文件的內容每一行的格式為詞 TF IDF 詞性,詞是必須的,而 TF 詞頻(Term Frequency)、IDF 反文檔頻率(Inverse Document Frequency) 和 詞性 都是可選的,除非確定自己的詞典資料是對的且符合 scws 的配置,不然最好還是留空,讓 scws 自已確定;
  2. 在 postgresql.conf 中設置 zhparser.extra_dicts = "mydict.txt" 同時設置 zhparser.dict_in_memory = true
  3. 命令行進入 PgSQL,執行一條分詞語句 select to_tsquery('parser', '隨便一個詞') ,分詞會極慢,請耐心(請保證此時只有一個分詞語句在執行);
  4. 分詞成功后,在/tmp/目錄下找到生成的 scws-xxxx.xdb 替換掉 share/tsearch_data/dict.utf8.xdb
  5. 刪除剛加入的 extra_dicts dict_in_memory 配置,重啟服務器。

擴展

由於查詢的是 POI 的名稱,一般較短,且很多詞並無語義,又考慮到用戶的輸入習慣,一般會輸入 POI 名稱的前幾個字符,而且 scws 的分詞准確率也不能達到100%,於是我添加了名稱的前綴查詢來提高查詢的准確率,即使用 B樹索引 實現 LIKE '關鍵詞%' 的查詢。這里需

這里要注意的是,創建索引時要根據字段類型配置 操作符類,不然索引可能會不生效,如在 字段類型為 varchar 的字段上創建索引需要使用語句CREATE INDEX idx_name ON table(COLUMN varchar_pattern_ops),這里的 varcharpatternops 就是操作符類,操作符類的介紹和選擇可以查看文檔:11.9. 操作符類和操作符族

自此,一個良好的全文檢索系統就完成了。


總結

簡單的數據遷移並不是終點,后續要做的還有很多,如整個系統的數據同步、查詢效率優化、查詢功能優化(添加拼音搜索、模糊搜索)等。特別是查詢效率,不知道是不是我配置有問題,完全達不到那種 E級毫秒 的速度,1kw 的數據效率在進行大結果返回時就大幅下降(200ms),只好老老實實地提前進行了分表,目前百萬級查詢速度在 20ms 以內,優化還有一段路要走。

不過這次倒是對 技術的“生態”有了個更深的體會,這方面 PgSQL 確實和 MySQL 差遠了,使用 MySQL 時再奇葩的問題都能在網上快速找到答案,而 PgSQL 就尷尬了,入門級的問題搜索 stackoverflow 來來回回就那么幾個對不上的回答。雖然也有阿里的“德哥”一樣的大神在辛苦布道,但用戶的數量才是根本。不過,隨着 PgSQL 越來越完善,使用它的人一定會越來越多的,我這篇文章也算是為 PgSQL 加溫了吧,哈哈~希望能幫到后來的使用者。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM