Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

$lookup when foreignField is in nested array

I have two collections :

Student

 {    
    _id: ObjectId("657..."),  
    name:'abc'
 },  
 {  
    _id: ObjectId("593..."),  
    name:'xyz'
 }

Library

 {    
    _id: ObjectId("987..."),  
    book_name:'book1',
    issued_to: [
      {
       student: ObjectId("657...")
      },
      {
       student: ObjectId("658...")
      }
    ]
 },  
 {  
    _id: ObjectId("898..."),  
    book_name:'book2',
    issued_to: [
     {
       student: ObjectId("593...")
     },
     {
       student: ObjectId("594...")
     }
   ] 
 }

I want to make a Join to Student collection that exists in issued_to array of object field in Library collection.

I would like to make a query to student collection to get the student data as well as in library collection, that will check in issued_to array if the student exists or not if exists then get the library document otherwise not. I have tried $lookup of mongo 3.6 but I didn`t succeed.

db.student.aggregate([{$match:{_id: ObjectId("593...")}}, $lookup: {from: 'library', let: {stu_id:'$_id'}, pipeline:[$match:{$expr: {$and:[{"$hotlist.clientEngagement": "$$stu_id"]}}]}])

But it thorws error please help me in regard of this. I also looked at other questions asked at stackoverflow like. question on stackoverflow, question2 on stackoverflow but these are comapring simple fields not array of objects. please help me

like image 458
Arjun Shrivastava Avatar asked Jun 18 '18 13:06

Arjun Shrivastava


3 Answers

I am not sure I understand your question entirely but this should help you:

db.student.aggregate([{
    $match: { _id: ObjectId("657...") }
}, {
    $lookup: {
        from: 'library',
        localField: '_id' ,
        foreignField: 'issued_to.student',
        as: 'result'
    }
}])

If you want to only get the all book_names for each student you can do this:

db.student.aggregate([{
    $match: { _id: ObjectId("657657657657657657657657") }
}, {
    $lookup: {
        from: 'library',
        let: { 'stu_id': '$_id' },
        pipeline: [{
            $unwind: '$issued_to' // $expr cannot digest arrays so we need to unwind which hurts performance...
        }, {
            $match: { $expr: { $eq: [ '$issued_to.student', '$$stu_id' ] } }
        }, {
            $project: { _id: 0, "book_name": 1 } // only include the book_name field
        }],
        as: 'result'
    }
}])
like image 66
dnickless Avatar answered Nov 12 '22 20:11

dnickless


This might not be a very good answer, but if you can change your schema of Library to:

{    
  _id: ObjectId("987..."),  
  book_name:'book1'
  issued_to: [
    ObjectId("657..."),
    ObjectId("658...")
  ]
},  
{  
  _id: "ObjectId("898...")",  
  book_name:'book2'
  issued_to: [
    ObjectId("593...")
    ObjectId("594...")
  ] 
}

Then when you do:

{
  $lookup: {
    from: 'student',
    localField: 'issued_to',
    foreignField: '_id',
    as: 'issued_to_students',  // this creates a new field without overwriting your original 'issued_to'
  }
},

You should get, based on your example above:

{    
  _id: ObjectId("987..."),  
  book_name:'book1'
  issued_to_students: [
    { _id: ObjectId("657..."), name: 'abc', ... },
    { _id: ObjectId("658..."), name: <name of this _id>, ... }
  ]
},  
{  
  _id: "ObjectId("898...")",  
  book_name:'book2'
  issued_to: [
    { _id: ObjectId("593..."), name: 'xyz', ... },
    { _id: ObjectId("594..."), name: <name of this _id>, ... }
  ] 
}
like image 1
Flynn Hou Avatar answered Nov 12 '22 20:11

Flynn Hou


You need to $unwind the issued_to from library collection to match the issued_to.student with _id

db.student.aggregate([
  { "$match": { "_id": mongoose.Types.ObjectId(id) } },
  { "$lookup": {
    "from": Library.collection.name,
    "let": { "studentId": "$_id" },
    "pipeline": [
      { "$unwind": "$issued_to" },
      { "$match": { "$expr": { "$eq": [ "$issued_to.student", "$$studentId" ] } } }
    ],
    "as": "issued_to"
  }}
])
like image 1
Ashh Avatar answered Nov 12 '22 20:11

Ashh