五.Elasticsearch教程 - 查询优化 - 手工控制搜索结果精准度_operator": "and-程序员宅基地

技术标签: ElasticSearch  java  elasticsearch  数据库  es  大数据  

注:version:elasticsearch-7.11.2

造测试数据:

#新建mapping映射
PUT /developer
{
  "mappings": {
    "properties": {
      "name": {
        "type": "keyword",
        "index": true,
        "store": true
      },
      "gender": {
        "type": "integer",
        "index": true,
        "store": true
      },
      "age": {
        "type": "integer",
        "index": true,
        "store": true
      },
      "remark": {
        "type": "text",
        "index": true,
        "store": true,
        "analyzer": "ik_smart",
        "search_analyzer": "ik_smart"
      },
      "address": {
        "type": "text",
        "index": true,
        "store": true,
        "analyzer": "ik_smart",
        "search_analyzer": "ik_smart"
      }
    }
  },
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 1
  }
}

#添加两条数据
POST _bulk
{"create":{"_index":"developer","_type":"_doc","_id":1}}
{"id":1,"name":"李雷","age":18,"gender":1,"remark":"是一个初级程序员","address": "浙江省杭州市西湖区"}
{"create":{"_index":"developer","_type":"_doc","_id":2}}
{"id":2,"name":"韩梅","age":17,"gender":0,"remark":"是一个女程序员","address": "安徽省合肥市蜀山区"}

remark分词结果:

image.png

1.operator用法

GET /developer/_search
{
  "query":{
    "match": {
      "remark": {
        "query": "初级程序员",
        "operator": "and"
      }
    }
  }
}

image.png

"初级程序员"会被分词为"初级"和"程序员"。使用"operator": "and",就必须同时满足“初级”和“程序员”两个词。如果不写的话默认是"operator": "or"

2.minimum_should_match用法

minimum_should_match 用来控制最小匹配数量,可以是数字百分比(例: 75%)

GET /developer/_search
{
  "query":{
    "match": {
      "remark": {
        "query": "一个初级程序员",
        "minimum_should_match": "3"
      }
    }
  }
}

image.png

上图中的分词,可以匹配到3个词,分别是"一个"、"初级"、"程序员"。minimum_should_match 设置小于3的值都可以匹配到结果

搭配组合查询使用:

GET developer/_doc/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "remark": "一个"
          }
        },
        {
          "match": {
            "remark": "初级"
          }
        },
        {
          "match": {
            "remark": "女"
          }
        },
        {
          "match": {
            "remark": "程序员"
          }
        }
      ],
      "minimum_should_match": 3
    }
  }
}

image.png

上图至少满足3个条件,所以把添加的两条数据都查询出来了。

3.match 的底层转换

在ES中,执行match搜索的时候,ES底层通常都会对搜索条件进行底层转换,把叶子查询转换为组合查询,来实现最终的搜索结果。例如:

1).operator为 or 时的拆分

原查询语句(没写operator时默认为or):

GET developer/_doc/_search
{
  "query": {
    "match": {
      "remark":"初级程序员"
    }
  }
}

由叶子查询转换为组合查询:

GET developer/_doc/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "remark": "初级"
          }
        },
        {
          "term": {
            "remark": "程序员"
          }
        }
      ]
    }
  }
}

2).operator为 and 时的拆分

原查询:

GET developer/_doc/_search
{
  "query": {
    "match": {
      "remark": {
        "query": "初级程序员",
        "operator": "and"
      }
    }
  }
}

由叶子查询转换为组合查询:

GET developer/_doc/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "remark": "初级"
          }
        },
        {
          "term": {
            "remark": "程序员"
          }
        }
      ]
    }
  }
}

建议,如果不怕麻烦,尽量使用转换后的语法执行搜索,效率更高。如果开发周期短,工作量大,使用简化的写法。

4.boost权重控制

GET developer/_doc/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "remark": "程序员"
          }
        }  
      ],
      "should": [
        {
          "match": {
            "address": {
              "query": "安徽省",
              "boost": 2
            }
          }
        },
        {
          "match": {
            "address": {
              "query": "浙江省",
              "boost": 1
            }
          }
        }
      ]
    }
  }
}

