全文检索问题:根据车牌号查询,检索结果跟预期不一致的问题的排查与解决方法

编程入门 行业动态 更新时间:2024-10-11 15:16:20

全文检索问题:根据车牌号查询,检索结果跟预期不一致的问题的排查与<a href=https://www.elefans.com/category/jswz/34/1770089.html style=解决方法"/>

全文检索问题:根据车牌号查询,检索结果跟预期不一致的问题的排查与解决方法

1.问题复现及原因

这是在生产环境遇到的问题。
由于生产环境的数据量太多,不方便进行操作,因此这里重新弄了一个索引来复现该问题,准备的索引和数据(已进行脱敏)如下:

# 为防止index已存在,可先进行删除
DELETE idx-susu-test-car-number# 设置index的settings和mappings配置
PUT idx-susu-test-car-number
{"settings": {"number_of_shards": 1,"number_of_replicas": 0,"analysis" : {"char_filter" : {"json_key_char_filter" : {"type" : "pattern_replace","pattern" : """("\w*")(\s*)(:)""","replacement" : ""}},"analyzer" : {"dmp_custom_json_ik_analyzer": {"char_filter": ["json_key_char_filter"],"tokenizer": "ik_max_word"}}}},"mappings": {"properties": {"requestData": {"analyzer": "dmp_custom_json_ik_analyzer","type": "text"}}}
}# 批量插入数据(这里伪造了车牌号可能出现的几种情形)
PUT idx-susu-test-car-number/_bulk?refresh=true
{ "index": { "_id": "1" }}
{ "requestData": """{"head":{"sign":"0b02feea339667e96c6aaaaaaaaaaa","systemSign":"dkgc","timeStamp":"1646780548613","version":"20191213"},"body":{"queryDate":"20220307","vehiclePlateNoAndColorList":["津C36267@白色","津C36285@黑色","津C36580@蓝色","津C36605@黄绿色","津C36633@黄色","津C37178@渐变绿色]}}"""}
{ "index": { "_id": "2" }}
{ "requestData": """{"head":{"sign":"1da02bc2e0945187bbbbbbbbbbbbbb","systemSign":"kfpt","timeStamp":"1646830874618","version":"20181023"},"body":{"endTime":1646830771000,"page":-1,"pageSize":-1,"radius":0,"startTime":1646830721000,"tplType":-1,"vehiclePlateNoAndColor":"津B2M936"}}"""}
{ "index": { "_id": "3" }}
{ "requestData": """{"head":{"sign":"1da02bc2e0945187bbbbbbbbbbbbbb","systemSign":"kfpt","timeStamp":"1646830874618","version":"20181023"},"body":{"endTime":1646830771000,"page":-1,"pageSize":-1,"radius":0,"startTime":1646830721000,"tplType":-1,"vehiclePlateNoAndColor":"车牌号在末尾津B2M937@黄绿色"}}"""}
{ "index": { "_id": "4" }}
{ "requestData": """{"head":{"sign":"1da02bc2e0945187bbbbbbbbbbbbbb","systemSign":"kfpt","timeStamp":"1646830874618","version":"20181023"},"body":{"endTime":1646830771000,"page":-1,"pageSize":-1,"radius":0,"startTime":1646830721000,"tplType":-1,"vehiclePlateNoAndColor":"津B2M938车牌号在开头"}}"""}
{ "index": { "_id": "5" }}
{ "requestData": """{"head":{"sign":"1da02bc2e0945187bbbbbbbbbbbbbb","systemSign":"kfpt","timeStamp":"1646830874618","version":"20181023"},"body":{"endTime":1646830771000,"page":-1,"pageSize":-1,"radius":0,"startTime":1646830721000,"tplType":-1,"vehiclePlateNoAndColor":"车牌号在中间津B2M938@蓝色车牌号在中间"}}"""}
{ "index": { "_id": "6" }}
{ "requestData": """{"head":{"sign":"1da02bc2e0945187bbbbbbbbbbbbbb","systemSign":"kfpt","timeStamp":"1646761070268","version":"20181023"},"body":{"endTime":0,"page":-1,"pageSize":-1,"startTime":0,"vehicleNoAndcolors":["鄂R21081@黄色"]}}"""}

