关于Elasticsearch查询(match、match_phrase、query_string和term)

news2025/1/9 16:58:05

 由上图看出, QueryBuilder 是整个查询操作的核心,决定了查询什么样的数据和期望得到什么结果这些核心的问题。

QueryBuilder 只是一个接口,需要具体的实体类才可以。那么如何创建 QueryBuilder 的实例呢?有两种方式

  • 通过 QueryBuilder 实现类的构造函数
  • 使用 QueryBuilders 工具类创建

Building Queries

下面就来看下常用的查询及其 API 有哪些

匹配所有的查询

查询语句如下

GET /_search
{
    "query": {
        "match_all": {}
    }
}

对应的 QueryBuilder Class 为 MatchAllQueryBuilder

具体方法为 QueryBuilders.matchAllQuery()

全文查询 Full Text Queries

什么是全文查询?

像使用 match 或者 query_string 这样的高层查询都属于全文查询,

  • 查询 日期(date) 或整数(integer) 字段,会将查询字符串分别作为日期或整数对待。
  • 查询一个( not_analyzed )未分析的精确值字符串字段,会将整个查询字符串作为单个词项对待。
  • 查询一个( analyzed )已分析的全文字段,会先将查询字符串传递到一个合适的分析器,然后生成一个供查询的词项列表

组成了词项列表,后面就会对每个词项逐一执行底层查询,将查询结果合并,并且为每个文档生成最终的相关度评分。

Match

match 查询的单个词的步骤是什么?

  1. 检查字段类型,查看字段是 analyzednot_analyzed
  2. 分析查询字符串,如果只有一个单词项, match 查询在执行时就会是单个底层的 term 查询
  3. 查找匹配的文档,会在倒排索引中查找匹配文档,然后获取一组包含该项的文档
  4. 为每个文档评分

构建 Match 查询

match 查询可以接受 text/numeric/dates 格式的参数,分析,并构建一个查询。

GET /_search
{
    "query": {
        "match" : {
            "message" : "this is a test"
        }
    }
}

上面的实例中 message 是一个字段名。

对应的 QueryBuilder class : MatchQueryBuilder

具体方法 : QueryBuilders.matchQuery()

全文查询 API 列表

全部的 API 列表如下(链接均指向 elasticsearch 官网)

Search QueryQueryBuilder ClassMethod in QueryBuilders
MatchMatchQueryBuilderQueryBuilders.matchQuery()
Match PhraseMatchPhraseQueryBuilderQueryBuilders.matchPhraseQuery()
Match Phrase PrefixMatchPhrasePrefixQueryBuilderQueryBuilders.matchPhrasePrefixQuery()
Multi MatchMultiMatchQueryBuilderQueryBuilders.multiMatchQuery()
Common TermsCommonTermsQueryBuilderQueryBuilders.commonTermsQuery()
Query StringQueryStringQueryBuilderQueryBuilders.queryStringQuery()
Simple Query StringSimpleQueryStringBuilderQueryBuilders.simpleQueryStringQuery()

基于词项的查询

这种类型的查询不需要分析,它们是对单个词项操作,只是在倒排索引中查找准确的词项(精确匹配)并且使用 TF/IDF 算法为每个包含词项的文档计算相关度评分 _score

Term

term 查询可用作精确值匹配,精确值的类型则可以是数字,时间,布尔类型,或者是那些 not_analyzed 的字符串。

对应的 QueryBuilder class 是TermQueryBuilder

具体方法是 QueryBuilders.termQuery()

Terms

terms 查询允许指定多个值进行匹配。如果这个字段包含了指定值中的任何一个值,就表示该文档满足条件。

对应的 QueryBuilder class 是 TermsQueryBuilder

具体方法是 QueryBuilders.termsQuery()

Wildcard

wildcard 通配符查询是一种底层基于词的查询,它允许指定匹配的正则表达式。而且它使用的是标准的 shell 通配符查询:

  • ? 匹配任意字符
  • * 匹配 0 个或多个字符

wildcard 需要扫描倒排索引中的词列表才能找到所有匹配的词,然后依次获取每个词相关的文档 ID。

由于通配符和正则表达式只能在查询时才能完成,因此查询效率会比较低,在需要高性能的场合,应当谨慎使用。

对应的 QueryBuilder class 是 WildcardQueryBuilder

具体方法是 QueryBuilders.wildcardQuery()

基于词项 API 列表