image.png

安徽省 和 浙江省 给的权重比例是2:1,查询结果 _score 分值安徽省更大,排在前面。

5.基于dis_max实现best fields策略进行多字段搜索

best fields策略: 搜索的document中的某一个field,尽可能多的匹配搜索条件。与之相反的是,尽可能多的字段匹配到搜索条件(most fields策略)。如百度搜索使用这种策略。

优点:精确匹配的数据可以尽可能的排列在最前端,且可以通过minimum_should_match来去除长尾数据,避免长尾数据字段对排序结果的影响。

长尾数据比如说我们搜索4个关键词,但很多文档只匹配1个,也显示出来了,这些文档其实不是我们想要的

缺点:相对排序不均匀。

dis_max语法: 直接获取搜索的多条件中的,单条件query相关度分数最高的数据,以这个数据做相关度排序。

GET /developer/_doc/_search
{
  "query": {
    "dis_max": {
      "queries": [
        {
          "match": {
            "remark": "女"
          }
        },
        {
          "match": {
            "remark": "初级程序员"
          }
        }
      ]
    }
  }
}

image.png

 

以上查询的 queries 有 2 个条件,

"remark": "女" 匹配到一文档

"remark": "初级程序员" 匹配到两个文档

使用 "remark": "初级程序员" 查询的分值最高,那么"初级程序员"这条数据的分值更多,排序靠前。

6.基于tie_breaker参数优化dis_max搜索效果

dis_max 是将多个搜索query条件中相关度分数最高的用于结果排序,忽略其他query分数,在某些情况下,可能还需要其他query条件中的相关度介入最终的结果排序,这个时候可以使用 tie_breaker 参数来优化dis_max搜索。

tie_breaker 参数代表的含义是:将其他query搜索条件的相关度分数乘以参数值,再参与到结果排序中。如果不定义此参数,相当于参数值为0。所以其他query条件的相关度分数被忽略。

GET /developer/_doc/_search
{
  "query": {
    "dis_max": {
      "queries": [
        {
          "match": {
            "remark": "女"
          }
        },
        {
          "match": {
            "remark": "初级程序员"
          }
        }
      ],
      "tie_breaker":0.5
    }
  }
}

image.png

 

相比较下"韩梅"的分值有所增加

7.multi_match实现字段加权重查询

使用multi_match语法为:其中type常用的有 best_fields most_fields^n 代表权重,相当于 "boost":n

GET /developer/_doc/_search
{
  "query": {
    "multi_match": {
      "query": "李雷一个程序员",
      "fields": ["name", "remark^2"],
      "type": "best_fields",
      "tie_breaker": 0.5,
      "minimum_should_match": 2
    }
  }
}

minimum_should_match是针对分词后,每个条件的匹配个数的。比如这里分词为"李雷"、"一个"、"程序员"一共3个,那么name必须匹配其中两个,或者remark必须匹配其中两个

8.cross fields搜索

cross fields搜索:在多个fields中搜索唯一标识的数据。例如在多个字段中搜索人名、地址。

实现这种搜索,一般都是使用most fields搜索策略。因为这就不是一个field的问题。

Cross fields搜索策略,是从多个字段中搜索条件数据。默认情况下,和most fields搜索的逻辑是一致的,计算相关度分数是和best fields策略一致的。一般来说,如果使用cross fields搜索策略,那么都会携带一个额外的参数operator。用来标记搜索条件如何在多个字段中匹配。

most field策略缺点:most fields策略是尽可能匹配更多的字段,所以会导致精确搜索结果排序问题。又因为cross fields搜索,不能使用minimum_should_match来去除长尾数据。所以在使用most fields和cross fields策略搜索数据的时候,都有不同的缺陷。所以商业项目开发中,都推荐使用best fields策略实现搜索。

GET /developer/_doc/_search
{
  "query": {
    "multi_match": {
      "query": "浙江省杭州市西湖区",
      "fields": ["name", "address"],
      "type": "cross_fields",
      "operator" : "and"
    }
  }
}

