概要
[基础知识点]重点掌握:ElasticSearch Query DSL查询语句仅支持两类查询子句:
- 基础查询子句
- 和复合查询子句
将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
查询内部使用must
、should
、must_not
和filter
子句来组合多个查询。
单一的查询定义
在Elasticsearch
的查询DSL(Domain Specific Language)
中,query 字段只能包含一个单一的查询定义。意味着不能直接在query
字段下并列放置多个查询定义,比如 match
和 bool
。
查询DSL的这种结构是为了确保查询的清晰性和可解析性。
如果尝试在 query 字段下并列放置多个查询定义,Elasticsearch将无法解析这个查询,因为它不知道如何处理这种格式。
单一的查询语义|并不意味着只能查询一个字段或只使用一种查询类型
这并不意味着只能查询一个字段或只能使用一种查询类型。
相反,可以使用组合查询(如bool
查询)来组合多个查询子句,并在单个query
字段中指定它们。
正确的做法是将多个查询子句组合成一个单一的查询定义,通常通过使用 bool 查询来实现的。
例如,bool
查询允许我们组合多个查询子句,如must
、should
、must_not
和filter
,这些子句都可以是任何有效的查询类型,如match
、term
、range
等。
但是,所有这些子句都必须被嵌套在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
查询,该查询有must
、should
和must_not
子句。每个子句都可以包含其他类型的查询,如
match
、term
和match_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
(抽象语法树),由两种类型的子句组成:
- 一种是最基础查询语句(简单的查询,如仅单个match的查询,或者仅仅单个的term查询,或者仅仅单个的range查询)
- 一种是复合查询子句,可以包含基础查询子句和复合查询子句(这和设计模式中的组合模式可以对比联想下,加深理解)。并被用于以逻辑方式组合多个查询(如
bool
或dis_max
查询),或者改变这些查询的行为(如constant_score
查询)。
如何理解呢?
为什么官方只支持两种类型的查询语句?
难道这两种查询语句就囊括了所有情况?
是的,没错,这个DSL语法树结构的抽象方式和设计模式中的组合模式非常类似。
一棵树,最基本的抽象就是叶子和树枝,树枝上可以组合叶子和树枝,这样就可以将一棵树表达出来
当抽象出最基础的查询方式,也就是ES所支持的最基础查询语法如match
,term
,range
,multi_match
等;
在一些复杂场景,使用复合查询语句,可以组合基础查询语句。
当这个复合查询写好,也可作为一个元素组合到另外的组合查询中。
其他示例查看
可以再分析一个同样的问题,一个稍复杂的查询语句,加深下理解
Elasticsearch malformed query, expected [END_OBJECT] but found [FIELD_NAME]