内嵌文档查询
本教程共 50 篇 · 第 18 篇 · 更新于 2026-07-30 · 约 6 分钟阅读
18. 内嵌文档查询
本节目标:学会用点号路径查询内嵌文档的子字段,理解整文档匹配的写法,并弄懂为什么整文档匹配对字段顺序那么挑剔。
在 MongoDB 里,一个字段的值可以又是一个完整文档。这种「文档套文档」的结构叫内嵌文档(Embedded Document)。我们的 products 集合里就有一个 location 字段,它存的是地理坐标点:
// 先建示例集合,后面所有查询都基于它
db.products.insertMany([
{ _id: 101, name: "机械键盘", price: 399, category: "外设",
stock: 50, tags: ["热销", "机械"],
location: { type: "Point", coordinates: [116.40, 39.90] } },
{ _id: 102, name: "无线鼠标", price: 129, category: "外设",
stock: 120, tags: ["无线"],
location: { type: "Point", coordinates: [121.47, 31.23] } },
{ _id: 103, name: "27寸显示器", price: 1099, category: "显示器",
stock: 30, tags: ["4K"],
location: { type: "Point", coordinates: [113.26, 23.13] } }
])
用点号路径匹配子字段
想查 location 里面的某个子字段,不能直接写 { location.type: "Point" } 这种语法——花括号会被当成别的东西。正确做法是用点号(dot notation)把外层字段和子字段连起来:
// 找出所有坐标类型为 Point 的商品
db.products.find({ "location.type": "Point" })
点号路径就是「外层字段.子字段」。注意字段名要用引号包起来,避免点号被当成小数。
Tip点号路径可以一直往下挖。如果
location里又套了一层{ type: { kind: "Point" } },就用"location.type.kind"去匹配。
整文档精确匹配
如果你想按整个内嵌文档来匹配,就把子文档原样写进查询条件:
// 找出坐标是北京那一支点的商品
db.products.find({
location: { type: "Point", coordinates: [116.40, 39.90] }
})
这条会命中 _id: 101 那条,因为它整体长得一模一样。
整文档匹配对字段顺序敏感
这是新手最容易踩的坑。整文档匹配要求字段顺序、字段名、值都完全一致。把上面例子的顺序换一下就不匹配了:
// 顺序反了:coordinates 写在 type 前面,查不到任何东西
db.products.find({
location: { coordinates: [116.40, 39.90], type: "Point" }
})
Warning整文档匹配是「结构 + 顺序 + 值」三位一体的比较。只要字段顺序不同,即使内容一样也查不到。生产环境里字段顺序谁也说不准,所以整文档匹配要慎用。
点号路径 vs 整文档匹配,怎么选
我的经验是:绝大多数场景用点号路径。它只关心某个子字段的值,不管其他字段和顺序,稳健得多。
| 方式 | 写法 | 顺序敏感 | 适用场景 |
|---|---|---|---|
| 点号路径 | { "location.type": "Point" } | 否 | 只关心某个子字段 |
| 整文档 | { location: { ... } } | 是 | 确认整段结构完全一致 |
点号路径也能加比较运算符
子字段一样可以配合比较运算符。比如只想看经度大于 120 的商品:
// coordinates 是数组,[0] 是经度
db.products.find({ "location.coordinates.0": { $gt: 120 } })
这里 "location.coordinates.0" 连用了两层点号,先下钻到 coordinates,再取下标第 0 个元素。
Note数组下标也是通过点号访问的,写法像
"字段.0"。数组查询我们后面会专门讲,这里先混个眼熟。
学完这章,你已经能从容查询任意层级的内嵌文档了。下章我们聊一个更微妙的话题:怎么查「没有值」或「字段根本不存在」的文档。