image.png

9.copy_to组合fields

在搜索地址的时候,需要同时在省、市、街道、3个字段同时搜索,怎么做呢?

第一种方法可以使用 multi_match ,上面有写过实例。

此处讲第二种方法,使用 copy_to 组合 fields

copy_to : 就是将多个字段,复制到一个字段中,实现一个多字段组合。copy_to可以解决cross fields搜索问题,在商业项目中,也用于解决搜索条件默认字段问题。

如果需要使用copy_to语法,则需要在定义index的时候,手工指定mapping映射策略。

copy_to语法:

PUT /address/_mapping
{
  "properties": {
    "provice": {
      "type": "text",
      "analyzer": "ik_max_word",
      "copy_to": "address"
    },
    "city": {
      "type": "text",
      "analyzer": "ik_max_word",
      "copy_to": "address"
    },
    "street": {
      "type": "text",
      "analyzer": "ik_max_word",
      "copy_to": "address"
    },
    "address": {
      "type": "text",
      "analyzer": "ik_max_word"
    }
  }
}

以上mapping会把provice、city、street三个字段的值,会自动复制到address字段中,实现一个字段的组合。那么在搜索地址的时候,就可以在address字段中做条件匹配,从而避免most fields策略导致的问题。在维护数据的时候,不需对address字段特殊的维护。因为address字段是一个组合字段,是由ES自动维护的。类似java代码中的推导属性。在存储的时候,未必存在,但是在逻辑上是一定存在的,因为address是由3个物理存在的属性province、city、street组成的。

10.match phrase 短语匹配

match phrase:短语匹配。代表搜索条件不可分割。

造测试数据:

#新建索引
PUT /test_match_phrase
#为索引添加映射
PUT /test_match_phrase/_mappings
{
  "properties": {
    "remark":{
      "type": "text",
      "analyzer": "standard"
    }
  }
}
#添加两条测试数据
POST /_bulk
{"create":{"_index": "test_match_phrase", "_type":"_doc", "_id": 1}}
{"id":1,"remark": "Hellow world"}
{"create":{"_index": "test_match_phrase", "_type":"_doc", "_id": 2}}
{"id":2,"remark": "Hellow daliu, I love the world"}
# Hellow world 短语匹配
GET /test_match_phrase/_search
{
  "query": {
    "match_phrase": {
      "remark": "Hellow world"
    }
  }
}

image.png

如上图这样只能匹配到 id=1 的文档。

match phrase原理

1.文档在建立的时候会进行分词,并建立倒排索引,分词结果如下:

image.png

position:以词为单位,代表这个词在整个数据中的下标。

倒排索引建立如下:(word记录分词,index 记录分词所在的文档id)

image.png

2.将搜索条件 "Hellow world" 分词为 "Hellow" 和 "world",然后在倒排索引中进行检索,匹配到文档id包含[1,2] 的两个文档,然后分别在这两个文档中检查"Hellow"和"world"的position是否连续,如果是连续的则代表成功,否则失败。此例中文档id=1的那个文档的position是连续的,所以返回了文档id=1的那条数据。

match phrase搜索参数:slop

slop代表match phrase短语搜索的时候,单词最多移动多少次,可以实现数据匹配。在所有匹配结果中,多个单词距离越近,相关度评分越高,排序越靠前。

这种使用slop参数的match phrase搜索,就称为近似匹配(proximity search)

上面的查询语句加上slop参数:

GET /test_match_phrase/_search
{
  "query": {
    "match_phrase": {
      "remark": {
        "query": "Hellow world",
        "slop": 4
      }
    }
  }
}

image.png

如上图,加上slop:4的参数就可以匹配到id=2的文档

 

上面的 slop:4 代表可以移动4次。

原理:

搜索条件"Hellow world"被分词为"Hellow"和"world"两个,在倒排索引中匹配到文档[1,2]后,在这两个文档中检查position,在index=2的文档中会根据slop参数执行单词的移动。

如下:

image.png

移动4的时候匹配成功

11.前缀搜索 prefix search

