jalatif
jalatif

Reputation: 43

return specific mongodb embedded document

I want to query mongodb document to return a specific document

Suppose I have the below document and I want to search only the specific details of a given room. Say, I want query to return notes for room_num = 210. How do I do it?

I was able to query by db.locations.findOne({"facility.room.room_num" : room_num}); I don't know how to project room only. Generalizing the question, how do I project only the embedded document specific entries only.

My document looks like this --->

{
  _id: Object("xxx"),
  name: "Campus",
  facilities: [
     { name : "Science",
     rooms : [
          {
             room_num: 210,
             notes: "Chemistry Lab 1",
             Dimension: "x*x"
          },{
             room_num: 120,
             notes: "Chemistry Lab 2",
             Dimension: "x*x"
          }
      ]

    },
    { name : "Arts",
     rooms: [
          {
             room_num: 90,
             notes: "Drawing 1",
             Dimension: "x*x"
          },{
             room_num: 100,
             notes: "Drawing 2",
             Dimension: "x*x"
          }
      ]

    }
  ]

Upvotes: 2

Views: 242

Answers (3)

Neil Lunn
Neil Lunn

Reputation: 151072

Since you have an embedded array (one within another) then you can indeed do this with aggregate. You just need to do a "double" unwind operation:

db.collection.aggregate([
    { "$unwind": "$facilities" },
    { "$unwind": "$facilities.rooms" },
    { "$match": { "facilities.rooms.room_num": 210 } }
])

And if you really want it to look like the original document, then take it a little further:

db.collection.aggregate([
    { "$unwind": "$facilities" },
    { "$unwind": "$facilities.rooms" },
    { "$match": { "facilities.rooms.room_num": 210 } },
    { "$group": {
        "_id": "$_id",
        "name": { "$first": "$name" },
        "facName": { "$first": "$facilities.name" },
        "rooms": { "$push": "$facilities.rooms" }
    }},
    { "$group": {
        "_id": "$_id",
        "name": { "$first": "$name" },
        "facities": { "$push": {
            "name": "$facName" ,
            "rooms": "$rooms"
        }}
    }}
])

So from your data this would be the result:

{
    "_id" : {
            "0" : "x",
            "1" : "x",
            "2" : "x"
    },
    "name" : "Campus",
    "facities" : [
       {
           "name" : "Science",
           "rooms" : [
               {
                   "room_num" : 210,
                   "notes" : "Chemistry Lab 1",
                   "Dimension" : "x*x"
               }
           ]
       }
    ]
}

Or finally if all you need is the inner rooms part of the arrays, then just use a $project statement at the end, as from the first example:

db.campus.aggregate([
    { "$unwind": "$facilities" },
    { "$unwind": "$facilities.rooms" },
    { "$match": { "facilities.rooms.room_num": 210 } },
    { "$project": {
        "_id": 0,
        "room_num": "$facilities.rooms.room_num",
        "notes": "$facilities.rooms.notes",
        "Dimension": "$facilities.rooms.Dimension"
    }}
])

With a result like this:

{ "room_num" : 210, "notes" : "Chemistry Lab 1", "Dimension" : "x*x" }

And that should clear it up.

Upvotes: 1

yaoxing
yaoxing

Reputation: 4173

You can make use of the aggregation framework. I believe the $project and $unwind operators are what you are looking for.

db.rooms.aggregate([{
  $project: {
    _id: 0,
    facilities: "$facilities.rooms.room_num"
  }
}, {
  $unwind: "$facilities"
}, {
  $unwind: "$facilities"
}])

and you would get something like:

{
  "result" : [
    {
      "facilities" : 210
    },
    {
      "facilities" : 120
    },
    {
      "facilities" : 90
    },
    {
      "facilities" : 100
    }
  ],
  "ok" : 1
}

Upvotes: 0

Lalit Agarwal
Lalit Agarwal

Reputation: 2354

Try the below query

db.locations.aggregate( 
 { $unwind: "facilities.rooms" },
 { $match: { "facilities.rooms.room_num" : room_num}},
 { $project: { "facilities.rooms.notes" : 1 }}
)

Upvotes: 0

Related Questions