1. 前言
在Elasticsearch这种分布式系统上执行完全类似SQL风格的连接查询代价比较高,作为替代,Elasticsearch提供了两种可水平伸缩的连接(Join)形式的查询。
- 嵌套查询(nested query)
- has_child和has_parent查询
2. 嵌套查询
嵌套查询(nested query)搜索嵌套(nested)类型的字段对象,就好像它们被索引为单独的文档一样。如果一个对象匹配搜索条件,嵌套查询将返回对象所在的父文档。
首先创建包含嵌套类型的映射的索引,示例如下:
PUT /my_index_01
{
"mappings" : {
"properties" : {
"my_object" : {
"type" : "nested"
}
}
}
}
PUT /my_index_01/_doc/1
{
"my_object" : [
{"name": "blue", "count": 10},
{"name": "red", "count": 1},
{"name": "yellow", "count": 20}
]
}
通过嵌套查询,示例如下:
GET /my_index_01/_search
{
"query": {
"nested" : {
"path" : "my_object",
"query" : {
"bool" : {
"must" : [
{ "match" : {"my_object.name" : "blue"} },
{ "range" : {"my_object.count" : {"gt" : 5}} }
]
}
},
"score_mode" : "avg"
}
}
}
nested查询顶级参数
- path:(必需的,string)要搜索的嵌套对象的路径。
- query:(必需的,查询object)
(1)在路径中的嵌套对象上运行的查询。
(2)可以使用包含完整路径(带点符号)搜索嵌套字段,比如my_object.name。
(3)自动支持并检测多层嵌套,内部嵌套查询自动匹配相关嵌套级别。 - score_mode:(可选的,string)表示匹配子对象的分数如何影响父文档的相关性分数。参数有效值如下:
(1)avg(默认):使用所有匹配子对象的相关性得分的平均值。
(2)max:使用所有匹配子对象中相关性得分最高值。
(3)min:使用所有匹配子对象中相关性得分最低值。
(4)none:不使用匹配子对象的相关性得分,设置父文档相关性得分为0。
(5)sum:将所有匹配子对象的相关性得分相加。 - ignore_unmapped:(可选的,boolean)表示是否忽略未映射的path字段。如果设置为true,那么Elasticsearch遇到未映射的path字段输入值将会忽略错误,并且不返回任何文档。默认值为false,默认情况下,如果path字段输入值未映射,Elasticsearch将返回一个错误。
多级嵌套查询
首先定义一个多级嵌套索引并索引数据,示例如下:
PUT /my_index_02
{
"mappings": {
"properties": {
"driver": {
"type": "nested",
"properties": {
"last_name": {
"type": "text"
},
"vehicle": {
"type": "nested",
"properties": {
"make": {
"type": "text"
},
"model": {
"type": "text"
}
}
}
}
}
}
}
}
PUT /my_index_02/_doc/1
{
"driver": {
"last_name": "McQueen",
"vehicle": [
{
"make": "Powell Motors",
"model": "Canyonero"
},
{
"make": "Miller-Meteor",
"model": "Ecto-1"
}
]
}
}
PUT /my_index_02/_doc/2?refresh
{
"driver": {
"last_name": "Hudson",
"vehicle": [
{
"make": "Mifune",
"model": "Mach Five"
},
{
"make": "Miller-Meteor",
"model": "Ecto-1"
}
]
}
}
多级嵌套查询,示例如下:
GET /my_index_02/_search
{
"query": {
"nested": {
"path": "driver",
"query": {
"nested": {
"path": "driver.vehicle",
"query": {
"bool": {
"must": [
{"match": { "driver.vehicle.make": "Powell Motors" }},
{"match": { "driver.vehicle.model": "Canyonero" }}
]
}
}
}
}
}
}
}
3. has_child查询
如果连接(join)的子文档与所输入的查询匹配,那么返回父文档,可以使用连接(join)字段映射在同一个索引的文档之间创建父子关系,如果要使用has_child查询,索引必须包含一个连接(join)字段映射。
使用has_child查询,首先创建包含连接(join)类型字段的索引,示例如下:
PUT /my_index_03
{
"mappings": {
"properties": {
"my_num": {
"type": "keyword"
},
"view_count": {
"type": "integer"
},
"my-join-field": {
"type": "join",
"relations": {
"parent": "child"
}
}
}
}
}
然后索引父文档和子文档,示例如下:
# 创建父文档
PUT /my_index_03/_doc/1
{"my_num":"001", "view_count": 10, "my-join-field":{"name":"parent"}}
# 创建子文档,parent指定父文档id
PUT /my_index_03/_doc/2?routing=1&refresh
{
"my_num": "002",
"view_count": 5,
"my-join-field": {
"name": "child",
"parent": "1"
}
}
使用has_child查询来查询文档中子文档匹配条件的文档,示例如下:
GET my_index_03/_search
{
"query": {
"has_child": {
"type": "child",
"query": {
"match_all": {}
},
"max_children": 10,
"min_children": 1,
"score_mode": "min"
}
}
}
has_child查询顶级参数
- type:(必需的,string)指定连接子关系的名称。
- query:(必需的,查询object)在type字段指定名称的子文档上运行的查询。如果其子文档与搜索匹配,查询将返回父文档。
- max_children:(可选的,integer)父文档中匹配的最大子文档数。如果父文档超过了这个限制,则从搜索结果中排除它。
- min_children:(可选的,integer)父文档中匹配所需的最小子文档数。如果父文档不满足此限制,则从搜索结果中排除它。
- score_mode:(可选的,string)表示匹配子文档的分数如何影响父文档的相关性分数。参数有效值如下:
(1)none(默认):不使用匹配子文档的相关性得分,查询返回的父文档相关性得分为0。
(2)max:使用所有匹配子文档中相关性得分最高值。
(3)min:使用所有匹配子文档中相关性得分最低值。
(4)avg:使用所有匹配子文档的相关性得分的平均值。
(5)sum:将所有匹配子文档的相关性得分相加。 - ignore_unmapped:(可选的,boolean)表示是否忽略未映射的type字段。默认值为false,默认情况下,如果type字段输入值未映射,Elasticsearch将返回一个错误。
排序
- 不支持标准排序选项对has_child查询的结果进行排序。
- 如果需要对返回的文档进行排序,可以使用function_score查询并按_score排序。例如,下面的查询根据子文档的view_count字段对返回的文档进行排序,示例如下:
GET my_index_03/_search
{
"query": {
"has_child": {
"type": "child",
"query": {
"function_score": {
"script_score": {
"script": "_score * doc['view_count'].value"
}
}
},
"score_mode": "max"
}
}
}
4. has_parent查询
如果连接(join)的父文档与所输入的查询匹配,那么返回子文档,可以使用连接(join)字段映射在同一个索引的文档之间创建父子关系。如果要使用has_parent查询,索引必须包含一个连接(join)字段映射。(与has_child查询用法类似)
使用has_parent查询,查询my_index_03索引中,文档的父文档中字段“my_num”值为“001”的子文档,示例如下:
GET /my_index_03/_search
{
"query": {
"has_parent": {
"parent_type": "parent",
"query":{"term":{"my_num":{"value":"001"}}}
}
}
}
返回匹配的子文档,结果片段如下:
has_parent查询顶级参数
- parent_type:(必需的,string)指定连接父关系的名称。
- query:(必需的,查询object)在type字段指定名称的父文档上运行的查询。如果其父文档与搜索匹配,查询将返回子文档。
- score:(可选的,boolean)表示是否将匹配父文档的相关性分数聚合到子文档中。默认值为false,默认情况下,Elasticsearch将忽略父文档的相关性得分。如果为true,则将匹配父文档的相关性得分聚合到子文档的相关性得分中。
- ignore_unmapped:(可选的,boolean)表示是否忽略未映射的parent_type字段。默认值为false,默认情况下,如果parent_type字段输入值未映射,Elasticsearch将返回一个错误。
排序
- 不支持标准排序选项对has_parent查询的结果进行排序。
- 如果需要对返回的文档进行排序,可以使用function_score查询并按_score排序。例如,下面的查询根据父文档的view_count字段对返回的文档进行排序,示例如下:
GET /my_index_03/_search
{
"query": {
"has_parent": {
"parent_type": "parent",
"score" : true,
"query" : {
"function_score" : {
"script_score": {
"script": "_score * doc['view_count'].value"
}
}
}
}
}
}
5. 父ID查询
parent_id查询返回匹配指定父ID的子文档。
例如,在my_index_03索引中,查询父ID等于1的子文档,示例如下:
GET /my_index_03/_search
{
"query": {
"parent_id": {
"type": "child",
"id": "1"
}
}
}
parent_id查询顶级参数
- type:(必需的,string)指定连接子关系的名称。
- id:(必需的,string)父文档的ID。查询将返回这个父文档的子文档。。
- ignore_unmapped:(可选的,boolean)表示是否忽略未映射的type字段。默认值为false,默认情况下,如果type字段输入值未映射,Elasticsearch将返回一个错误。