概要

本篇介绍怎样在全文字段中搜索到最相关的文档,包含手动控制搜索的精准度,搜索条件权重控制。

手动控制搜索的精准度

搜索的两个重要维度:相关性(Relevance)和分析(Analysis)。

相关性是评价查询条件与结果的相关程度,并对相关程度进行排序,一般使用TF/IDF方法。

分析是指将索引文档与查询条件规范化的一个过程,目的是建立倒排索引时,尽可能地提升召回率。

match查询原理

匹配查询match是核心查询语法,它的主要应用场景就是全文搜索,我们举一个示例:

GET /music/children/_search
{
"query": {
"match": {
"name": "wake"
}
}
}

Elasticsearch执行的步骤:

  1. 检索字段类型:match的字段name为text类型,是一个analyzed的字段,那么查询条件的字符串也应该被analyzed。
  2. 分析查询字符串:将查询字符串"wake"传入分词器中(与mapping的分词器一致),因为只有一个单词,所以match最终执行的是单个底层的term查询。
  3. 查找匹配文档:用term倒排索引中查找wake然后获取一组包含该词的文档。
  4. 为每个文档评分:用term查询计算每个文档相关度评分,即TF、IDF、length norm算法。

得到的结果如下:

{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.2876821,
"hits": [
{
"_index": "music",
"_type": "children",
"_id": "2",
"_score": 0.2876821,
"_source": {
"id": "a810fad4-54cb-59a1-9b7a-82adb46fa58d",
"author": "John Smith",
"name": "wake me, shark me",
"content": "don't let me sleep too late, gonna get up brightly early in the morning",
"language": "english",
"tags": "enlighten",
"length": 55,
"isRelease": true,
"releaseDate": "2019-12-21"
}
}
]
}
}

因为样本数据的问题,暂时只有一条文档匹配。

搜索name中包含"you"或"sunshine"的文档

GET /music/children/_search
{
"query": {
"match": {
"name": "you sunshine"
}
}
}

搜索name中包含"you"和"sunshine"的文档

GET /music/children/_search
{
"query": {
"match": {
"name": {
"query": "you sunshine",
"operator": "and"
}
}
}
}

搜索精准度控制的第一步:使用and关键字。如果希望所有搜索关键字都要匹配,可以用and来实现。

搜索"you"、"my"、"sunshine"、"teeth" 4个关键字中,至少包含3个的文档

GET /music/children/_search
{
"query": {
"match": {
"name": {
"query": "you my sunshine teeth",
"minimum_should_match": "75%"
}
}
}
}

搜索精准度控制的第二步:指定至少匹配其中的多少个关键字,才能作为结果返回

bool组合多个搜索条件

用bool组合,可以完成更加个性化的搜索需求,例如我们查找名称包含"sunshine",但不包含"teeth",允许出现"you"、"my"关键字,示例如下:

GET /music/children/_search
{
"query": {
"bool": {
"must": { "match": { "name": "sunshine" }},
"must_not": { "match": { "name": "teeth" }},
"should": [
{ "match": { "name": "my" }},
{ "match": { "name": "you" }}
]
}
}
}

should对相关度评分计算的影响

以上面的bool为例子,我们只讨论匹配的文档,按自己的理解,对文档进行粗略排名:

  1. 最符合搜索条件的

文档中同时包含should中的"my"、"you"两个关键字。

  1. 很符合搜索条件的

文档中包含should中的"my"或"you"两个关键字的其中一个。

  1. 符合搜索条件的

文档中不包含should中的"my"和"you"。

像must not这种硬性条件,不匹配都不会出现在结果集里,主要起到排除文档作用,不参与评分计算。但should也能影响相关度评分,匹配得越多,评分就越高。

bool查询会为每个文档计算相关度评分_score ,再将所有匹配的 must 和 should 语句的分数 _score 求和,最后除以 must 和 should 语句的总数。

这里不详细讲解评分计算的具体细节和分数,了解should对其有影响即可。

should匹配原则

如果查询条件中有must存在,那么should匹配的数量不作要求;如果没有must,则should必须要匹配一个,要不然全是should条件的,所有文档都能匹配,就失去了搜索的意义。

如果带上minimum_should_match,那么就能做更精细的控制,可以指定必须要匹配几个should,才能返回结果集,如下两个示例是等同的:

GET /music/children/_search
{
"query": {
"match": {
"name": {
"query": "you my sunshine teeth",
"minimum_should_match": "75%"
}
}
}
}
GET /music/children/_search
{
"query": {
"bool": {
"should": [
{ "match": { "name": "you" }},
{ "match": { "name": "my" }},
{ "match": { "name": "sunshine" }},
{ "match": { "name": "teeth" }}
],
"minimum_should_match": 3
}
}
}

