一个线上问题引发的思考——elasticsearch8.x如何实现更精准的检索?

铭毅天下 铭毅天下     2023-01-06     273

关键词:

1、线上问题

——问题来自:死磕Elasticsearch 知识星球微信群

这个问题涉及到业务细节,至今没有定论。不过,该问题引发了我的思考。

2、我的一点思考

我们使用 Elasticsearch 到底用来做什么?

除了 Elasticsearch 早已不是10年前因“菜谱”而火出技术圈的搜索引擎组件,它早已不是“单兵作战”,而是 ELKB 形成的 Elastic Stack “行走江湖”。

但,至少技术选型涉及到大数据的检索几乎无一例外 Elasticsearch 都是“首发阵容”。

2.1 关于全文检索,用户更关注什么?

关于全文搜索,《这就是搜索引擎》张俊林博士从搜索引擎的角度阐述了用户的关注点,核心就是两个。

  • 其一:精准率;

  • 其二:召回率。

通俗点讲:

  • 精准率是站在用户角度,召回的数据贴合用户的预期,越准确越好。

当然,大数据时代的今天,单纯的字、词匹配早已跟不上时代的步伐,基于用户行为的推荐(如:抖音、网易云音乐)往往更吊起用户的胃口。

  • 而召回率是满足检索条件的语句都尽可能的召回,到底要什么,让用户在结果中二次再做选择。

这两种都有应用场景,无所谓谁对谁错。

提到 Elasticsearch 精准召回数据,先不谈“精准”,先说一下召回。

如下图所示,可以分两部分看:数据的写入过程、数据检索过程。

数据写入过程要比图中复杂,我们着重关注建立倒排索引的过程,因为后面我们要基于倒排索引做全文检索。

2.2 数据写入过程

写入的文本如下:

基于 ik_smart 分词后,倒排索引的中的分词词典如下所示:

2.3 数据检索过程

Elasticsearch 提到检索,这其实是一个大的概念,不信你看下面的脑图。

Elasticsearch 检索从大的角度可以看成黑盒,类似:Google、baidu 搜索框。用户搜索框输入内容,检索召回数据。

但是,技术人员的眼里看搜索,更关注用哪种类型搜索。这涉及到检索分类。

Elasticsearch 检索大致可以分为:全文检索、精准匹配检索、多表关联检索、组合检索、不常用检索。

精准匹配检索回答的是“是和否、存在或不存在”问题?比如:小明的手机号为:“13566668888”,多一位、少一位、错一位都不能被召回!完全满足检索条件就召回,不满足检索条件就不要召回。不存在中间情况。

而全文检索回答的是“相关度”的问题?如文章开头提到的“手表”、“手表带”、“表带”就有相关度,哪些数据该召回?谁优先被召回(也就是谁排在前面)。

match_phrase 和 match 等实现检索的机制是不一样的,“profile:true" API 能帮我们更精细的看到底层的实现。

简单点说:match_phrase 走的是短语检索匹配,而 match 走的是多字段拆解后的 term query 的 bool 语句组合体。

2.4 如何理解精准?

其实这个没有普适的标准,不同的业务系统是不一样的。

建议,结合业务需求、产品经理和技术经理、项目经理、核心技术人员共同敲定。

满足用户要求的“精准”才算是精准。

比如文章开头提到的“手表”,其实有多种理解?

  • 其一:只有“手表”两个字,没有任何其他,这种叫精准。

  • 其二:分词词典里有“手表”,就要召回,这种也叫精准。

  • 其三:只要文本里有就要召回,这也是某种意义的精准。比如:“南京市长江大桥”,搜索“江大桥”也要求召回。

等等......不一而足。

有了上面的思考,我们尝试解读一下开篇的问题。

3、Elasticsearch 8.X 更精准检索实现

如下示例,拿数据说话!

3.1 字词混合索引检索方案

  • 词索引——使用 ik_smart 分词。

  • 字索引——使用  standard 标准分词。

保留了 keyword 类型,便于方案二的精准字符匹配。

DELETE test-20220928
PUT test-20220928

  "mappings": 
    "properties": 
      "title": 
        "type": "text",
        "analyzer": "ik_smart",
        "fields": 
          "standard": 
            "type": "text",
            "analyzer": "standard"
          ,
          "keyword": 
            "type": "keyword"
          
        
      
    
  


