最近接到一个需求,要求实现搜索框的搜索结果可以按照中文排序,本人灵机一动,那不很简单吗,直接按照es自带的sort功能处理下不就行了吗?两分钟的代码量,半天的喝茶时间,白赚半天的故事点,嘿嘿。
然而问题才刚刚开始,多次测试结果表明,当文本以中文开头,排序结果总是不正确。翻阅资料才知道这么一回事:ES是用的unicode的字节码做排序的。即先对字符(包括汉字)转换成byte[]数组,然后对字节数组进行排序。这种排序规则对ASIC码(英文)是有效的,但对于中文等亚洲国家的字符不适用。
作为一个es小白,对这块懂的还是太少了。没办法,只要了半天的故事点,这次真是把自己给坑了。还好是周末可以抽点时间研究,说干就干,捣鼓了一番,终于明白要实现拼音排序功能得依靠一个es插件-拼音分词器。
切入正文,以下是个demo,演示了拼音排序的具体实现方案。
数据下载链接: https://pan.baidu.com/s/1X6qA_dI4Jz2V0ihDWhUjzQ 密码:gjcv
数据来源于官网:https://download.elastic.co/demos/kibana/gettingstarted/accounts.zip。
因为官网数据没有中文字段,所以我做了修改,增加了fullName字段,并将其中200条数据的fullName设置为中文,其余数据的fullName设置为原始数据的firstname+lastname。
需要下载拼音分词器插件,下载地址: 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,如果插件安装成功则不会报错。
如果你不喜欢敲命令,则用postman来提交请求。我放上了每一步的截图,并粘贴了每一步需要的请求参数。直接按照流程一步一步走即可。
如果习惯用macos,linux敲命令的同学,直接在终端通过curl来提交请求即可。命令我已经总结好,直接运行即可。(ps:windows对curl命令相当不友好,需要用转义符 / 来转义请求参数中的双引号。)
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开头的数据排在了最前面,这恰好符合预期,证明实验成功。
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 这条指令即可。
由于是客户方是银行,es版本相当老,仍然是2.4.6版本,所以es的字段类型没有用到keyword和text,这种字段早期版本是不支持的,5.X 以后的版本才可以使用这两个类型。