【Elasticsearch】映射:Nested 类型

索引 & 映射》系列,共包含以下文章:

😊 如果您觉得这篇文章有用 ✔️ 的话,请给博主一个一键三连 🚀🚀🚀 吧 (点赞 🧡、关注 💛、收藏 💚)!!!您的支持 💖💖💖 将激励 🔥 博主输出更多优质内容!!!

Nested(嵌套)类型是 Elasticsearch 中一种特殊的数据类型,用于处理 对象数组object arrays)中对象的独立性。在默认情况下,Elasticsearch 会将对象数组 扁平化flattened),这会导致 数组内对象之间的关联丢失。Nested 类型解决了这个问题,它使数组中的每个对象被独立索引和查询。

1.为什么需要 Nested 类型

考虑以下文档结构:

{
  "user": "John",
  "comments": [
    {
      "text": "Great product!",
      "votes": 5
    },
    {
      "text": "Terrible experience",
      "votes": 1
    }
  ]
}

默认情况下(使用 object 类型)查询 text 包含 Great,且 votes 等于 1 会错误地匹配这个文档,因为 Elasticsearch 将数组扁平化为:

comments.text: ["Great product!", "Terrible experience"]
comments.votes: [5, 1]

使用 nested 类型可以保持数组内对象的独立性。

2.如何定义 Nested 类型

在映射中明确声明字段为 nested 类型:

PUT /my_index
{
  "mappings": {
    "properties": {
      "comments": {
        "type": "nested", 
        "properties": {
          "text": { "type": "text" },
          "votes": { "type": "integer" }
        }
      }
    }
  }
}

在这里插入图片描述

3.相关操作

3.1 索引包含 Nested 数据的文档

PUT /my_index/_doc/1
{
  "user": "John",
  "comments": [
    {
      "text": "Great product!",
      "votes": 5
    },
    {
      "text": "Terrible experience",
      "votes": 1
    }
  ]
}

在这里插入图片描述

3.2 查询 Nested 数据

使用 nested 查询来查询嵌套对象:

GET /my_index/_search
{
  "query": {
    "nested": {
      "path": "comments",
      "query": {
        "bool": {
          "must": [
            { "match": { "comments.text": "Great" } },
            { "range": { "comments.votes": { "gte": 5 } } }
          ]
        }
      }
    }
  }
}

在这里插入图片描述

3.3 聚合 Nested 数据

使用 nested 聚合分析嵌套字段:

GET /my_index/_search
{
  "aggs": {
    "comments_agg": {
      "nested": {
        "path": "comments"
      },
      "aggs": {
        "avg_votes": {
          "avg": { "field": "comments.votes" }
        }
      }
    }
  }
}

在这里插入图片描述

3.4 排序 Nested 数据

使用 nested 排序:

GET /my_index/_search
{
  "sort": [
    {
      "comments.votes": {
        "order": "desc",
        "nested": {
          "path": "comments"
        }
      }
    }
  ]
}

在这里插入图片描述

3.5 更新 Nested 文档中的特定元素

需要使用脚本更新:

POST /my_index/_update/1
{
  "script": {
    "source": """
      if (ctx._source.comments != null) {
        for (int i = 0; i < ctx._source.comments.size(); i++) {
          if (ctx._source.comments[i].text == 'Great product!') {
            ctx._source.comments[i].votes++;
          }
        }
      }
    """
  }
}

在这里插入图片描述

返回结果说明如下:

核心字段

字段
含义
_index文档所属的索引名称(这里是 "my_index")。
_type文档类型(Elasticsearch 7.x 后默认为 "_doc",表示单类型索引)。
_id被更新文档的唯一 ID(这里是 "1")。
_version文档的当前版本号(从 1 变为 2,表示这是第二次修改)。
result操作结果("updated" 表示文档已成功更新)。

