$push to an array in MongoDB with mongoose doesn't work - arrays

errmsg: 'The field \'weight\' must be an array but is of type int in
document
My Schema:
weight: [{
type: Number
}]
and my post request:
app.post('/edit', function(req, res){
var update = { $push: {"weight": req.body.weight}};
User.findOneAndUpdate(conditions, update, options, function (err)
{
if (err)
{
console.log(err);
}
else
{
console.log('yep');
}
})
});

If there are multiple documents in the collection that match your conditions, you can update only suitable one by adding { weight: { $type: 4 } } to your conditions.
Otherwise your application's schema doesn't match data in the database.

This might work.
//Schema
weight: [Number]
http://mongoosejs.com/docs/schematypes.html
//Or this way too if pushing objects into array
//Schema
weight: [{
weight: {
type: Number
}
}]
//Then in API
var update = { $push: {"weight": { "weight": req.body.weight }}};

Related

Remove from array of objectId using update mongoDB

I have a model named courier, it has an array named order and orders has some ObjectIds of model order, how can I remove an element from orders array using update or something else ?
(for example removing an order with specific id)
Here is my model:
courier:
var courierSchema = new Schema({
name: { type: String },
orders:[{type:Schema.Types.ObjectId,ref:'order'}],
});
I tried this code but it fails :
courier.update({
name: 'Mahan'
}, {
$pull : {
orders: {
_id: order._id
}
}
}, (err, count, obj) => {
if(err) {
console.log(err);
return handleError(err, reply);
}
console.log(count);
});
Is there any way to do this not using find, remove and then save ?
This would be the most efficient format to achieve what you are trying to do
db.collection.update({<cond to identify document}, {$pull: {'orders': {'id': <id>}}} )

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:
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.
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.
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.
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
}
);
}
});
});
You should make your data to be inserted as
{ name: 'salesman',
inputs: [{name: 'salesman',
type: 'String',
value: 'Harry Potter',
adjustable: true}]}
i.e. true without quotes and inputs as an array
or else in schema make adjustable as String and remove input as array in model definition
var sqlSchema = mongoose.Schema({ // Schema to store information about SQL-procedures
'name': String,
'type': String,
'value': String,
'adjustable': String
});

How to get data from array in mongoose?

