当前位置: 首页 > news >正文

第三阶段 25 · nested 嵌套对象与查询(数组对象最大的坑)

25 · nested 嵌套对象与查询(数组对象最大的坑)

阶段:第三阶段 / 查询 + 聚合
ES:nested类型 +nestedquery / agg | PostgreSQL:一对多子表 JOIN / JSONB 数组


1. 概念:为什么普通对象数组会“串味”

ES 默认把对象数组扁平化存储。假设一个订单里有多个明细:

{"order_no":"SO-1","items":[{"sku":"A","qty":10},{"sku":"B","qty":99}]}

默认(object类型)ES 内部会拆成:items.sku = [A, B]items.qty = [10, 99]——
字段间的对应关系丢了。于是你查「sku=A 且 qty=99」时,会错误命中这条文档
(因为 A 在 sku 列里、99 在 qty 列里,跨对象串味了)。

解决办法:把items声明为nested。nested 会把数组里每个对象作为独立的隐藏子文档
索引,字段对应关系得以保留——概念上就像一张一对多子表。


2. PostgreSQL 对照

-- nested 约等于一对多子表 JOINSELECTo.*FROMorders oJOINorder_items iONi.order_no=o.order_noWHEREi.sku='A'ANDi.qty=99;-- 条件作用在“同一行明细”上

关键点:nestedquery 的语义就是「同一个子对象内同时满足多个条件」,
对应 SQL 里条件落在 JOIN 后的同一行


3. ES DSL

3.1 定义 nested 字段(mapping)

PUT orders_idx { "mappings": { "properties": { "order_no": { "type": "keyword" }, "items": { "type": "nested", "properties": { "sku": { "type": "keyword" }, "qty": { "type": "integer" } } } } } }

3.2 nested 查询(同一子对象内满足多条件)

GET orders_idx/_search { "query": { "nested": { "path": "items", "query": { "bool": { "filter": [ { "term": { "items.sku": "A" } }, { "term": { "items.qty": 99 } } ] } }, "inner_hits": {} // 可选:回传到底是哪个子对象命中的 } } }

没有nested包裹时,上面这组条件会串味误命中;包了nested就只命中「同一条明细里 sku=A 且 qty=99」。

3.3 nested 聚合(先进子文档再聚合)

GET orders_idx/_search { "size": 0, "aggs": { "items_agg": { "nested": { "path": "items" }, "aggs": { "by_sku": { "terms": { "field": "items.sku" }, "aggs": { "total_qty": { "sum": { "field": "items.qty" } } } } } } } }

4. Spring Boot 实现

@ComponentpublicclassDoc25NestedQuery{@AutowiredprivateElasticsearchClientelasticsearchClient;/** 同一条明细里 sku=? 且 qty=?,并回传命中的子对象 */publicList<Map<String,Object>>byItem(StringindexName,Stringsku,intqty)throwsIOException{SearchResponse<Map>resp=elasticsearchClient.search(s->s.index(indexName).query(q->q.nested(n->n.path("items").query(nq->nq.bool(b->b.filter(f->f.term(t->t.field("items.sku").value(sku))).filter(f->f.term(t->t.field("items.qty").value(qty))))).innerHits(ih->ih))),// inner_hits:拿到具体命中的子对象Map.class);returnresp.hits().hits().stream().map(Hit::source).filter(Objects::nonNull).collect(Collectors.toList());}}

nestedpath必须与 mapping 里的 nested 字段名一致;子字段用「path.子字段」引用。
inner_hits拿命中的子对象:hit.innerHits().get("items").hits().hits()


5. 坑与最佳实践

  1. 必须先在 mapping 里声明nested:类型一旦建好不能从object原地改成nested,需 reindex(第 33 篇)。
  2. 查询/聚合都要用nested包裹:直接term("items.sku")不会串味报错,但语义错(跨对象命中)。
  3. nested 有成本:每个子对象是独立 Lucene 文档,数量大时占空间、变慢;用index.mapping.nested_objects.limit兜底。
  4. 数组内对象少而稳定才适合 nested;关系复杂、需独立更新子项,考虑 join(parent-child,代价更高)。
  5. inner_hits才能知道“哪条子对象命中”,做高亮/回显时很有用。

需要「独立文档的父子关系 / 跨文档关联 / 什么时候该反范式」,见第 51 篇《join 父子关系与关系建模》。


下一篇

26-highlight-高亮.md


← 上一篇:24-filter-bucket聚合 | 总览 | 下一篇:26-highlight-高亮 →

http://www.jsqmd.com/news/1312619/

相关文章:

  • 合肥注册公司科普:经营范围一般项目与许可项目合规填报指南
  • PMP报考培训机构怎么核验和报名? - 众智商学院职业教育
  • (2026最新)南通防水补漏本地人必选正规靠谱公司推荐|房屋漏水检测维修师傅上门|卫生间厨房阳台房顶外墙漏水精准测漏 - 吉林同城获客
  • 如何免费解锁Wand游戏修改器的高级功能?3步实现完整体验
  • GTA5线上小助手终极指南:技术革命重塑游戏体验
  • 崩坏星穹铁道三月七小助手:终极全自动游戏辅助完全指南
  • 2026通辽家装行业观察,美佳岚筑整体家居解读整装发展趋势 - 国麟测评
  • Wio Terminal连接Xbox手柄:USB Host模式与嵌入式交互开发实践
  • 苏州姑苏区防水维修白皮书:5项国标硬标准+12大全场景对症+本地避坑(2026.8新) - 超人防水
  • Pix4D正射影像生成全流程记录:从数据到成果的规范化追溯
  • 如何用三月七小助手每天节省2小时星穹铁道游戏时间?
  • 口碑好的橡胶板厂家怎么选 零套路价格透明不踩雷 - 工业设备
  • 软件测试实战:Bug生命周期、等级划分与报告撰写全解析
  • Exeinfo PE逆向分析入门:从文件识别到加壳检测实战指南
  • 网盘直链下载助手终极指南:九大平台文件直链解析技术深度解析
  • 计算机组成原理期末复习:从核心考点到解题策略的底层逻辑
  • 2026 年新发布:义马比较好的车辆称重系统供货厂家哪家权威,货车过磅时的猫腻,竟被这玩意儿给戳破了?-正飞地磅 - 行业推荐【认证官】
  • 成都摩托车后备箱供应商厂家推荐:2026年优质企业筛选指南 - 优质品牌商家
  • 开放平台API签名不一致排查指南:从原理到实战解决
  • 系统设计基石:可靠性、可用性、一致性等核心性质深度解析
  • (2026最新)泰州防水补漏本地人必选的正规靠谱公司推荐-房屋漏水检测维修师傅上门-卫生间厨房阳台房顶外墙漏水检测精准测漏 - 吉林同城获客
  • 2026静音门窗十大热门厂家真实横评,选定再买不交智商税 - 工业设备
  • 机房供电安全
  • Dubbo 线程池爆满排查实录:当线程池满了不是因为请求太多
  • 质量好的成都紧固件批发哪里有卖?2026年厂家推荐与采购指南 - 优质品牌商家
  • PyCharm配置PySide6开发环境:自动化UI编译与高效工作流搭建
  • Unity游戏开发:宝箱随机事件系统设计与实现实战
  • ROS与MoveIt!实现协作机器人复杂轨迹规划实战
  • 深耕抖店运营才懂:铺货不做图自查,商品频繁下架是常态坑 - 抖大侠
  • 终极指南:如何用TPFanCtrl2实现ThinkPad风扇的128级精准控制