多词查询的底层原理

上一节当中我们提到的多词match的查询,Elasticsearch会将多词的term查询转换为bool查询,or查询使用should替代, and查询使用must,我们回顾一下上一节的示例:

or查询

下面两个查询是等价的

GET /music/children/_search
{
"query": {
"match": {
"name": "you sunshine"
}
}
} GET /music/children/_search
{
"query": {
"bool": {
"should": [
{"term": {"name": "you"}},
{"term": {"name": "sunshine"}}
]
}
}
}

and查询

下面两个查询也是等价的

GET /music/children/_search
{
"query": {
"match": {
"name": {
"query": "you sunshine",
"operator": "and"
}
}
}
} GET /music/children/_search
{
"query": {
"bool": {
"must": [
{"term": {"name": "you"}},
{"term": {"name": "sunshine"}}
]
}
}
}

minimum_should_match语法

下面两个查询仍然是等价的

GET /music/children/_search
{
"query": {
"match": {
"name": {
"query": "you my sunshine teeth",
"minimum_should_match": "75%"
}
}
}
} GET /music/children/_search
{
"query": {
"bool": {
"should": [
{ "match": { "name": "you" }},
{ "match": { "name": "my" }},
{ "match": { "name": "sunshine" }},
{ "match": { "name": "teeth" }}
],
"minimum_should_match": 3
}
}
}

minimum_should_match的值可以改,也会根据实际的条件来换算,比如我写个75%,但实际的搜索词条就只有3个,那么minimum_should_match的值就会变成66.6%,即至少需要匹配2条。

查询语句权重控制

bool查询里的多条件并列,默认权重是一样的,但实际的搜索当中,我们可能会特别某一些关键词给予特殊的关注,希望匹配关注度高的文档排序能靠前一些,boost语法可以帮助我们实现这一需求。

boost默认是1,可以在查询语句中自行设置,如下示例,我希望sunshine的权重要大一些:

GET /music/children/_search
{
"query": {
"bool": {
"must": {
"match": {
"name": {
"query": "sunshine",
"boost": 2
}
}
},
"should": [
{
"match": {
"name": "my"
}
},
{
"match": {
"name": "you"
}
}
]
}
}
}

在查询结果里可以看到,符合条件的文档的_score值变得更高一些。boost值的设置不是简单的线性增长,boost设置为2不表示_score也会简单的翻一倍,boost值在计算时有归一化处理,但具体的计算数值及过程不在此篇作详细的解释。

评分计算的小问题

我们的演示环境是单node多shard模式的,有些示例发现评分会出现特别高的现象,给人感觉不是很准确,这是为什么呢?

不准确的原因

我们简单回顾一下评分计算的几个算法:TF、IDF、Length Norm,其中IDF本意上是在所有的document中,搜索关键词的出现次数,实际上IDF默认在本地shard执行的次数统计,一个shard只包含部分数据,不能代表所有数据,这样做比较高效,但会带来误差,也是造成结果不准确的原因。

演示环境中出现较大偏差,还有一个原因是演示数据相对较少,如果只有一个document,并且该document符合查询条件,那么IDF的分数就会变得很高。

解决办法

  1. 生产环境数据量相对较大,默认使用_id进行路由,在概率分布下,其实每个shard的数据基本上比较均匀,不用太担心演示环境的这种情况。
  2. 演示环境可以将primary shard设置为1,只有一个shard,那IDF的值肯定是对的。
  3. 演示环境进行搜索时,带上search_type=dfs_query_then_fetch参数,会将local IDF取出来计算global IDF。注意性能问题,生产上禁用。

小结

本篇主要介绍了全文搜索几种手动控制精度的方式:逻辑操作符变换、should命中率设置、权重调整等手段,最后对评分计算的小问题进行的简单描述,谢谢。

专注Java高并发、分布式架构,更多技术干货分享与心得,请关注公众号:Java架构社区

可以扫左边二维码添加好友,邀请你加入Java架构社区微信群共同探讨技术