I am new to mongoose node.js and mongoDB, I have a db Schema like
Project:{
projectName:"String",
projectManager:"String",
task:[{
taskName:"String",
timetakeninhrs:"String"
}]
};
So what I want is to get only the details of task with particular task name.
I am writing sql script so that you can know what I want :
Select taskname,timetakeninhrs from project where taskName ='DB create';
The $elemMatch projection operator would come in handy for this:
Project
.where('task.taskName', 'DB create') // or where('task.taskName').equals('DB create').
.select({_id: 0, task: {$elemMatch: {'taskName': 'DB create'}})
.exec(function(err, docs){
var tasks = docs.map(function(doc){ return doc.task[0]; });
console.log(tasks[0].taskName); // 'DB create'
console.log(tasks[0].timetakeninhrs); // '3'
});
In the above, the where() method acts as a static helper method of the Mongoose model that builds up a query using chaining syntax, rather than specifying a JSON object. So
// instead of writing:
Project.find({ 'task.taskName': 'DB create' }, callback);
// you can instead write:
Project.where('task.taskName', 'DB create');
// or
Project.where('task.taskName').equals('DB create');
and then chain the select() method to project the 'task' array field using $elemMatch. In the exec() method (which executes the query asynchronously), you need to pass in a callback which follows the pattern callback(error, results). What results is depends on the operation: For findOne() it is a potentially-null single document, find() a list of documents, count() the number of documents, update() the number of documents affected, etc. In this case this returns an array of documents in the format:
[
/* 0 */
{
"task" : [
{
"taskName" : "DB create",
"timetakeninhrs" : "3"
}
]
},
/* 1 */
{
"task" : [
{
"taskName" : "DB create",
"timetakeninhrs" : "9"
}
]
}
/* etc */
]
In your callback you can do a bit of data manipulation to get an object that only has those properties you specified, hence the use of the native JavaScript map() function to create a new array of objects with those fields
i create this example that can help you:
var async=require('async');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var uri = 'mongodb://localhost/myDb';
mongoose.connect(uri);
// define a schema
var ProjectSchema = new Schema({
projectName: "String",
projectManager: "String",
task: [{
taskName: "String",
timetakeninhrs: "String"
}]
});
// compile our model
var Project = mongoose.model('Project', ProjectSchema);
// create a documents
var Project01 = new Project({
projectName: "Project01",
projectManager: "Manager01",
task: [{
taskName: "tsk01_Project01",
timetakeninhrs: "1111-1111"
}, {
taskName: "tsk02_Project01",
timetakeninhrs: "1111-2222"
}, {
taskName: "tsk03_Project01",
timetakeninhrs: "1111-3333"
}, {
taskName: "tsk04_Project01",
timetakeninhrs: "1111-4444"
}]
});
var Project02 = new Project({
projectName: "Project02",
projectManager: "Manager02",
task: [{
taskName: "tsk01_Project02",
timetakeninhrs: "2222-1111"
}, {
taskName: "tsk02_Project02",
timetakeninhrs: "2222-2222"
}, {
taskName: "tsk03_Project02",
timetakeninhrs: "2222-3333"
}, {
taskName: "tsk04_Project02",
timetakeninhrs: "2222-4444"
}]
});
//delete existing documents and create them again
Project.remove({}, function() {
Project01.save(function() {
Project02.save(function() {
//for example we find taskName: "tsk03_Project02"
Project.find({'task': {$elemMatch: {taskName: "tsk03_Project02"}}},'task.taskname task.timetakeninhrs',function(err, docs) {
if (!err) {
console.log(docs);
}
});
});
});
});

MongoDB Reference Issue

I have two collections set up at the moment. One collection lists all of the products in my store. The other collection stores the ratings on a scale of 1-5. The ratings store in the ratings collection successfully, and the products are stored and listed successfully. However, I am attempting to reference the appropriate rating for the individual product that is listed. I am using an ng-repeat to list all of the products in my product database. I'm not sure what is going on, but my reference to the ratings is returning an empty array.
How can I get the ratings to show for each product?
Product Schema:
var mongoose = require('mongoose');
var productSchema = new mongoose.Schema({
title: {
type: String,
unique: true,
required: true,
index: true
},
description: {
type: String,
required: true
},
price: {
type: Number,
required: true,
min: 0,
},
rating: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Rating'
}],
image: {
type: String,
required: true
}
});
module.exports = mongoose.model('Product', productSchema);
Product Controller (just the read portion):
var Product = require('../models/Product');
module.exports = {
read: function (req, res) {
Product.find(req.query)
.populate('Rating')
.exec(function (err, result) {
if (err) { return res.status(500).send(err);}
console.log("this is in the product ctrl", result);
{res.send(result);}
});
},
};
Rating Schema:
var mongoose = require('mongoose');
var ratingSchema = new mongoose.Schema({
rating: {
type: Number,
enum: [1, 2, 3, 4, 5]
}
});
module.exports = mongoose.model('Rating', ratingSchema);
Rating Controller (just read and create shown):
var Rating = require('../models/Rating');
module.exports = {
create: function (req, res) {
var newRating = new Rating(req.body);
newRating.save(function (err, result) {
if (err) return res.status(500).send(err);
else res.send(result);
});
},
read: function (req, res) {
Rating.find(req.query)
.populate('type')
.exec(function (err, result) {
if (err) return res.status(500).send(err);
else res.send(result);
});
}
};
Screenshot of view:
If more information is needed please let me know. I thought that something may be wrong with my .populate, but after reading this documentation I think everything is good. I'm stumped.
http://mongoosejs.com/docs/populate.html

mongoose update with push operations on array and set operation on object

I have this mongoose schema
var ContactSchema = module.exports = new mongoose.Schema({
name: {
type: String,
required: true
},
phone: {
type: Number,
required: true,
},
messages: [
{
title: {type: String, required: true},
msg: {type: String, required: true}
}],
address:{ city:String,
state:String
}
});
I have initially the collection set with name and phone field. I need to update the collection with new messages into messages array and new address into address object. the function must also need to handle any single operation, ie in some case i have only update to messages array or updates to both name and address. so how i can i do all operations in a single function.
var messages= {
title: req.body.title,
msg: req.body.msg
}
Model.findOneAndUpdate({'_id': req.body.id,},{$push: {messages:message}},{upsert: true}, function (err, data) {
if (err) {
return res.status(500).send(err);
}
if (!data) {
return res.status(404).end();
}
return res.status(200).send(data);
});
You could try use both the $set and $push operators in your update object. Suppose, for example, you want to update both name and address fields in one single operation, use the $set on the name field and a $push operation to the address array:
var messages= {
title: req.body.title,
msg: req.body.msg
},
query = {'_id': req.body.id},
update = {
$set: {name: req.body.name},
$push: {messages: message}
},
options = {upsert: true};
Model.findOneAndUpdate(query, update, options, function (err, data) {
if (err) {
return res.status(500).send(err);
}
if (!data) {
return res.status(404).end();
}
return res.status(200).send(data);
});

Resources