Link Search Menu Expand Document Documentation Menu

检索内部命中

在 OpenSearch 中,当你使用嵌套对象父子关联执行搜索时,底层命中(嵌套的内部对象或子文档)默认是隐藏的。你可以通过在搜索查询中使用 inner_hits 参数来检索内部命中。

你还可以将 inner_hits 用于以下功能:

嵌套对象的内部命中

嵌套对象允许你索引一个对象数组,并保持它们在同一文档中的关系。以下示例请求使用 inner_hits 参数来检索底层内部命中。

  1. 创建带有嵌套对象的索引映射

     PUT /my_index
     {
       "mappings": {
         "properties": {
           "user": {
             "type": "nested",
             "properties": {
               "name": { "type": "text" },
               "age": { "type": "integer" }
             }
           }
         }
       }
     }
    

  2. 索引数据

     POST /my_index/_doc/1
     {
       "group": "fans",
       "user": [
         {
           "name": "John Doe",
           "age": 28
         },
         {
           "name": "Jane Smith",
           "age": 34
         }
       ]
     }
    

  3. 使用 inner_hits 进行查询

     GET /my_index/_search
     {
       "query": {
         "nested": {
           "path": "user",
           "query": {
             "bool": {
               "must": [
                 { "match": { "user.name": "John" } }
               ]
             }
           },
           "inner_hits": {}
         }
       }
     }
    

上述查询搜索包含名称“John”的嵌套用户对象,并在响应的 inner_hits 部分返回匹配的嵌套文档。