POST test-20220928/_bulk
"index":"_id":1
"title":"手表带真好看"
"index":"_id":2
"title":"手表最近卖的不好,咋整"
"index":"_id":3
"title":"卡西欧手表不错哦"
"index":"_id":4
"title":"手表"

先看“手表带真好看”这个文档 ik_smart 的分词结果。

其他几个文档“2”,“3”,“4” 都包含手表的分词,大家可以自己验证,篇幅原因,没有截图。

如下检索是 bool 组合混合体。

对于:must 条件要求单字相连的多字(可以理解为短语,但不见得是有意义的短语,如:江大桥)必须满足,用 短语 match_phrase 进行检索。

对于:should 条件满足 ik_smart 分词存在结果,则召回数据,且极大的提升评分权重。

POST test-20220928/_search

  "query": 
    "bool": 
      "should": [
        
          "match_phrase": 
            "title": 
              "query": "手表",
              "boost": 50
            
          
        
      ],
      "must": [
        
          "match": 
            "title.standard": "手表"
          
        
      ]
    
  

明显看出来:包含手表要排在前面。

3.2 自定义评分实现精准检索

使用前提:针对是 keyword 类型。

大家记住:sort 排序、aggregation 聚合、script 脚本都只能针对 keyword 类型,text 类型都是不支持的,除非开启“fielddata”(必要性非常小,使用场景也小,不建议开启)。

如下脚本的含义,如果字段精准匹配,没有多余字符,则评分极高,设置为1000;如果字段以给定关键词开头,则评分高,设置为500;如果属于包含关系,则评分也较高,设置为100;如果没有包含,那评分为10。

POST test-20220928/_search

  "query": 
    "script_score": 
      "query": 
        "match_all": 
      ,
      "script": 
        "source": "if(doc['title.keyword'].value == params.keyword)  return 1000;  else if(doc['title.keyword'].value.startsWith(params.keyword)) return 500;  else if(doc['title.keyword'].value.contains(params.keyword))  return 100;   else  return 10;",
        "params": 
          "keyword": "手表"
        
      
    
  

相当于我们人工干预了评分,基于字段精准匹配的情况实现了评分的区分。这样,最先召回的结果数据就是我们最期望的精准匹配结果了。

4、小结

针对企业级实战问题,引发了思考,并根据思考尝试做了解答。

当然,这道业务题目会有具体的细节业务场景,还需要进一步沟通交流。

本文涉及的技术点都不复杂。包含如下:

  • 分词(中文分词器、默认分词器)

  • 组合分词(fields)

  • 组合检索

  • 排序(评分)+ 全文检索+召回

  • 自定义评分(自己定义的规则来进行数据的评分,进而将评分高的优先返回,排在前面进行返回!)

仅就本文的讨论和实现,相信你也遇到过类似问题,欢迎留言交流下你的思考!

 推荐

1探究 | 明明存在,怎么搜索不出来呢?

2干货 | Elasticsearch 检索类型选型指南

3、Elasticsearch 检索性能优化实战指南

4、如何从0到1打磨一门 Elasticsearch 线上直播课?

更短时间更快习得更多干货!

中国50%+Elastic认证专家出自于此!

在不确定的时代,寻求确定性!

比同事抢先一步学习进阶干货!

一个sql注释引发的线上问题(代码片段)

...查起来了.这里有一点要说一下,在上线前夕,产品临时添加一个新的需求,商品的搜索状态不可判断这 查看详情

批量处理性能瓶颈引发线上问题

需求描述 运营商给代理商发放奖励,后台处理功能。系统自动获取待处理的代理商数据,分批次处理数据,直至所有数据处理完成。业务逻辑描述 1、后台功能自动获取文件的第N~第N+100条数据,开始逐一处理。 2、每... 查看详情

mvc系列——一个异常消息传递引发的思考(代码片段)

原文:MVC系列——一个异常消息传递引发的思考前言:最近在某个项目里面遇到一个有点纠结的小问题,经过半天时间的思索和尝试,问题得到解决。在此记录一下解决的过程,以及解决问题的过程中对.net里面MVC异常处理的思考... 查看详情

mvc系列——一个异常消息传递引发的思考

前言:最近在某个项目里面遇到一个有点纠结的小问题,经过半天时间的思索和尝试,问题得到解决。在此记录一下解决的过程,以及解决问题的过程中对.net里面MVC异常处理的思考。都是些老生常谈的问题,不多说,直接上&ldqu... 查看详情

