Как объединить две коллекции в mongoose

у меня есть две схемы, как указано ниже:

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

кто-нибудь знает, как мне этого достичь?

2 ответов


не хочешь .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 и верните "все", затем отфильтруйте любые null результаты .populate() с опцией запроса "match":

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, вы должны охватывать его концепции, а не придерживаться принципов реляционного дизайна. Если вы последовательно моделируете отношения, вы можете также использовать реляционную базу данных, так как вы не получите никакой выгоды от решения, которое имеет другие способы справиться с этим.


начиная с версии 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
});