概要

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

手动控制搜索的精准度

搜索的两个重要维度:相关性(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. TPO9-2Reflection in Teaching

    Teachers, it is thought, benefit from the practice of reflection, the conscious act of thinking deep ...

  2. rare alleles

    I.4 Where the rare alleles are found p是基因A的频率,N是个体数目(也就是基因型个数,所以基因个数是2n,所以全部个体的基因A的个数是2np),p方是PAA,np ...

  3. pycharm、Django+node.js、vue搭建web项目

    参考文章:https://www.wandouip.com/t5i35466/  在此感谢 本篇接着上一篇:windows10使用npm安装vue.vue-cli 首先Django项目是搭建好的,就是 ...

  4. iOS 10 新增plist文件属性

    大概统计了一下需要加的一些字段列在下面: NSContactsUsageDescription -> 通讯录 NSMicrophoneUsageDescription -> 麦克风 NSP ...

  5. bat脚本修改dns(判断系统版本)

    @echo off systeminfo if "%OS 名称%"=="%7%" goto windows7:windows7echo 正在设置本机主DNS , ...

  6. BaseAdapter教程(1) 最简单地使用BaseAdapter

    Adapter就是适配器,而设计模式里也有Adapter Pattern. 而BaseAdapter就是设计模式里的思维,把一些不相关的东西放进去,经过适配器,最终都会出产同一样的东西. 就像Base ...

  7. iTOP-4412开发板qt4文件的移植和修改

    在 7.4 小节介绍配置文件的时候,提到迅为制作的触摸库文件“/usr/local/tslib”,迅为 已经将触摸功能编译到 Qt/E4.7.1 的库文件中,开发板可以支持触摸和鼠标功能. 用户在前面 ...

  8. 移植linux4.14内核到四核Exynos4412开发板

    最近法师收到了很多留言,其中有一部分问法师什么时候更新,还有一大部分问法师我是买迅为的IMX6UL精英版好呢还是买4412精英版好呢,因为我们这俩个都不贵.法师的建议的是入手4412!为什么呢? 第一 ...

  9. 研究NLP100篇必读的论文---已整理可直接下载

    100篇必读的NLP论文 100 Must-Read NLP 自己汇总的论文集,已更新 链接:https://pan.baidu.com/s/16k2s2HYfrKHLBS5lxZIkuw 提取码:x ...

  10. numpy模块介绍

    import numpy as np np.array([1,2,3]) array([1, 2, 3]) np.array([[1,2,3],[4,5,6]]) array([[1, 2, 3], ...