I have documents like this:
{
"many" : {},
"other" : {},
"fields" : {},
"phases" : [
{
"type" : 10,
"states" : [
{
"type" : 10,
"time" : ISODate("2018-04-25T13:06:42.990+02:00")
},
{
"type" : 20,
"time" : ISODate("2018-04-25T13:26:12.122+02:00")
},
{
"type" : 30,
"time" : ISODate("2018-04-25T13:26:30.124+02:00")
}
]
},
{
"type" : 20,
"states" : [
{
"type" : 10,
"time" : ISODate("2018-04-25T13:27:58.201+02:00")
}
]
}
]
}
Within an aggregation, I'm trying to flatten the states
including the parent's type
like this (desired output):
"states" : [
{
"phase": 10,
"type" : 10,
"time" : ISODate("2018-04-25T13:06:42.990+02:00")
},
{
"phase": 10,
"type" : 20,
"time" : ISODate("2018-04-25T13:26:12.122+02:00")
},
{
"phase": 10,
"type" : 30,
"time" : ISODate("2018-04-25T13:26:30.124+02:00")
},
{
"phase": 20,
"type" : 10,
"time" : ISODate("2018-04-25T13:27:58.201+02:00")
}
]
I already accomplished the additional states
field without the phase
field with this aggregation:
db.docs.aggregate([
{
$addFields: {
states: {
$reduce: {
input: "$phases",
initialValue: [],
in: { $concatArrays: ["$$value", "$$this.states"] }
}
}
}
}
])
The 'many other fields' should be preserved, so I believe grouping is not an option.
MongoDB version is 3.4.
I tried many things without result. I wonder if and how this is possible.
db.state.aggregate(
// Pipeline
[
// Stage 1
{
$unwind: {
path : "$phases",
includeArrayIndex : "arrayIndex", // optional
preserveNullAndEmptyArrays : false // optional
}
},
// Stage 2
{
$unwind: {
path : "$phases.states",
includeArrayIndex : "arrayIndex", // optional
preserveNullAndEmptyArrays : false // optional
}
},
// Stage 3
{
$project: {
"phases":"$phases.type",
"type":"$phases.states.type",
"time":"$phases.states.time",
}
},
// Stage 4
{
$group: {
"_id":"$_id",
states: { $push: { phases: "$phases", type: "$type",time: "$time" } }
}
},
]);
You can use below aggregation. Use $map
to format the states array to include phase field.
db.docs.aggregate([
{"$addFields":{
"states":{
"$reduce":{
"input":"$phases",
"initialValue":[],
"in":{
"$concatArrays":[
"$$value",
{"$map":{
"input":"$$this.states",
"as":"state",
"in":{"phase":"$$this.type","type":"$$state.type","time":"$$state.time"}
}}
]
}
}
}
}}
])
The code of Mohit Kumar Bordia looks great. In addition, if you want to also return the old object (based in Mohit code):
db.getCollection('flatten').aggregate(
// Pipeline
[
// Stage 1
{
$unwind: {
path : "$phases",
includeArrayIndex : "arrayIndex", // optional
preserveNullAndEmptyArrays : false // optional
}
},
// Stage 2
{
$unwind: {
path : "$phases.states",
includeArrayIndex : "arrayIndex", // optional
preserveNullAndEmptyArrays : false // optional
}
},
// Stage 3
{
$project: {
"_id" : "$_id",
"many" : "$many",
"other" :"$other",
"fields" : "$fields",
"phases":"$phases.type",
"type":"$phases.states.type",
"time":"$phases.states.time",
}
},
// Stage 4
{
$group: {
"_id":"$_id",
many : { $first: "$many"},
other: { "$first": "$other" },
fields: { "$first": "$fields" },
states: { $push: { phases: "$phases", type: "$type",time: "$time" } }
}
},
]);
You need to loop over each array using $map
and then you can use $reduce
to $concat
the final arrays
db.collection.aggregate([
{ "$addFields": {
"states": { "$reduce": {
"input": { "$map": {
"input": "$phases",
"as": "pa",
"in": { "$map": {
"input": "$$pa.states",
"as": "st",
"in": { "type": "$$st.type", "time": "$$st.time", "phase": "$$pa.type" }
}}
}},
"initialValue": [],
"in": { "$concatArrays": ["$$value", "$$this"] }
}}
}}
])
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With