I have a collection which contains balance object that needs to be flattened and the fields within that object need to renamed based on another flag in collection –
[
{
"id": "1234",
"documentType": "IV",
"balance": {
"total": 100,
"openBalance": 60,
"paid": 40
}
},
{
"id": "9012",
"documentType": "CM",
"balance": {
"total": 50,
"applied": 0,
"available": 50
}
}
]
if documentType === "IV", then "balance.paid" would become "totalAmountPaid" in $$ROOT, whereas if it is "CM", then "balance.applied" would be renamed as appliedAmount and "balance.available" as availableAmount, so the final collection after flattening the balance object becomes –
[
{
"id": "1234",
"documentType": "IV",
"total": 100,
"openBalance": 60,
"totalAmountPaid": 40
},
{
"id": "9012",
"documentType": "CM",
"total": 50,
"appliedAmount": 0,
"availableAmount": 50
}
]
I tried using $set and $cond like this but it doesn’t work and I am not very familiar with MongoDB commands –
db.collection.aggregate([
{
$set: {
$cond: {
if: {
$eq: [
"$documentType",
"IV"
]
},
then: {
"total": "$balance.total",
"openBalance": "$balance.openBalance",
"totalAmountPaid": "$balance.paid",
"balance": "$$REMOVE"
},
else: {
"$total": "$balance.total",
"$availableAmount": "$balance.available",
"$appliedAmount": "$balance.applied",
"balance": "$$REMOVE"
}
}
}
}
])
2
Answers
The issue you’re facing in
$set
is because you’re trying to create multiple fields (the new object) within one, and you haven’t provided the name.Since you’re creating multiple new fields it would be easier to first create them as a sub-object within one field and then replace the original doc with that sub-object.
(Also, in your
else
clause, you’re using$total
as the field name instead of justtotal
.)Changes needed:
newdoc
and that expression contains your previous code."balance": "$$REMOVE"
since it will be replaced with thenewdoc
anyway$unset
the unneededbalance
field and the childnewdoc
field.Mongo Playground
(Previous Mongo Playground)
My previous answer provides a solution with the fewest changes needed to your original aggregation pipeline.
But if you had many document types (not just two), each with their own resulting "newdoc" with different fields, then they would need to be written as nested
$cond
‘s within theelse
clauses. That would become unreadable and hard to maintain.Example data:
For such cases, using
$switch
would be better/easier/cleaner.Here, the
$switch
replaces$cond
and theif-else
parts each becomecase-then
‘s with an optional finaldefault
:Mongo Playground with additional types