Guid
Guid

Reputation: 2216

one to many with mongoose

I want to define a one-to-many relationship between my Student model and my Formation model: 1 student belongs to 1 formation and a formation can be composed of N students.

My needs are:

So I wrote:

let student = new Schema({
    firstName: {
        type: String
    },
    lastName: {
        type: String
    },
    formation : {
        type: Schema.Types.ObjectId,
        ref: 'Formation'
    }
});

let formation = new Schema({
    title: {
        type: String
    },
    students: [{ type: Schema.Types.ObjectId, ref: 'Student' }]
}

When I'm searching for documentation about the one-to-many relation with mongodb/mongoose, I'm surprised to see that when I want to create a Student document (and add it to a formation), I must not only set its "formation" field with the formation id and also push it to the formation document "students" field.
Does it imply that when I want to replace the formation of a student with another formation, I'll need to 1/ update its formation field 2/ remove myself the student id from the formation "students" field and re-add it to the students field of the new formation?
It seems a little redundant to me. Am I missing something?

Upvotes: 1

Views: 500

Answers (2)

awimley
awimley

Reputation: 712

It is in fact redundant because the way you have defined your schemas is redundant. The array of students should be a property of the formation, but not the other way around. Subdocuments can be searched like normal collections. If you change your schema to:

let student = new Schema({
    firstName: {
        type: String
    },
    lastName: {
        type: String
    }
});

let formation = new Schema({
    title: {
        type: String
    },
    students: [{ type: Schema.Types.ObjectId, ref: 'Student' }]
}

You should be able to accomplish your needs with fairly short commands. Assuming formations and students are your collection names:

1: Generating a student and saving both the formation and student documents.

var stud = new student({name: "Sally"}); //Assuming you modeled to student
stud.save(function (err, student) {
  formations.findOne({}, function (err, form) { //Dont know how you find formations, I assume you know
    form.students.push(stud._id);
    form.save(callback);
  }  
});

2. Retrieving the formation of a student with a given ID:

formations.findOne({"students" : { $elemMatch: {_id: id}}}, function (err, doc) {})
  1. Even moving a student is relatively simple.

    formNew.students.push (

formOld.students.pop(formOld.students.indexOf(formOld.students.findOne( //elemMatch like above )))

Sorry about the weird formatting.

Upvotes: 1

chrisbajorin
chrisbajorin

Reputation: 6153

Having students on the Formation model is both bad practice and redundant. Potentially infinite arrays are a poor design as they can lead to hitting document size limits. In regards to being redundant, you only have two queries in this situation:

1) You have a student in memory, and you want to find their formation:

Formation.findOne({_id: student.formation},callback);

2) You have a formation in memory, and want to find all students in that formation:

Student.find({formation: formation._id}, callback);

Neither of which require having students on the Formation schema.

Upvotes: 2

Related Questions