{
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.6931471,
    "hits" : [
      {
        "_index" : "my_index",
        "_id" : "1",
        "_score" : 0.6931471,
        "_source" : {
          "group" : "fans",
          "user" : [
            {
              "name" : "John Doe",
              "age" : 28
            },
            {
              "name" : "Jane Smith",
              "age" : 34
            }
          ]
        },
        "inner_hits" : {
          "user" : {
            "hits" : {
              "total" : {
                "value" : 1,
                "relation" : "eq"
              },
              "max_score" : 0.6931471,
              "hits" : [
                {
                  "_index" : "my_index",
                  "_id" : "1",
                  "_nested" : {
                    "field" : "user",
                    "offset" : 0
                  },
                  "_score" : 0.6931471,
                  "_source" : {
                    "name" : "John Doe",
                    "age" : 28
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

父/子对象的内部命中

父子关联允许你在同一索引中创建不同类型文档之间的关系。以下示例请求使用父/子对象搜索 inner_hits

  1. 创建带有父子关联字段的索引

     PUT /my_index
     {
       "mappings": {
         "properties": {
           "my_join_field": {
             "type": "join",
             "relations": {
               "parent": "child"
             }
           },
           "text": {
             "type": "text"
           }
         }
       }
     }
    

  2. 索引数据

     # Index a parent document
     PUT /my_index/_doc/1
     {
       "text": "This is a parent document",
       "my_join_field": "parent"
     }
        
     # Index a child document
     PUT /my_index/_doc/2?routing=1
     {
       "text": "This is a child document",
       "my_join_field": {
         "name": "child",
         "parent": "1"
       }
     }
    

  3. 使用 inner_hits 进行搜索

     GET /my_index/_search
     {
       "query": {
         "has_child": {
           "type": "child",
           "query": {
             "match": {
               "text": "child"
             }
           },
           "inner_hits": {}
         }
       }
     }
    

上述查询搜索包含符合查询条件(在本例中,包含词条 "child")的子文档的父文档。它在响应的 inner_hits 部分返回匹配的子文档。

{
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "my_index",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "text" : "This is a parent document",
          "my_join_field" : "parent"
        },
        "inner_hits" : {
          "child" : {
            "hits" : {
              "total" : {
                "value" : 1,
                "relation" : "eq"
              },
              "max_score" : 0.6931471,
              "hits" : [
                {
                  "_index" : "my_index",
                  "_id" : "2",
                  "_score" : 0.6931471,
                  "_routing" : "1",
                  "_source" : {
                    "text" : "This is a child document",
                    "my_join_field" : {
                      "name" : "child",
                      "parent" : "1"
                    }
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

将父子关联和嵌套对象与 inner_hits 一起使用

以下示例演示了如何将父子关联和嵌套对象与 inner_hits 一起使用。

  1. 创建具有以下映射的索引

     PUT /my_index
     {
       "mappings": {
         "properties": {
           "my_join_field": {
             "type": "join",
             "relations": {
               "parent": "child"
             }
           },
           "text": {
             "type": "text"
           },
           "comments": {
             "type": "nested",
             "properties": {
               "user": { "type": "text" },
               "message": { "type": "text" }
             }
           }
         }
       }
     }
    

  2. 索引数据

     # Index a parent document
     PUT /my_index/_doc/1
     {
       "text": "This is a parent document",
       "my_join_field": "parent"
     }
        
     # Index a child document with nested comments
     PUT /my_index/_doc/2?routing=1
     {
       "text": "This is a child document",
       "my_join_field": {
         "name": "child",
         "parent": "1"
       },
       "comments": [
         {
           "user": "John",
           "message": "This is a comment"
         },
         {
           "user": "Jane",
           "message": "Another comment"
         }
       ]
     }
    

  3. 使用 inner_hits 进行查询

     GET /my_index/_search
     {
       "query": {
         "has_child": {
           "type": "child",
           "query": {
             "nested": {
               "path": "comments",
               "query": {
                 "bool": {
                   "must": [
                     { "match": { "comments.user": "John" } }
                   ]
                 }
               },
               "inner_hits": {}
             }
           },
           "inner_hits": {}
         }
       }
     }
    

上述查询搜索包含 John 发表的评论的子文档的父文档。指定 inner_hits 可确保返回匹配的子文档及其嵌套评论。

{
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "my_index",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "text" : "This is a parent document",
          "my_join_field" : "parent"
        },
        "inner_hits" : {
          "child" : {
            "hits" : {
              "total" : {
                "value" : 1,
                "relation" : "eq"
              },
              "max_score" : 0.6931471,
              "hits" : [
                {
                  "_index" : "my_index",
                  "_id" : "2",
                  "_score" : 0.6931471,
                  "_routing" : "1",
                  "_source" : {
                    "text" : "This is a child document",
                    "my_join_field" : {
                      "name" : "child",
                      "parent" : "1"
                    },
                    "comments" : [
                      {
                        "user" : "John",
                        "message" : "This is a comment"
                      },
                      {
                        "user" : "Jane",
                        "message" : "Another comment"
                      }
                    ]
                  },
                  "inner_hits" : {
                    "comments" : {
                      "hits" : {
                        "total" : {
                          "value" : 1,
                          "relation" : "eq"
                        },
                        "max_score" : 0.6931471,
                        "hits" : [
                          {
                            "_index" : "my_index",
                            "_id" : "2",
                            "_nested" : {
                              "field" : "comments",
                              "offset" : 0
                            },
                            "_score" : 0.6931471,
                            "_source" : {
                              "message" : "This is a comment",
                              "user" : "John"
                            }
                          }
                        ]
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

inner_hits 参数

你可以将以下附加参数传递给使用嵌套对象和父子关联的 inner_hits 搜索:

  • from:在 inner_hits 结果中开始获取命中的偏移量。
  • size:要返回的最大内部命中数。
  • sort:内部命中的排序顺序。
  • name:响应中内部命中的自定义名称。这有助于区分单个查询中的多个内部命中。

示例:带有嵌套对象的 inner_hits 参数

  1. 创建具有以下映射的索引

     PUT /products
     {
       "mappings": {
         "properties": {
           "product_name": { "type": "text" },
           "reviews": {
             "type": "nested",
             "properties": {
               "user": { "type": "text" },
               "comment": { "type": "text" },
               "rating": { "type": "integer" }
             }
           }
         }
       }
     }
    

  2. 索引数据

     POST /products/_doc/1
     {
       "product_name": "Smartphone",
       "reviews": [
         { "user": "Alice", "comment": "Great phone", "rating": 5 },
         { "user": "Bob", "comment": "Not bad", "rating": 3 },
         { "user": "Charlie", "comment": "Excellent", "rating": 4 }
       ]
     }
    

     POST /products/_doc/2
     {
       "product_name": "Laptop",
       "reviews": [
         { "user": "Dave", "comment": "Very good", "rating": 5 },
         { "user": "Eve", "comment": "Good value", "rating": 4 }
       ]
     }
    

  3. 使用 inner_hits 进行查询并提供附加参数

     GET /products/_search
     {
       "query": {
         "nested": {
           "path": "reviews",
           "query": {
             "match": { "reviews.comment": "Good" }
           },
           "inner_hits": {
             "from": 0,
             "size": 2,
             "sort": [
               { "reviews.rating": { "order": "desc" } }
             ],
             "name": "top_reviews"
           }
         }
       }
     }
    

以下是预期结果:

{
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.83740485,
    "hits" : [
      {
        "_index" : "products",
        "_id" : "2",
        "_score" : 0.83740485,
        "_source" : {
          "product_name" : "Laptop",
          "reviews" : [
            {
              "user" : "Dave",
              "comment" : "Very good",
              "rating" : 5
            },
            {
              "user" : "Eve",
              "comment" : "Good value",
              "rating" : 4
            }
          ]
        },
        "inner_hits" : {
          "top_reviews" : {
            "hits" : {
              "total" : {
                "value" : 2,
                "relation" : "eq"
              },
              "max_score" : null,
              "hits" : [
                {
                  "_index" : "products",
                  "_id" : "2",
                  "_nested" : {
                    "field" : "reviews",
                    "offset" : 0
                  },
                  "_score" : null,
                  "_source" : {
                    "rating" : 5,
                    "comment" : "Very good",
                    "user" : "Dave"
                  },
                  "sort" : [
                    5
                  ]
                },
                {
                  "_index" : "products",
                  "_id" : "2",
                  "_nested" : {
                    "field" : "reviews",
                    "offset" : 1
                  },
                  "_score" : null,
                  "_source" : {
                    "rating" : 4,
                    "comment" : "Good value",
                    "user" : "Eve"
                  },
                  "sort" : [
                    4
                  ]
                }
              ]
            }
          }
        }
      }
    ]
  }
}

示例:带有父子关联的 inner_hits 参数

  1. 创建具有以下映射的索引

     PUT /company
     {
       "mappings": {
         "properties": {
           "my_join_field": {
             "type": "join",
             "relations": {
               "employee": "task"
             }
           },
           "name": { "type": "text" },
           "description": {
             "type": "text",
             "fields": {
               "keyword": { "type": "keyword" }
             }
           }
         }
       }
     }
    

  2. 索引数据

     # Index a parent document
     PUT /company/_doc/1
     {
       "name": "Alice",
       "my_join_field": "employee"
     }
    

     # Index child documents
     PUT /company/_doc/2?routing=1
     {
       "description": "Complete the project",
       "my_join_field": {
         "name": "task",
         "parent": "1"
       }
     }
    

     PUT /company/_doc/3?routing=1
     {
       "description": "Prepare the report",
       "my_join_field": {
         "name": "task",
         "parent": "1"
       }
     }
    

     PUT /company/_doc/4?routing=1
     {
       "description": "Update project",
       "my_join_field": {
         "name": "task",
         "parent": "1"
       }
     }
    

  3. 使用 inner_hits 参数进行查询

     GET /company/_search
     {
       "query": {
         "has_child": {
           "type": "task",
           "query": {
             "match": { "description": "project" }
           },
           "inner_hits": {
             "from": 0,
             "size": 10,
             "sort": [
               { "description.keyword": { "order": "asc" } }
             ],
             "name": "related_tasks"
           }
         }
       }
     }
    

以下是预期结果:

{
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits": [
      {
        "_index": "company",
        "_id": "1",
        "_score": 1,
        "_source": {
          "name": "Alice",
          "my_join_field": "employee"
        },
        "inner_hits": {
          "related_tasks": {
            "hits": {
              "total": {
                "value": 2,
                "relation": "eq"
              },
              "max_score": null,
              "hits": [
                {
                  "_index": "company",
                  "_id": "2",
                  "_score": null,
                  "_routing": "1",
                  "_source": {
                    "description": "Complete the project",
                    "my_join_field": {
                      "name": "task",
                      "parent": "1"
                    }
                  },
                  "sort": [
                    "Complete the project"
                  ]
                },
                {
                  "_index": "company",
                  "_id": "4",
                  "_score": null,
                  "_routing": "1",
                  "_source": {
                    "description": "Update project",
                    "my_join_field": {
                      "name": "task",
                      "parent": "1"
                    }
                  },
                  "sort": [
                    "Update project"
                  ]
                }
              ]
            }
          }
        }
      }
    ]
  }
}

使用 inner_hits 的好处

  • 详细查询结果

    你可以使用 inner_hits 直接从父文档的搜索结果中检索匹配的嵌套或子文档的详细信息。这对于理解匹配的上下文和具体内容特别有用,而无需执行额外的查询。

    用例示例:在博客文章索引中,评论作为嵌套对象。搜索包含特定评论的博客文章时,你可以检索与搜索条件匹配的相关评论以及文章信息。

  • 优化性能

    如果不使用 inner_hits,你可能需要运行多个查询来获取相关文档。使用 inner_hits 将这些查询合并到一个查询中,减少了与 OpenSearch 服务器的往返次数,并提高了整体性能。

    用例示例:在电子商务应用程序中,产品是父文档,评论是子文档。使用 inner_hits 的单个查询可以获取产品及其相关评论,避免多次单独查询。

  • 简化查询逻辑

    你可以将父子或嵌套文档逻辑组合在一个查询中,以简化应用程序代码并降低复杂性。这有助于通过在 OpenSearch 中集中查询逻辑来确保代码更易于维护和一致。

    用例示例:在招聘门户中,职位是父文档,申请是嵌套或子文档。你可以通过在一个查询中同时获取职位和特定申请来简化应用程序逻辑。

  • 上下文相关性

    使用 inner_hits 通过精确显示哪些嵌套或子文档匹配查询条件来提供上下文相关性。这对于结果相关性取决于文档中与查询匹配的特定部分的应用程序至关重要。

    用例示例:在客户支持系统中,工单是父文档,评论或更新是嵌套或子文档。你可以确定哪条特定评论与搜索匹配,以便更好地理解工单搜索的上下文。

后续步骤