前言
最近接到一個需求,要求實現搜索框的搜索結果可以按照中文排序,本人靈機一動,那不很簡單嗎,直接按照es自帶的sort功能處理下不就行了嗎?兩分鐘的代碼量,半天的喝茶時間,白賺半天的故事點,嘿嘿。
然而問題纔剛剛開始,多次測試結果表明,當文本以中文開頭,排序結果總是不正確。翻閱資料才知道這麼一回事:ES是用的unicode的字節碼做排序的。即先對字符(包括漢字)轉換成byte[]數組,然後對字節數組進行排序。這種排序規則對ASIC碼(英文)是有效的,但對於中文等亞洲國家的字符不適用。
作爲一個es小白,對這塊懂的還是太少了。沒辦法,只要了半天的故事點,這次真是把自己給坑了。還好是週末可以抽點時間研究,說幹就幹,搗鼓了一番,終於明白要實現拼音排序功能得依靠一個es插件-拼音分詞器。
切入正文,以下是個demo,演示了拼音排序的具體實現方案。
1.準備實驗數據
數據下載鏈接: https://pan.baidu.com/s/1X6qA_dI4Jz2V0ihDWhUjzQ 密碼:gjcv
數據來源於官網:https://download.elastic.co/demos/kibana/gettingstarted/accounts.zip。
因爲官網數據沒有中文字段,所以我做了修改,增加了fullName字段,並將其中200條數據的fullName設置爲中文,其餘數據的fullName設置爲原始數據的firstname+lastname。
2.準備拼音分詞器
需要下載拼音分詞器插件,下載地址: https://github.com/medcl/elasticsearch-analysis-pinyin/ ,根據當前elasticsearch版本找到對應的release版本進行下載。在elasticsearch安裝目錄的plugins下新建文件夾pinyin, 將分詞器壓縮包的所有文件解壓到這個pinyin文件夾。
注意:
1.如果你還不知道當前es版本,訪問 http://localhost:9200/ ,version對象的number就是版本。
2.如果你沒找到對應的release版本,就找離當前es版本最近的,然後修改plugin-descriptor.properties文件,設置elasticsearch.version爲自己的es版本號。
3.必須重啓es,如果插件安裝成功則不會報錯。
3.創建索引,設置mapping,導入數據並測試。
如果你不喜歡敲命令,則用postman來提交請求。我放上了每一步的截圖,並粘貼了每一步需要的請求參數。直接按照流程一步一步走即可。
如果習慣用macos,linux敲命令的同學,直接在終端通過curl來提交請求即可。命令我已經總結好,直接運行即可。(ps:windows對curl命令相當不友好,需要用轉義符 / 來轉義請求參數中的雙引號。)
-
使用postman
1.創建索引並設置分詞器
定義索引爲actor,使用了拼音分詞器,分詞器的各項參數功能見官方文檔,這裏只用到了部分參數。
URL和ReqeustBody:
http://localhost:9200/actor
{
"index" : {
"analysis" : {
"analyzer" : {
"pinyin_analyzer" : {
"tokenizer" : "my_pinyin"
}
},
"tokenizer" : {
"my_pinyin" : {
"type": "pinyin",
"keep_first_letter": false,
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_none_chinese_in_joined_full_pinyin": true,
"keep_none_chinese_in_first_letter": true,
"none_chinese_pinyin_tokenize": false,
"lowercase": true
}
}
}
}
}
2.設置mapping
定義了文檔類型爲account,且對fullName字段使用分詞器。
URL和RequestBody:
http://localhost:9200/actor/account/_mapping
{
"account": {
"properties": {
"fullName": {
"type": "string",
"analyzer": "pinyin_analyzer",
"fields": {
"keyword": {
"type": "string",
"ignore_above": 256
}
}
}
}
}
}
3.測試分詞器
直接調用es的分詞器接口_analyze,測試對文檔‘李雲龍’的分詞效果。
URL和RequestBody:
http://localhost:9200/actor/_analyze
{
"analyzer": "pinyin_analyzer",
"text": "李雲龍"
}
從返回結果可以看出分詞效果爲liyunlong,這確實是正確的,且tokens數組只返回一個元素,只有這種情況下,中文拼音排序纔是準的,否則不準。
4.導入實驗數據
URL:
http://localhost:9200/actor/account/_bulk
5.測試排序效果
URL和RequestBody:
http://localhost:9200/actor/account/_search
{
"from": 0,
"size": 50,
"sort": [
{
"fullName": {
"order": "desc",
"unmapped_type": "string"
}
}
]
}
部分返回結果如下圖:
可以看出,fullName的拼音以z開頭的數據排在了最前面,這恰好符合預期,證明實驗成功。
-
使用curl
1.創建索引並設置分詞器
curl -XPUT http://localhost:9200/actor -d '
{
"index": {
"analysis": {
"analyzer": {
"pinyin_analyzer": {
"tokenizer": "my_pinyin"
}
},
"tokenizer": {
"my_pinyin": {
"type": "pinyin",
"keep_first_letter": false,
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_none_chinese_in_joined_full_pinyin": true,
"keep_none_chinese_in_first_letter": true,
"none_chinese_pinyin_tokenize": false,
"lowercase": true
}
}
}
}
}'
2.創建mapping
curl -XPUT http://localhost:9200/actor/account/_mapping -d '
{
"account": {
"properties": {
"fullName": {
"type": "string",
"analyzer": "pinyin_analyzer",
"fields": {
"keyword": {
"type": "string",
"ignore_above": 256
}
}
}
}
}
}'
3.測試分詞器
curl -XPOST http://localhost:9200/actor/_analyze -d'{
"analyzer": "pinyin_analyzer",
"text": "李雲龍"
}'|json_pp
4.導入實驗數據
curl -XPOST 'localhost:9200/actor/account/_bulk?pretty' --data-binary @new_account.json
5.測試排序
curl -XPOST http://localhost:9200/actor/account/_search -d'
{
"from": 0,
"size": 50,
"sort": [
{
"fullName": {
"order": "desc",
"unmapped_type": "string"
}
}
]
}
'|json_pp
注意:json_pp用來美化json格式的輸出結果,如果你的環境沒有這個工具則去掉 | json_pp 這條指令即可。