Elasticsearch系列---深入全文搜索的更多相关文章

  1. ElasticSearch 2 (14) - 深入搜索系列之全文搜索

    ElasticSearch 2 (14) - 深入搜索系列之全文搜索 摘要 在看过结构化搜索之后,我们看看怎样在全文字段中查找相关度最高的文档. 全文搜索两个最重要的方面是: 相关(relevance ...

  2. elasticsearch系列四:搜索详解(搜索API、Query DSL)

    一.搜索API 1. 搜索API 端点地址 从索引tweet里面搜索字段user为kimchy的记录 GET /twitter/_search?q=user:kimchy 从索引tweet,user里 ...

  3. elasticsearch中文分词+全文搜索demo

    本文假设你已经搭建好elasticsearch服务器,并在上面装了kibana和IK中文分词组件 elasticsearch+kibana+ik的安装,之前的文章有介绍,可参考. mapping介绍: ...

  4. Elasticsearch系列---结构化搜索

    概要 结构化搜索针对日期.时间.数字等结构化数据的搜索,它们有自己的格式,我们可以对它们进行范围,比较大小等逻辑操作,这些逻辑操作得到的结果非黑即白,要么符合条件在结果集里,要么不符合条件在结果集之外 ...

  5. elasticsearch系列五:搜索详解(查询建议介绍、Suggester 介绍)

    一.查询建议介绍 1. 查询建议是什么? 查询建议,为用户提供良好的使用体验.主要包括: 拼写检查: 自动建议查询词(自动补全) 拼写检查如图: 自动建议查询词(自动补全): 2. ES中查询建议的A ...

  6. Elasticsearch系列---多字段搜索

    概要 本篇介绍一下multi_match的best_fields.most_fields和cross_fields三种语法的场景和简单示例. 最佳字段 bool查询采取"more-match ...

  7. ElasticSearch 2 (18) - 深入搜索系列之控制相关度

    ElasticSearch 2 (18) - 深入搜索系列之控制相关度 摘要 处理结构化数据(比如:时间.数字.字符串.枚举)的数据库只需要检查一个文档(或行,在关系数据库)是否与查询匹配. 布尔是/ ...

  8. ElasticSearch 2 (17) - 深入搜索系列之部分匹配

    ElasticSearch 2 (17) - 深入搜索系列之部分匹配 摘要 到目前为止,我们介绍的所有查询都是基于完整术语的,为了匹配,最小的单元为单个术语,我们只能查找反向索引中存在的术语. 但是, ...

  9. ElasticSearch 2 (16) - 深入搜索系列之近似度匹配

    ElasticSearch 2 (16) - 深入搜索系列之近似度匹配 摘要 标准的全文搜索使用TF/IDF处理文档.文档里的每个字段或一袋子词.match 查询可以告诉我们哪个袋子里面包含我们搜索的 ...

随机推荐

  1. pyCharm专业版最新2018激活码激活

    说明:本人亲测有用,对Window.Linux.Mac都稳定有效. 缺点:需要修改hosts文件 步骤: 由于管理权限问题,大部分电脑都不能直接修改hosts文件,所以我们可以先将hosts文件复制到 ...

  2. Sequence Diagram时序图 - 应该是最简洁有力的业务了

    直接看UML吧,一目了然,不用解释.自信男人,无须多言. 这是用ListView显示Post的流程. 这是Uppdate User Profile的流程.自信男人,无须多言.

  3. 十六、linux系统网络基础

    1.网络是由IP构成的:network + host,以至于我们使用网络向外发信息,不会发错. 2.子网掩码这里要知道两点: 1)子网掩码不可能出现交叉部分,换句话说不会出现01010101的交叉现象 ...

  4. SSID

    无线网络中SSID,是路由器发送的无线信号的名字!如果你将你的无线路由器的SSID:命名为:gouwancheng ,那么当你的无线路由器开启,并启用了无线功能,和允许了SSID广播,那么你就可以轻易 ...

  5. tomcat端口占用异常

    错误记录--更改tomcat端口号方法,Several ports (8005, 8080, 8009) 2011年01月18日 01:34:00 阅读数:202700 启动Tomcat服务器报错: ...

  6. 用C语言实现的轴对称变换

    #include<stdio.h> main() { int i,p,n,k,f,c,h,g,w; ][]; ;i<=;i++) { ;p<=;p++) { a[i][p]=i ...

  7. 吴裕雄--天生自然python学习笔记:python OpenCV 基本绘图

    Open CV 提供了绘制直线.圆形.矩形等基本绘 图的功能 . Open CV 画直线的语法为: 在画布上添加文字的语法为 : 用 Open CV 绘制基本图形 以 OpenCV 基本绘图绘制各种图 ...

  8. JDK和Spring中的设计模式

    创建型 1)工厂方法 Collection.iterator() 由具体的聚集类来确定使用哪一个Iterator 2)单例模式 Runtime.getRuntime() 3)建造者模式 StringB ...

  9. Reading

    Reading一共18min 需要背诵专业学科分类的词汇. 单词上,背四级词汇和托福词汇,达到约1w词汇. 句子上,练习速度和用词准确,其中准确包括含义准确和语序准确.  

  10. 12款优秀的 JavaScript 日历和时间选择控件

    这些插件能够帮助  Web 开发人员更快速的实现各种精美的日历和时间选择效果. 1. The Coolest Calendar 界面非常漂亮的一款日期选择插件,有详细的使用文档,最新版本 1.5. 点 ...