GET /developer/_search
{
  "query": {
    "prefix": {
     "address": {
       "value": "浙江省"
     }
    }
  }
}

image.png

前缀搜索效率比较低。前缀搜索不会计算相关度分数。前缀越短,效率越低。如果使用前缀搜索,建议使用长前缀。因为前缀搜索需要扫描完整的索引内容,所以前缀越长,相对效率越高。

12.通配符搜索 wildcard search

ES中也有通配符。但是和java还有数据库不太一样。通配符可以在倒排索引中使用,也可以在keyword类型字段中使用。

常用通配符:

? - 一个任意字符

* - 0~n个任意字符

GET /test_match_phrase/_search
{
  "query": {
    "wildcard": {
      "remark": "*da*"
    }
  }
}

image.png

使用通配符模糊匹配,即使在分词匹配不到的时候也能搜索到结果

性能也很低,也是需要扫描完整的索引。不推荐使用。

13.正则搜索 regexp

ES支持正则表达式。可以在倒排索引或keyword类型字段中使用。

常用符号:

[] :范围,如 [0-9]是0~9的范围数字

. : 一个字符

+ :前面的表达式可以出现多次。

GET /test_match_phrase/_search
{
  "query": {
    "regexp": {
      "remark": "[A-z].+"
    }
  }
}

image.png

14.搜索推荐 match_phrase_prefix

image.png

实现类似百度的这种效果。

match_phrase_prefix 的作用正如其名,就是 match_phrase + prefix

不但可以前缀匹配,而且可以移动词匹配

GET /test_match_phrase/_search
{
  "query": {
    "match_phrase_prefix": {
      "remark": {
        "query": "Hellow w",
        "slop" : 4,
        "max_expansions": 10
      }
    }
  }
}

image.png

slop: 如同match_phrase一样,指定最大分词位数。

max_expansions: 指定prefix最多匹配多少个term(单词)。

 

这种语法的限制是,只有最后一个term会执行前缀搜索。

执行性能很差,毕竟最后一个term是需要扫描所有符合slop要求的倒排索引的term。

因为效率较低,如果必须使用,则一定要使用参数max_expansions。

15.模糊搜索技术 fuzzy

搜索的时候可能输入错误,例如 "hellow world" 输入成了 "hellow word"。fuzzy可以让这种拼写错误也能查询数据(在英文中很有效,在中文中几乎无效)

GET /test_match_phrase/_search
{
  "query": {
    "fuzzy": {
      "remark": {
        "value": "word",
        "fuzziness":2
      }
    }
  }
}

image.png

fuzziness:允许字母误差个数。代表value的值word可以修改多少个字母来进行拼写错误的纠正(修改字母的数量包含字母变更,增加或减少字母),例如本例中的 world 去掉 l 就能匹配上,所以fuzziness = 1的时候也能查询出结果

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_33743572/article/details/115356684

智能推荐

while循环&CPU占用率高问题深入分析与解决方案_main函数使用while(1)循环cpu占用99-程序员宅基地