Search QueryQueryBuilder ClassMethod in QueryBuilders
TermTermQueryBuilderQueryBuilders.termQuery()
TermsTermsQueryBuilderQueryBuilders.termsQuery()
RangeRangeQueryBuilderQueryBuilders.rangeQuery()
ExistsExistsQueryBuilderQueryBuilders.existsQuery()
PrefixPrefixQueryBuilderQueryBuilders.prefixQuery()
WildcardWildcardQueryBuilderQueryBuilders.wildcardQuery()
RegexpRegexpQueryBuilderQueryBuilders.regexpQuery()
FuzzyFuzzyQueryBuilderQueryBuilders.fuzzyQuery()
TypeTypeQueryBuilderQueryBuilders.typeQuery()
IdsIdsQueryBuilderQueryBuilders.idsQuery()

复合查询

什么是复合查询?

复合查询会将其他的复合查询或者叶查询包裹起来,以嵌套的形式展示和执行,得到的结果也是对各个子查询结果和分数的合并。可以分为下面几种:

  • constant_score query

    经常用在使用 filter 的场合,所有匹配的文档分数都是一个不变的常量

  • bool query

    可以将多个叶查询和组合查询再组合起来,可接受的参数如下

    • must : 文档必须匹配这些条件才能被包含进来
    • must_not 文档必须不匹配才能被包含进来
    • should 如果满足其中的任何语句,都会增加分数;即使不满足,也没有影响
    • filter 以过滤模式进行,不评分,但是必须匹配
  • dis_max query

    叫做分离最大化查询,它会将任何与查询匹配的文档都作为结果返回,但是只是将其中最佳匹配的评分作为最终的评分返回。

  • function_score query

    允许为每个与主查询匹配的文档应用一个函数,可用来改变甚至替换原始的评分

  • boosting query

    用来控制(提高或降低)复合查询中子查询的权重。

复合查询列表

Search QueryQueryBuilder ClassMethod in QueryBuilders
Constant ScoreConstantScoreQueryBuilderQueryBuilders.constantScoreQuery()
BoolBoolQueryBuilderQueryBuilders.boolQuery()
Dis MaxDisMaxQueryBuilderQueryBuilders.disMaxQuery()
Function ScoreFunctionScoreQueryBuilderQueryBuilders.functionScoreQuery()
BoostingBoostingQueryBuilderQueryBuilders.boostingQuery()

特殊查询

Wrapper Query

这里比较重要的一个是 Wrapper Query,是说可以接受任何其他 base64 编码的字符串作为子查询。

主要应用场合就是在 Rest High-Level REST client 中接受 json 字符串作为参数。比如使用 gson 等 json 库将要查询的语句拼接好,直接塞到 Wrapper Query 中查询就可以了,非常方便。

Wrapper Query 对应的 QueryBuilder class 是WrapperQueryBuilder

具体方法是 QueryBuilders.wrapperQuery()

小结

本文对 elasticsearch rest high client 中的查询构建进行了总结和整理,对常用的 API 做了简要的介绍。读者如果要查看完整的构建查询的 API 列表,可参考此处

参考文档

  1. elasticsearch High level client Building Queries

一)text字段和keyword字段的区别

所以将字段设置成keyword的时候查询的时候已有的值不会被分词。而text类型的字段会被分词,查询的时候如果用拆开查可以查询的到,但是要是直接全部查,就是查询不到。

注意“1, 2”会被拆分成[1, 2],但是"1,2"是不拆分的,少了个空格。

一、match、match_phrase、query_string和term的区别

1、match和term的区别

1.1、term

 1)term查询keyword字段。

term不会分词。而keyword字段也不分词。需要完全匹配才可。

 2)term查询text字段

因为text字段会分词,而term不分词,所以term查询的条件必须是text字段分词后的某一个。

1.2.match

1)match查询keyword字段

match会被分词,而keyword不会被分词,match的需要跟keyword的完全匹配可以。

其他的不完全匹配的都是失败的。

2)match查询text字段

match分词,text也分词,只要match的分词结果和text的分词结果有相同的就匹配。

1.3.match_phrase

1)match_phrase匹配keyword字段。

这个同上必须跟keywork一致才可以。

2)match_phrase匹配text字段。

match_phrase是分词的,text也是分词的。match_phrase的分词结果必须在text字段分词中都包含,而且顺序必须相同,而且必须都是连续的。

1.4.query_string

1)query_string查询keyword类型的字段,试过了,无法查询。

2)query_string查询text类型的字段。

和match_phrase区别的是,不需要连续,顺序还可以调换。

二、关于Elasticsearch的精确值查找(term)不生效问题

