How to join two collections in mongoose

前端 未结 2 1113
孤独总比滥情好
孤独总比滥情好 2021-02-04 11:45

I have two Schema defined as below:

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


        
相关标签:
2条回答
  • 2021-02-04 12:09

    As of version 3.2, you can use $lookup in aggregation pipeline to perform left outer join.

    Student.aggregate([{
        $lookup: {
            from: "worksnapsTimeEntries", // collection name in db
            localField: "_id",
            foreignField: "student",
            as: "worksnapsTimeEntries"
        }
    }]).exec(function(err, students) {
        // students contain WorksnapsTimeEntries
    });
    
    0 讨论(0)
  • 2021-02-04 12:23

    You don't want .populate() here but instead you want two queries, where the first matches the Student objects to get the _id values, and the second will use $in to match the respective WorksnapsTimeEntry items for those "students".

    Using async.waterfall just to avoid some indentation creep:

    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
           }
        }
    )
    

    If you really must, then you can .populate("student") on the second query to get populated items from the other table.

    The reverse case is to query on WorksnapsTimeEntry and return "everything", then filter out any null results from .populate() with a "match" query option:

    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
    });
    

    So that is not a desirable action, since the "database" is not filtering what is likely the bulk of results.

    Unless you have a good reason not to do so, then you probably "should" be "embedding" the data instead. That way the properties like "status" are already available on the collection and additional queries are not required.

    If you are using a NoSQL solution like MongoDB you should be embracing it's concepts, rather than sticking to relational design principles. If you are consistently modelling relationally, then you might as well use a relational database, since you won't be getting any benefit from the solution that has other ways to handle that.

    0 讨论(0)
提交回复
热议问题