注:

  1. 从索引的mappings定义中,可看出来该索引只有一个名为requestData的字段,数据类型是text,且使用了一个名为dmp_custom_json_ik_analyzer的分词器。
  2. 结合往该字段中插入的数据,可看到,往该字段中插入的数据,其实就是调用一个POST接口时的body入参。如下所示:
{"head": {"sign": "1da02bc2e0945187bbbbbbbbbbbbbb","systemSign": "kfpt","timeStamp": "1646830874618","version": "20181023"},"body": {"endTime": 1646830771000,"page": -1,"pageSize": -1,"radius": 0,"startTime": 1646830721000,"tplType": -1,"vehiclePlateNoAndColor": "车牌号在末尾津B2M937@黄绿色"}
}
  1. 结合settings中该分词器的定义,可以看出该分词器的功能为“先将输入的json串的key给去掉,然后对value值使用ik分词器进行切分”。再结合插入requestData字段的数据的格式,可知其实设计该分词器的初衷,就是想将json串中的value给提取出来,并使用IK分词器进行一个分词即可。

上面,已经定义了一个index,并往里面插入了6条数据,并且由倒数第2和倒数第3条数据可知,数据中是有津B2M938这么一个车牌号的,于是我们在requestData字段上指定该车牌号进行查询:

# 在requestData字段上查询关键字“津B2M938”,此时发现返回结果并非是用户所期望的结果
GET idx-susu-test-car-number/_search
{"query": {"match": {"requestData": {"query": "津B2M938"}}},"highlight": {"fields": {"requestData":{}}}
}

查询结果如下:

如上所示,返回的结果并不满足我们的要求,因为**id=6这一条数据也被查询出来了**!而从highlight的结果不难看出,这条数据之所以被查询出来,是因为它匹配上了b这个词项。
这是因为对于IK分词器来说,车牌号津B2M938并不是一个term,在使用分词器对其进行切分时,会得到如下多个token:

也正是因为如此,当使用津B2M938去进行检索时,数据中只要包含了 [“津”, “b2m938”, “b”, “2”, “m”, “938”] 这些token中的任意一个,则都会被作为结果返回回来(只不过相关度评分_score的高低有所不同而已)。

综上,不难看出发生该问题的本质原因,就是因为对于IK分词器来说,类似于津B2M938这样的车牌号,默认情况下IK分词器并不会将其识别为一个token,而是会将其进行进一步的切分

2.解决方法概述

解决方法有两种:

  1. 将车牌号加入到IK分词器的热词词库当中去
    该方法可以参考IK分词器在github上的官方描述。
    根据官方描述,可以有几种不同的方法去添加我们需要的热词——但是,无论是更新IK分词器的Dictionary Configuration,还是提供一个远程服务提供热更新,又或者是通过IK分词器的源码去满足热更新词库——这几种方法,无一例外,都需要重启ES服务
  2. 自定义分词器,从输入中提取出车牌号
    自定义分词器的具体步骤可看到ES官网。

综上,由于第一种方法需要重启服务器,动作有点儿大,肯定是有风险的,所以暂时不考虑,优先选择第二种方法。
而之所以能够使用第二种方法,是因为车牌号是有规律可循的,我们可以通过写一段Java的正则表达式,从一段文字中将车牌号识别出来,如下图:

(([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[警京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]{0,1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1})(@(白|蓝|黄|黑|绿)色)?)


那么,结合在自定义ES的分词器时可以使用正则表达式来进行处理,接下来将使用第二种方法来解决当前问题。具体查看下一篇博客《ElasticSearch 自定义分词器Analyzer示例》。

更多推荐

全文检索问题:根据车牌号查询,检索结果跟预期不一致的问题的排查与解决方法

本文发布于:2024-02-11 21:08:25,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1683494.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:解决方法   车牌号   全文

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!