2.1、问题
常用的 term 查询, 可以用它处理数字(numbers)、布尔值(Booleans)、日期(dates)以及文本(text)。term查询数字的时候并没有什么问题,但是当我们对字符串类型的字段进行term查询时可能会得到意想不到的情况,可能明明有记录却查询不到,也可能查询出不符合预期的记录。
原因
ES会默认给每个字段进行分词然后建立倒排索引。比如,有两条JSON数据如下:

[
    {
        "id" : 1
        "searchField" : "abc#def"
    },
    {
        "id" : 2
        "searchField" : "abc#xyz"
    }
]

使用ES提供的analyze API 可以看到分词结果如下:

按照上面的分词结果,那么当我们将这两条数据插入ES的时候,建立的倒排索引如下:
Term    Counter    DocId
abc      2        1,2
def       1        1
xyz       1        2
注意:如果字段内容是大写的,那么在分词生成索引后,索引的项目会变成小写,比如上面的两条数据是ABC#DEF和ABC#XYZ,那么生成的索引也和上面的一样。此时由于索引项是小写,因此term查询ABC是查不到的,必须要查询abc;match查询ABC是可以查询到的,因为match会进行分词然后再匹配。
①使用term精确查询searchField为abc#def的记录:

{
  "query": {
    "term": {
      "searchField":"abc#def"
    }
  }
}

此时得到的结果是空,我们无法获得期望的结果,问题不在 term 查询,而在于abc#def并不在我们的倒排索引中。
②使用term精确查询searchField为abc的记录:

{
  "query": {
    "term": {
      "searchField":"abc"
    }
  }
}

此时得到的结果是两条数据都被检索出来。

根据建立的倒排索引不难发现,当精确匹配abc时,那么会命中如下的索引,它的DocId是1,2,因此会查出两条记录。


解决方案
①将字段的type设置为keyword
明确字段是否需要分词,不需要分词的字段就将type设置为keyword,可以节省空间和提高写性能
ElasticSearch 5.0以后,String字段被拆分成两种新的数据类型: text用于全文搜索,会分词,而keyword用于关键词搜索,不进行分词。对于字符串类型的字段,ES默认会再生成一个keyword字段用于精确索引。默认mapping如下:

"mapping": {
    "properties": {
      "id": {
        "type": "long"
      },
      "searchField": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      }
 }

②将该字段设置成 not_analyzed 无需分析的
告诉 Elasticsearch该字段具有精确值,要将index属性设置成 not_analyzed 无需分析的。也是在mapping中进行设置,例如:

"mapping": {
    "properties": {
      "id": {
        "type": "long"
      },
      "searchField": {
        "type": "text",
        "index": "not_analyzed"
      }
 }

如果是使用Java High Level REST Client 操作Elasticsearch的话可以参考官方API进行设置。
例如:

XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
{
    builder.startObject("properties");
    {
        builder.startObject("message");
        {
            builder.field("type", "text");
        }
        builder.endObject();
    }
    builder.endObject();
}
builder.endObject();
request.source(builder);

index 属性控制怎样索引字符串。它可以是下面三个值:
① analyzed:首先分析字符串,然后索引它。换句话说,以全文索引这个域。
② not_analyzed:索引这个域,所以它能够被搜索,但索引的是精确值。不会对它进行分析。
③ no:不索引这个域。这个域不会被搜索到。
注意:其他简单类型(例如 long , double , date 等)也接受 index 参数,但有意义的值只有 no 和 not_analyzed , 因为它们永远不会被分析。

2.2、elasticsearch大小写无法使用term查询的问题

在 Elasticsearch 中处理字符串类型的数据时,如果我们想把整个字符串作为一个完整的 term 存储,我们通常会将其类型 type 设定为 keyword。但有时这种设定又会给我们带来麻烦,比如同一个数据再写入时由于没有做好清洗,导致大小写不一致,比如 appleApple两个实际都是 apple,但当我们去搜索 apple时却无法返回 Apple的文档。

原因是elasticsearch在创建倒排索引时,就已经将大写转为小写,而后写入索引。解决方法:

  • 一种是在传入查询条件的时候,使用toLowerCase()转化为小写,但是条件一多,代码量颇多,不太适用。
  • 一种是在设置mapping的时候设置normalizer要解决这个问题。
PUT test_normalizer{
  "mappings": {
    "doc":{
      "properties": {
        "type":{
          "type":"keyword"
        }
      }
    }
  }
}
PUT test_normalizer/doc/1{
  "type":"apple"}
PUT test_normalizer/doc/2{
  "type":"Apple"}
# 查询一 GET test_normalizer/_search{
  "query": {
    "match":{
      "type":"apple"
    }
  }
}
# 查询二GET test_normalizer/_search{
  "query": {
    "match":{
      "type":"aPple"
    }
  }}

 

 原因:

  1.  Docs写入 Elasticsearch时由于 type是 keyword,分词结果为原始字符串
  2. 查询 Query 时分词默认是采用和字段写时相同的配置,因此这里也是 keyword,因此分词结果也是原始字符
  3. 两边的分词进行匹对,便得出了我们上面的结果