文章浏览阅读3.8k次,点赞9次,收藏28次。直接上一个工作中碰到的问题,另外一个系统开启多线程调用我这边的接口,然后我这边会开启多线程批量查询第三方接口并且返回给调用方。使用的是两三年前别人遗留下来的方法,放到线上后发现确实是可以正常取到结果,但是一旦调用,CPU占用就直接100%(部署环境是win server服务器)。因此查看了下相关的老代码并使用JProfiler查看发现是在某个while循环的时候有问题。具体项目代码就不贴了,类似于下面这段代码。​​​​​​while(flag) {//your code;}这里的flag._main函数使用while(1)循环cpu占用99

【无标题】jetbrains idea shift f6不生效_idea shift +f6快捷键不生效-程序员宅基地

文章浏览阅读347次。idea shift f6 快捷键无效_idea shift +f6快捷键不生效

node.js学习笔记之Node中的核心模块_node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是-程序员宅基地

文章浏览阅读135次。Ecmacript 中没有DOM 和 BOM核心模块Node为JavaScript提供了很多服务器级别,这些API绝大多数都被包装到了一个具名和核心模块中了,例如文件操作的 fs 核心模块 ,http服务构建的http 模块 path 路径操作模块 os 操作系统信息模块// 用来获取机器信息的var os = require('os')// 用来操作路径的var path = require('path')// 获取当前机器的 CPU 信息console.log(os.cpus._node模块中有很多核心模块,以下不属于核心模块,使用时需下载的是

数学建模【SPSS 下载-安装、方差分析与回归分析的SPSS实现(软件概述、方差分析、回归分析)】_化工数学模型数据回归软件-程序员宅基地

文章浏览阅读10w+次,点赞435次,收藏3.4k次。SPSS 22 下载安装过程7.6 方差分析与回归分析的SPSS实现7.6.1 SPSS软件概述1 SPSS版本与安装2 SPSS界面3 SPSS特点4 SPSS数据7.6.2 SPSS与方差分析1 单因素方差分析2 双因素方差分析7.6.3 SPSS与回归分析SPSS回归分析过程牙膏价格问题的回归分析_化工数学模型数据回归软件

利用hutool实现邮件发送功能_hutool发送邮件-程序员宅基地

文章浏览阅读7.5k次。如何利用hutool工具包实现邮件发送功能呢?1、首先引入hutool依赖<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.19</version></dependency>2、编写邮件发送工具类package com.pc.c..._hutool发送邮件

docker安装elasticsearch,elasticsearch-head,kibana,ik分词器_docker安装kibana连接elasticsearch并且elasticsearch有密码-程序员宅基地

文章浏览阅读867次,点赞2次,收藏2次。docker安装elasticsearch,elasticsearch-head,kibana,ik分词器安装方式基本有两种,一种是pull的方式,一种是Dockerfile的方式,由于pull的方式pull下来后还需配置许多东西且不便于复用,个人比较喜欢使用Dockerfile的方式所有docker支持的镜像基本都在https://hub.docker.com/docker的官网上能找到合..._docker安装kibana连接elasticsearch并且elasticsearch有密码

随便推点

Python 攻克移动开发失败!_beeware-程序员宅基地

文章浏览阅读1.3w次,点赞57次,收藏92次。整理 | 郑丽媛出品 | CSDN(ID:CSDNnews)近年来,随着机器学习的兴起,有一门编程语言逐渐变得火热——Python。得益于其针对机器学习提供了大量开源框架和第三方模块,内置..._beeware

Swift4.0_Timer 的基本使用_swift timer 暂停-程序员宅基地

文章浏览阅读7.9k次。//// ViewController.swift// Day_10_Timer//// Created by dongqiangfei on 2018/10/15.// Copyright 2018年 飞飞. All rights reserved.//import UIKitclass ViewController: UIViewController { ..._swift timer 暂停

元素三大等待-程序员宅基地

文章浏览阅读986次,点赞2次,收藏2次。1.硬性等待让当前线程暂停执行,应用场景:代码执行速度太快了,但是UI元素没有立马加载出来,造成两者不同步,这时候就可以让代码等待一下,再去执行找元素的动作线程休眠,强制等待 Thread.sleep(long mills)package com.example.demo;import org.junit.jupiter.api.Test;import org.openqa.selenium.By;import org.openqa.selenium.firefox.Firefox.._元素三大等待

Java软件工程师职位分析_java岗位分析-程序员宅基地

文章浏览阅读3k次,点赞4次,收藏14次。Java软件工程师职位分析_java岗位分析

Java:Unreachable code的解决方法_java unreachable code-程序员宅基地

文章浏览阅读2k次。Java:Unreachable code的解决方法_java unreachable code

标签data-*自定义属性值和根据data属性值查找对应标签_如何根据data-*属性获取对应的标签对象-程序员宅基地

文章浏览阅读1w次。1、html中设置标签data-*的值 标题 11111 222222、点击获取当前标签的data-url的值$('dd').on('click', function() { var urlVal = $(this).data('ur_如何根据data-*属性获取对应的标签对象

推荐文章

热门文章

相关标签