我有以下收藏
{
"_id" : ObjectId("57315ba4846dd82425ca2408"),
"myarray" : [
{
userId : ObjectId("570ca5e48dbe673802c2d035"),
point : 5
},
{
userId : ObjectId("613ca5e48dbe673802c2d521"),
point : 2
},
]
}
如果userId不存在,我想推入myarray,它应该附加到myarray。如果userId存在,则应将其更新为point。
db.collection.update({
_id : ObjectId("57315ba4846dd82425ca2408"), "myarray.userId" : ObjectId("570ca5e48dbe673802c2d035")
},{
$set: {"myarray.$.point": 10}
})
但如果userId不存在,则没有任何反应。
db.collection.update({
_id : ObjectId("57315ba4846dd82425ca2408")
},{
$push: {"myarray": {userId: ObjectId("570ca5e48dbe673802c2d035"), point: 10}}
})
但是如果userId对象已经存在,它将再次推送。
在MongoDB中执行此操作的最佳方法是什么?
答案 0 :(得分:14)
Flying Fisher接受的回答是,现有记录将首先删除,然后再次推送。
更安全的方法(常识)是首先尝试更新记录,如果找不到匹配,请插入它,如下所示:
// first try to overwrite existing value
var result = db.collection.update(
{
_id : ObjectId("57315ba4846dd82425ca2408"),
"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")
},
{
$set: {"myarray.$.point": {point: 10}}
}
);
// you probably need to modify the following if-statement to some async callback
// checking depending on your server-side code and mongodb-driver
if(!result.nMatched)
{
// record not found, so create a new entry
// this can be done using $addToSet:
db.collection.update(
{
_id: ObjectId("57315ba4846dd82425ca2408")
},
{
$addToSet: {
myarray: {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
}
);
// OR (the equivalent) using $push:
db.collection.update(
{
_id: ObjectId("57315ba4846dd82425ca2408"),
"myarray.userId": {$ne: ObjectId("570ca5e48dbe673802c2d035"}}
},
{
$push: {
myarray: {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
}
);
}
这也应该给(常识,未经测试)性能提升,如果在大多数情况下记录已经存在,则只会执行第一个查询。
答案 1 :(得分:7)
试试这个
db.collection.update(
{ _id : ObjectId("57315ba4846dd82425ca2408")},
{ $pull: {"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")}}
)
db.collection.update(
{ _id : ObjectId("57315ba4846dd82425ca2408")},
{ $push: {"myarray": {
userId:ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}}
)
答案 2 :(得分:3)
从 MongoDB v4.2 开始,有一个名为 update documents with aggregation pipeline 的选项,
$cond
if userId
是否在 myarray.userId
中$map
迭代 myarray
数组的循环并检查条件是否 userId
匹配,然后使用 $mergeObjects
与新文档合并$concatArrays
连接新对象和 myarray
let _id = ObjectId("57315ba4846dd82425ca2408");
let uodateDoc = {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
};
db.collection.update(
{ _id: _id },
[{
$set: {
myarray: {
$cond: [
{ $in: [updateDoc.userId, "$myarray.userId"] },
{
$map: {
input: "$myarray",
in: {
$mergeObjects: [
"$$this",
{
$cond: [
{ $eq: ["$$this.userId", updateDoc.userId] },
uodateDoc,
{}
]
}
]
}
}
},
{ $concatArrays: ["$myarray", [uodateDoc]] }
]
}
}
}]
)
答案 3 :(得分:1)
不幸的是,在嵌入式阵列上无法进行“upsert”操作。操作符根本不存在,因此在单个语句中不可能这样做。因此,您必须执行两个更新操作才能执行所需操作。此外,这两个更新的应用顺序对于获得所需结果非常重要。
答案 4 :(得分:0)
当您要更新或在数组中插入值时尝试
数据库中的对象
key:name,
key1:name1,
arr:[
{
val:1,
val2:1
}
]
查询
var query = {
$inc:{
"arr.0.val": 2,
"arr.0.val2": 2
}
}
.updateOne( { "key": name }, query, { upsert: true }
key:name,
key1:name1,
arr:[
{
val:3,
val2:3
}
]
答案 5 :(得分:0)
在MongoDB 3.6中,现在可以upsert elements in an array。
答案 6 :(得分:0)
我还没有找到任何基于一个原子查询的解决方案。取而代之的是,基于两个查询的序列有3种方式:
总是先$pull
(从数组中删除项目),然后再$push
(将更新后的项目添加到数组中)
db.collection.update(
{ _id : ObjectId("57315ba4846dd82425ca2408")},
{ $pull: {"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")}}
)
db.collection.update(
{ _id : ObjectId("57315ba4846dd82425ca2408")},
{
$push: {
"myarray": {
userId:ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
}
)
尝试进行$set
(如果存在则更新数组中的项),然后获取结果并检查更新操作是否成功或是否需要$push
(插入项)
var result = db.collection.update(
{
_id : ObjectId("57315ba4846dd82425ca2408"),
"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")
},
{
$set: {"myarray.$.point": {point: 10}}
}
);
if(!result.nMatched){
db.collection.update({_id: ObjectId("57315ba4846dd82425ca2408")},
{
$addToSet: {
myarray: {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
);
始终$addToSet
(如果不存在则添加项),然后始终$set
更新数组中的项
db.collection.update({_id: ObjectId("57315ba4846dd82425ca2408")}, myarray: { $not: { $elemMatch: {userId: ObjectId("570ca5e48dbe673802c2d035")} } } },
{
$addToSet : {
myarray: {
userId: ObjectId("570ca5e48dbe673802c2d035"),
point: 10
}
}
},
{ multi: false, upsert: false});
db.collection.update({
_id: ObjectId("57315ba4846dd82425ca2408"),
"myArray.userId": ObjectId("570ca5e48dbe673802c2d035")
},
{ $set : { myArray.$.point: 10 } },
{ multi: false, upsert: false});
1和2方式不安全,因此必须建立交易以避免两个并发请求可能push
相同项目产生重复项。
3种方法很安全。 $addToSet
仅在该项不存在时添加,否则什么也不会发生。如果有两个并发请求,则只有其中一个将缺少的项添加到数组中。
答案 7 :(得分:0)
数组更新和创建不要混入一个查询中,如果您非常关心原子性,那么可以使用以下解决方案:
标准化您的架构,
{
"_id" : ObjectId("57315ba4846dd82425ca2408"),
userId : ObjectId("570ca5e48dbe673802c2d035"),
point : 5
}