Search code examples
mongodbmongodb-queryaggregation-frameworkphp-mongodb

$unwind and $lookup with php mongodb


I have a table TblStudent in mongodb like

       {
      "_id": ObjectId("5baa85041d7859f40d000029"),
       "Name": "John Doe",
       "RollNo": 12,
       "Class": "Ist"
        ....
       }

I have another table TblRoute like

   {
   "_id": ObjectId("5baa818d1d78594010000029"),
   "Name": "New york City",
   "StopDetails": [
   {
    "StopId": "abc777",
    "Name": "Block no 3"
   },
   {
   "StopId": "abc888",
   "Name": "Block no 4"
   }
 ],
"NumberOfSeats": "10",
"StudentDetails": [
 {
   "StudentId": ObjectId("5baa85041d7859f40d000029"),
   "VehicleId": "7756"
  },
  {
   "StudentId": ObjectId("5baa85f61d7859401000002a"),
   "VehicleId": "7676"
 }
 ]
}

I am using mongodb 3.6 platform. I am using below lines of code after getting online help!!!

      $query = ['_id' => new MongoDB\BSON\ObjectID($this->id)];
      $cursor = $this->db->TblRoute->aggregate([
      ['$match' => $query],
      ['$lookup' => [
         'from' => "TblStudent",
         'let' => ['studentid' => '$StudentDetails.StudentId'],
         'pipeline' => [
         ['$match' => ['$expr'=> ['$in' => ['$_id','$$studentid' ]]]],
         ['$project' => ['Name'=> 1,'RollNo' => 1, '_id'=> 1]]
         ],
         'as' => 'studentObjects',
       ]],
       ['$unwind'=> '$studentObjects' ],
        // Group back to arrays
       [ '$group'=> [
               'StudentDetails.StudentId'=> '$_id',
                'StudentDetails.StudentData'=> [ '$push'=> '$studentObjects' ]
       ]]

     ]);

It is throwing error message Uncaught exception 'MongoDB\Driver\Exception\RuntimeException' with message 'The field 'StudentDetails.StudentId' must be an accumulator object"

Basically I wanted to fetch the data from another table using lookup aggregation. So after online help and further research, I was able to write the above code. The main problem here is that the code before $unwind statement generate the output as separate array. If we write 'as' => 'StudentDetails.studentObjects' in $lookup the data was overwritten with the new data and thus resulting the loss of other fields like vehicleid etc. I want to preserve them. So after research I tried to add $group to put it back to the StudentDetails embedded document.

Desired output

        {
"_id": ObjectId("5baa818d1d78594010000029"),
"Name": "New york City",
"StopDetails": [
     .....
  ],
   "StudentDetails": [
   {
   "StudentId": ObjectId("5baa85041d7859f40d000029"),
   "VehicleId": "7756",
   "StudentData": [
      "Name": ..
      "RollNo":...
    ]
    },
    {
    "StudentId": ObjectId("5baa85f61d7859401000002a"),
    "VehicleId": "7676",
     "StudentData": [
      "Name": ..
      "RollNo":...
    ]
   }
   ]
  }

Please help me in sorting out the problem


Solution

  • You can do something like this

    $cursor = $this->db->TblRoute->aggregate([
      [ "$match" => $query ],
      [ "$unwind": "$StudentDetails" ],
      [ "$lookup" => [
        "from" => "TblStudent",
        "let" => [ "studentid" => "$StudentDetails.StudentId" ],
        "pipeline" => [
          [ "$match" => [ "$expr"=> [ "$eq" => [ "$_id", "$$studentid" ]]]],
          [ "$project" => [ "Name"=> 1, "RollNo" => 1, "_id"=> 1 ]]
        ],
        "as" => "StudentDetails.StudentId"
      ]],
      [ "$unwind": "$StudentDetails.StudentId" ],
      [ "$group"=> [
        "_id"=> "$_id",
        "Name": [ "$first"=> "$Name" ],
        "StopDetails": [ "$first"=> "$StopDetails" ],
        "StudentDetails"=> [ "$push"=> "$StudentDetails" ]
      ]]
    ])