Search code examples
arraysjsonnode.jsmongooseupdatemodel

NodeJS & Mongoose: Add element to existing empty array


I am building a NodeJS application for creating reports based on data from a MSQL database. All application relevant data is stored in a MongoDB using Mongoose. My mongoose model contains an empty array which is then filled by the user via a Rest-API. I get an error when adding a new element to the array. I already tried it with model.array.push(object); model.save() and findByIdAndUpdate(...). Find my code including the two different attempts below:

Mongoose schema

var sqlSchema = mongoose.Schema({ // Schema to store information about SQL-procedures
    'name': String,
    'inputs': [
        {
            'name': String,
            'type': String,
            'value': String,
            'adjustable': Boolean
        }
    ]
});

REST API

My application accepts new elements for the 'inputs'-array via POST:

var mongoose = require('mongoose'),
    SqlProcedure = mongoose.model('SqlProcedure');

// ...

router.post('/sql/:id/inputs', function(req, res) {
    SqlProcedure.findById(req.params.id, function(err, sql) {
        if(err) {
            res.send(msg.error('error retrieving sql-procedure', err));
        } else {
            if(sql.inputs.length > 0) {     // catch empty array
                for(var key in sql.inputs) {
                    if(sql.inputs[key].name == req.body.name) {
                        return res.send(msg.error('input already in inputs', err));
                    }
                }
            }

            var data = req.body;

            var input = {
                name: data.name,
                type: data.type,
                value: data.value,
                adjustable: data.adjustable
            };

            // attempt one or two
        }
    });
});

attempt one:

            sql.inputs.push(input); // EXCEPTION 1

            sql.save(function(err) {
                // fancy errorhandling
                return res.send(msg.ok(sql));
            });

attempt two:

            SqlProcedure.findByIdAndUpdate(req.params.id,
                {$push: {inputs: input}}, // EXCEPTION 2
                {safe: true, upsert: true},
                function(err, sql) {
                    // fancy errorhandling
                    res.send(msg.ok('input added to sql-procedure' + req.params.id));
                }
            );

Exceptions

Attempt one:

throw new CastError('string', value, this.path);
  ^
Error
    at MongooseError.CastError (\node_modules\mongoose\lib\error\cast.js:18:16)
    at SchemaString.cast (\node_modules\mongoose\lib\schema\string.js:434:9)
    at Array.MongooseArray.mixin._cast (\node_modules\mongoose\lib\types\array.js:124:32)
    at Array.MongooseArray.mixin._mapCast (\node_modules\mongoose\lib\types\array.js:295:17)
    at Object.map (native)
    at Array.MongooseArray.mixin.push (\node_modules\mongoose\lib\types\array.js:308:25)
    at Query.<anonymous> (\app\api\sql_procedure.js:69:28)
    at \node_modules\mongoose\node_modules\kareem\index.js:177:19
    at \node_modules\mongoose\node_modules\kareem\index.js:109:16
    at doNTCallback0 (node.js:417:9)
    at process._tickCallback (node.js:346:13)

Attempt two:

"stack": "Error
at MongooseError.CastError (\\node_modules\\mongoose\\lib\\error\\cast.js:18:16)
at SchemaArray.cast (\\node_modules\\mongoose\\lib\\schema\\array.js:156:15)
at SchemaArray.cast (\\node_modules\\mongoose\\lib\\schema\\array.js:167:17)
at Query._castUpdateVal (\\node_modules\\mongoose\\lib\\query.js:2384:22)
at Query._walkUpdatePath (\\node_modules\\mongoose\\lib\\query.js:2298:27)
at Query._castUpdate (\\node_modules\\mongoose\\lib\\query.js:2227:23)
at castDoc (\\node_modules\\mongoose\\lib\\query.js:2430:18)
at Query._findAndModify (\\node_modules\\mongoose\\lib\\query.js:1752:17)
at Query._findOneAndUpdate (\\node_modules\\mongoose\\lib\\query.js:1620:8)
at \\ITZReport\\node_modules\\mongoose\\node_modules\\kareem\\index.js:156:8
at \\node_modules\\mongoose\\node_modules\\kareem\\index.js:18:7
at doNTCallback0 (node.js:417:9)\n    at process._tickCallback (node.js:346:13)",
    "message": "Cast to undefined failed for value \"[object Object]\" at path \"inputs\"",
    "name": "CastError",
    "value": [
        {
            "adjustable": "true",
            "value": "Harry Potter",
            "type": "String",
            "name": "salesman"
        }
    ],
    "path": "inputs"

data to be inserted

{ name: 'salesman',
  type: 'String',
  value: 'Harry Potter',
  adjustable: 'true' }

I am new to NodeJS and mongoose and tried to solve this on my own for many hours. It would be great if anyone out there could help me!

Thanks in advance, dj2bee

Update

I think I should clarify the process of the user interacting with the REST-API:

  1. The user creates a new record by passing over the value for the name. At this point the name is set and the inputs-array is empty.
  2. In the next step, the user adds new records to the inputs-array one by one. The name stays as it is and only new inputs are added to the array.
  3. The user should be able to edit or remove entries from the inputs-array.

Changing the data-type of adjustable to String did not made any changes. I also tried hard coding attributes and not passing them via HTTP-POST - still the same exception.


Solution

  • After hours of searching the web and testing the weirdest things in my code I found the solution: You can't name a field 'type' in the mongoose-schema. That's it.

    The correct code looks like this:

    Mongoose schema

    var sqlSchema = mongoose.Schema({
        'name': String,
        'inputs': {
            'name': String,
            'datatype': String, // you can't name this field 'type'
            'value': String,
            'adjustable': Boolean
        }
    });
    

    REST API

    router.post('/sql/:id/inputs', function(req, res) {
        SqlProcedure.findById(req.params.id, function(err, sql) {
            if(err) {
                res.send(msg.error('error retrieving sql-procedure', err));
            } else {
                if(!sql) {
                    return res.send(msg.error('no sql-procedure found with id '
                        + req.params.id, null));
                }
                // check for duplicates
    
                var data = req.body;
    
                var input = {
                    'name': data.name,
                    'datatype': data.type,
                    'value': data.value,
                    'adjustable': data.adjustable
                };
    
                SqlProcedure.findByIdAndUpdate(req.params.id,
                    {$push: {inputs: input}},
                    {safe: true, upsert: true},
                    function(err, sql) {
                        // fancy error handling
                    }
                );
            }
        });
    });