How to build query for relating parent document's field to embedded array document's field. Using MongoDB aggregation Operators

Problem

Consider the following document in the collection named 'CityAssociation'

{
  "_id" : "MY_ID",
  "ThisCityID" : "001",
  "CityIDs" : [{
      "CityID" : "001",
      "CityName" : "Bangalore"
    }, {
      "CityID" : "002",
      "CityName" : "Mysore"
    }],
   "CityUserDetails": {
       "User" : "ABCD"
   }
}

Now I have User value i.e. in above case I have value ABCD and want to find it with only city where the first level's field ThisCityID matches to the embedded array documnet's field CityID. Finally I need to project as follows (for the above case):

{
'UserName': 'ABCD',
'HomeTown':'Bangalore'
}

In Node.js + MongoDB native drive, I wrote a aggregation query as follows which is not working as expected.

collection.aggregate([
{ $match: { 'CityUserDetails.User': 'ABCD', 'CityIDs': { $elemMatch: { CityID: ThisCityID}}} },
{ $unwind: "$CityIDs" },
{ $group: {
    _id: '$_id',
    CityUserDetails: { $first: "$CityUserDetails" },
    CityIDs: { $first: "$CityIDs" }
   }
},
    { $project: {
        _id: 0,
        "UserName": "$CityUserDetails.User",
        "HomeTown": "$CityIDs.CityName"
       }
    }
    ], function (err, doc) {
        if (err) return console.error(err);
        console.dir(doc);
    }
);

Can anyone tell me how this can be done with query.

Note: On MongoDB schema we don't have control to change it.

Problem courtesy of: Amol M Kulkarni

Solution

You can use the $eq operator to check if the first level's field ThisCityID matches embedded array document's field CityID.

db.city.aggregate([
   { $match : { "CityUserDetails.User" : "ABCD" }}, 
   { $unwind : "$CityIDs" },
   { $project : {
         matches : { $eq: ["$CityIDs.CityID","$ThisCityID"]},
         UserName : "$CityUserDetails.User",
         HomeTown : "$CityIDs.CityName"
    }},
   { $match : { matches : true }},
   { $project : {
         _id : 0,
         UserName : 1,
         HomeTown : 1
   }},
])

And the result is:

{
    "result" : [
        {
            "UserName" : "ABCD",
            "HomeTown" : "Bangalore"
        }
    ],
    "ok" : 1
}
Solution courtesy of: Linda Qin

Discussion

There is currently no discussion for this recipe.

This recipe can be found in it's original form on Stack Over Flow.