HHH
HHH

Reputation: 861

Retrieve the self-generated _id value of the object after pushing it to a existing collection

I have a mongodb document, let's say having the schema

class: String,
class_teacher: String,
students: [
    { 
       student: String,
       marks: number
    }
]

In the above example if I push a new student to the collection, it'll generate a _id for each student and I am doing it by using the update query for the entire document.

  1. Is it the right way to do it?

  2. If yes, how can I get the _id for each student as soon as I push the student object?

  3. After some time, if I want to update the marks of a single student, what is the best way to do it??

Upvotes: 0

Views: 91

Answers (1)

Neil Lunn
Neil Lunn

Reputation: 151112

In nice mode, I'll answer all of your "questions", here is some demonstration code:

var mongoose = require("mongoose"),
    Schema = mongoose.Schema;


mongoose.connect('mongodb://localhost/test');

var classSchema = new Schema({
  class: String,
  teacher: String,
  students: [{ student: String, marks: Number }]
});

var Class = mongoose.model("Class", classSchema);

var myclass = new Class();

myclass.students.push(
  { student: "bill", marks: 10 },
  { student: "ted", marks: 5 }
);

console.log("Whole:\n %s\n",myclass);
console.log("Last added _id: %s\n",myclass.students.slice(-1)[0]._id);

myclass.save(function(err,myclass) {
  if (err) throw err;

  Class.findOneAndUpdate(
    { "students.student": "ted" },
    { "$set": { "students.$.marks": 7 } },
    function(err,doc) {
      if (err) throw err;

      console.log(doc);
    }
  );

});

So overall:

  1. Sure not a problem, even though you might find defining your schema for "Student" separately to be a bit more maintainable in the long run:

    var studenSchema = new Schema({
        student: String,
        marks: Number
    });
    
    var classSchema = new Schema({
        class: String,
        teacher: String,
        students: [studentSchema]
    });
    
  2. Well the last added element in the array when you modify this way can just be pulled off with .slice(), then you can get the last _id value.

    console.log("Last added _id: %s\n",myclass.students.slice(-1)[0]._id);
    
  3. Updating marks can be best done with .findOneAndUpdate() or a similar operation. Here you query to match the document and array element and update the correct one with use of the positional $ operator:

    Class.findOneAndUpdate(
      { "students.student": "ted" },
      { "$set": { "students.$.marks": 7 } },
      function(err,doc) {
        if (err) throw err;
    
        console.log(doc);
      }
    );
    

This also returns the document that is modified so you can see the changes. For "bulk" operations you would use the .update() method along with the "multi" option.

You can also "add" new students to the array using the $push operator, and just as before the document is returned with the method shown, so you could also apply getting the new _id value that way:

  Class.findOneAndUpdate(
    { "students.student": "ted" },
    { "$push": { "students": { "student": "sally", "marks": 4 }} },
    function(err,doc) {
      if (err) throw err;

      console.log("Whole:\n %s", doc);
      console.log("Last added _id: %s\n",doc.students.slice(-1)[0]._id);
    }
  );

And that produces results like this:

Whole:
{ _id: 53bc9844c1cbd4d24eac8db6,
  __v: 0,
  students:
   [ { student: 'bill', marks: 10, _id: 53bc9844c1cbd4d24eac8db7 },
     { student: 'ted', marks: 7, _id: 53bc9844c1cbd4d24eac8db8 },
     { student: 'sally', marks: 4, _id: 53bca6c346d93b32624e5af4 } ] }

Last added id: 53bca6c346d93b32624e5af4

If you need other schema validation features or other hooks on field values then you will need to individually .find() documents, modify in code and then .save(). But these are the alternate approaches, which usually create less "wire traffic" than dealing with whole documents.

Upvotes: 2

Related Questions