ES支持的子句类型|ES单一查询定义|解决:[xxx] malformed query, expected [END_OBJECT] but found [FIELD_NAME]报错

概要

[基础知识点]重点掌握:ElasticSearch Query DSL查询语句仅支持两类查询子句:

  1. 基础查询子句
  2. 和复合查询子句

将Query DSL视为AST抽象语法树,——树就是由枝干和叶子节点构成,叶子代表最基础的查询语法,复合查询语句就是枝干,可以组合枝干(复合查询语句)和叶子(基础查询)。

NOTE:如果对这个结构比较模糊或者不太容易理解,可以参考下设计模式中的组合模式,有异曲同工之妙!

引言

报错说明

这个报错可以是下面任何一种形式,(甚至其他的任何基础查询类型的报错,这里不再追加列举)

"[multi_match] malformed query, expected [END_OBJECT] but found [FIELD_NAME]"

"[match] malformed query, expected [END_OBJECT] but found [FIELD_NAME]"

"[range] malformed query, expected [END_OBJECT] but found [FIELD_NAME]"

"[term] malformed query, expected [END_OBJECT] but found [FIELD_NAME]"

完整报错内容

{
  "error" : {
    "root_cause" : [
      {
        "type" : "parsing_exception",
        "reason" : "[multi_match] malformed query, expected [END_OBJECT] but found [FIELD_NAME]",
        "line" : 10,
        "col" : 5
      }
    ],
    "type" : "parsing_exception",
    "reason" : "[multi_match] malformed query, expected [END_OBJECT] but found [FIELD_NAME]",
    "line" : 10,
    "col" : 5
  },
  "status" : 400
}

查询语句

问题复现:

  • 单独的只有query.multi_match的查询没问题,可以正常查询
  • 单独的query.bool.filter组合多个过滤条件(上面简写省略了其他过滤字段,道理一样的),查询也正常
  • 但是如果我想干一件事情:我要多字段匹配同一个文本(在这些属性字段中,只要出现了这个文本就可以查询出该文档),同时按照一些条件过滤数据,想当然的在query下写了multi_match查询语句与并且并列的写了bool.filter语句,就报错了(查询语句如下所示)。

报错的查询语句

简化后如下:

GET product/_search
{
  "query": {
      "multi_match": { //这里引起报错的可以是match查询,range查询,term查询...
      "query": "手机",
      "fields": [
        "name",
        "subTitle"
      ]
    },
    "bool": {
      "filter": [
        {
          "range": {
            "price": {
              "gte": 5000,
              "lte": 20000
            }
          }
        }
      ]
    }
  }
}

parsing_exception|根本原因

parsing_exception表明是解析异常,也就是错误原因中的malformed query 格式有问题,也就是ES语法问题

单一的查询定义

这里要特别注意关键词语:单一的

query字段下不能同时包含match和bool查询作为并列的兄弟元素。在Elasticsearch中,每个query字段只能包含一个查询可以在bool查询内部使用mustshouldmust_notfilter子句来组合多个查询

单一的查询定义

Elasticsearch的查询DSL(Domain Specific Language)中,query 字段只能包含一个单一的查询定义。意味着不能直接在query字段下并列放置多个查询定义,比如 matchbool

查询DSL的这种结构是为了确保查询的清晰性和可解析性。

如果尝试在 query 字段下并列放置多个查询定义,Elasticsearch将无法解析这个查询,因为它不知道如何处理这种格式

单一的查询语义|并不意味着只能查询一个字段或只使用一种查询类型

这并不意味着只能查询一个字段或只能使用一种查询类型
相反,可以使用组合查询(如bool查询)来组合多个查询子句,并在单个query字段中指定它们。


正确的做法是将多个查询子句组合成一个单一的查询定义,通常通过使用 bool 查询来实现的。

例如,bool查询允许我们组合多个查询子句,如mustshouldmust_notfilter,这些子句都可以是任何有效的查询类型,如matchtermrange等。

但是,所有这些子句都必须被嵌套在bool查询内bool查询本身则是query字段的值

简单示例

这里是一个简单的例子,说明如何在单个query字段中使用bool查询来组合多个查询子句:

{  
  "query": {  
    "bool": {  
      "must": [  
        {  
          "match": {  
            "field1": "value1"  
          }  
        },  
        {  
          "term": {  
            "field2": "value2"  
          }  
        }  
      ],  
      "should": [  
        {  
          "match_phrase": {  
            "field3": "phrase value"  
          }  
        }  
      ],  
      "must_not": [  
        {  
          "term": {  
            "field4": "unwanted_value"  
          }  
        }  
      ]  
    }  
  }  
}

