[
{
"id": 1,
"items": [
{
id: 15,
score: 10
},
{
id: 14,
score: 100
},
{
id: 12,
score: 1
}
]
},
{
"id": 2,
"items": []
}
]
Now, I try to update items whose id is 14,15 & used the following query.
db.collection.update({
"items.id": {
$in: [
14,
15
]
}
},
{
$set: {
"items.$.score": 444
}
},
{
multi: true
}
)
but it updated only the first match in items that is that is id with 15, what can be wrong?
[
{
"_id": ObjectId("5a934e000102030405000000"),
"id": 1,
"items": [
{
"id": 15,
"score": 444
},
{
"id": 14,
"score": 100
},
{
"id": 12,
"score": 1
}
]
},
{
"_id": ObjectId("5a934e000102030405000001"),
"id": 2,
"items": []
}
]
Use arrayFilters with targeted array elements:
here is the doc
db.collection.update({
"id": 1
},
{
"$set": {
"items.$[ele].score": 20
}
},
{
arrayFilters: [
{
"ele.id": {
"$in": [
15,
14
]
}
}
]
})
see play ground code https://mongoplayground.net/p/cdgu1aqLwsI
You can do it with Positional identifiers $[]
db.collection.update({
"items.id": {
$in: [
14,
15
]
}
},
{
$set: {
"items.$[element].score": 444
}
},
{
arrayFilters: [
{
"element.id": {
$in: [
14,
15
]
}
}
],
multi: true
})
try it here
Related
Firstly, sorry for my bad English.
Secondly, I want to ask, how to multi-update my document.
I have a row structure like this:
Data Model
[
{
"id": '00001',
"exp": '192',
"items": [
{ "qty": 23, "ID": ObjectId("kieryu35261twerts73940djr") },
{ "qty": 77, "ID": ObjectId("1240a1ffuw33dbcv6ut8888zz") },
{ "qty": 1, "ID": ObjectId("5600r0e0rr67cbd60a1234y5") },
{ "qty": 5, "ID": ObjectId("32bbd0e0rr92cdb60a4386c7") }
],
"stats": [
{ "strenght": 1, "ID": ObjectId("3eruskdjfhrutiwo4059yuol3") },
{ "strenght": 2, "ID": ObjectId("3jdks0d9r2seifldvjmw0km2e") },
{ "strenght": 1, "ID": ObjectId("aslkdnasklnc2112uhnsjssad") },
{ "strenght": 5, "ID": ObjectId("1230msafmlklkmasfmcio3kms") }
]
},
{
"id": '00002',
"exp": '100',
"items": [
{ "strenght": 10, "ID": ObjectId("21312kn3kaklmasklcklasmck") },
{ "strenght": 10, "ID": ObjectId("kldsafklsajdfklmsadlkaskl") }
],
"stats": [
{ "strenght": 9, "ID": ObjectId("aslkclkamiior2oinrkl2adsa") },
{ "strenght": 0, "ID": ObjectId("asdoasjdosmdkl123123kmnsd") }
]
}
]
I want to update one document row by finding the id of the document, and multi-deep elements by ID too. Like this:
{
"id": '00001',
"exp": '555',
"items": [
{ "qty": 44, "ID": ObjectId("kieryu35261twerts73940djr") },
{ "qty": 55, "ID": ObjectId("1240a1ffuw33dbcv6ut8888zz") },
{ "qty": 66, "ID": ObjectId("5600r0e0rr67cbd60a1234y5") },
{ "qty": 77, "ID": ObjectId("32bbd0e0rr92cdb60a4386c7") }
],
"stats": [
{ "strenght": 10, "ID": ObjectId("3eruskdjfhrutiwo4059yuol3") },
{ "strenght": 20, "ID": ObjectId("3jdks0d9r2seifldvjmw0km2e") },
{ "strenght": 12, "ID": ObjectId("aslkdnasklnc2112uhnsjssad") },
{ "strenght": 54, "ID": ObjectId("1230msafmlklkmasfmcio3kms") }
]
}
And last, just for information, I do before this:
await DataModel.findOneAndUpdate(
{
"id" : idvariable // == 00001
},
{
"$set" : {
"exp" : 555,
"items": {
//update qty == 44 where ID == kieryu35261twerts73940djr
//update qty == 55 where ID == 1240a1ffuw33dbcv6ut8888zz
//update qty == 66 where ID == 5600r0e0rr67cbd60a1234y5
//update qty == 77 where ID == 32bbd0e0rr92cdb60a4386c7
},
"stats": {
//update strenght == 10 where ID == 3eruskdjfhrutiwo4059yuol3
//update strenght == 20 where ID == 3jdks0d9r2seifldvjmw0km2e
//update strenght == 12 where ID == aslkdnasklnc2112uhnsjssad
//update strenght == 54 where ID == 1230msafmlklkmasfmcio3kms
}
}
}
)
Please, I don't know how to update it with a single query update, or other technique. Thank you.
Possible, but a bit long query.
Working on the Update with Aggregation Pipeline,
$map - Iterate the items in the array and return a new array.
$switch - Switch-case (statement) to match the ID of the current iterated document and update the document if matched. If all the case conditions are failed to match, remain the existing document.
db.collection.update({
"id": idvariable// == 00001
},
[
{
"$set": {
"exp": 555,
"items": {
$map: {
input: "$items",
in: {
$switch: {
branches: [
{
case: {
$eq: [
"$$this.ID",
"kieryu35261twerts73940djr"
]
},
then: {
$mergeObjects: [
"$$this",
{
qty: 44
}
]
}
},
{
case: {
$eq: [
"$$this.ID",
"1240a1ffuw33dbcv6ut8888zz"
]
},
then: {
$mergeObjects: [
"$$this",
{
qty: 55
}
]
}
},
{
case: {
$eq: [
"$$this.ID",
"5600r0e0rr67cbd60a1234y5"
]
},
then: {
$mergeObjects: [
"$$this",
{
qty: 66
}
]
}
},
{
case: {
$eq: [
"$$this.ID",
"32bbd0e0rr92cdb60a4386c7"
]
},
then: {
$mergeObjects: [
"$$this",
{
qty: 77
}
]
}
}
],
default: "$$this"
}
}
}
},
"stats": {
$map: {
input: "$stats",
in: {
$switch: {
branches: [
{
case: {
$eq: [
"$$this.ID",
"3eruskdjfhrutiwo4059yuol3"
]
},
then: {
$mergeObjects: [
"$$this",
{
strenght: 10
}
]
}
},
{
case: {
$eq: [
"$$this.ID",
"3jdks0d9r2seifldvjmw0km2e"
]
},
then: {
$mergeObjects: [
"$$this",
{
strenght: 20
}
]
}
},
{
case: {
$eq: [
"$$this.ID",
"aslkdnasklnc2112uhnsjssad"
]
},
then: {
$mergeObjects: [
"$$this",
{
strenght: 12
}
]
}
},
{
case: {
$eq: [
"$$this.ID",
"1230msafmlklkmasfmcio3kms"
]
},
then: {
$mergeObjects: [
"$$this",
{
strenght: 54
}
]
}
}
],
default: "$$this"
}
}
}
}
}
}
])
For the items and stats with ID to be updated, make sure that you need to parse as ObjectId [Note that Mongo Playground doesn't recognize your provided IDs, I manually change the dataset for those IDs as string but the concept for the update is the same].
Sample Mongo Playground
So I have a mongodb document
{
"id": 1,
"balance": 10,
"transactions": [
{
"balance": 10,
"transaction_amount": 10
}
]
}
I want to add another object inside of transactions array and that object uses previous balance and adds the newer value.
For eg, transaction amount is 20
the document should be updated in this way
{
"id": 1,
"balance": 30,
"transactions": [
{
"balance": 10,
"transaction_amount": 10
},
{
"balance": 30,
"transaction_amount": 20
}
]
}
Now if I want to add one more document in transactions array with transaction_amount as 5 the resultant document becomes
{
"id": 1,
"balance": 35,
"transactions": [
{
"balance": 10,
"transaction_amount": 10
},
{
"balance": 30,
"transaction_amount": 20
},
{
"balance": 35,
"transaction_amount": 5
}
]
}
Is there any way to do this using aggregation pipeline
Simply use $add and $concatArrays to wrangle the data
db.collection.update({
id: 1
},
[
{
"$addFields": {
"txnAmt": <amount you want>
}
},
{
$addFields: {
balance: {
$add: [
"$balance",
"$txnAmt"
]
}
}
},
{
"$addFields": {
"transactions": {
"$concatArrays": [
"$transactions",
[
{
balance: "$balance",
transaction_amount: "$txnAmt"
}
]
]
}
}
},
{
"$unset": "txnAmt"
}
],
{
multi: true
})
Here is the Mongo playground for your reference.
I have a collection from which I need specific obj e.g. notes.blok2 and notes.curse5 as an object, not as an array
{
"year":2020,
"grade":4,
"seccion":"A",
"id": 100,
"name": "pedro",
"notes":[{"curse":5,
"block":1,
"score":{ "a1": 5,"a2": 10, "a3": 15}
},{"curse":5,
"block":2,
"score":{ "b1": 10,"b2": 20, "b3": 30}
}
]
}
My query
notas.find({
"$and":[{"grade":1},{"seccion":"A"},{"year":2020}]},
{"projection":{ "grade":1, "seccion":1,"name":1,"id":1,
"notes":{"$elemMatch":{"block":2,"curse":5}},"notes.score":1} })
It works but returns notes like array
{
"_id": "55",
"id": 100,
"grade": 5,
"name": "pedro",
"seccion": "A",
"notes": [
{"score": { "b1": 10,"b2": 20, "b3": 30} }
]
}
But I NEED LIKE THIS: score at the same level as others and if doesn't exist show empty "score":{}
{
"year":2020,
"grade":5,
"seccion":"A",
"id": 100,
"name": "pedro",
"score":{ "b1": 10,"b2": 20, "b3": 30}
}
Demo - https://mongoplayground.net/p/XlJqR2DYW1X
You can use aggregation query
db.collection.aggregate([
{
$match: { // filter
"grade": 1,
"seccion": "A",
"year": 2020,
"notes": {
"$elemMatch": {
"block": 2,
"curse": 5
}
}
}
},
{ $unwind: "$notes" }, //break into individual documents
{
$match: { // match query on individual note
"notes.block": 2,
"notes.curse": 5
}
},
{
$project: { // projection
"grade": 1,
"seccion": 1,
"name": 1,
"id": 1,
"score": "$notes.score"
}
}
])
Update
Demo - https://mongoplayground.net/p/mq5Kue3UG42
Use $filter
db.collection.aggregate([
{
$match: {
"grade": 1,
"seccion": "A",
"year": 2020
}
},
{
$set: {
"score": {
"$filter": {
"input": "$notes",
"as": "note",
"cond": {
$and: [
{
$eq: [ "$$note.block",3]
},
{
$eq: [ "$$note.curse", 5 ]
}
]
}
}
}
}
},
{
$project: {
// projection
"grade": 1,
"seccion": 1,
"name": 1,
"id": 1,
"score": {
"$first": "$score.score"
}
}
}
])
If you want empty object for score when match not found you can do -
Demo - https://mongoplayground.net/p/dumax58kgrc
{
$set: {
score: {
$cond: [
{ $size: "$score" }, // check array length
{ $first: "$score" }, // true - take 1st
{ score: {} } // false - set empty object
]
}
}
},
I need to add items to a specific subobject in my collection. Is is possible to do this using just the update method considering that the array may not exist in some objects?
Here's a sample structure:
[
{
"key": 1,
"someValue": "abc",
"installments": [
{
"number": 1,
"payments": [
{
"paymentNumber": 1,
"value": 29
},
{
"paymentNumber": 2,
"value": 22
}
]
}
]
},
{
"key": 2,
"someValue": "xyz",
"installments": [
{
"number": 1,
"payments": [
{
"paymentNumber": 3,
"value": 10
},
{
"paymentNumber": 4,
"value": 1
},
{
"paymentNumber": 5,
"value": 5
}
]
}
]
}
]
And here's the syntax I'm using:
db.collection.update({
"installments.payments.paymentNumber": 4
},
{
"$push": {
"installments.payments": [
{
"receipts": [
{
"receiptNumber": 1
}
]
}
]
}
})
This is causing this error:
fail to run update: multiple write errors: [{write errors: [{Cannot create field 'payments' in element {installments: [ { number: 1.0, payments: [ { paymentNumber: 3.0, value: 10.0 }, { paymentNumber: 4.0, value: 1.0 }, { paymentNumber: 5.0, value: 5.0 } ] } ]}}]}, {<nil>}]
Is it possible?
Here's the Mongo Playground link
And here's the expected result:
[
{
"key": 1,
"someValue": "abc",
"installments": [
{
"number": 1,
"payments": [
{
"paymentNumber": 1,
"value": 29
},
{
"paymentNumber": 2,
"value": 22
}
]
}
]
},
{
"key": 2,
"someValue": "xyz",
"installments": [
{
"number": 1,
"payments": [
{
"paymentNumber": 3,
"value": 10
},
{
"paymentNumber": 4,
"value": 1,
"receipts": [{
"receiptNumber": 1
}]
},
{
"paymentNumber": 5,
"value": 5
}
]
}
]
}
]
To update nested arrays, the filtered positional operator $[identifier] identifies the array elements that match the arrayFilters conditions for an update operation.
Try the following query to $push in nested array:
db.collection.update({
"installments.payments.paymentNumber": 4,
},
{
"$push": {
"installments.$.payments.$[payments].receipts": {
"receiptNumber": 1
}
}
},
{
"arrayFilters": [
{
"payments.paymentNumber": 4
}
],
multi: true
})
MongoDB Playground
I have two documents as shown. Their common factor is a node in the subdocument (type,veg_type). I have also added the same common node to each individual document (udf_type, udf_veg_type).One is a legacy data (with key node veggies) and another is new data (with key node vegetables).
How do I project the combined data of vegetables and veggies(in key node vegs) without the type and veg_type nodes? I use user_id for matching.
Intended Output
{
"user_id": 31,
"veggies": [
{
"udf_type": "green_vegetables",
"tot": 28560,
"itms": [
{
"num": 1,
"itm_det": {
"name": "spinach",
"qty": 18
}
}
],
"chksum": "d1583afab3a04f4b32589cfa64392765n78782ff60a0e0dc24b295868083"
},
{
"udf_type": "vegetables",
"tot": 2860,
"itms": [
{
"num": 1,
"itm_det": {
"name": "onion",
"qty": 1
}
}
],
"chksum": "e497c7b288e50e3be4c6bc676e4c849e4n5645n64a2d77748e185d7a1bce8c"
},
{
"udf_veg_type": "green_vegetables",
"tot": 2352000,
"itms": [
{
"num": 1,
"itm_det": {
"name": "kale",
"qty": 18
}
}
],
"chksum": "87b239cd9b39baa48b4564b5754009a131f542622ba018f37cd1fdb5"
}
]
}
{
"_id" : ObjectId("1"),
"user_id": 31,
"veggies": [
{
"type": "green_vegetables",
"desc": [
{
"udf_type": "green_vegetables",
"tot": 28560,
"itms": [
{
"num": 1,
"itm_det": {
"name": "spinach",
"qty": 18
}
}
],
"chksum": "d1583afab3a04f4b32589cfa64392765n78782ff60a0e0dc24b295868083"
}
]
},
{
"type": "vegetables",
"desc": [
{
"udf_type": "vegetables",
"tot": 2860,
"itms": [
{
"num": 1,
"itm_det": {
"name": "onion",
"qty": 1
}
}
],
"chksum": "e497c7b288e50e3be4c6bc676e4c849e4n5645n64a2d77748e185d7a1bce8c"
}
]
}
]
}
{
"_id" : ObjectId("2"),
"user_id": 31,
"vegetables": [
{
"veg_type": "green_vegetables",
"desc": [
{
"udf_veg_type": "green_vegetables",
"tot": 2352000,
"itms": [
{
"num": 1,
"itm_det": {
"name": "kale",
"qty": 18
}
}
],
"chksum": "87b239cd9b39baa48b4564b5754009a131f542622ba018f37cd1fdb5"
}
]
}
]
}
Assuming that the desc arrays always have just one item, try this:
db.collection.aggregate([
{
$match: {
"user_id": 31 // change this into the user_id variable
}
},
{
$group: {
_id: "$user_id",
veggies: {
$max: "$veggies"
},
vegetables: {
$max: "$vegetables"
}
}
},
{
$project: {
"user_id": "$_id",
"veggies": {
$concatArrays: [
{
$cond: [
"$veggies",
{
$map: {
input: "$veggies",
in: {
$arrayElemAt: [
"$$this.desc",
0
]
}
}
},
[]
]
},
{
$cond: [
"$vegetables",
{
$map: {
input: "$vegetables",
in: {
$arrayElemAt: [
"$$this.desc",
0
]
}
}
},
[]
]
}
]
}
}
}
])