mongoose + nodejs push array to database - arrays

I'm new to NodeJS + Mongoose and having trouble pushing an array to my database via mongoose.
I have the following schema:
const StudentSchema = mongoose.Schema({
name: {
type: String
},
quizzes: [{
quiz: String,
answers: []
}]
});
What I'm trying to do is have an array of quiz objects in which the quiz number is shown and the array of answers for that particular quiz.
When I create a new user, the student object looks like this in mongoose:
{
"_id" : ObjectId(id here),
"name" : "John",
"quizzes" : [ ],
"__v" : 0
}
The function I'm using to update the quiz array:
module.exports.addQuiz = function(student_id, answers, callback){
Student.findByIdAndUpdate(
student_id,
{$push: {"quizzes": {answers: answers}}},
{safe: true, upsert: true},
function(err, model) {
console.log(err);
}
);
}
And this is my route which calls the function whenever the endpoint is hit with a student_id, which will then be used to find the student and push to the array
router.post('/quiz/:student_id', (req, res, next) => {
var student_id = req.params.id;
var answers = req.body.answers;
Student.addQuiz(student_id, answers, (err, answers) => {
//error handling
})
});
I'm trying to test this by sending a post request to /quiz/:student_id with an id of a student in my database with the following JSON sent in the body:
[{
"quiz": "Quiz 1",
"answers": ["Answer 1", "Answer 2"]
}]
Although when I try this it ends up getting hung somewhere and the request never completes - I also get a "null" in the console.
Can anyone help me out? Thank you.

Related

How to programmatically add to an array in database from frontend React

I basically have this route
app.post("/order/:orderId/:productId", async (req, res) => {
const result = await Order.findByIdAndUpdate(
req.params.orderId,
{
$push: {
products: req.params.productId
}
},
{ new: true }
);
res.send(result);
});
I have two collections, namely Product and Orders. The goal is to get, for instance, a particular Order with id(5ddfc649e1e9e31220ce6a16) , and post a product with id(5de02c4a0ed3160368b9a550) inside an Array field inside this Orders collection. In Postman, I can do that manually by just adding the ObjectIds in the URL like so:
Http://localhost:3000/orders/5ddfc649e1e9e31220ce6a16/5de02c4a0ed3160368b9a550.
and I get this Response :
{
"products": [
"5ddfb388b14c5b41e0607a5e",
"5de02c4a0ed3160368b9a550" // newly added Product
],
"_id": "5ddfc649e1e9e31220ce6a16",
"issuedBy": "issuedBy",
"collectedBy": "collectedBy",
"quantity": 123,
"createdAt": "2019-11-28T11:48:40.500Z",
"updatedAt": "2019-11-28T11:59:51.659Z",
"__v": 0
}
My challenge is, how do I do this programmatically from the UI(Reactjs) side?
// Product Schema
const mongoose = require("mongoose");
const ProductSchema = new mongoose.Schema(
{
name: String,
description: String,
price: Number,
quantity: Number,
supplier: String
},
{ timestamps: true }
);
module.exports = mongoose.model("Product", ProductSchema);
// Orders Schema
const mongoose = require("mongoose");
const OrderSchema = new mongoose.Schema(
{
issuedBy: String,
collectedBy: String,
quantity: Number,
products: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Product",
required: true
}
]
},
{ timestamps: true }
);
module.exports = mongoose.model("Order", OrderSchema);
I would really appreciate any suggestion or a sample code snippet
I think what you are looking for is, you need to hit this API from your front end i.e. React app.
http://localhost:3000/orders/<order-id-here>/<product-id-here>
And then supply the dynamic value for your order id and product id.
You can hit api using fetch. More info
You can simply take some variables in your front end app. As I don't know how your order id and product id are being generated so you need to check on that and then something like,
let orderId = someValue
let productId = someOtherValue
let url = "http://localhost:3000/orders/"+orderId+"/"+productId
But I think the concern would be GET and POST method. Right now you have app.post but the params are in URL so instead you should go for GET method i.e. app.get

Mongoose doesn't create subdocument from JSON array

