ES的嵌套文档Nested类型和数组扁平化?

小豆丁 21天前 ⋅ 9 阅读

"certificationList" : { "type" : "nested", "dynamic" : "false", "properties" : { "certificationTypeId" : { "type" : "keyword" }, "title" : { "type" : "keyword" } } }, ES的 "type" : "nested", 类型,可以直接执行 "query": { "term": { "certificationList.certificationTypeId" : "1976178135328456712" }} 吗?


在 Elasticsearch 中,对于定义为 "type": "nested" 的字段(如你的 certificationList),不能直接使用你提供的简单 term 查询来精确匹配嵌套文档内部的字段。

原因如下:

  1. 扁平化问题: 默认情况下,Elasticsearch 会将对象数组(object 类型)"扁平化"。这意味着数组中的每个对象的字段会被视为彼此独立的字段值列表。例如:

    JSON
    {
      "certificationList": [
        { "certificationTypeId": "A", "title": "Title A" },
        { "certificationTypeId": "B", "title": "Title B" }
      ]
    }
    

    在内部会被索引(近似)为:

    JSON
    {
      "certificationList.certificationTypeId": ["A", "B"],
      "certificationList.title": ["Title A", "Title B"]
    }
    

    你的 term 查询 "certificationList.certificationTypeId": "1976178135328456712" 会在这个扁平化的字段上查找,只要数组中任何一个嵌套文档的 certificationTypeId 匹配 1976178135328456712,该父文档就会被匹配。这看起来似乎能达到目的。

  2. nested 类型的目的: nested 类型正是为了解决这种扁平化带来的问题。当你将字段定义为 nested 时,Elasticsearch 会将数组中的每个对象作为一个独立的、隐藏的文档进行索引。这些嵌套文档与父文档是关联的,但它们在 Lucene 层面是独立的实体。

  3. 简单查询的局限性:termmatch 这样的简单查询,是作用在根文档的字段(或扁平化后的字段)上的。它们无法深入到这些独立的嵌套文档内部去精确匹配特定嵌套文档的字段组合。

如何正确查询 nested 字段?

你必须使用专门的 nested 查询。nested 查询允许你指定:

  • path: 要查询的嵌套字段的路径(这里是 "certificationList")。
  • query: 一个完整的查询子句,用于在该路径下的嵌套文档内部进行匹配。

修改后的正确查询:

JSON
{
  "query": {
    "nested": {
      "path": "certificationList", // 指定嵌套字段的路径
      "query": {
        "term": {
          "certificationList.certificationTypeId": { // 在嵌套文档内部进行 term 查询
            "value": "1976178135328456712"
          }
        }
      }
    }
  }
}

这个查询的含义是:

查找那些在 certificationList 这个嵌套数组中,至少包含一个嵌套文档,且该嵌套文档的 certificationTypeId 字段精确等于 "1976178135328456712" 的父文档。

总结:

直接使用 "term": { "certificationList.certificationTypeId" : "1976178135328456712" } 不会报错语法错误,Elasticsearch 会执行它。但是,它的行为是基于字段扁平化的逻辑,而不是你期望的、针对独立嵌套文档的精确匹配逻辑。对于 nested 类型的字段,为了确保查询在独立的嵌套文档上下文中执行并获得预期结果(即匹配包含具有特定属性值的特定嵌套对象的父文档),必须使用 nested 查询


