深度剖析Elasticsearch嵌套类型Nested

1、Nested 类型介绍

Elasticsearch 中的 nested 类型用于处理复杂数据结构中的嵌套对象。默认情况下,Elasticsearch 会将对象数组扁平化处理,导致查询时无法区分不同对象中的字段。nested 类型解决了这一问题,允许对嵌套对象进行独立索引和查询。

在 Elasticsearch 中,对象数组默认被扁平化。例如:

{
  "user": [
    {
      "name": "Alice",
      "age": 25
    },
    {
      "name": "Bob",
      "age": 30
    }
  ]
}

默认情况下,Elasticsearch 会将其转换为:

{
  "user.name": ["Alice", "Bob"],
  "user.age": [25, 30]
}

这种扁平化使得无法区分 name 和 age 的对应关系,导致查询时无法准确匹配。

本文需要读者对 nested 类型有足够的了解,如有需要,请移步:

2、场景案例

2.1 场景描述

假设在ES中有order索引存储商品订单信息,和order_goods,用以存储订单相关商品信息,商品数量可以是多个,一个订单可能对应多个商品,order_goods使用nested类型存储,现在我希望对订单及订单商品进行检索,下面的所有内容将围绕这一场景讲解。

2.2 测试数据

order 索引:其中 order_goods 为 nested 类型

PUT /order
{
  "mappings": {
    "properties": {
      "order_id": {
        "type": "keyword"
      },
      "user_id": {
        "type": "keyword"
      },
      "total_amount": {
        "type": "float"
      },
      "status": {
        "type": "keyword"
      },
      "created_at": {
        "type": "date"
      },
      "order_goods": {
        "type": "nested",
        "properties": {
          "goods_id": {
            "type": "keyword"
          },
          "goods_name": {
            "type": "text"
          },
          "price": {
            "type": "float"
          },
          "quantity": {
            "type": "integer"
          }
        }
      }
    }
  }
}


为索引添加两条测试数据,如下:

POST /order/_doc/1
{
  "order_id": "2001",
  "user_id": "3001",
  "total_amount": 1049.98,
  "status": "completed",
  "created_at": "2023-10-02T10:00:00Z",
  "order_goods": [
    {
      "goods_id": "1001",
      "goods_name": "iPhone 13",
      "price": 799.99,
      "quantity": 1
    },
    {
      "goods_id": "1003",
      "goods_name": "AirPods Pro",
      "price": 249.99,
      "quantity": 1
    }
  ]
}

POST /order/_doc/2
{
  "order_id": "2002",
  "user_id": "3002",
  "total_amount": 1999.99,
  "status": "pending",
  "created_at": "2023-10-02T11:00:00Z",
  "order_goods": [
    {
      "goods_id": "1002",
      "goods_name": "MacBook Pro",
      "price": 1999.99,
      "quantity": 1
    }
  ]
}

2.3 需求描述及DSL实现

查询订单 id1的订单,并且查看订单中与手机相关的商品

GET order/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "order_id": 2001
          }
        },
        {
          "nested": {
            "path": "order_goods",
            "query": {
              "match": {
                "order_goods.goods_name": "iPhone"
              }
            }
          }
        }
      ]
    }
  }
}

查询结果如下:
在这里插入图片描述

3、问题和剖析

3.1 坑

上述查询的语义其实是这样的:查询订单为1,且订单的商品与手机相关的记录,
这样其实会把订单中的其他商品也会查询出来,因为查询中并未对order_goods进行筛选,order_goods的查询,仅仅是对文档做筛选。其实上述查询,和下面查询本质上没什么区别,因为bool query中的两个查询子句查询的文档是重合的

# 下面查询和 3.2 中的查询,在本案例中,结果是没有区别的
GET order/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "order_id": 2001
          }
        }
      ]
    }
  }
}

3.2 解决方案

本质上,第二个查询子句,是希望对
如果需要筛选 order_goods中的商品,可以利用 nested查询结合inner_hits来实现:

{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "id": 1
          }
        },
        {
          "nested": {
            "path": "order_goods",
            "query": {
              "match": {
                "order_goods.goods_name": "iPhone"
              }
            },
            "inner_hits": {}
          }
        }
      ]
    }
  }
}

查询结果如下:
在这里插入图片描述

3.3 问题剖析

查询结果中,多出了inner_hits,并且结结果中 _source 已经是筛选之后的结果了,也就是说,以下这个查询逻辑:

"match": {
	"order_goods.goods_name": "iPhone"
}

是对 nested 中嵌套的 order_goods 生效的,而非上层查询生效,致辞即实现了第一小节中描述的场景的查询。

