嵌套查询

编辑

包装另一个查询以搜索嵌套字段。

nested 查询会像搜索单独的文档一样搜索嵌套字段对象。如果对象与搜索匹配,则 nested 查询会返回根父文档。

示例请求

编辑

索引设置

编辑

要使用 nested 查询,您的索引必须包含一个嵌套字段映射。例如

resp = client.indices.create(
    index="my-index-000001",
    mappings={
        "properties": {
            "obj1": {
                "type": "nested"
            }
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'my-index-000001',
  body: {
    mappings: {
      properties: {
        "obj1": {
          type: 'nested'
        }
      }
    }
  }
)
puts response
res, err := es.Indices.Create(
	"my-index-000001",
	es.Indices.Create.WithBody(strings.NewReader(`{
	  "mappings": {
	    "properties": {
	      "obj1": {
	        "type": "nested"
	      }
	    }
	  }
	}`)),
)
fmt.Println(res, err)
const response = await client.indices.create({
  index: "my-index-000001",
  mappings: {
    properties: {
      obj1: {
        type: "nested",
      },
    },
  },
});
console.log(response);
PUT /my-index-000001
{
  "mappings": {
    "properties": {
      "obj1": {
        "type": "nested"
      }
    }
  }
}

示例查询

编辑
resp = client.search(
    index="my-index-000001",
    query={
        "nested": {
            "path": "obj1",
            "query": {
                "bool": {
                    "must": [
                        {
                            "match": {
                                "obj1.name": "blue"
                            }
                        },
                        {
                            "range": {
                                "obj1.count": {
                                    "gt": 5
                                }
                            }
                        }
                    ]
                }
            },
            "score_mode": "avg"
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index-000001',
  body: {
    query: {
      nested: {
        path: 'obj1',
        query: {
          bool: {
            must: [
              {
                match: {
                  "obj1.name": 'blue'
                }
              },
              {
                range: {
                  "obj1.count": {
                    gt: 5
                  }
                }
              }
            ]
          }
        },
        score_mode: 'avg'
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithIndex("my-index-000001"),
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "nested": {
	      "path": "obj1",
	      "query": {
	        "bool": {
	          "must": [
	            {
	              "match": {
	                "obj1.name": "blue"
	              }
	            },
	            {
	              "range": {
	                "obj1.count": {
	                  "gt": 5
	                }
	              }
	            }
	          ]
	        }
	      },
	      "score_mode": "avg"
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  index: "my-index-000001",
  query: {
    nested: {
      path: "obj1",
      query: {
        bool: {
          must: [
            {
              match: {
                "obj1.name": "blue",
              },
            },
            {
              range: {
                "obj1.count": {
                  gt: 5,
                },
              },
            },
          ],
        },
      },
      score_mode: "avg",
    },
  },
});
console.log(response);
GET /my-index-000001/_search
{
  "query": {
    "nested": {
      "path": "obj1",
      "query": {
        "bool": {
          "must": [
            { "match": { "obj1.name": "blue" } },
            { "range": { "obj1.count": { "gt": 5 } } }
          ]
        }
      },
      "score_mode": "avg"
    }
  }
}

nested 的顶层参数

编辑
path
(必需,字符串)要搜索的嵌套对象的路径。
query

(必需,查询对象)您希望在 path 中的嵌套对象上运行的查询。如果对象与搜索匹配,则 nested 查询会返回根父文档。

您可以使用点表示法来搜索嵌套字段,包括完整路径,例如 obj1.name

自动支持并检测多级嵌套,从而产生一个内部嵌套查询,以自动匹配相关的嵌套级别,而不是根,如果它存在于另一个嵌套查询中。

有关示例,请参阅多级嵌套查询

score_mode

(可选,字符串)指示匹配的子对象的得分如何影响根父文档的相关性得分。有效值为

avg(默认)
使用所有匹配的子对象的平均相关性得分。
max
使用所有匹配的子对象的最高相关性得分。
min
使用所有匹配的子对象的最低相关性得分。
none
不使用匹配的子对象的相关性得分。该查询为父文档分配 0 的分数。
sum
将所有匹配的子对象的相关性得分相加。
ignore_unmapped

(可选,布尔值)指示是否忽略未映射的 path 且不返回任何文档,而不是返回错误。默认为 false

如果为 false,则如果 path 是未映射的字段,Elasticsearch 将返回错误。

您可以使用此参数来查询可能不包含字段 path 的多个索引。

注释

编辑

脚本查询的上下文

编辑

如果在嵌套查询中运行script 查询,则只能访问嵌套文档中的 doc values,而不能访问父文档或根文档。

多级嵌套查询

编辑

要了解多级嵌套查询的工作原理,首先需要一个具有嵌套字段的索引。以下请求定义了带有嵌套 makemodel 字段的 drivers 索引的映射。

resp = client.indices.create(
    index="drivers",
    mappings={
        "properties": {
            "driver": {
                "type": "nested",
                "properties": {
                    "last_name": {
                        "type": "text"
                    },
                    "vehicle": {
                        "type": "nested",
                        "properties": {
                            "make": {
                                "type": "text"
                            },
                            "model": {
                                "type": "text"
                            }
                        }
                    }
                }
            }
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'drivers',
  body: {
    mappings: {
      properties: {
        driver: {
          type: 'nested',
          properties: {
            last_name: {
              type: 'text'
            },
            vehicle: {
              type: 'nested',
              properties: {
                make: {
                  type: 'text'
                },
                model: {
                  type: 'text'
                }
              }
            }
          }
        }
      }
    }
  }
)
puts response
res, err := es.Indices.Create(
	"drivers",
	es.Indices.Create.WithBody(strings.NewReader(`{
	  "mappings": {
	    "properties": {
	      "driver": {
	        "type": "nested",
	        "properties": {
	          "last_name": {
	            "type": "text"
	          },
	          "vehicle": {
	            "type": "nested",
	            "properties": {
	              "make": {
	                "type": "text"
	              },
	              "model": {
	                "type": "text"
	              }
	            }
	          }
	        }
	      }
	    }
	  }
	}`)),
)
fmt.Println(res, err)
const response = await client.indices.create({
  index: "drivers",
  mappings: {
    properties: {
      driver: {
        type: "nested",
        properties: {
          last_name: {
            type: "text",
          },
          vehicle: {
            type: "nested",
            properties: {
              make: {
                type: "text",
              },
              model: {
                type: "text",
              },
            },
          },
        },
      },
    },
  },
});
console.log(response);
PUT /drivers
{
  "mappings": {
    "properties": {
      "driver": {
        "type": "nested",
        "properties": {
          "last_name": {
            "type": "text"
          },
          "vehicle": {
            "type": "nested",
            "properties": {
              "make": {
                "type": "text"
              },
              "model": {
                "type": "text"
              }
            }
          }
        }
      }
    }
  }
}

接下来,将一些文档索引到 drivers 索引。

$params = [
    'index' => 'drivers',
    'id' => '1',
    'body' => [
        'driver' => [
            'last_name' => 'McQueen',
            'vehicle' => [
                [
                    'make' => 'Powell Motors',
                    'model' => 'Canyonero',
                ],
                [
                    'make' => 'Miller-Meteor',
                    'model' => 'Ecto-1',
                ],
            ],
        ],
    ],
];
$response = $client->index($params);
$params = [
    'index' => 'drivers',
    'id' => '2',
    'body' => [
        'driver' => [
            'last_name' => 'Hudson',
            'vehicle' => [
                [
                    'make' => 'Mifune',
                    'model' => 'Mach Five',
                ],
                [
                    'make' => 'Miller-Meteor',
                    'model' => 'Ecto-1',
                ],
            ],
        ],
    ],
];
$response = $client->index($params);
resp = client.index(
    index="drivers",
    id="1",
    document={
        "driver": {
            "last_name": "McQueen",
            "vehicle": [
                {
                    "make": "Powell Motors",
                    "model": "Canyonero"
                },
                {
                    "make": "Miller-Meteor",
                    "model": "Ecto-1"
                }
            ]
        }
    },
)
print(resp)

resp1 = client.index(
    index="drivers",
    id="2",
    refresh=True,
    document={
        "driver": {
            "last_name": "Hudson",
            "vehicle": [
                {
                    "make": "Mifune",
                    "model": "Mach Five"
                },
                {
                    "make": "Miller-Meteor",
                    "model": "Ecto-1"
                }
            ]
        }
    },
)
print(resp1)
response = client.index(
  index: 'drivers',
  id: 1,
  body: {
    driver: {
      last_name: 'McQueen',
      vehicle: [
        {
          make: 'Powell Motors',
          model: 'Canyonero'
        },
        {
          make: 'Miller-Meteor',
          model: 'Ecto-1'
        }
      ]
    }
  }
)
puts response

response = client.index(
  index: 'drivers',
  id: 2,
  refresh: true,
  body: {
    driver: {
      last_name: 'Hudson',
      vehicle: [
        {
          make: 'Mifune',
          model: 'Mach Five'
        },
        {
          make: 'Miller-Meteor',
          model: 'Ecto-1'
        }
      ]
    }
  }
)
puts response
{
	res, err := es.Index(
		"drivers",
		strings.NewReader(`{
	  "driver": {
	    "last_name": "McQueen",
	    "vehicle": [
	      {
	        "make": "Powell Motors",
	        "model": "Canyonero"
	      },
	      {
	        "make": "Miller-Meteor",
	        "model": "Ecto-1"
	      }
	    ]
	  }
	}`),
		es.Index.WithDocumentID("1"),
		es.Index.WithPretty(),
	)
	fmt.Println(res, err)
}

{
	res, err := es.Index(
		"drivers",
		strings.NewReader(`{
	  "driver": {
	    "last_name": "Hudson",
	    "vehicle": [
	      {
	        "make": "Mifune",
	        "model": "Mach Five"
	      },
	      {
	        "make": "Miller-Meteor",
	        "model": "Ecto-1"
	      }
	    ]
	  }
	}`),
		es.Index.WithDocumentID("2"),
		es.Index.WithRefresh("true"),
		es.Index.WithPretty(),
	)
	fmt.Println(res, err)
}
const response = await client.index({
  index: "drivers",
  id: 1,
  document: {
    driver: {
      last_name: "McQueen",
      vehicle: [
        {
          make: "Powell Motors",
          model: "Canyonero",
        },
        {
          make: "Miller-Meteor",
          model: "Ecto-1",
        },
      ],
    },
  },
});
console.log(response);

const response1 = await client.index({
  index: "drivers",
  id: 2,
  refresh: "true",
  document: {
    driver: {
      last_name: "Hudson",
      vehicle: [
        {
          make: "Mifune",
          model: "Mach Five",
        },
        {
          make: "Miller-Meteor",
          model: "Ecto-1",
        },
      ],
    },
  },
});
console.log(response1);
PUT /drivers/_doc/1
{
  "driver" : {
        "last_name" : "McQueen",
        "vehicle" : [
            {
                "make" : "Powell Motors",
                "model" : "Canyonero"
            },
            {
                "make" : "Miller-Meteor",
                "model" : "Ecto-1"
            }
        ]
    }
}

PUT /drivers/_doc/2?refresh
{
  "driver" : {
        "last_name" : "Hudson",
        "vehicle" : [
            {
                "make" : "Mifune",
                "model" : "Mach Five"
            },
            {
                "make" : "Miller-Meteor",
                "model" : "Ecto-1"
            }
        ]
    }
}

您现在可以使用多级嵌套查询来匹配基于 makemodel 字段的文档。

resp = client.search(
    index="drivers",
    query={
        "nested": {
            "path": "driver",
            "query": {
                "nested": {
                    "path": "driver.vehicle",
                    "query": {
                        "bool": {
                            "must": [
                                {
                                    "match": {
                                        "driver.vehicle.make": "Powell Motors"
                                    }
                                },
                                {
                                    "match": {
                                        "driver.vehicle.model": "Canyonero"
                                    }
                                }
                            ]
                        }
                    }
                }
            }
        }
    },
)
print(resp)
response = client.search(
  index: 'drivers',
  body: {
    query: {
      nested: {
        path: 'driver',
        query: {
          nested: {
            path: 'driver.vehicle',
            query: {
              bool: {
                must: [
                  {
                    match: {
                      'driver.vehicle.make' => 'Powell Motors'
                    }
                  },
                  {
                    match: {
                      'driver.vehicle.model' => 'Canyonero'
                    }
                  }
                ]
              }
            }
          }
        }
      }
    }
  }
)
puts response
res, err := es.Search(
	es.Search.WithIndex("drivers"),
	es.Search.WithBody(strings.NewReader(`{
	  "query": {
	    "nested": {
	      "path": "driver",
	      "query": {
	        "nested": {
	          "path": "driver.vehicle",
	          "query": {
	            "bool": {
	              "must": [
	                {
	                  "match": {
	                    "driver.vehicle.make": "Powell Motors"
	                  }
	                },
	                {
	                  "match": {
	                    "driver.vehicle.model": "Canyonero"
	                  }
	                }
	              ]
	            }
	          }
	        }
	      }
	    }
	  }
	}`)),
	es.Search.WithPretty(),
)
fmt.Println(res, err)
const response = await client.search({
  index: "drivers",
  query: {
    nested: {
      path: "driver",
      query: {
        nested: {
          path: "driver.vehicle",
          query: {
            bool: {
              must: [
                {
                  match: {
                    "driver.vehicle.make": "Powell Motors",
                  },
                },
                {
                  match: {
                    "driver.vehicle.model": "Canyonero",
                  },
                },
              ],
            },
          },
        },
      },
    },
  },
});
console.log(response);
GET /drivers/_search
{
  "query": {
    "nested": {
      "path": "driver",
      "query": {
        "nested": {
          "path": "driver.vehicle",
          "query": {
            "bool": {
              "must": [
                { "match": { "driver.vehicle.make": "Powell Motors" } },
                { "match": { "driver.vehicle.model": "Canyonero" } }
              ]
            }
          }
        }
      }
    }
  }
}

搜索请求返回以下响应

{
  "took" : 5,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 3.7349272,
    "hits" : [
      {
        "_index" : "drivers",
        "_id" : "1",
        "_score" : 3.7349272,
        "_source" : {
          "driver" : {
            "last_name" : "McQueen",
            "vehicle" : [
              {
                "make" : "Powell Motors",
                "model" : "Canyonero"
              },
              {
                "make" : "Miller-Meteor",
                "model" : "Ecto-1"
              }
            ]
          }
        }
      }
    ]
  }
}

must_not 子句和 nested 查询

编辑

如果 nested 查询匹配文档中的一个或多个嵌套对象,它将返回该文档作为命中项。即使文档中的其他嵌套对象与查询不匹配,也是如此。当使用包含内部 must_not 子句nested 查询时,请记住这一点。

使用inner_hits参数查看哪些嵌套对象与 nested 查询匹配。

例如,以下搜索使用带有内部 must_not 子句的外部 nested 查询。

resp = client.indices.create(
    index="my-index",
    mappings={
        "properties": {
            "comments": {
                "type": "nested"
            }
        }
    },
)
print(resp)

resp1 = client.index(
    index="my-index",
    id="1",
    refresh=True,
    document={
        "comments": [
            {
                "author": "kimchy"
            }
        ]
    },
)
print(resp1)

resp2 = client.index(
    index="my-index",
    id="2",
    refresh=True,
    document={
        "comments": [
            {
                "author": "kimchy"
            },
            {
                "author": "nik9000"
            }
        ]
    },
)
print(resp2)

resp3 = client.index(
    index="my-index",
    id="3",
    refresh=True,
    document={
        "comments": [
            {
                "author": "nik9000"
            }
        ]
    },
)
print(resp3)

resp4 = client.search(
    index="my-index",
    query={
        "nested": {
            "path": "comments",
            "query": {
                "bool": {
                    "must_not": [
                        {
                            "term": {
                                "comments.author": "nik9000"
                            }
                        }
                    ]
                }
            }
        }
    },
)
print(resp4)
response = client.indices.create(
  index: 'my-index',
  body: {
    mappings: {
      properties: {
        comments: {
          type: 'nested'
        }
      }
    }
  }
)
puts response

response = client.index(
  index: 'my-index',
  id: 1,
  refresh: true,
  body: {
    comments: [
      {
        author: 'kimchy'
      }
    ]
  }
)
puts response

response = client.index(
  index: 'my-index',
  id: 2,
  refresh: true,
  body: {
    comments: [
      {
        author: 'kimchy'
      },
      {
        author: 'nik9000'
      }
    ]
  }
)
puts response

response = client.index(
  index: 'my-index',
  id: 3,
  refresh: true,
  body: {
    comments: [
      {
        author: 'nik9000'
      }
    ]
  }
)
puts response

response = client.search(
  index: 'my-index',
  body: {
    query: {
      nested: {
        path: 'comments',
        query: {
          bool: {
            must_not: [
              {
                term: {
                  'comments.author' => 'nik9000'
                }
              }
            ]
          }
        }
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "my-index",
  mappings: {
    properties: {
      comments: {
        type: "nested",
      },
    },
  },
});
console.log(response);

const response1 = await client.index({
  index: "my-index",
  id: 1,
  refresh: "true",
  document: {
    comments: [
      {
        author: "kimchy",
      },
    ],
  },
});
console.log(response1);

const response2 = await client.index({
  index: "my-index",
  id: 2,
  refresh: "true",
  document: {
    comments: [
      {
        author: "kimchy",
      },
      {
        author: "nik9000",
      },
    ],
  },
});
console.log(response2);

const response3 = await client.index({
  index: "my-index",
  id: 3,
  refresh: "true",
  document: {
    comments: [
      {
        author: "nik9000",
      },
    ],
  },
});
console.log(response3);

const response4 = await client.search({
  index: "my-index",
  query: {
    nested: {
      path: "comments",
      query: {
        bool: {
          must_not: [
            {
              term: {
                "comments.author": "nik9000",
              },
            },
          ],
        },
      },
    },
  },
});
console.log(response4);
PUT my-index
{
  "mappings": {
    "properties": {
      "comments": {
        "type": "nested"
      }
    }
  }
}

PUT my-index/_doc/1?refresh
{
  "comments": [
    {
      "author": "kimchy"
    }
  ]
}

PUT my-index/_doc/2?refresh
{
  "comments": [
    {
      "author": "kimchy"
    },
    {
      "author": "nik9000"
    }
  ]
}

PUT my-index/_doc/3?refresh
{
  "comments": [
    {
      "author": "nik9000"
    }
  ]
}

POST my-index/_search
{
  "query": {
    "nested": {
      "path": "comments",
      "query": {
        "bool": {
          "must_not": [
            {
              "term": {
                "comments.author": "nik9000"
              }
            }
          ]
        }
      }
    }
  }
}

搜索返回

{
  ...
  "hits" : {
    ...
    "hits" : [
      {
        "_index" : "my-index",
        "_id" : "1",
        "_score" : 0.0,
        "_source" : {
          "comments" : [
            {
              "author" : "kimchy"
            }
          ]
        }
      },
      {
        "_index" : "my-index",
        "_id" : "2",
        "_score" : 0.0,
        "_source" : {
          "comments" : [
            {
              "author" : "kimchy"              
            },
            {
              "author" : "nik9000"             
            }
          ]
        }
      }
    ]
  }
}

此嵌套对象与查询匹配。因此,搜索返回该对象的父文档作为命中项。

此嵌套对象与查询不匹配。由于同一文档中的另一个嵌套对象与查询匹配,因此搜索仍然返回父文档作为命中项。

要排除任何嵌套对象与 nested 查询匹配的文档,请使用外部 must_not 子句。

resp = client.search(
    index="my-index",
    query={
        "bool": {
            "must_not": [
                {
                    "nested": {
                        "path": "comments",
                        "query": {
                            "term": {
                                "comments.author": "nik9000"
                            }
                        }
                    }
                }
            ]
        }
    },
)
print(resp)
response = client.search(
  index: 'my-index',
  body: {
    query: {
      bool: {
        must_not: [
          {
            nested: {
              path: 'comments',
              query: {
                term: {
                  'comments.author' => 'nik9000'
                }
              }
            }
          }
        ]
      }
    }
  }
)
puts response
const response = await client.search({
  index: "my-index",
  query: {
    bool: {
      must_not: [
        {
          nested: {
            path: "comments",
            query: {
              term: {
                "comments.author": "nik9000",
              },
            },
          },
        },
      ],
    },
  },
});
console.log(response);
POST my-index/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "nested": {
            "path": "comments",
            "query": {
              "term": {
                "comments.author": "nik9000"
              }
            }
          }
        }
      ]
    }
  }
}

搜索返回

{
  ...
  "hits" : {
    ...
    "hits" : [
      {
        "_index" : "my-index",
        "_id" : "1",
        "_score" : 0.0,
        "_source" : {
          "comments" : [
            {
              "author" : "kimchy"
            }
          ]
        }
      }
    ]
  }
}