user10256891
user10256891

Reputation:

Merge documents with its nested arrays and their nested arrays

I'm trying to create a query with the aggregation framework, but I could not get the result I want. I have a collection of resellers, each reseller have a list of clients, each clients have a list of members, the structure is as below :

[
{
  "userID" : "xxx",
  "userType" : "RESELLER",
  "clients" : [
     {
        "userID" : "xxx",
        "userType" : "CLIENT",
        "members" : [
           {
              "userID" : "xxx",
              "userType" : "MEMBER"
           },
           {
              "userID" : "xxx",
              "userType" : "MEMBER"
           }
        ]
     },
{
        "userID" : "xxx",
        "userType" : "CLIENT",
        "members" : [
           {
              "userID" : "xxx",
              "userType" : "MEMBER"
           },
           {
              "userID" : "xxx",
              "userType" : "MEMBER"
           }
        ]
     }
   ]
},
{
  "userID" : "xxx",
  "userType" : "RESELLER",
  "clients" : [
     {
        "userID" : "xxx",
        "userType" : "CLIENT",
        "members" : [
           {
              "userID" : "xxx",
              "userType" : "MEMBER"
           },
           {
              "userID" : "xxx",
              "userType" : "MEMBER"
           }
        ]
     },
{
        "userID" : "xxx",
        "userType" : "CLIENT",
        "members" : [
           {
              "userID" : "xxx",
              "userType" : "MEMBER"
           },
           {
              "userID" : "xxx",
              "userType" : "MEMBER"
           }
        ]
     }
   ]
}
]

The result I want to get is :

[
   {
      "userID" : "xxx",
      "userType" : "RESELLER"
   },
   {
      "userID" : "xxx",
      "userType" : "RESELLER"
   },
   {
      "userID" : "xxx",
      "userType" : "CLIENT"
   },
   {
      "userID" : "xxx",
      "userType" : "CLIENT"
   },
   {
      "userID" : "xxx",
      "userType" : "CLIENT"
   },
   {
      "userID" : "xxx",
      "userType" : "CLIENT"
   },
   {
      "userID" : "xxx",
      "userType" : "MEMBER"
   },
   {
      "userID" : "xxx",
      "userType" : "MEMBER"
   },
   {
      "userID" : "xxx",
      "userType" : "MEMBER"
   },
   {
      "userID" : "xxx",
      "userType" : "MEMBER"
   },
   {
      "userID" : "xxx",
      "userType" : "MEMBER"
   }
]

I did so many try but I did not to get this result. The closest solution that I did is :

db.resellers.aggregate([
{
    $unwind: "$clients"
},
{
    $project: {
        _id : 0,
        teamMembers : "$clients.members"
    }
},
{
    $unwind: "$members"
},
{
    $project: {
        _id : 0,
        userID : "$members.userID",
        type : "$members.type"
    }
}
]).pretty()

This solution returns only the list of members, so what i have to do to get a list containing all the resellers, the clients and the members all together?

Upvotes: 2

Views: 158

Answers (2)

Sede
Sede

Reputation: 61225

Well, you can do this in the $project stage.

[
    {
        "$project": {
            "members": {
                "$reduce": {
                    "input": {
                        "$map": {
                            "input": "$clients",
                            "in": {
                                "$concatArrays": [
                                    [
                                        {
                                            "userID": "$userID",
                                            "userType": "$userType"
                                        },
                                        {
                                            "userID": "$$this.userID",
                                            "userType": "$$this.userType"
                                        }
                                    ],
                                    "$$this.members"
                                ]
                            }
                        }
                    },
                    "initialValue": [

                    ],
                    "in": {
                        "$concatArrays": [
                            "$$this",
                            "$$value"
                        ]
                    }
                }
            }
        }
    }
]

Playground

Upvotes: 0

mickl
mickl

Reputation: 49945

You can use $reduce with $concatArrays to flatten your data structure and then run $unwind with $replaceRoot to get single member per document:

db.collection.aggregate([
  { "$project": {
    "members": {
      "$concatArrays": [
        [{ "userID": "$userID", "userType": "$userType" }],
        { "$reduce": {
          "input": "$clients",
          "initialValue": [],
          "in": {
            "$concatArrays": [
              "$$value",
              [{ "userID": "$$this.userID", "userType": "$$this.userType" }],
              "$$this.members"
            ]
          }
        }}
      ]
    }
  }},
  { "$unwind": "$members" },
  { "$replaceRoot": { "newRoot": "$members" }}
])

Mongo Playground

Upvotes: 1

Related Questions