4、总结

4.1 使用场景

nested 类型适用于以下场景:

  • 对象数组的独立查询:当需要对数组中的每个对象进行独立查询时,例如查询博客的评论、订单的商品等。

  • 复杂数据结构的精确匹配:当需要确保数组中的对象字段之间的关联性时,例如查询某个用户的特定评论。

  • 嵌套对象的聚合操作:当需要对嵌套对象进行聚合分析时,例如计算评论的平均评分。

典型场景示例:

  • 博客系统中的评论(每条评论是一个嵌套对象)。

  • 电商系统中的订单商品(每个商品是一个嵌套对象)。

  • 用户系统中的用户地址(每个地址是一个嵌套对象)。

4.2 特点

4.2.1 优点

  • 精确查询:能够对嵌套对象中的字段进行精确匹配,避免默认扁平化导致的字段关联丢失问题。

  • 独立索引:每个嵌套对象被独立索引,支持复杂的查询和聚合操作。

  • 灵活性:支持嵌套对象的多层嵌套,适合处理复杂的文档结构。

4.2.2 缺点

  • 性能开销
    • 索引开销:每个嵌套对象都会被独立存储和索引,增加了索引的大小和写入的开销。
    • 查询开销:nested 查询比普通查询更复杂,可能导致查询性能下降,尤其是在嵌套对象数量较多时。
  • 复杂性:nested 查询和聚合的语法相对复杂,增加了开发和维护的难度。
  • 存储成本:由于嵌套对象被独立存储,可能会增加存储空间的占用。

4.3 建议

  • 仅在必要时使用:如果对象数组中的字段不需要独立查询或聚合,可以使用默认的扁平化处理,避免不必要的性能开销。

  • 控制嵌套层级:尽量避免多层嵌套,因为嵌套层级越深,查询和索引的开销越大。

  • 优化查询性能:

    • 使用 nested 查询时,尽量限制查询范围(例如通过 path 和 score_mode 参数优化)。

    • 对嵌套字段使用合适的索引设置(例如 doc_values 和 index 配置)。

  • 数据建模:

    • 在设计数据模型时,评估是否可以将嵌套对象拆分为独立的文档,通过父子关系(join 类型)或外部关联来替代嵌套结构。

    • 如果嵌套对象数量较少且查询需求简单,可以考虑使用 flattened 类型替代 nested 类型。

4.4 避坑

  • 避免过度嵌套:多层嵌套会导致查询性能急剧下降,建议将嵌套层级控制在 2 层以内。

  • 注意查询性能:

    • 避免在大规模嵌套对象上执行复杂的 nested 查询。

    • 使用 inner_hits 时,注意返回的嵌套对象数量,避免返回过多数据。

  • 索引设计:

    • 在索引设计阶段评估是否需要 nested 类型,避免后期重构。

    • 对嵌套字段的字段类型和索引设置进行优化,例如关闭不必要的字段索引。

  • 聚合性能:

    • 在嵌套对象上进行聚合时,注意聚合的深度和范围,避免性能问题。

    • 使用 reverse_nested 聚合时,确保聚合路径正确。

Pass the Pivotal Certified Professional exam using source code examples, study summaries, and mock exams. In this book, you’ll find a descriptive overview of certification-related Spring modules and a single example application demonstrating the use of all required Spring modules. Also, it is suitable as an introductory primer for Spring newcomers. Furthermore, in Pivotal Certified Professional Spring Developer Exam: A Study Guide each chapter contains a brief study summary and question set, and the book’s free downloadable source code package includes one mock exam (50 questions – like a real exam). After using this study guide, you will be ready to take and pass the Pivotal Certified Professional exam. When you become Pivotal Certified, you will have one of the most valuable credentials in Java. The demand for Spring skills is skyrocketing. Pivotal certification helps you advance your skills and your career, and get the maximum benefit from Spring. Passing the exam demonstrates your understanding of Spring and validates your familiarity with: container-basics, aspect oriented programming (AOP), data access and transactions, Spring Security, Spring Boot, microservices and the Spring model-view-controller (MVC). Good luck! What You’ll Learn Understand the core principles of the popular Spring Framework Use dependency injection Work with aspects in Spring and do AOP (aspect oriented programming) Control transactional behavior and work with SQL and NoSQL (MongoDB) databases Create and secure web applications based on Spring MVC Get to know the format of exam and type of questions in it Create Spring microservices applications Who This Book Is For Spring developers who have taken the Pivotal Core Spring class are eligible to take the Pivotal Certified Professional exam.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Elastic开源社区

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

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

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

打赏作者

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

抵扣说明:

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

余额充值