Elasticsearch 中 object 和 nested 类型有什么区别?
简化版
object 会把对象数组扁平化,数组内多个对象之间的字段关系可能丢失;nested 会把数组里的每个对象作为隐藏子文档索引,能保证同一个数组元素内的条件匹配。对象数组需要按元素关联查询时用 nested,否则普通 object 更轻。
详细版
例如一篇文档有两个评论:
[
{ "user": "Alice", "score": 5 },
{ "user": "Bob", "score": 1 }
]
如果用普通 object,查询 user=Alice AND score=1 可能错误命中,因为扁平化后用户和分数的对应关系丢了。nested 会保留每个对象边界,必须用 nested query 查询。面试要强调:nested 更准确但更重,写入、查询和聚合成本更高,不要无脑使用。
完整版教学
一、问题来自对象数组的关系丢失
Elasticsearch 没有传统关系型数据库里的行内子表概念。普通 object 字段在索引时会被展开成多个字段。
文档:
{
"comments": [
{ "user": "Alice", "score": 5 },
{ "user": "Bob", "score": 1 }
]
}
如果作为 object 处理,逻辑上容易变成:
comments.user = ["Alice", "Bob"]
comments.score = [5, 1]
Alice 和 5 的配对关系不再天然保留。
记忆钩子:object 展平字段,nested 保留对象边界。
二、object 的误命中例子
假设我们想查“Alice 给了 1 分”的文档:
{
"query": {
"bool": {
"must": [
{ "term": { "comments.user": "Alice" } },
{ "term": { "comments.score": 1 } }
]
}
}
}
普通 object 可能命中上面的文档,因为它只知道 comments.user 里有 Alice,也知道 comments.score 里有 1,却不知道这两个值来自不同评论对象。
这个坑在订单明细、商品规格、用户标签属性、问卷答案里非常常见。
三、nested 用隐藏子文档保留边界
nested 会把数组中的每个对象作为独立的隐藏文档建立索引,同时仍然属于父文档。
{
"mappings": {
"properties": {
"comments": {
"type": "nested",
"properties": {
"user": { "type": "keyword" },
"score": { "type": "integer" }
}
}
}
}
}
查询时要使用 nested query:
{
"query": {
"nested": {
"path": "comments",
"query": {
"bool": {
"must": [
{ "term": { "comments.user": "Alice" } },
{ "term": { "comments.score": 1 } }
]
}
}
}
}
}
这样两个条件必须在同一个 nested 对象里成立。
四、nested 的成本比 object 高
nested 不是免费精确。一个父文档里有 20 个 nested 对象,底层可能对应 1 个父文档加 20 个隐藏子文档。
1 parent document
+ 20 nested objects
= 21 Lucene documents
这会增加索引体积、写入成本、查询成本和聚合复杂度。父文档更新时,相关 nested 子文档也会受影响。
所以如果只是展示嵌套结构,不需要按数组元素内部多个条件关联查询,普通 object 通常更合适。
五、nested 聚合和 inner_hits 是常见追问
如果要对 nested 字段聚合,需要 nested aggregation。否则聚合可能在父文档语义下理解错误。
{
"aggs": {
"comments": {
"nested": { "path": "comments" },
"aggs": {
"avg_score": { "avg": { "field": "comments.score" } }
}
}
}
}
如果想知道到底哪个 nested 对象命中,可以用 inner_hits 返回匹配的内部对象。这对调试很有用。
六、怎么在 object、nested 和拆索引之间选
选择可以按查询需求判断。
| 建模方式 | 适合场景 | 代价 |
|---|---|---|
object | 只展示嵌套字段,条件不需同元素关联 | 可能误命中 |
nested | 对象数组内多条件必须同元素匹配 | 索引和查询更重 |
| 拆成独立索引 | 子对象很多、生命周期独立 | 应用侧 join 成本 |
面试里给出这个取舍,会比只背定义更像真实工程经验。
七、常见误区与追问
- 误区:object 能天然保证数组对象内字段配对。 object 会扁平化,多条件可能匹配到不同对象。
- 误区:nested 性能一定更好。 nested 是为准确性付出成本,不是性能优化银弹。
- 误区:nested 查询可以直接用普通 bool。 nested 字段要用 nested query 指定 path。
- 追问:inner_hits 有什么用? 返回命中的 nested 子对象,方便解释哪个内部元素满足条件。
- 追问:nested 对文档数量有什么影响? 每个 nested 对象会成为隐藏 Lucene 文档,数量和成本都会增加。
- 追问:什么时候拆成独立索引? 子对象数量很大、独立更新频繁、需要单独检索或生命周期独立时考虑。
八、加强记忆
object 和 nested 记成“展平 vs 保边界”。普通 object 会把对象数组字段分别展开,容易出现跨对象误命中;nested 把每个对象作为隐藏子文档,用 nested query 保证条件在同一对象内成立。nested 准确但更重,只有当数组对象内部字段关系真的要被查询时才值得用。