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
]
}
}
},
[]
]
}
]
}
}
}
])
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
I tried using the aggregation lookup but it didn't work, when I try to replace the objectId or at least replace it the old data get overridden and I loose the the "users" array.
I would like to get some help.
This is my current DB.
db={
"Persons": [
{
"_id": {
"$oid": "62d7b53009b0fd96353c043f"
},
"Name": "Alice",
"petName": "Alice jr"
},
{
"_id": {
"$oid": "62e7cc0e264d240455f66110"
},
"Name": "Bob",
"Age": 27
}
],
"Accounts": [
{
"_id": {
"$oid": "62e788b5264d240455f6610c"
},
"banned": [],
"active": [
{
"users": [
{
"first": "BobRules",
"second": "BobTheThird"
}
],
"person": {
"$oid": "62e7cc0e264d240455f66110"
},
},
{
"users": [
{
"first": "AliceRules",
"second": "AliceTheSecond"
},
{
"first": "SecretAlice",
"second": "AliceAndEve"
}
],
"person": {
"$oid": "62d7b53009b0fd96353c043f"
}
}
]
}
]
}
And this is the output I would like to get.
{
"_id": {
"$oid": "62e788b5264d240455f6610c"
},
"banned": [],
"active": [
{
"users": [
{
"first": "BobRules",
"second": "BobTheThird"
}
],
"person": {
"$oid": "62e7cc0e264d240455f66110",
"Name": "Bob",
"Age": 27
},
},
{
"users": [
{
"first": "AliceRules",
"second": "AliceTheSecond"
},
{
"first": "SecretAlice",
"second": "AliceAndEve"
}
],
"person": {
"$oid": "62d7b53009b0fd96353c043f",
"Name": "Alice",
"petName": "Alice jr"
}
}
]
}
Finally I was able to get the wanted result, thanks to the answer in this question: MongoDB join data inside an array of objects
The aggregation had to be:
[{
$addFields: {
active: {
$ifNull: [
'$active',
[]
]
}
}
}, {
$lookup: {
from: 'Persons',
localField: 'active.person',
foreignField: '_id',
as: 'persons'
}
}, {
$addFields: {
active: {
$map: {
input: '$active',
'in': {
$mergeObjects: [
'$$this',
{
person: {
$arrayElemAt: [
'$persons',
{
$indexOfArray: [
'$persons._id',
'$$this.person'
]
}
]
}
}
]
}
}
}
}
}, {
$project: {
persons: 0
}
}]
My sample document
{ "pId":12345, "charges": [
{
"type": "asr",
"dId": 123,
"value": 100
},
{
"type": "asr",
"dId": 124,
"value": 120
},
{
"type": "asp",
"dId": 125,
"value": 130
},
{
"type": "asn",
"dId": 126,
"value": 130
},
{
"type": "aso",
"dId": 127,
"value": 150
}....
] }
Excluded charges input:
charges [
{
"type": "asr",
"dId": 123
},
{
"type": "asr",
"dId": 124
} ...
]
I need to fetch all charges from the sample document except Excluded charges. Can someone help me to solve this?
I tried this
{}
{"$project" :{
"_id" : 0, "pId" : 1,
"charges": { "$filter" : { "input" : "$charges", "as" : "charge",
"cond" :{
{ "$not" : { "$and" : [{ "$eq" : ["$$charge.type", "asr"]}, { "$eq" : ["$$charge.dId", 123]}]}}
}
}}
When I have multiple excluded charges how can we do this
use this :
[
{
'$project': {
'charges': {
'$map': {
'input': {
'$filter': {
'input': '$charges',
'as': 'featuresT',
'cond': {
'$eq': [
{
'$or': [
{
'$and': [
{
'$eq': [
'$$featuresT.type', 'asr'
]
}, {
'$eq': [
'$$featuresT.dId', 123
]
}
]
}, {
'$and': [
{
'$eq': [
'$$featuresT.type', 'asr'
]
}, {
'$eq': [
'$$featuresT.dId', 124
]
}
]
}
]
}, false
]
}
}
},
'as': 'featuresF',
'in': {
'type': '$$featuresF.type',
'dId': '$$featuresF.dId',
'value': '$$featuresF.value'
}
}
}
}
}
]
found a simple way.
db.collection.aggregate([
{
$match: {
"pId": {
$eq: 12345
}
}
},
{
"$project": {
"_id": 0,
"pId": 1,
"charges": {
"$filter": {
"input": "$charges",
"as": "charge",
"cond": {
"$not": {
"$or": [
{
"$and": [
{
"$eq": [
"$$charge.type",
"asr"
]
},
{
"$eq": [
"$$charge.dId",
123
]
}
]
},
{
"$and": [
{
"$eq": [
"$$charge.type",
"asr"
]
},
{
"$eq": [
"$$charge.dId",
124
]
}
]
}
]
}
}
}
}
}
}
])
mongoplayground
$filter to filter charges array
$in with $not to exclude only the values that you want
db.collection.aggregate([
{
"$project": {
"_id": 0,
"pId": 1,
"charges": {
"$filter": {
"input": "$charges",
"cond": {
"$not": {
"$in": [
"$$this.dId",
[123, 124]
]
}
}
}
}
}
}
])
Here is the working example: https://mongoplayground.net/p/0uIdoml384h
[
{
"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
{
"_id" : ObjectId("52f504bb2f9dd91186211537"),
"Data": {
"Stage": {
"FirstArray": [
{
"Name": "FirstLevelArray-FirstObject",
"_id": ObjectId("5fe1a5fa2d8e360ac4093b7e"),
"SecondArray": [
{
"Name": "1-SecondLevelArray-FirstObject",
"_id": ObjectId("5fe1a7a52d8e360ac4093b81")
},
{
"Name": "1-SecondLevelArray-SecondObject",
"_id": ObjectId("5fe1a7a52d8e360ac4093b82")
}
]
},
{
"Name": "FirstLevelArray-SecondObject",
"_id": ObjectId("5fdc9dced45fa417d417c441"),
"SecondArray": [
{
"Name": "2-SecondLevelArray-FirstObject",
"_id": ObjectId("5fde08564d28f313acc0c93b")
},
{
"Name": "2-SecondLevelArray-SecondObject",
"_id": ObjectId("5fde08d64d28f313acc0c93c")
}
]
}
]
}
}
}
This is the sample format of my code.
I want to delete this object { "Name": "2-SecondLevelArray-SecondObject", "_id": ObjectId("5fde08d64d28f313acc0c93c") } from this record.
I tried this query
model.update(
{ $and: [{ "_id": ObjectId("52f504bb2f9dd91186211537") }},
{"Data.Stage.FirstArray.SecondArray._id":ObjectId("5fde08d64d28f313acc0c93c")}] ,
{ $pull:{
"Data.Stage.FirstArray.$.SecondArray._id": ObjectId("5fe1a7a52d8e360ac4093b82")
}
},
{new:true,upsert:false})
How would I achieve this in MongoDB ?
Here is the expected result of find({"_id" : ObjectId("52f504bb2f9dd91186211537")}) after the update
EDIT: {
"_id" : ObjectId("52f504bb2f9dd91186211537"),
"Data": {
"Stage": {
"FirstArray": [
{
"Name": "FirstLevelArray-FirstObject",
"_id": ObjectId("5fe1a5fa2d8e360ac4093b7e"),
"SecondArray": [
{
"Name": "1-SecondLevelArray-FirstObject",
"_id": ObjectId("5fe1a7a52d8e360ac4093b81")
},
{
"Name": "1-SecondLevelArray-SecondObject",
"_id": ObjectId("5fe1a7a52d8e360ac4093b82")
}
]
},
{
"Name": "FirstLevelArray-SecondObject",
"_id": ObjectId("5fdc9dced45fa417d417c441"),
"SecondArray": [
{
"Name": "2-SecondLevelArray-FirstObject",
"_id": ObjectId("5fde08564d28f313acc0c93b")
}
]
}
]
}
}
}
model.update({ _id: ObjectId("52f504bb2f9dd91186211537"), Data.Stage.FirstArray:{ $elemMatch: { SecondArray:{$elemMatch:{"_id":ObjectId("5fde08d64d28f313acc0c93c")}}}}},
{ $pull:{ "Data.Stage.FirstArray.$.SecondArray":{"_id": ObjectId("5fde08d64d28f313acc0c93c") }}},{new:true,upsert:false})
My idea is to filter out those with your given Name field
model.updateMany({}, {
$set: { "Data.Stage.FirstArray.SecondArray": { $filter: {
input: "$Data.Stage.FirstArray.SecondArray",
as: "item",
cond: { $eq: [ "$$item.Name", '2-SecondLevelArray-SecondObject' ] }
} } },
});
Honestly, I'm not sure it will be working but it worths a try.