{ "query": { "nested": { "path": "certificationList", // 指定嵌套字段的路径 "query": { "term": { "certificationList.certificationTypeId": { // 在嵌套文档内部进行 term 查询 "value": "1976178135328456712" } } } } } }和{ "query": {

  "path": "certificationList", // 指定嵌套字段的路径
  "query": {
    "term": {
      "certificationList.certificationTypeId": { // 在嵌套文档内部进行 term 查询
        "value": "1976178135328456712"
      }
    }
  }

} 的结果不一样??

头像

是的,这两个查询的结果肯定不一样,原因在于第二个查询的语法是错误的,会导致 Elasticsearch 解析失败。

让我们分析一下:

第一个查询(正确)

JSON
{
  "query": {
    "nested": { // 关键:使用了 nested 查询类型
      "path": "certificationList",
      "query": {
        "term": {
          "certificationList.certificationTypeId": {
            "value": "1976178135328456712"
          }
        }
      }
    }
  }
}
  • 结构正确: 它明确使用了 nested 查询类型。
  • 含义:certificationList 这个嵌套字段中,查找那些包含 certificationTypeId 等于 "1976178135328456712" 的嵌套文档的父文档。
  • 结果: 返回符合嵌套文档条件的父文档。

第二个查询(错误)

JSON
{
  "query": {
    "path": "certificationList", // 错误:缺少查询类型 (nested)
    "query": {
      "term": {
        "certificationList.certificationTypeId": {
          "value": "1976178135328456712"
        }
      }
    }
  }
}
  • 结构错误:query 层级下,直接出现了 pathquery 字段。Elasticsearch 期望 query 对象里包含的是一个具体的查询类型(如 match, term, bool, nested 等),而不是直接出现 pathquery
  • 结果: Elasticsearch 无法解析这个查询结构。它会抛出一个类似以下的错误:
    JSON
    {
      "error": {
        "root_cause": [
          {
            "type": "parsing_exception",
            "reason": "[nested] malformed query, expected [END_OBJECT] but found [FIELD_NAME]",
            "line": 3,
            "col": 15
          }
        ],
        "type": "parsing_exception",
        "reason": "[nested] malformed query, expected [END_OBJECT] but found [FIELD_NAME]",
        "line": 3,
        "col": 15
      },
      "status": 400
    }
    
    这个错误明确表示解析失败,它期望的是一个查询类型的结束符(}),却遇到了一个字段名(path)。它根本不会执行任何搜索操作。

总结

  • 第一个查询: 语法正确,使用 nested 查询类型,能正确搜索嵌套文档并返回匹配的父文档。
  • 第二个查询: 语法错误,缺少必要的查询类型标识符(nested),会导致 Elasticsearch 返回解析错误(HTTP 400 Bad Request),不会返回任何搜索结果。

因此,这两个查询的结果完全不同:一个返回有效结果,另一个返回错误信息。


{ "query": { "nested": { "path": "certificationList", // 指定嵌套字段的路径 "query": { "term": { "certificationList.certificationTypeId": { // 在嵌套文档内部进行 term 查询 "value": "1976178135328456712" } } } } } }和{

  "query": {
    "term": {
      "certificationList.certificationTypeId": {
        "value": "1976178135328456712"
      }
    }

} 的结果不一样??


您这次提供的两个查询确实不同,并且会产生不同的结果:

第一个查询(正确使用 nested 查询)

JSON
{
  "query": {
    "nested": {
      "path": "certificationList",
      "query": {
        "term": {
          "certificationList.certificationTypeId": {
            "value": "1976178135328456712"
          }
        }
      }
    }
  }
}

第二个查询(简单的 term 查询)

JSON
{
  "query": {
    "term": {
      "certificationList.certificationTypeId": {
        "value": "1976178135328456712"
      }
    }
  }
}

为什么结果不同?

  1. nested 查询(第一个)

    • 在嵌套文档内部进行精确匹配
    • 只匹配那些包含至少一个 certificationList 嵌套文档,且该嵌套文档的 certificationTypeId 等于 "1976178135328456712" 的父文档
    • 这是正确查询嵌套类型字段的方式
  2. 简单 term 查询(第二个)

    • 由于 certificationListnested 类型,Elasticsearch 在内部将其存储为独立的文档
    • 在父文档级别,不存在名为 certificationList.certificationTypeId 的字段
    • 这个查询会返回零结果,因为它试图查询一个不存在的字段

关键区别

特性 nested 查询 简单 term 查询
查询对象 嵌套文档内部 父文档字段
字段存在性 嵌套文档中存在该字段 父文档中不存在该字段
匹配逻辑 匹配包含特定嵌套文档的父文档 试图匹配父文档中不存在的字段
结果 返回匹配的父文档 返回零结果
适用性 正确查询嵌套字段的方式 对嵌套字段无效

简单来说

  • 第一个查询:正确地问"请找出所有包含至少一个认证类型ID为X的认证的简历"
  • 第二个查询:错误地问"请找出所有认证类型ID字段值为X的简历"(但这个字段在简历文档中根本不存在)

因此,这两个查询的结果会完全不同:第一个会返回匹配的文档,第二个会返回空结果。

全部评论: 0

    我有话说: