检索内部命中 (Inner Hits)
使用带有 inner_hits 参数的 search API 来检索导致每个搜索命中匹配的确切嵌套文档或父/子文档。parent-join 和 nested 功能允许返回在不同作用域中具有匹配项的文档。在父/子情况下,根据子文档中的匹配项返回父文档,或者根据父文档中的匹配项返回子文档。在嵌套情况下,根据嵌套内部对象中的匹配项返回文档。
在这两种情况下,导致文档返回的不同作用域中的实际匹配项是隐藏的。在许多情况下,了解哪些内部嵌套对象(对于嵌套情况)或子/父文档(对于父/子情况)导致返回了特定信息非常有用。内部命中功能可用于此目的。此功能在搜索响应的每个搜索命中中返回额外的嵌套命中,这些嵌套命中导致搜索命中在不同的作用域中匹配。
可以通过在 nested、has_child 或 has_parent 查询和过滤器上定义 inner_hits 定义来使用内部命中。其结构如下所示
"<query>" : {
"inner_hits" : {
<inner_hits_options>
}
}
如果在支持它的查询上定义了 inner_hits,则每个搜索命中将包含一个具有以下结构的 inner_hits json 对象
"hits": [
{
"_index": ...,
"_type": ...,
"_id": ...,
"inner_hits": {
"<inner_hits_name>": {
"hits": {
"total": ...,
"hits": [
{
"_id": ...,
...
},
...
]
}
}
},
...
},
...
]
内部命中支持以下选项
from- 从返回的常规搜索命中中,获取每个
inner_hits的第一个命中的起始偏移量。 size- 每个
inner_hits返回的最大命中数。默认情况下返回前三个匹配的命中。 sort- 每个
inner_hits的内部命中应如何排序。默认情况下,命中按评分排序。 name- 响应中用于特定内部命中定义的名称。当在单个搜索请求中定义了多个内部命中时,这非常有用。默认值取决于在哪个查询中定义了内部命中。对于
has_child查询和过滤器,这是子类型;对于has_parent查询和过滤器,这是父类型;对于 nested 查询和过滤器,这是嵌套路径。
内部命中还支持以下按文档的功能
嵌套的 inner_hits 可用于将嵌套内部对象包含为搜索命中的内部命中。
PUT test
{
"mappings": {
"properties": {
"comments": {
"type": "nested"
}
}
}
}
PUT test/_doc/1?refresh
{
"title": "Test title",
"comments": [
{
"author": "kimchy",
"number": 1
},
{
"author": "nik9000",
"number": 2
}
]
}
POST test/_search
{
"query": {
"nested": {
"path": "comments",
"query": {
"match": {"comments.number" : 2}
},
"inner_hits": {}
}
}
}
- nested 查询中的内部命中定义。不需要定义其他选项。
可以从上述搜索请求生成的响应代码片段示例
{
...,
"hits": {
"total" : {
"value": 1,
"relation": "eq"
},
"max_score": 1.0,
"hits": [
{
"_index": "test",
"_id": "1",
"_score": 1.0,
"_source": ...,
"inner_hits": {
"comments": {
"hits": {
"total" : {
"value": 1,
"relation": "eq"
},
"max_score": 1.0,
"hits": [
{
"_index": "test",
"_id": "1",
"_nested": {
"field": "comments",
"offset": 1
},
"_score": 1.0,
"_source": {
"author": "nik9000",
"number": 2
}
}
]
}
}
}
}
]
}
}
- 搜索请求中内部命中定义中使用的名称。可以通过
name选项使用自定义键。
在上面的示例中,_nested 元数据至关重要,因为它定义了此内部命中来自哪个内部嵌套对象。field 定义了嵌套命中所属的对象数组字段,以及相对于其在 _source 中的位置的 offset。由于排序和评分,命中对象在 inner_hits 中的实际位置通常与定义嵌套内部对象的位置不同。
默认情况下,inner_hits 中的命中对象也会返回 _source,但这可以更改。可以通过 _source 过滤功能返回部分源或将其禁用。如果在嵌套级别定义了存储字段,这些字段也可以通过 fields 功能返回。
一个重要的默认设置是,inner_hits 内部命中中返回的 _source 是相对于 _nested 元数据的。因此,在上面的示例中,每个嵌套命中仅返回评论部分,而不返回包含该评论的顶层文档的整个源。
嵌套文档没有 _source 字段,因为文档的整个源与其根文档一起存储在其 _source 字段下。要仅包含嵌套文档的源,需要解析根文档的源,并将与嵌套文档相关的部分作为源包含在内部命中中。对每个匹配的嵌套文档执行此操作会影响执行整个搜索请求所需的时间,尤其是当 size 和内部命中的 size 设置得高于默认值时。为了避免为嵌套内部命中进行相对昂贵的源提取,可以禁用包含源并完全依赖 doc values 字段。就像这样
PUT test
{
"mappings": {
"properties": {
"comments": {
"type": "nested"
}
}
}
}
PUT test/_doc/1?refresh
{
"title": "Test title",
"comments": [
{
"author": "kimchy",
"text": "comment text"
},
{
"author": "nik9000",
"text": "words words words"
}
]
}
POST test/_search
{
"query": {
"nested": {
"path": "comments",
"query": {
"match": {"comments.text" : "words"}
},
"inner_hits": {
"_source" : false,
"docvalue_fields" : [
"comments.text.keyword"
]
}
}
}
}
如果映射具有多层分层嵌套对象字段,则可以通过点表示法路径访问每一层。例如,如果有一个包含 votes 嵌套字段的 comments 嵌套字段,并且希望将 votes 与根命中直接一起返回,则可以定义以下路径
PUT test
{
"mappings": {
"properties": {
"comments": {
"type": "nested",
"properties": {
"votes": {
"type": "nested"
}
}
}
}
}
}
PUT test/_doc/1?refresh
{
"title": "Test title",
"comments": [
{
"author": "kimchy",
"text": "comment text",
"votes": []
},
{
"author": "nik9000",
"text": "words words words",
"votes": [
{"value": 1 , "voter": "kimchy"},
{"value": -1, "voter": "other"}
]
}
]
}
POST test/_search
{
"query": {
"nested": {
"path": "comments.votes",
"query": {
"match": {
"comments.votes.voter": "kimchy"
}
},
"inner_hits" : {}
}
}
}
其外观如下
{
...,
"hits": {
"total" : {
"value": 1,
"relation": "eq"
},
"max_score": 0.6931471,
"hits": [
{
"_index": "test",
"_id": "1",
"_score": 0.6931471,
"_source": ...,
"inner_hits": {
"comments.votes": {
"hits": {
"total" : {
"value": 1,
"relation": "eq"
},
"max_score": 0.6931471,
"hits": [
{
"_index": "test",
"_id": "1",
"_nested": {
"field": "comments",
"offset": 1,
"_nested": {
"field": "votes",
"offset": 0
}
},
"_score": 0.6931471,
"_source": {
"value": 1,
"voter": "kimchy"
}
}
]
}
}
}
}
]
}
}
这种间接引用仅支持嵌套内部命中。
父/子 inner_hits 可用于包含父文档或子文档
PUT test
{
"mappings": {
"properties": {
"my_join_field": {
"type": "join",
"relations": {
"my_parent": "my_child"
}
}
}
}
}
PUT test/_doc/1?refresh
{
"number": 1,
"my_join_field": "my_parent"
}
PUT test/_doc/2?routing=1&refresh
{
"number": 1,
"my_join_field": {
"name": "my_child",
"parent": "1"
}
}
POST test/_search
{
"query": {
"has_child": {
"type": "my_child",
"query": {
"match": {
"number": 1
}
},
"inner_hits": {}
}
}
}
- 内部命中定义类似于嵌套示例。
可以从上述搜索请求生成的响应代码片段示例
{
...,
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 1.0,
"hits": [
{
"_index": "test",
"_id": "1",
"_score": 1.0,
"_source": {
"number": 1,
"my_join_field": "my_parent"
},
"inner_hits": {
"my_child": {
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 1.0,
"hits": [
{
"_index": "test",
"_id": "2",
"_score": 1.0,
"_routing": "1",
"_source": {
"number": 1,
"my_join_field": {
"name": "my_child",
"parent": "1"
}
}
}
]
}
}
}
}
]
}
}