我搜索了SO并抓取了Mongoose / Mongo文档,但没有用,所以我的问题。
我想$inc
位于嵌套数组中的对象中的值,或者如果该对象尚未存在则创建$setOnInsert
。
我看到的Mongo文件如下:
{
"_id": "123",
"members": [
{
"first": "johnny",
"last": "knoxville",
"score": 2
},
{
"first": "johnny",
"last": "cash",
"score": 3
},
// ...and so on
],
}
基于此示例,我的用例是:
count
变量(如基于first
和last
找到),则会增加该变量score
为1的集合中
来自this post我明白我不能$set
我想同时$inc
变量$
。好的 - 这是有道理的。
This post帮助理解了位置myDoc = { first: 'johnny', last: 'cash' };
myCollection.findOneAndUpdate(
{
_id: '123',
'members.first': 'johnny',
'members.last': 'cash'
},
{
$inc: { "members.$.score": 1 }
}
);
运算符,以便找到嵌套对象并将其递增。
如果我知道该文档存在,我可以按如下方式进行更新:
score: 1
但是如果我想插入会员({{1}}),如果它还不存在怎么办?
我的问题是当我使用upsert时:true,位置运算符会抛出错误,因为它可能不会与upsert一起使用(请参阅official documentation)。
我尝试了各种组合,并希望避免2 db访问(读/写)。
有没有办法在一次操作中执行此操作?
答案 0 :(得分:0)
在MongoDB 4.2和更高版本中,更新方法现在可以获取文档或an aggregate pipeline,其中可以使用以下阶段:
$addFields
及其别名$set
$project
及其别名$unset
$replaceRoot
及其别名$replaceWith
。基于以上所述,您对聚合管道的更新操作将根据条件(即模板如下)覆盖members字段:
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
}
}
}
]
);
要获取第一个条件的表达式 members数组是否包含myDoc?,您需要一种方法来满足满足以下条件的第一个和最后一个属性$filter
分别与myDoc
相同的值,即
{
"$filter": {
"input": "$members",
"cond": {
"$and": [
{ "$eq": ["$$this.first", myDoc.first] },
{ "$eq": ["$$this.last", myDoc.last] },
]
}
}
}
检查结果数组的第一个元素
{
"$arrayElemAt": [
{
"$filter": {
"input": "$members",
"cond": {
"$and": [
{ "$eq": ["$$this.first", myDoc.first] },
{ "$eq": ["$$this.last", myDoc.last] },
]
}
}
},
0
]
}
如果没有匹配项,则上述内容将为null,我们可以用$ifNull
用作主要条件的值替换该null:
{
"$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": [
{ "$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 }
);