0

I have structure shown below:

{
  field1: "somevalue",
  name:"xtz",
  nested_documents: [ 
                        {   
                            x:1,
                            y:2,
                            info:[
                                    {name:"sachin",value:"test"},
                                    {name:"sachin", value:"test"}
                                 ]
                        },
                        {
                            x:1,
                            y:3,
                            info:[
                                    {name:"sachin1",value:"test"},
                                    {name:"sachin2", value:"test"}
                                 ]
                        },
                        {
                            x:4,
                            y:3,
                            info:[
                                    {name:"sachin",value:"test"},
                                    {name:"sachin", value:"test"}
                                 ]
                        }
                    ]
    }

I know that I can retrieve element present inside 1st array using below code:

db.test.find({"nested_documents.x": 1},{_id: 0, nested_documents: {$elemMatch: {x: 1}}}

But, I want to apply same logic for name attribute. I want to retrieve only document that has name `sachin'. What I have tries is shown below:

db.test.find({"nested_documents.info.name": "sachin"}, 
        {_id: 0, 'nested_documents.info': {$elemMatch: {name: "sachin"}}});

But Mongo db says it does not support '.' operator inside projection :(.
Is there any other way to do this?(Using command prompt or code)

Command to insert document is shown below:

db.test.insert( {
  field1: "somevalue",
  name:"xtz",
  nested_documents: [ 
                        {   
                            x:1,
                            y:2,
                            info:[
                                    {name:"sachin",value:"test"},
                                    {name:"sachin", value:"test"}
                                 ]
                        },
                        {
                            x:1,
                            y:3,
                            info:[
                                    {name:"sachin1",value:"test"},
                                    {name:"sachin2", value:"test"}
                                 ]
                        },
                        {
                            x:4,
                            y:3,
                            info:[
                                    {name:"sachin",value:"test"},
                                    {name:"sachin", value:"test"}
                                 ]
                        }
                    ]
    }
    )

I am expecting output as:

{ "_id" : ObjectId("5142e0f153cd2aab3a3bae5b"), 
"nested_documents" : [ 
                        {       "x" : 1,        "y" : 2,       
                        "info" : [     
                                    {       "name" : "sachin",      "value" : "test" },   
                                    {       "name" : "sachin",      "value" : "test" } 
                                ] 
                        },
                        {      "x" : 4,        "y" : 3,        
                        "info" : [      {       "name" : "sachin",      "value" : "test" },
                                        {       "name" : "sachin",      "value" : "test" } 
                                ] 
                        } 
                    ]
}
Sachin
  • 3,424
  • 3
  • 21
  • 42
  • 1
    It would help if you edited your example doc so that it could be directly pasted into an insert. – JohnnyHK Mar 14 '13 at 14:28
  • This is just another flavor of your other question. See the duplicate I linked to on that question regarding how to do this with the aggregation framework. – JohnnyHK Mar 15 '13 at 13:20
  • 1
    @JohnnyHK: This question is related to retrieve sub-document present inside the sub-document. The other question was related to retrieve all matching document as "db.test.find({"nested_documents.x": 1},{_id: 0, nested_documents: {$elemMatch: {x: 1}}}" does not retrieve all document. while in this question nesting in projection is not allowed, so I was asking for is there any workaround for it. – Sachin Mar 15 '13 at 14:37

1 Answers1

0

You would have to use aggregate() with a double $unwind, like this:

db.test.aggregate([
    // filter for documents with x=1
    // note: this will use an index, if defined on "nested_documents.x"
    //{ $match: { "nested_documents.x": 1 } },
    // reduce data to nested_documents, as other fields are not considered
    { $project: { nested_documents: 1 } },
    // flatten the outer array
    { $unwind: "$nested_documents" },
    // filter for nested_documents with x=1
    // note that at this point nested_documents is no longer an array
    //{ $match: { "nested_documents.x": 1 } },
    // flatten the inner array
    { $unwind: "$nested_documents.info" },
    // filter for nested_documents.info.name = "sachin"
    // note that at this point nested_documents.info is no longer an array
    { $match: { "nested_documents.info.name": "sachin" } },
    // format output: re-create inner array
    { $group: { _id: { id: "$_id", 
                       nested_documents: { 
                          x: "$nested_documents.x",
                          y: "$nested_documents.y" 
                       }
                     }, 
                     info: { $push: "$nested_documents.info" } } },
    { $project: { "nested_documents.x": "$_id.nested_documents.x",
                  "nested_documents.y": "$_id.nested_documents.y",
                  "nested_documents.info": "$info" } },
    // format output: re-create outer array
    { $group: { _id: "$_id.id", nested_documents: { $push: "$nested_documents" } } },
])

note: I put in as //comments the logic to filter for x=1 as you had in a previous example

and the result is:

{
    "result" : [
        {
            "_id" : ObjectId("515d873457a0887a97cc8d19"),
            "nested_documents" : [
                {
                    "x" : 4,
                    "y" : 3,
                    "info" : [
                        {
                            "name" : "sachin",
                            "value" : "test"
                        },
                        {
                            "name" : "sachin",
                            "value" : "test"
                        }
                    ]
                },
                {
                    "x" : 1,
                    "y" : 2,
                    "info" : [
                        {
                            "name" : "sachin",
                            "value" : "test"
                        },
                        {
                            "name" : "sachin",
                            "value" : "test"
                        }
                    ]
                }
            ]
        }
    ],
    "ok" : 1
}

For more information on aggregate, refer to http://docs.mongodb.org/manual/applications/aggregation/

ronasta
  • 2,519
  • 1
  • 15
  • 8