在这个例子中,query字段包含一个bool查询,该查询有mustshouldmust_not子句。

每个子句都可以包含其他类型的查询,如matchtermmatch_phrase

通过这种方式,我们可以在单个query字段中指定一个复杂的查询逻辑。

问题解决

通过上面的的分析可以得知:
我们之前报错的语句是写了一个基础查询语句multi_match)又并列写了一个bool复合查询语句,导致语法错误,无法解析,

因此,把基础查询语句multi_match)移到bool复合查询语句的内部,保证query单一查询语义

即改成下面的形式,可以正常运行


GET product/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "手机",
            "fields": [
              "name",
              "subTitle"
            ]
          }
        }
      ],
      "filter": [
        {
          "range": {
            "price": {
              "gte": 5000,
              "lte": 20000
            }
          }
        }
      ]
    }
  }
}

从Client API中也可印证:单一的查询(query)语义

high level rest client api

客户端构造查询的部分代码:
在这里插入图片描述

它调用的方法org.elasticsearch.search.builder.SearchSourceBuilder#query(org.elasticsearch.index.query.QueryBuilder)
在这里插入图片描述

源码中可见,它是直接赋值(注释中也写明了:设置search query到request中,没有说是添加到request中,它是一个单个字段,不是一个列表),只允许有一个query字段定义,即单一的查询定义,不是列表不能添加多个query对象

对照地,我们看下filter,它的源码是列表可以添加多个
在这里插入图片描述

Java api client:

客户端代码:
在这里插入图片描述

调用的方法是co.elastic.clients.elasticsearch.core.SearchRequest.Builder#query(java.util.function.Function<co.elastic.clients.elasticsearch._types.query_dsl.Query.Builder,co.elastic.clients.util.ObjectBuilder<co.elastic.clients.elasticsearch._types.query_dsl.Query>>)
在这里插入图片描述

它会调用同一个类中重载的query方法
co.elastic.clients.elasticsearch.core.SearchRequest.Builder#query(co.elastic.clients.elasticsearch._types.query_dsl.Query)
在这里插入图片描述

他也是直接query字段赋值,而不是列表添加进去,这意味着只能设置一个query对象进去

官方文档印证 | ES支持的子句类型

Query DSL

在这里插入图片描述

ES支持两种类型的查询语句(ES就两类查询)

(官方文档中的leaf quey,直译是叶子查询,可以理解为最基础的查询方式,可以联想到设计模式的组合模式,叶子和枝干,叶子就是最基础的元素,枝干可以组合叶子和其他枝干元素,也就是在这个结构中,叶子是最基础的存在。对应到查询中,就是最基础的查询,这样可能更好理解些。而复合查询就是可以组合最基础查询和复合查询。如果leaf query翻译成叶子查询,就不容易一下子理解)

同时,也可以这样理解,将Query DSL视为AST抽象语法树,——树就是由枝干和叶子节点构成,叶子代表最基础的查询语法,复合查询语句就是枝干,可以组合枝干(复合查询语句)和叶子(基础查询)。

Elasticsearch 提供了基于 JSON 的完整查询 DSL(领域特定语言)来定义查询。将查询 DSL 视为查询的 AST(抽象语法树),由两种类型的子句组成

  1. 一种是最基础查询语句(简单的查询,如仅单个match的查询,或者仅仅单个的term查询,或者仅仅单个的range查询)
  2. 一种是复合查询子句可以包含基础查询子句和复合查询子句(这和设计模式中的组合模式可以对比联想下,加深理解)。并被用于以逻辑方式组合多个查询(如 booldis_max 查询),或者改变这些查询的行为(如 constant_score 查询)。

如何理解呢?

为什么官方只支持两种类型的查询语句?
难道这两种查询语句就囊括了所有情况?
是的,没错,这个DSL语法树结构的抽象方式和设计模式中的组合模式非常类似。

一棵树,最基本的抽象就是叶子和树枝,树枝上可以组合叶子和树枝,这样就可以将一棵树表达出来

当抽象出最基础的查询方式,也就是ES所支持的最基础查询语法如match,term,range,multi_match等;

在一些复杂场景,使用复合查询语句,可以组合基础查询语句

当这个复合查询写好,也可作为一个元素组合到另外的组合查询中。

其他示例查看

可以再分析一个同样的问题,一个稍复杂的查询语句,加深下理解
Elasticsearch malformed query, expected [END_OBJECT] but found [FIELD_NAME]

  • 28
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值