MongoDB 的文档模型支持数组类型字段,能够灵活存储多个同类型或不同类型的元素。在实际业务中,这类字段常用于保存标签、评论列表、关联 ID 集合等数据。掌握数组的创建、更新、查询和聚合方法,是进行 MongoDB 开发的基础能力,也能帮助开发者更准确地表达一对多关系。

数组字段既可以是简单值数组,也可以是对象数组。简单值数组适合保存字符串、数字等可直接比较的数据;对象数组适合保存包含多个属性的记录。字段结构一旦确定,后续查询、更新和聚合都会围绕字段路径展开,因此设计阶段需要明确数组元素类型和子字段含义。
数组字段的基础创建
在插入文档时,可以直接将某个字段设置为数组形式。数组元素可以是任意合法的数据类型,包括字符串、数字、对象,甚至嵌套数组。虽然 MongoDB 允许数组中混合不同类型,但为了后续查询和聚合稳定,通常建议同一数组中的元素保持相近类型。
以下示例插入一个用户文档,其中 tags 是字符串数组,scores 是数字数组,address 是对象数组。这个文档体现了数组字段的基本形态:字段名对应一个有序集合,集合中的每个元素都可以被单独访问。
// 插入包含数组字段的文档
db.users.insertOne({
name: "张三",
age: 25,
tags: ["前端", "JavaScript", "Vue"],
scores: [90, 85, 92],
address: [
{ city: "北京", area: "海淀区" },
{ city: "上海", area: "浦东新区" }
]
})
创建数组后,可以通过字段名访问整个数组,也可以通过下标访问具体元素。例如 tags 表示整个数组,tags.0 表示第一个元素,address.0.city 表示第一个地址对象中的城市字段。清晰的字段路径是后续更新和查询的前提。
数组元素的增删改操作
数组的更新操作主要通过更新操作符完成。不同操作符对应不同语义:向末尾追加、避免重复、按位置删除、按条件删除、按下标修改、按过滤条件修改等。选择操作符时,需要先明确业务目标是追加、去重、删除还是局部修改,避免误改数组中其他元素。
添加元素时,$push 可以向数组末尾添加一个或多个元素,并允许重复值存在;$addToSet 只会添加数组中不存在的元素,适合需要避免重复的场景。使用 $push 一次添加多个元素时,需要配合 $each,否则会把整个数组作为单个元素插入。
// 向 tags 数组末尾添加单个元素
db.users.updateOne(
{ name: "张三" },
{ $push: { tags: "React" } }
)
// 向 tags 数组末尾追加多个元素
db.users.updateOne(
{ name: "张三" },
{ $push: { tags: { $each: ["Node.js", "MongoDB"] } } }
)
// 添加已存在的元素,数组不会变化
db.users.updateOne(
{ name: "张三" },
{ $addToSet: { tags: "JavaScript" } }
)
// 添加不存在的元素
db.users.updateOne(
{ name: "张三" },
{ $addToSet: { tags: "TypeScript" } }
)
删除元素时,$pop 通过值控制删除方向,1 表示删除最后一个元素,-1 表示删除第一个元素;$pull 可以删除满足条件的所有元素;$pullAll 可以一次性删除多个指定元素。理解它们的作用范围,可以避免误删数组中本应保留的数据。
// 删除 tags 数组最后一个元素
db.users.updateOne(
{ name: "张三" },
{ $pop: { tags: 1 } }
)
// 删除 tags 数组第一个元素
db.users.updateOne(
{ name: "张三" },
{ $pop: { tags: -1 } }
)
// 删除 tags 数组中值为 Vue 的元素
db.users.updateOne(
{ name: "张三" },
{ $pull: { tags: "Vue" } }
)
// 删除 scores 数组中大于 90 的元素
db.users.updateOne(
{ name: "张三" },
{ $pull: { scores: { $gt: 90 } } }
)
// 删除 tags 数组中的 React 和 Node.js
db.users.updateOne(
{ name: "张三" },
{ $pullAll: { tags: ["React", "Node.js"] } }
)
修改元素时,可以直接通过下标定位,也可以使用数组过滤操作符。下标方式适合已知位置的场景;$[] 会作用于数组中的所有元素;$[<identifier>] 则配合 arrayFilters 只修改满足条件的元素。对于对象数组,还可以按子字段路径更新,使局部修改更精确。
// 修改 tags 数组第一个元素
db.users.updateOne(
{ name: "张三" },
{ $set: { "tags.0": "前端开发" } }
)
// 给 scores 数组所有元素加 5 分
db.users.updateOne(
{ name: "张三" },
{ $inc: { "scores.$[]": 5 } }
)
// 只给 scores 数组中小于 90 的元素加 10 分
db.users.updateOne(
{ name: "张三" },
{ $inc: { "scores.$[score]": 10 } },
{ arrayFilters: [{ "score": { $lt: 90 } }] }
)
数组的查询与聚合操作
查询数组字段时,可以根据元素值、元素位置、数组长度等条件进行匹配。直接指定元素值可以匹配数组中包含该元素的文档;使用 $all 可以要求同时包含多个元素;使用下标可以限定元素位置;使用 $size 可以匹配指定长度的数组。这些方式使数组字段既能表达集合语义,又能保留顺序信息。
// 查询 tags 数组包含 JavaScript 的文档
db.users.find({ tags: "JavaScript" })
// 查询 tags 数组同时包含 JavaScript 和 Vue 的文档
db.users.find({ tags: { $all: ["JavaScript", "Vue"] } })
// 查询 tags 数组第一个元素为 前端 的文档
db.users.find({ "tags.0": "前端" })
// 查询 tags 数组长度为 3 的文档
db.users.find({ tags: { $size: 3 } })
在聚合管道中,可以使用 $unwind、$group、$push 等阶段对数组进行处理。$unwind 可以将数组字段拆分为多个文档,每个文档包含数组中的一个元素,适合按元素展开统计;$group 可以将多个文档中的字段重新聚合为数组,适合按分组条件汇总。两者结合,可以完成标签展开、分组聚合等常见数据处理任务。
// 将 tags 数组展开,每个标签生成一个文档
db.users.aggregate([
{ $match: { name: "张三" } },
{ $unwind: "$tags" }
])
// 按年龄分组,将同年龄的用户名聚合为数组
db.users.aggregate([
{ $group: { _id: "$age", names: { $push: "$name" } } }
])
总体来看,MongoDB 数组操作的关键在于先明确字段结构,再选择合适的操作符。创建阶段决定数组是简单值还是对象,更新阶段选择追加、去重、删除或局部修改,查询阶段根据业务需要按值、位置或长度匹配,聚合阶段负责展开和重组。只要字段路径清晰、操作符语义理解准确,数组字段就能在文档模型中发挥简洁、灵活的优势。
MongoDB数组操作document聚合管道update_operator修改时间:2026-07-12 18:24:13