对于 MongoDB 4.2 及更高版本,更新方法现在可以获取文档或an aggregate pipeline,其中可以使用以下阶段:
-
$addFields 及其别名 $set
-
$project 及其别名 $unset
-
$replaceRoot 及其别名 $replaceWith。
有了上述内容,您对聚合管道的更新操作将根据条件覆盖成员字段,即模板如下:
var myDoc = { first: 'johnny', last: 'cash', score: 1 };
db.getCollection('myCollection').update(
{ "_id" : "123" },
[
"$set": {
"members": {
"$cond": {
"if": {}, // does the members array contain myDoc?
"then": {}, // map the members array and increment the score
"else": {} // add myDoc to the existing members array
}
}
}
]
);
要获得第一个条件的表达式成员数组是否包含 myDoc?,您需要一种方法来 $filter 基于满足第一个和最后一个属性相同的条件的数组值分别为myDoc,即
{
"$filter": {
"input": "$members",
"cond": {
"$and": [
{ "$eq": ["$$this.first", myDoc.first] },
{ "$eq": ["$$this.last", myDoc.last] },
]
}
}
}
用$arrayElemAt检查结果数组的第一个元素
{
"$arrayElemAt": [
{
"$filter": {
"input": "$members",
"cond": {
"$and": [
{ "$eq": ["$$this.first", myDoc.first] },
{ "$eq": ["$$this.last", myDoc.last] },
]
}
}
},
0
]
}
如果不匹配,则上述内容将为 null,我们可以将 null 替换为可用作 $ifNull 的主要条件的值:
{
"$ifNull": [
{
"$arrayElemAt": [
{
"$filter": {
"input": "$members",
"cond": {
"$and": [
{ "$eq": ["$$this.first", myDoc.first] },
{ "$eq": ["$$this.last", myDoc.last] },
]
}
}
},
0
]
},
0
]
}
以上内容成为我们使用$ne检查不等式的第一个IF语句的条件的基础:
{ "$ne": [
{
"$ifNull": [
{
"$arrayElemAt": [
{
"$filter": {
"input": "$members",
"cond": {
"$and": [
{ "$eq": ["$$this.first", myDoc.first] },
{ "$eq": ["$$this.last", myDoc.last] },
]
}
}
},
0
]
},
0
]
},
0
] }
如果上述条件为真,则$map表达式变为
{
"$map": {
"input": "$members",
"in": {
"$cond": [
{ "$eq": [{ "$ifNull": ["$$this.score", 0 ] }, 0] },
{ "$mergeObjects": ["$$this", { "score": 1 } ] },
{ "$mergeObjects": ["$$this", { "score": { "$sum": ["$$this.score", 1] } } ] }
]
}
}
}
使用$concatArrays 将新文档添加到现有成员数组中
{
"$concatArrays": [
{ "$ifNull": ["$members", []] },
[ myDoc ]
]
}
您的最终更新操作变为:
var myDoc = { first: 'johnny', last: 'cash', score: 1 };
db.getCollection("myCollection").update(
{ "_id" : "123" },
[
{ "$set": {
"members": {
"$cond": {
"if": {
"$ne": [
{
"$ifNull": [
{
"$arrayElemAt": [
{
"$filter": {
"input": "$members",
"cond": {
"$and": [
{ "$eq": ["$$this.first", myDoc.first] },
{ "$eq": ["$$this.last", myDoc.last] },
]
}
}
},
0
]
},
0
]
},
0
]
},
"then": {
"$map": {
"input": "$members",
"in": {
"$cond": [
{ "$eq": [{ "$ifNull": ["$$this.score", 0 ] }, 0] },
{ "$mergeObjects": ["$$this", { "score": 1 } ] },
{ "$mergeObjects": ["$$this", { "score": { "$sum": ["$$this.score", 1] } } ] }
]
}
}
},
"else": {
"$concatArrays": [
{ "$ifNull": ["$members", []] },
[ myDoc ]
]
}
}
}
} }
],
{ "upsert": true }
);