I'm trying to write a JSON object that contains both first-level data along with arrays into MongoDB.
What happens instead is all first-level data is stored, but anything contained in an array isn't. When logging the data the server receives, I see the entire object, which leads me to believe there's something wrong with my Mongoose code.
So for example if I send something like this:
issueId: "test1",
issueTitle: "testtest",
rows: [
{order:1,data: [object]},
{order:2,data: [object]},
]
Only the following gets stored:
issueId: "test1",
issueTitle: "testtest",
lastUpdated: Date,
I have the following model for Mongo:
//model.js
var mongoose = require('mongoose');
var model = mongoose.Schema({
issueId : String,
issueTitle : String,
lastUpdated : {type: Date, default : Date.now},
rows : [{
order : Number,
data : [
{
title : String,
text : String,
link : String,
}
]
}]
});
module.exports = mongoose.model('Model', model);
And the routing code, where I believe the problem likely is:
//routes.js
const mongoose = require('mongoose');
const Model = require('./model.js');
...
app.post('/api/data/update', function(req, res) {
let theData = req.body.dataToInsert;
console.log(JSON.stringify(theData,null,4));
Model.findOneAndUpdate(
{issueId : theData.issueId},
{theData},
{upsert: true},
function(err,doc){
if(err) throw err;
console.log(doc);
});
});
As well, here's the part of the Angular controller storing the data. I don't think there's any problem here.
pushToServer = function() {
$http.post('/api/data/update',{
dataToInsert : $scope.dataObject,
}).then(function successCallback(res){
console.log("all good", JSON.stringify(res,null,3));
}, function errorCallback(res){
console.log("arg" + res);
});
}
Look at the first question in the mongoose FAQ:
http://mongoosejs.com/docs/faq.html
Mongoose doesn't create getters/setters for array indexes; without them mongoose never gets notified of the change and so doesn't know to persist the new value. The work-around is to use MongooseArray#set available in Mongoose >= 3.2.0.
// query the document you want to update
// set the individual indexes you want to update
// save the document
doc.array.set(3, 'changed');
doc.save();
EDIT
I think this would work to update all of the rows. I'd be interested to know if it does work.
let rowQueries = [];
theData.rows.forEach(row => {
let query = Model.findOneAndUpdate({
issueId: theData.issueId,
'row._id': row._id
}, {
$set: {
'row.$': row
}
});
rowQueries.push(query.exec());
});
Promise.all(rowQueries).then(updatedDocs => {
// updated
});

Embed the collections using mongodb

I am just start using mongodb and nodejs. I know that mongodb does not supports joins.I simply insert the data in mongodb and my documents looks:
{
"_id": ObjectId("564dacf84d52785c1d8b4567"),
"content": "This blog created by karanSofat",
"html": "<p>This blog created by karanSofat</p>\n",
}
Now user comments on this post. it should be like this:
{
"_id": ObjectId("564dacf84d52785c1d8b4567"),
"comments": [
{
"name": "sumit",
"email": "sumit#ggi.net",
"comment": "this is also well for me",
"posted_at": ISODate("2015-11-19T11:06:27.172Z")
}
],
"content"▼: "This blog created by karanSofat",
"html": "<p>This blog created by karanSofat</p>\n",
}
Here is my models,
//post model
// grab the mongoose module
var mongoose = require('mongoose');
// define our nerd model
// module.exports allows us to pass this to other files when it is called
module.exports = mongoose.model('post', {
content : {type : String, default: ''},
html : {type : String, default: ''}
});
//comment model
var mongoose = require('mongoos
module.exports = mongoose.model('comment', {
name : {type : String, default: ''},
email : {type : String, default: ''},
comment : {type : String, default: ''},
posted_at : {type : date, default: ''}
});
My Problem is that I don't know on which way I insert comments data using nodejs and my document will embed.
Here is my code:
app.post('/comments/:id', function(req, res) {
var Comment = require("../app/models/comments");//comment Model
var blog = require("../app/models/blog");//blog model
var id = req.params.id; //postId
var comments = JSON.parse(JSON.stringify(req.body)); //commentdata
//code Should be here
res.json({data:id,data2:input});
});
Please help
Karan,
Let's assume you have the following schema:
var Comments = new Schema({
name: String,
email: String,
comment: String,
, posted_at: Date
});
var BlogPost = new Schema({
content : String,
html : String,
comments : [Comments],
});
mongoose.model('BlogPost', BlogPost);
You can add an embed document to an array as such:
// retrieve my model
var BlogPost = mongoose.model('BlogPost');
// create a blog post
var post = new BlogPost();
// create a comment
post.comments.push({
"name": "sumit",
"email": "sumit#ggi.net",
"comment": "this is also well for me",
"posted_at": ISODate("2015-11-19T11:06:27.172Z")
});
post.save(function (err) {
if (!err) console.log('Success!');
});

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);
}
});
});
});
});

Resources