[英]Add new element to mongoDB document array only if a certain condition is met
[英]Update element of an array if and only if data for element is present in new document mongodb
我在更新數組時遇到問題。
樣本文件:
_id:11,
marksObtained:[{physics:10}, {chemistry:12}, {maths: 11}, {biology:9}, {social:9}]
name:"Someone"
要更新的字段是:
[{chemistry:13},{zoology:12}]
所以新文件看起來像:
_id:11,
marksObtained:[{physics:10}, {chemistry:13}, {maths: 11}, {biology:9}, {social:9},{zoology:12}]
name:"Someone"
化學標記更新為 13,rest 值保持原樣
我試過 $push, $addToSet, update, updateOne,updateMany
我想避免編寫迭代和比較標簽的代碼。
鍵和值來自動態設備,所以我想更新數組中已經存在的鍵,如果有新鍵,它必須附加到數組中
這其實是一種痛苦。 但是有幾個選擇:
1 - 批量操作
您的操作只是一個$pull
(從數組中刪除現有值,如果存在)和$push
(添加正確的值)。 您可以通過批量操作 api 來確保寫入盡可能原子化:
const bulkOperation = db.students.initializeOrderedBulkOp()
bulkOperation.find({_id:11}).updateOne({$pull: {marksObtained: { chemistry: { $exists: true } } } })
bulkOperation.find({_id:11}).updateOne({$push: {marksObtained: { chemistry: 12 } } })
bulkOperation.execute()
例如:動態地在代碼中:
const _idToUpdate = 11
const updateFields = [{ chemistry: 13 }, { zoology: 12 }]
const bulkOperation = db.collection('students').initializeOrderedBulkOp()
for (const field of updateFields) {
for (const key in field) {
const value = field[key]
const pullOp = { $pull: { marksObtained: { } } }
pullOp.$pull.marksObtained[key] = { $exists: true }
const pushOp = { $push: { marksObtained: { } } }
pushOp.$push.marksObtained[key] = value
console.log('name', key, 'value', value, 'pullOp', JSON.stringify(pullOp), 'pushOp', JSON.stringify(pushOp))
bulkOperation.find({ _id: _idToUpdate }).updateOne(pullOp)
bulkOperation.find({ _id: _idToUpdate }).updateOne(pushOp)
}
}
bulkOperation.execute()
2 - 自定義$function
運行 javascript 來設置值
根據這個游樂場: https://mongoplayground.net/p/PcV6dMoyJ6Y 。
您可以設置自定義 javascript function 來為您完成臟活。
令人討厭的是,在操場上,它的格式不是很好,因此它在一行中,但是一個例子是這樣的:
db.collection.update({
_id: 11
},
[
{
$set: {
marksObtained: {
$function: {
body: "function(marks) { const updateFields = { chemistry: 13, zoology: 12, french: 11 }; for (const updateFieldName in updateFields) { let updated = false;for (const markObtained of marks) { for (const markObtainedKey in markObtained) { if (markObtainedKey === updateFieldName) { markObtained[markObtainedKey] = updateFields[updateFieldName]; updated = true; } } } if (!updated) { const newTopic = {}; newTopic[updateFieldName] = updateFields[updateFieldName]; marks.push(newTopic); } } return marks }",
args: [
"$marksObtained"
],
lang: "js"
}
}
}
}
])
其中 function 是:
const updateFields = { chemistry: 13, zoology: 12, french: 11 }
for (const updateFieldName in updateFields) {
let updated = false
for (const markObtained of marks) {
for (const markObtainedKey in markObtained) {
if (markObtainedKey === updateFieldName) {
markObtained[markObtainedKey] = updateFields[updateFieldName]
updated = true
}
}
}
if (!updated) {
const newTopic = {}
newTopic[updateFieldName] = updateFields[updateFieldName]
marks.push(newTopic)
}
}
return marks
顯然,您必須像我所做的那樣按照單個 object 設置值,但您可以根據需要進行調整。
注意:如果您將marksObtained
格式化為:
marksObtained: {
physics: 10,
chemistry: 12,
maths: 11,
biology: 9,
social: 9
}
要么
marksObtained: [
{lesson: 'physics', score: 10},
{lesson: 'chemistry', score: 12},
{lesson: 'maths', score: 11},
{lesson: 'biology', score: 9},
{lesson: 'social', score: 9}
]
擁有一個包含單個 object 的數組並沒有多大意義。
它可以在一個更新查詢中完成。 如果數組中項目的順序很重要,那就有點笨拙了。 否則它可以很優雅:
項目的順序很重要:
db.collection.updateMany(
{_id: 11},
[
{$set: {
marksObtained: {
$map: {
input: "$marksObtained",
in: {$first: {$objectToArray: "$$this"}}
}
},
dataToUpdate: {
$map: {
input: [
{chemistry: 13},
{zoology: 12}
],
in: {$first: {$objectToArray: "$$this"}}
}
}
}},
{$set: {
updateData: {
$filter: {
input: "$dataToUpdate",
cond: {$in: ["$$this.k", "$marksObtained.k"]}
}
},
newData: {
$filter: {
input: "$dataToUpdate",
cond: {$not: {$in: ["$$this.k", "$marksObtained.k"]}}
}
},
dataToUpdate: "$$REMOVE"
}},
{$set: {
marksObtained: {
$concatArrays: [
{
$map: {
input: "$marksObtained",
in: {
$cond: [
{$in: ["$$this.k", "$updateData.k"]},
{$arrayElemAt:
["$updateData", {$indexOfArray: ["$updateData.k", "$$this.k"]}]
},
"$$this"
]
}
}
},
"$newData"
]
},
newData: "$$REMOVE",
updateData: "$$REMOVE"
}
},
{$set: {
marksObtained: {
$map: {input: "$marksObtained", in: ["$$this"]}
}
}},
{$set: {
marksObtained: {
$map: {
input: "$marksObtained",
in: {$arrayToObject: "$$this"}
}
}
}}
])
在playground 示例中查看它是如何工作的
項目的順序並不重要:
db.collection.updateMany(
{_id: 11},
[
{$set: {
marksObtained: {$map: {
input: "$marksObtained",
in: {$first: {$objectToArray: "$$this"}}
}},
dataToUpdate: {$map: {
input: [{chemistry: 13}, {zoology: 12}],
in: {$first: {$objectToArray: "$$this"}}
}}
}},
{$set: {
marksObtained: {$concatArrays: [
{$filter: {
input: "$marksObtained",
cond: {$not: {$in: ["$$this.k", "$dataToUpdate.k"]}}
}},
"$dataToUpdate"
]},
dataToUpdate: "$$REMOVE"
}},
{$set: {
marksObtained: {$map: {input: "$marksObtained", in: ["$$this"]}}
}},
{$set: {
marksObtained: {$map: {input: "$marksObtained", in: {$arrayToObject: "$$this"}}}
}}
])
在playground 示例中查看它是如何工作的
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.