Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nodejs Mongoose Saving model undefined is not a function

I work with Nodejs Express routes and Mongoose to persist data. I did the core routes CRUD operations with no problem. However, when I try to perform some operations on one of the fields of the Model and then try to save the Model with model.save it says about .save() method: "undefined is not a function"

So, here's the code. The snippet number (1) works just fine:

router.put('/:myId', function (req, res, next) {
var ourUpdateData = req.body;
Model.findOne({myId: req.params.myId.toString()}, function (err, foundModel) {
    if (err) throw err;

    if (ourUpdateData.fieldA) foundModel.fieldA = ourUpdateData.fieldA;
    if (ourUpdateData.fieldB) foundModel.fieldB = ourUpdateData.fieldB;
    if (ourUpdateData.fieldC) foundModel.fieldC = ourUpdateData.fieldC;
    if (ourUpdateData.fieldD) foundModel.fieldD = ourUpdateData.fieldD;
    if (typeof ourUpdateData.fieldArray === "object") ourUpdateData.fieldArray = ourUpdateData.fieldArray;

    foundModel.save(function (err, updatedModel) {
        if (err) throw err;
        res.send(updatedmodel);
    });
});

});

So the Model has 6 fields: fieldA,..B,..C,..D, myId to identify as index and one field is Array of some values fieldArray. The example above saves the Model, works fine. However if I now try to do something with array field fieldArray and then save the Model it throws me "undefined is not a function" when I use model.save() . So the snippet (2) is the code that produces this error:

    router.get('/:myId/:addThisToFieldArray', function(req, res, next) {
    var myId = req.params.myId;
    var addThisToFieldArray = req.params.addThisToFieldArray;
    Model.find({myId: myId}, function (err, model) {
        if (err) throw err;
        var fieldArray = model.fieldArray;
        fieldArray.push("New thing to FieldArray");
        var newFieldArray = fieldArray;
        if (typeof newFieldArray === "object") model.fieldArray = newFieldArray;
        model.save(function (err, updatedModel){
            if (err) throw err;
            res.send(updatedModel);
        });
    });
});

So that thing above throws "undefined is not a function" on using model.save(.. )

I also tried second variant of the snippet (2), let's call it snippet (3), incorporating the .exec() Also doesn't work, throws the same "undefined is not a function" on model.save(.. ) So the snippet (3) is this:

    router.get('/:myId/:addThisToFieldArray', function(req, res, next) {
    var myId = req.params.myId;
    var addThisToFieldArray = req.params.addThisToFieldArray;
    Model.find({myId: myId}).exec(function (err, model) {
        if (err) throw err;
        var fieldArray = model.fieldArray;
        fieldArray.push("New thing to FieldArray");
        var newFieldArray = fieldArray;
        if (typeof newFieldArray === "object") model.fieldArray = newFieldArray;
        model.save(function (err, updatedModel){
            if (err) throw err;
            res.send(updatedModel);
        });
    });
});

I'll be greatful for any inputs and suggestions!

Ansering to the attempt of Willson:

Yeah, I know when I call model.find(.. it gives array, if I call model.findOne(.. it gives one object json

I tried to simplify my example and in my version I actualy did use the: "model[0].fieldArray = newFieldArray" to get the thing from Array fist (the array itself) and then assign to the new value.

The problem still persists, it gives me on model.save(.. "undefined is not a function " )

The current code is:

    router.get('/:myId/:addThisToFieldArray', function(req, res, next) {
    var myId = req.params.myId;
    var addThisToFieldArray = req.params.addThisToFieldArray;
    Model.find({myId: myId}).exec(function (err, model) {
        if (err) throw err;
        var fieldArray = model[0].fieldArray;
        fieldArray.push("New thing to FieldArray");
        var newFieldArray = fieldArray;
        if (typeof newFieldArray === "object") model[0].fieldArray = newFieldArray;
        model.save(function (err, updatedModel){
            if (err) throw err;
            res.send(updatedModel);
        });
    });
});

This snippet (4) above gives on model.save(.. "undefined is not a function"

like image 640
JavaJedi Avatar asked Jul 10 '15 12:07

JavaJedi


2 Answers

When you use in Mongoose the find method, it will return an array since it could discover one or many documents, so in your example you are querying to one specific element by its id, you should grab the first element on the returned array:

     Model.find({myId: myId}).exec(function (err, documents) {
            var model = documents[0];

            if (err) throw err;
            var fieldArray = model[0].fieldArray;

Here is an example:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost:27017/java-jedi');

var HackerSchema = new Schema({
  name: String,
  languages: [String]
});

var Hacker = mongoose.model('Hacker', HackerSchema);


// instantiating the model.
var oneHacker = new Hacker();
oneHacker.name = 'Richard Stallman';

oneHacker.save(function(err) {
  if (err) throw err;

  // finding the document intentionally for this example
  Hacker.find({_id: oneHacker._id}, function(err, hackers) {
    var hacker = hackers[0];

    // modifying the document and updating it.
    hacker.languages.push('Lisp');
    hacker.save(function(err) {
      if (err) throw err;

      console.log(hacker);
    });
  });

});
like image 142
Wilson Avatar answered Oct 17 '22 06:10

Wilson


OK guys! I want to thank Wilson Balderrama, because he basically pointed to the right direction.

The code works! But let me clearify a bit.

  Hacker.find({_id: oneHacker._id}, function(err, hackers) {
var hacker = hackers[0];

// modifying the document and updating it.
hacker.languages.push('Lisp');
hacker.save(function(err) {
  if (err) throw err;

  console.log(hacker);
});

});

So basically since the Model.find(.. returns an array
when we save we have to grab the thing from array before saving.

So corrected and final working version of my example will be:

    router.get('/:myId/:addThisToFieldArray', function(req, res, next) {
    var myId = req.params.myId;
    var addThisToFieldArray = req.params.addThisToFieldArray;
    Model.find({myId: myId}).exec(function (err, model) {
        if (err) throw err;
        var fieldArray = model[0].fieldArray;
        fieldArray.push("New thing to FieldArray");
        var newFieldArray = fieldArray;
        if (typeof newFieldArray === "object") model[0].fieldArray = newFieldArray;
        model[0].save(function (err, updatedModel){
            if (err) throw err;
            res.send(updatedModel);
        });
    });
});

Or we can use just Model.findOne(.. to avoid confusing ourselves with this arry return

In this case we grab directly:

router.get('/:myId/:addThisToFieldArray', function(req, res, next) {
var myId = req.params.myId;
var addThisToFieldArray = req.params.addThisToFieldArray;
Model.findOne({myId: myId}).exec(function (err, model) {
    if (err) throw err;
    var fieldArray = model.fieldArray;
    fieldArray.push("New thing to FieldArray");
    var newFieldArray = fieldArray;
    if (typeof newFieldArray === "object") model.fieldArray = newFieldArray;
    model.save(function (err, updatedModel){
        if (err) throw err;
        res.send(updatedModel);
    });
});
});

So in second case model[0].save(... becomes model.save(... direct grabbing and saving.

Thank you Wilson Balderrama again!!

like image 2
JavaJedi Avatar answered Oct 17 '22 06:10

JavaJedi