一个问题引发的思考

今天看到一个题,这是以前并没有去在意的,答案是80刚开始的时候只知道加不加括号的区别,但是为什么呢?#define:预编译预编译:又叫预处理。预编译不是编译,而是编译前的处理,预编译所... 查看详情

由爬虫引发的思考

前言  花了两天时间写一个简单的爬虫程序。目前所用的技术十分简单。就是获得目标页面的html文档内容,然后解析其中有用的内容。既没有实现模拟登陆,也没有任何防止反爬虫的措施,甚至没有使用多线程。不过在其中... 查看详情

由一个系统重构引发的思考

目录当时的状况&背景一、多人维护没有规范二、过分兼容老逻辑三、与国际化逻辑耦合严重四、历次漏洞改造带来的问题思考前一段时间在做某系统的重构,之所以要做这个重构,是因为之前该业务逻辑混乱,维... 查看详情

记线上jackson使用不当引发的血案(代码片段)

背景最近业务方反馈线上一个topic的数据延迟比较大,然后我查看了这个topic的数据是由一个Flink任务产生的,于是就找到了这个任务开始排查问题,发现这个任务是一个非常简单的任务,大致的逻辑是kafkasource->flatmap->filter->map-&... 查看详情

一个循环引发的思考

...函数需要解决的场景是这样的。在React中,需要渲染一个列表,列表中后端返回了一个数组格式的数据,需要前端做一个简单的展示。这时候就考虑后端修改接口返回值,但是问题来了,后端一般不会轻易去给... 查看详情

由一个emoji引发的思考(代码片段)

由一个emoji引发的思考从毕业以来,基本就一直在做移动端,但是一直就关于移动端的开发,各种适配问题的解决,在日常搬砖中处理了就过了,也没有把东西都沉淀下来,觉得甚是寒颜。现就一个小bug,让我们来了解一下我们... 查看详情

spring之loadtimeweaver——一个需求引发的思考

最近有个需求——记录应用中某些接口被调用的轨迹,说白了,记录下入参、出参等即可。我选用ApsectJ解决这个问题,前期讨论说在接口层埋点,但这样有个问题,代码侵入比较严重,需要修改每个需要关注的接口实现类。经... 查看详情

由findallreferences引发的思考。,

 今天在研究C#代码问题的时候遇到了一个Visual Studio的小问题。在Visual Studio2013中,使用Find All References功能不能找到同一类型不同版本的所有引用,具体情况请见下面例子。 为了更方便的展示这个问题,我... 查看详情

接口异步调用导致的一个低概率问题引发的思考。

最近5个月接触到的异步调用占工作以来接触到的一半以上,这些异步调用都是消费消息的方式。应用A在处理完业务后,需要调用应用B的接口做信息同步(记录数据或者更新数据),有两种方式:一般情况是采用同步方式,等待... 查看详情

一个小工具引发的两个思考(代码片段)

背景近期搞了一个小工具,主打的功能是辅助背单词,大致包含的功能如下图功能就不再赘述了,有兴趣的可以查看此repository。在此写一点在开发这个小工具期间,我的一些想法和感悟。有些地方说的也许不恰当... 查看详情

一个线上问题的思考:eureka注册中心集群如何实现客户端请求负载及故障转移?(代码片段)

前言先抛一个问题给我聪明的读者,如果你们使用微服务SpringCloud-Netflix进行业务开发,那么线上注册中心肯定也是用了集群部署,问题来了:你了解Eureka注册中心集群如何实现客户端请求负载及故障转移吗?可以先思考一分钟... 查看详情

两个多线程笔试问题引发的思考

...,那么a的终于结果可能是四条语句排列组合就可以(每一个程序的语句顺序不能颠倒)结果为4,13,15,26注:这里不存在写回的问题,由于a,b,c都是全局变量,两个线程共享这些变量。且程序中不存在自操作运算符,所以不存在写... 查看详情

一个典型的多表参与连接的复杂sql调优(sqltuning)引发的思考

...看崔华老师所著SQL优化一书时,看到他解决SQL性能问题的一个案例,崔华老师成功定位问题并进行了解决。这里,在崔华老师分析定位的基础上,做进一步分析和推理,以便大家一起研究探讨,下面简述该案例场景。1、发生性... 查看详情

typeof引发的思考

...博主遇到过类似的问题,有一句重点,‘status是window下的一个属性’。这引起了我的注意, 查看详情