分片信息(_shards

字段
含义
total需要更新的分片总数(主分片 + 副本分片,这里是 2)。
successful成功更新的分片数(这里是 2,表示主分片和副本分片均更新成功)。
failed更新失败的分片数(这里是 0,表示无失败)。
  • 如果集群有副本分片,total 可能大于 1(例如主分片 + 1个副本 = 2)。
  • failed > 0,需检查集群健康状态或分片分配问题。

并发控制字段

字段
含义
_seq_no序列号(Sequence Number),用于乐观并发控制(每次修改递增)。
_primary_term主分片任期(Primary Term),用于区分主分片是否发生过切换(如节点重启)。
  • 这两个字段可用于实现 乐观锁(Optimistic Concurrency Control)。
  • 例如,下次更新时可通过指定 if_seq_noif_primary_term 避免并发冲突。

业务含义总结

  • 文档更新成功
    • 版本号 _version1 变为 2,说明这是对文档的第二次修改。
    • result: "updated" 确认了更新操作已生效(如果是首次创建,会返回 "created")。
  • 集群状态健康
    • 所有分片(total: 2)均成功更新(successful: 2),无失败(failed: 0)。
  • 后续操作依据
    • _seq_no_primary_term 可用于后续的并发更新控制。

常见问题

  • 如果 resultnoop:表示脚本执行后未实际修改文档(例如条件不满足或值未变化)。
  • 如果 failed > 0:需检查副本分片是否不可用(如节点宕机或网络问题)。

如果需要进一步验证更新内容,可以通过 GET /my_index/_doc/1 查询文档最新状态。

在这里插入图片描述

4.Nested 类型的高级操作

4.1 内嵌 inner hits

🚀 inner_hits 是 Elasticsearch 中的一个功能,它允许你在查询嵌套对象或父子文档时,获取匹配的内部嵌套结果或子文档的详细信息。

获取匹配的嵌套对象详情:

GET /my_index/_search
{
  "query": {
    "nested": {
      "path": "comments",
      "query": {
        "match": { "comments.text": "product" }
      },
      "inner_hits": {}
    }
  }
}

在这里插入图片描述

4.2 多级嵌套

Elasticsearch 支持多级嵌套,但要注意性能影响:

PUT /my_index
{
  "mappings": {
    "properties": {
      "users": {
        "type": "nested",
        "properties": {
          "name": { "type": "text" },
          "comments": {
            "type": "nested",
            "properties": {
              "text": { "type": "text" },
              "votes": { "type": "integer" }
            }
          }
        }
      }
    }
  }
}

5.注意事项

  • 性能考虑
    • Nested 文档会作为独立文档索引,增加索引大小
    • 查询 nested 字段比普通字段更耗资源
    • 避免过度嵌套(通常不超过 2 2 2 级)
  • 限制
    • 一个索引默认最多包含 50 50 50nested 字段(index.mapping.nested_fields.limit
    • 一个文档中所有 nested 对象数默认不超过 10000 10000 10000 个(index.mapping.nested_objects.limit
  • 与父子文档对比
    • Nested 适合小规模、紧密关联的数据
    • 父子文档(join 类型)适合大规模、松散关联的数据
  • 更新开销
    • 更新父文档会替换整个 nested 数组
    • 频繁更新 nested 字段会影响性能
  • 查询特殊性
    • 必须使用 nested 查询来查询 nested 字段
    • 普通查询无法正确匹配 nested 对象间的关系

通过合理使用 nested 类型,可以准确建模和查询复杂的一对多关系数据,但要注意其对性能的影响。

### ElasticsearchNested 类型的用法和特性 #### 使用场景 在处理复杂数据结构时,特别是当文档内嵌有数组或其他复合对象的情况下,`Nested` 类型显得尤为重要。如果这些内部对象需要独立查询或过滤,则应考虑使用 `Nested` 字段而不是普通的对象映射。这是因为普通对象会被扁平化成一系列键值对,在这种情况下,跨多个子对象的关系会丢失[^4]。 对于那些包含多层结构的数据集来说,比如博客文章及其评论、产品与其属性标签等,利用 `Nested` 可以保持各条目之间的关联性并支持更精确地检索特定条件下的记录[^5]。 #### 示例说明 假设有一个名为 `my_index` 的索引用于存储博客帖子以及它们所附带的一组评论: ```json PUT my_index { "mappings": { "_doc": { "properties": { "title": { "type": "text" }, "comments": { "type": "nested", "properties": { "name": { "type": "keyword"}, "age": {"type":"integer"} } } } } } } ``` 上述配置定义了一个具有两个主要部分的 `_doc` 映射——一个是标题 (`title`) ,另一个是带有嵌套特性的 `comments` 数组。每个评论都由用户名(`name`) 和年龄(`age`) 组成[^1]。 为了向此索引添加一条新纪录,可以执行如下命令: ```json POST my_index/_doc/1 { "title": "Elasticsearch Join 示例", "comments":[ { "name":"张三", "age":28 }, { "name":"李四", "age":30 } ] } ``` 现在要查找所有含有名字叫 “John” 并且年龄为 34 岁的人作为评论者的博客贴文,可采用下面的方式构建查询语句: ```json GET /my_index/_search?pretty=true { "query": { "nested": { "path": "comments", "query": { "bool": { "must": [ { "match": { "comments.name": "John" }}, { "term": { "comments.age": 34 }} ] } } } } } ``` 注意这里使用了 `nested` 查询来指定路径,并针对该路径下具体的字段进行匹配操作。这确保了即使存在同名不同龄的情况也不会误判[^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大数据与AI实验室

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值