2. Normalizer

normalizer是 keyword的一个属性,可以对 keyword生成的单一 Term再做进一步的处理,比如 lowercase,即做小写变换。使用方法和自定义分词器有些类似,需要自定义,如下所示:

DELETE test_normalizer
# 自定义 normalizer
PUT test_normalizer{
  "settings": {
    "analysis": {
      "normalizer": {
        "lowercase": {
          "type": "custom",
          "filter": [
            "lowercase"
          ]
        }
      }
    }
  },
  "mappings": {
    "doc": {
      "properties": {
        "type": {
          "type": "keyword"
        },
        "type_normalizer": {
          "type": "keyword",
          "normalizer": "lowercase"
        }
      }
    }
  }}
PUT test_normalizer/doc/1{
  "type": "apple",
  "type_normalizer": "apple"}PUT test_normalizer/doc/2{
  "type": "Apple",
  "type_normalizer": "Apple"}
# 查询三GET test_normalizer/_search{
  "query": {
    "term":{
      "type":"aPple"
    }
  }}
# 查询四GET test_normalizer/_search{
  "query": {
    "term":{
      "type_normalizer":"aPple"
    }
  }}

 我们第一步是自定义了名为 lowercase的 normalizer,其中filter 类似自定义分词器中的 filter ,但是可用的种类很少,详情大家可以查看官方文档。然后通过 normalizer属性设定到字段type_normalizer中,然后插入相同的2条文档。执行发现,查询三无结果返回,查询四返回2条文档。

    • 文档写入时由于加入了 normalizer,所有的 term都会被做小写处理

    • 查询时搜索词同样采用有 normalizer的配置,因此处理后的 term也是小写的

    • 两边分词匹对,就得到了我们上面的结果

       

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/75513.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AOP开发明确的的事项

12.2. AOP开发明确的的事项 12.2.1、需要编写的内容 编写核心业务代码(目标类的目标方法) 编写切面类,切面类中有通知(增强功能方法) 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合 12.2.2、AOP 技…

什么是MinIO

本文为joshua317原创文章,转载请注明:转载自joshua317博客 什么是MinIO - joshua317的博客 什么是MinIO? MinIO 是一款高性能、分布式的对象存储系统. 它是一款软件产品, 可以100%的运行在标准硬件。即X86等低成本机器也能够很好的运行MinIO。 MinIO提…

容灾演练月报 | 福莱特集团核心系统完成“跨云”容灾切换演练

11月,美创科技携手福莱特集团、金华银行、稠州银行、慈溪市卫健委及医疗行业等总计19家用户完成容灾切换演练,其中完成2次灾难切换,有效验证了各用户单位容灾系统的可靠性及高可用性! 本月典型案例:福莱特集团 福莱特集…

CANoe.DiVa简单配置

🍅 我是蚂蚁小兵,专注于车载诊断领域,尤其擅长于对CANoe工具的使用🍅 寻找组织 ,答疑解惑,摸鱼聊天,博客源码,点击加入👉【相亲相爱一家人】🍅 玩转CANoe&…

RK3588核心板显示资源分配实操竟如此简单!飞凌嵌入式

前言 眼睛作为人类最重要的感觉器官,通过“视觉”帮助我们接收了大部分的外界信息,“视觉效果的好坏”直接影响着我们日常生产和生活质量的高低,因此“显示能力”成为了评价产品优劣的一项重要指标。在影音、游戏、智慧办公、工业自动化和交通…

OpenAI ChatGPT微信接入教程

