2016-04-23 24 views
4

我有兩個模式,定義如下:如何加入兩個集合的貓鼬

var WorksnapsTimeEntry = BaseSchema.extend({ 
student: { 
    type: Schema.ObjectId, 
    ref: 'Student' 
}, 
timeEntries: { 
    type: Object 
} 
}); 

var StudentSchema = BaseSchema.extend({ 
firstName: { 
    type: String, 
    trim: true, 
    default: '' 
    // validate: [validateLocalStrategyProperty, 'Please fill in your first name'] 
}, 
lastName: { 
    type: String, 
    trim: true, 
    default: '' 
    // validate: [validateLocalStrategyProperty, 'Please fill in your last name'] 
}, 
displayName: { 
    type: String, 
    trim: true 
}, 
municipality: { 
    type: String 
    } 
}); 

而且我想環路直通每個學生,並顯示它的時間條目。到目前爲止,我有這樣的代碼顯然是不正確的,因爲我仍然不知道如何加入WorksnapTimeEntry模式表。

Student.find({ status: 'student' }) 
     .populate('student') 
     .exec(function (err, students) { 
      if (err) { 
       return res.status(400).send({ 
        message: errorHandler.getErrorMessage(err) 
       }); 
      } 
      _.forEach(students, function (student) { 
       // show student with his time entries.... 
      }); 
      res.json(students); 
     }); 

任何人都知道我該如何做到這一點?

+0

你這樣做是錯誤的。你想''將'WorksnapTimeEntry'的'student'路徑作爲一個源文件'.populate()'。如果你期望它反過來,那麼你要麼在每個'Student'中包含引用作爲一個「數組」,要麼手動查詢和組合,查找'WorksnapTimeEntry'中的「student」字段。 '.populate()'方法只能用對象中的「已存儲」引用。 –

+0

@BlakesSeven我這樣做的事情是,我想先把所有的學生展示在桌面上,然後顯示每個人的時間條目,我不能顯示timeEntries,因爲他們是桌上的數百萬人......我正在嘗試類似這增加了學生模式,但仍然沒有工作:timeEntries:[{type:Schema.Types.ObjectId,ref:'WorksnapsTimeEntry'}], 然後.populate('學生')將像.populate 'timeEntries'),你能告訴我爲什麼這不起作用嗎? –

+0

不,它會是WorksnapsTimeEntry.find()。populate(「students」).exec({..})'因爲'「students」'是該模型中的「路徑」,而「路徑」就是你作爲參數提供給'.populate()'。如果你想要一個不同的格式,那麼你需要重新處理結果。或者正如我已經說過的,您需要創建對「Student」模型文檔中的每個「WorksnapTimeEntry」的引用。這就是'.populate()'的作用。你目前的想法是相反的。 –

回答

5

你不想.populate()這裏,而是你想兩個查詢,其中第一Student對象相匹配,以獲得_id值,第二個將使用$in匹配相應的WorksnapsTimeEntry項目爲那些「學生」。

使用async.waterfall只是爲了避免一些壓痕蠕變:

async.waterfall(
    [ 
     function(callback) { 
      Student.find({ "status": "student" },{ "_id": 1 },callback); 
     }, 
     function(students,callback) { 
      WorksnapsTimeEntry.find({ 
       "student": { "$in": students.map(function(el) { 
        return el._id 
       }) 
      },callback); 
     } 
    ], 
    function(err,results) { 
     if (err) { 
      // do something 
     } else { 
      // results are the matching entries 
     } 
    } 
) 

如果你真的必須從其他的表,那麼你可以.populate("student")第二查詢來獲取填充項目。

相反的情況是在WorksnapsTimeEntry查詢並返回「一切」,然後濾除來自.populate()任何null結果與「匹配」的查詢選項:

WorksnapsTimeEntry.find().populate({ 
    "path": "student", 
    "match": { "status": "student" } 
}).exec(function(err,entries) { 
    // Now client side filter un-matched results 
    entries = entries.filter(function(entry) { 
     return entry.student != null; 
    }); 
    // Anything not populated by the query condition is now removed 
}); 

所以這是不是一個理想的作用,因爲「數據庫」沒有過濾可能的大部分結果。

除非你有充分的理由不這樣做,否則你可能「應該」是「嵌入」數據。這樣,像"status屬性「已經可以在收集和額外的查詢是不需要的。

如果您正在使用的NoSQL解決方案像MongoDB的你應該擁抱它的概念,而不是死守關係設計原則。如果你一致地建模關係,那麼你也可以使用關係數據庫,因爲你不會從有其他方法來處理這個問題的解決方案中獲得任何好處。

+0

我越來越錯誤},回調) ;在這裏像預期的那樣:我該如何解決它? – Edit

2

從版本3.2開始,你可以使用$lookup進行聚合管道執行左外連接。

Student.aggregate([{ 
    $lookup: { 
     from: "worksnapsTimeEntries", // collection name in db 
     localField: "_id", 
     foreignField: "student", 
     as: "worksnapsTimeEntries" 
    } 
}]).exec(function(err, students) { 
    // students contain WorksnapsTimeEntries 
});