2

I would like to aggregate building with floors and rooms:

Given the following 3 collections:

/* buildings */
{
    "_id" : ObjectId("59a09abe388f595b15bb5fa6"),
    "name" : "home"
}

/* floors */
{
    "_id" : ObjectId("59a09abe388f595b15bb5fa3"),
    "buildingId" : ObjectId("59a09abe388f595b15bb5fa6"),
    "name" : "upstairs"
}
{
    "_id" : ObjectId("59a09abe388f595b15bb5fa2"),
    "buildingId" : ObjectId("59a09abe388f595b15bb5fa6"),
    "name" : "downstairs"
}

/* rooms */
{
    "_id" : ObjectId("59a09bce388f595b15bb5fb6"),
    "floorId" : ObjectId("59a09abe388f595b15bb5fa3"),
    "name" : "bathroom",
    "_userId" : ObjectId("590a08dba07c1a1bee87b310")
}
{
    "_id" : ObjectId("59a09bce388f595b15bb5fc6"),
    "floorId" : ObjectId("59a09abe388f595b15bb5fa3"),
    "name" : "living room",
    "_userId" : ObjectId("590a08dba07c1a1bee87b310")
}

I would like to lookup them together and tried it with the following query:

db.getCollection('buildings').aggregate([
    {
        "$lookup": {
            "from": "floors",
            "localField": "_id",
            "foreignField": "buildingId",
            "as": "floors"
        }
    },
    {
        "$lookup": {
            "from": "rooms",
            "localField": "floors._id",
            "foreignField": "floorId",
            "as": "floors.rooms"
        }
    }
  ]);

This gives the following result:

{
    "_id" : ObjectId("59a09abe388f595b15bb5fa6"),
    "_userId" : ObjectId("590a08dba07c1a1bee87b310"),
    "name" : "home",
    "floors" : {
        "rooms" : []
    }
}

But I would have the following result:

{
    "_id" : ObjectId("59a09abe388f595b15bb5fa6"),
    "_userId" : ObjectId("590a08dba07c1a1bee87b310"),
    "name" : "home",
    "floors" : [ 
        {
            "_id" : ObjectId("59a09abe388f595b15bb5fa3"),
            "buildingId" : ObjectId("59a09abe388f595b15bb5fa6"),
            "name" : "upstairs",
            "rooms": [
            {
                     "_id" : ObjectId("59a09bce388f595b15bb5fb6"),
                    "floorId" : ObjectId("59a09abe388f595b15bb5fa3"),
                    "name" : "bathroom"
                },
                {
                    "_id" : ObjectId("59a09bce388f595b15bb5fc6"),
                    "floorId" : ObjectId("59a09abe388f595b15bb5fa3"),
                    "name" : "living room"
                }
            ]
        }, 
        {
            "_id" : ObjectId("59a09abe388f595b15bb5fa2"),
            "buildingId" : ObjectId("59a09abe388f595b15bb5fa6"),
            "name" : "downstairs",
            "rooms" : [ ]
        }
    ]
}

As you see I would like to lookup all the references to get the building structure with it's floors and rooms.

How can I achieve that?

2 Answers 2

1

A glance at your aggregate query and the the kind of output it results makes me feel double $lookup (lookup inside another lookup) is not supported by MongoDB(till 3.4 version). So your best bet is to use $unwind and get the results closer to your expectation.

Here is the query:

db.getCollection('buildings').aggregate([
     {
         "$lookup": {
             "from": "floors",
             "localField": "_id",
             "foreignField": "buildingId",
             "as": "floors"
         }
     },
     {"$unwind":"$floors"},
     {
         "$lookup": {
             "from": "rooms",
             "localField": "floors._id",
             "foreignField": "floorId",
             "as": "floors.rooms"
         }
     }
   ]);

and its output:

{
        "_id" : ObjectId("59a09abe388f595b15bb5fa6"),
        "name" : "home",
        "floors" : {
                "_id" : ObjectId("59a09abe388f595b15bb5fa3"),
                "buildingId" : ObjectId("59a09abe388f595b15bb5fa6"),
                "name" : "upstairs",
                "rooms" : [
                        {
                                "_id" : ObjectId("59a09bce388f595b15bb5fb6"),
                                "floorId" : ObjectId("59a09abe388f595b15bb5fa3"),
                                "name" : "bathroom",
                                "_userId" : ObjectId("590a08dba07c1a1bee87b310")
                        },
                        {
                                "_id" : ObjectId("59a09bce388f595b15bb5fc6"),
                                "floorId" : ObjectId("59a09abe388f595b15bb5fa3"),
                                "name" : "living room",
                                "_userId" : ObjectId("590a08dba07c1a1bee87b310")
                        }
                ]
        }
}
{
        "_id" : ObjectId("59a09abe388f595b15bb5fa6"),
        "name" : "home",
        "floors" : {
                "_id" : ObjectId("59a09abe388f595b15bb5fa2"),
                "buildingId" : ObjectId("59a09abe388f595b15bb5fa6"),
                "name" : "downstairs",
                "rooms" : [ ]
        }
}
Sign up to request clarification or add additional context in comments.

Comments

1

$unwind makes floors $lookupable and $group adds them together again.

db.getCollection('buildings').aggregate([
    {
        "$lookup": {
            "from": "floors",
            "localField": "_id",
            "foreignField": "buildingId",
            "as": "floors"
        }
    },
    {
        "$unwind": "$floors"
    },
    {
        "$lookup": {
            "from": "rooms",
            "localField": "floors._id",
            "foreignField": "floorId",
            "as": "floors.rooms"
        }
    },
    {
        "$group": {
            "_id": "$_id",
            "name": { "$first": "$name" },
            "floors": { "$push": "$floors" }
        }
    }]);

The result is exactly what I requested (see question).

1 Comment

I will, but I have to wait 48 hours after asking the question until I can do that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.