MongoDB
中有一大类更新运算符用于操作数组。数组是常用且功能强大的数据结构:它们不仅是可以通过索引进行引用的列表,而且可以作为集合来使用。
创建 students
集合:
> db.students.insertOne( { _id: 1, scores: [ 44, 78, 38, 80 ] } )
{ acknowledged: true, insertedId: 1 }
如果数组已存在,$push
就会将元素添加到数组末尾;如果数组不存在,则会创建一个新的数组。
> db.students.updateOne(
... { _id: 1 },
... { $push: { scores: 89 } }
... )
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
> db.students.find()
[ { _id: 1, scores: [ 44, 78, 38, 80, 89 ] } ]
您可以使用 $push
操作符与以下修饰符一起使用:
修饰符 | 说明 |
---|---|
$each | 向数组字段追加多个值。 |
$slice | 限制数组元素的数量。需要使用 $each 修改器。 |
$sort | 对数组元素进行排序。需要使用 $each 修饰符。 |
$position | 指定数组中插入新元素的位置。需要使用 $each 修饰符。如果没有 $position 修饰符,$push 会将元素追加到数组的末尾。 |
将以下文档添加到 students
集合中:
> db.students.insertMany( [
... { _id: 2, scores: [ 45, 78, 38, 80, 89 ] } ,
... { _id: 3, scores: [ 46, 78, 38, 80, 89 ] } ,
... { _id: 4, scores: [ 47, 78, 38, 80, 89 ] }
... ] )
{ acknowledged: true, insertedIds: { '0': 2, '1': 3, '2': 4 } }
以下 $push
操作会向每个文档的 scores
数组追加 95
:
> db.students.updateMany(
... { },
... { $push: { scores: 95 } }
... )
{
acknowledged: true,
insertedId: null,
matchedCount: 4,
modifiedCount: 4,
upsertedCount: 0
}
> db.students.find()
[
{ _id: 1, scores: [ 44, 78, 38, 80, 89, 95 ] },
{ _id: 2, scores: [ 45, 78, 38, 80, 89, 95 ] },
{ _id: 3, scores: [ 46, 78, 38, 80, 89, 95 ] },
{ _id: 4, scores: [ 47, 78, 38, 80, 89, 95 ] }
]
使用 $push
和 $each
修饰符将多个值附加到数组字段。
以下示例将 [ 90, 92, 85 ]
的每个元素附加到文档的 scores
数组,其中 name
字段等于 joe
::
> db.students.updateOne( { _id: 1 }, { $push: { scores: { $each: [90, 92, 85] } } } )
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
> db.students.find({_id: 1})
[
{
_id: 1,
scores: [
44, 78, 38, 80, 89,
95, 90, 92, 85
]
}
]
$push
操作符如果只允许数组增长到某个长度,则可以使用 $slice
修饰符配合 $push
来防止数组的增长超过某个大小,从而有效地生成top N
列表:
> db.students.insertOne(
... {
... "_id" : 5,
... "quizzes" : [
... { "wk": 1, "score" : 10 },
... { "wk": 2, "score" : 8 },
... { "wk": 3, "score" : 5 },
... { "wk": 4, "score" : 6 }
... ]
... }
... )
{ acknowledged: true, insertedId: 5 }
> db.students.updateOne(
... { _id: 5 },
... {
... $push: {
... quizzes: {
... $each: [ { wk: 5, score: 8 }, { wk: 6, score: 7 }, { wk: 7, score: 6 } ],
... $sort: { score: -1 },
... $slice: 3
... }
... }
... }
... )
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
操作后,数组中只有三个得分最高的:
> db.students.findOne({_id: 5})
{
_id: 5,
quizzes: [ { wk: 1, score: 10 }, { wk: 2, score: 8 }, { wk: 5, score: 8 } ]
}
$slice
或 $sort
与 $push
配合使用,必须包含 $each
。$addToSet
$addToSet
操作符会将值添加到数组中,除非该值已经存在,在这种情况下,$addToSet
对该数组不执行任何操作。
$pop
$pop
操作符删除数组的第一个元素或最后一个元素。为 $pop
传入 -1
的值,可以删除数组中的第一个元素,传入 1
的值则可以删除数组中的最后一个元素。
> > db.students.insertOne( { _id: 1, scores: [ 8, 9, 10 ] } )
{ acknowledged: true, insertedId: 1 }
true
删除数组第一项:
> db.students.updateOne( { _id: 1 }, { $pop: { scores: -1 } } )
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
> db.students.findOne({_id: 1})
{ _id: 1, scores: [ 9, 10 ] }
删除数组最后一项
> db.students.updateOne( { _id: 1 }, { $pop: { scores: 1 } })
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
> db.students.findOne({_id: 1})
{ _id: 1, scores: [ 9 ] }
$pull
$pull
操作符会从现有数组中删除符合指定条件的所有匹配项:
> db.profiles.insertOne( { _id: 1, votes: [ 3, 5, 6, 7, 7, 8 ] } )
{ acknowledged: true, insertedId: 1 }
> db.profiles.updateOne( { _id: 1 }, { $pull: { votes: { $gte: 6 } } } )
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
更新后,文档只有小于6的值:
> db.profiles.findOne({_id: 1})
{ _id: 1, votes: [ 3, 5 ] }
定位运算符$
标识数组中要更新的元素,而无需显式指定该元素在数组中的位置。
创建集合:
> db.students.insertMany( [
... { "_id" : 1, "grades" : [ 85, 80, 80 ] },
... { "_id" : 2, "grades" : [ 88, 90, 92 ] },
... { "_id" : 3, "grades" : [ 85, 100, 90 ] }
... ] )
{ acknowledged: true, insertedIds: { '0': 1, '1': 2, '2': 3 } }
定位运算符只会更新第一个匹配到的元素:
> db.students.updateOne(
... { _id: 1, grades: 80 },
... { $set: { "grades.$" : 82 } }
... )
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
> db.students.find({"_id": 1})
[ { _id: 1, grades: [ 85, 82, 80 ] } ]
$
运算符可以更新包含嵌入文档的数组。使用 $
运算符,可通过 $
运算符上的点符号访问嵌入文档中的字段:
db.collection.updateOne(
{ <query selector> },
{ <update operator>: { "array.$.field" : value } }
)
使用 $
运算符来更新与“grade
等于 85
条件”相匹配的第一个数组元素的 std
字段:
> db.students.insertOne({
... _id: 4,
... grades: [
... { grade: 80, mean: 75, std: 8 },
... { grade: 85, mean: 90, std: 5 },
... { grade: 85, mean: 85, std: 8 }
... ]
... })
{ acknowledged: true, insertedId: 4 }
> db.students.updateOne(
... { _id: 4, "grades.grade": 85 },
... { $set: { "grades.$.std" : 6 } }
... )
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
> db.students.findOne({_id: 4})
{
_id: 4,
grades: [
{ grade: 80, mean: 75, std: 8 },
{ grade: 85, mean: 90, std: 6 },
{ grade: 85, mean: 85, std: 8 }
]
}
过滤位置操作符 $[<identifier>]
用于识别匹配更新操作 arrayFilters
条件的数组元素。
与 arrayFilters
选项一起使用时,$[<identifier>]
操作符的形式如下:
{ <update operator>: { "<array>.$[<identifier>]" : value } },
{ arrayFilters: [ { <identifier>: <condition> } ] }
创建集合:
> db.students.insertMany( [
{ "_id" : 1, "grades" : [ 95, 92, 90 ] },
{ "_id" : 2, "grades" : [ 98, 100, 102 ] },
{ "_id" : 3, "grades" : [ 95, 110, 100 ] }
] )
更新数组中 grades
大于或等于 100
的所有元素:
> db.students.updateMany(
... { },
... { $set: { "grades.$[element]" : 100 } },
... { arrayFilters: [ { "element": { $gte: 100 } } ] }
... )
{
acknowledged: true,
insertedId: null,
matchedCount: 3,
modifiedCount: 2,
upsertedCount: 0
}
> db.students.find()
[
{ _id: 1, grades: [ 95, 92, 90 ] },
{ _id: 2, grades: [ 98, 100, 100 ] },
{ _id: 3, grades: [ 95, 100, 100 ] }
]