OpenAI ChatGPT微信接入教程 OpenAI ChatGPT 接入 个人WeChat(微信),让AI互动更方便! 准备工作 Ⅰ,OpenAI 账号,参考ChatGPT注册试用全攻略 Ⅱ,微信账号(需要绑定支付方式,需支持网页登录&am…

JVM篇之牛刀小试(三)年轻代参数优化探索

前言 在JVM 篇之 牛刀小试 (一)我们谈到了关于年轻代参数的问题,就是当我没有设置-Xmn的时候,gc时间居然比我设置了500m的时候,时间还短,然后当时我就停止了探索。 后来我在公司飞书文档写了一篇文章分享&…

Java的注解和注解解析器是什么,运行方法如何,架构师必会知识体系

文章目录什么是[元数据](https://so.csdn.net/so/search?q元数据&spm1001.2101.3001.7020)(metadata)什么是注解(Annotation)?元注解自定义注解注解处理器什么是元数据(metadata) ​ 元数据由metadata译来,所谓的元数据就是“关于数据的数据”,更通俗的说就是…

网络篇汇总

路由器:属于网关设备,通过路由器可以将各种局域网、城域网、广域网连接起来,一般工作于网络层。它会根据信号的情况自动选择和设定路由,以最佳路径,按照前后顺序发送信号。路由器可连接多个逻辑上分开的网络&#xff0…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java随骑共享系统m7i8o

要对当前自己的学校对于计算机毕业设计的要求以及严格程度有所了解,这个主要是借助上一届已经毕业的学长学姐了解一下,但是有一点要注意的是,对于每一届毕业生的毕业设计的处理,学校都有很大的调整,这一点尤其重要。其…

静态HTML个人博客主页 简单个人网页设计作业 DW个人网站模板下载 大学生简单个人网页作品代码 个人网页制作 学生个人网页设计作业

🎉精彩专栏推荐👇🏻👇🏻👇🏻 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业…

力扣(LeetCode)139. 单词拆分(C++)

字符串哈希&动态规划 一眼暴力,发现根本做不了,想着想着,发现 dpdpdp 很好想,来试试。 枚举字符串 sss 的所有位置作为起始点,如图状态转移。 规定 : f[0]f[0]f[0] 表示空字符的集合,可以用空串组成…

【数据结构与算法】模拟实现LinkedList类

文章目录LinkedList简介头插法创建链表尾插法创建链表任意位置插入,第一个数据节点为0号下标查找是否包含关键字key是否在链表当中删除第一次出现关键字为key的节点删除所有值为key的节点得到链表的长度打印链表清空链表完整代码:总结:LinkedList简介 Java LinkedLi…

SAP PS 第6节 项目产成品产出

SAP PS 第6节 项目产成品产出及差异处理1 模拟场景说明1.1 拖拽负库存1.2 发料原材料及报工1.3 执行副产品入库migo发预留1.4 CNS0交货1.5 后面开票产生收入按照项目结算即可项目上有一类比较另类的玩法,就是舍弃PP,依靠网络活动的负库存,实现…

Android Edittext密码类型显示字符串修改实现

Android Edittext密码类型显示字符串修改实现 文章目录Android Edittext密码类型显示字符串修改实现一、前言:二、效果三、实现1、系统级设置2、应用级设置3、单个EditText设置(1)自定义显示符合类(2)EditText使用自定…

认识一下 Kubernetes 多集群服务 API

由于各种原因,采用 Kubernetes 的企业内部存在着几个、几十甚至上百个集群。比如处于研发流程上的考虑,不同环境下都存在独立的集群;监管层面的考虑,就地存储的用户数据需要搭配应用集群;单个集群的容量限制&#xff0…

(四)Redis的持久化

一 什么是redis持久化 因为Redis数据是基于内存读写,为防止Redis服务器关闭或者宕机造成数据的丢失,我们通常需要对redis做持久化,即:把内存中的数据(命令)保存一份到磁盘中来做一份备份,当redis服务关闭或宕机后,在Redis服务器重启后将数据从磁盘加载到内存中,不至于造成数据…

一招教你轻松使用公网远程访问公司内网

企业远程访问需求 众多企业都会在总部搭建各类项目管理办公系统(如OA、ERP、CRM、财务系统等等),以提高员工的办公及管理效率。 不少出差在外或者居家办公的员工需要从外部网络访问内网来登录各类系统,以满足办公协作管理的需…

电容笔和触控笔有啥区别?双十二质量好的电容笔推荐

从导电材料、作用机理、用途等方面,电容笔与普通触控笔相比有很大的不同。电容笔的笔尖尺寸适中,笔尖材质一般比较耐用。随着科技的进步,人们的生活水准不断提高,无论是绘制图纸,或是会议纪要,都需要一款更…

(附源码)ssm教学成绩管理系统 毕业设计 282029

教学成绩管理系统的设计与实现 摘 要 随着互联网趋势的到来,各行各业都在考虑利用互联网将自己推广出去,最好方式就是建立自己的互联网系统,并对其进行维护和管理。在现实运用中,应用软件的工作规则和开发步骤,采用Jav…