How to add object to nested Array using Node.js and Mongoose - javascript

How can I add object to my nested Array in PartnerSchema?
I separate documents, because in the future there will be more of nested arrays.
This is my schema:
var productSchema = new mongoose.Schema({
name: String
});
var partnerSchema = new mongoose.Schema({
name: String,
products: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Product'
}]
});
module.exports = {
Partner: mongoose.model('Partner', partnerSchema),
Product: mongoose.model('Product', productSchema)
}
And this is my backend:
var campSchema = require('../model/camp-schema');
router.post('/addPartner', function (req, res) {
new campSchema.Partner({ name : req.body.name }).save(function (err, response) {
if (err) console.log(err);
res.json(response);
});
});
router.post('/addProduct', function (req, res) {
campSchema.Partner.findByIdAndUpdate({ _id: req.body.partnerId },
{
$push: {
"products": {
name: req.body.dataProduct.name
}
}
}, { safe: true }, function (err, response) {
if (err) throw err;
res.json(response);
});
});
I can add Partner by using /addPartner and it works fine.
Problem is with second function /addProduct I can't add Product to Array in Partner Schema. I have an error: CastError: Cast to undefinded failed for value "[object Object]" at path "products"

Since the products field in Partner model is an array that holds _id refs to the Product model, you are supposed to push an _id to the array, not an object hence Mongoose complains with an error.
You should restructure your code to allow the saving of the Product _id ref to the Partner model:
router.post('/addProduct', function (req, res) {
var product = new campSchema.Product(req.body.dataProduct);
product.save(function (err) {
if (err) return throw err;
campSchema.Partner.findByIdAndUpdate(
req.body.partnerId,
{ "$push": { "products": product._id } },
{ "new": true },
function (err, partner) {
if (err) throw err;
res.json(partner);
}
);
});
});

Related

Remove document with references Mongoose

I'm having trouble removing Question when Survey gets deleted which is referenced in the Survey model. The survey gets deleted, but the question still remains in the database.
Survey Schema:
let surveyModel = mongoose.Schema(
{
Title: String,
Type: [String],
Questions: { type: mongoose.Schema.Types.ObjectId, ref: "questions" },
Answered: { type: Number, default: 0 }, // how many times users answered
DateCreated: { type: Date, default: Date.now }, // date created
Lifetime: { type: Date, default: Date.now }, // Survey expiry
User: { type: mongoose.Schema.Types.ObjectId, ref: "users" }
},
{
collection: "surveys",
}
);
Question Schema:
let questionModel = mongoose.Schema(
{
MC: {
QuestionText: String,
Options: [String],
},
TF: {
QuestionText: String,
Options: Boolean,
}
},
{
collection: "questions",
}
);
module.exports = mongoose.model("Question", questionModel);
Code I have right now:
// process survey delete
module.exports.processDeletion = (req, res, next) => {
let id = req.params.id;
Survey.remove({ _id: id }, (err) => {
Question.remove({_id: { $in: req.body.Questions }}, (err, res) => {
if (err) {
console.log(err);
res.end(err);
}
});
if (err) {
console.log(err);
res.end(err);
} else {
// refresh survey list
res.redirect("/live-surveys");
}
});
};
Your first step should be delete childrens, that is Question.
Note: i think "Questions" should be more of 1, then it must be an array of Reference in the Survey model. But, for this example it will to be as you have setted.
Then, your delete route, may to be some as:
router.delete("/delete/:surveyById", deleteSurvey");
router.param("surveyById", surveyId"); //This one is your middleware
//surveyController.js
const Survey = require("../models/Survey");
const Question = require("../models/Question");
exports.surveyId = (req, res, next, id) => {
Survey.findById(id).exec((err, data) => {
if(!data || err) return res.status(400).json({error: "Survey not found")};
else {
req.survey = data;
next();
}
)};
};
exports.deleteSurvey = (req, res) => {
Questions.findByIdAndRemove(req.survey.Questions) //Here your Questions Id
.exec((err, data)) => {
if(err) return res.status(400).json({error: "Error to delete questions"});
Survey.findByIdAndRemove(req.survey._id).exec((err, data) => {
if(err) return res.status(400).json({error: "Error to delete Survey"});
return res.json({ message: "Deleted")};
});
});
};
Also you can do with async await if you prefer, is the same, and you will have a better control about your code.

Db.find get null

We are trying to figure out how get the data from the database but the result get "null"
the model
const mongoose = require("mongoose");
const ClubSchema = new mongoose.Schema({
nomClub: String,
classement: String,
dateMatch: String,
classementDB: String,
logo: String,
adversaire: String,
});
const SportSchema = new mongoose.Schema({
nom: String,
clubs: [ClubSchema],
});
module.exports = mongoose.model("sport", SportSchema);
and the back
getSportAdversaire: (req, res) => {
Sport.findOne({ "clubs.nomClub": "Stade Rennais" }, (err, data) => {
if (err) {
console.log(err);
res.json({ message: "une erreur s'est produite" });
} else {
res.json(data);
console.log(data);
}
});
},
You dont need to use the key as clubs.nomClub. You can simply use the key as nomClub. Your key name is nomClub and not clubs.nomClub.
sport.findOne({ "nomClub": "Stade Rennais" }, (err, data) => {
if (err) {
console.log(err);
res.json({ message: "une erreur s'est produite" });
} else {
res.json(data);
console.log(data);
}
});

How could I reference a model to my User model with Express/Mongoose

I have two models, one being my User model and the other being my Course model. I would like to have it so when a User (Teacher) creates a course, it assigns that course to them and vice versa. Here are my models to explain better:
Course Schema/Model:
var CourseSchema = new Schema({
courseID: {
type: Number,
unique: true
},
courseName: String,
courseDesc: {
type: String,
default: "No course description provided."
},
coursePicture: {
type: String,
required: false
},
teacher: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
],
students: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Student'
}
]
})
User Schema/Model:
var UserSchema = new mongoose.Schema({
firstName: String,
lastName: String,
email: String,
courses: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Course'
}
],
password: String
});
Basically, I want to have it so on my frontend, I could do things like course.teacher.firstName or user.courses. My schemas are in two different files, but I believe that is fine. It's like assigning a user a post when they create it. I don't know how I could do this, as I've tried multiple things.
Right now, I currently have this for creating a course.
// Creates a new course
router.post('/create', function (req, res) {
Course.create({
courseID : req.body.courseID,
courseName : req.body.courseName,
courseDesc : req.body.courseDesc,
coursePicture : req.body.coursePicture,
teacher : req.body.id,
students: req.body.students
},
function (err, course) {
if (err) return res.status(500).send("There was a problem adding the information to the database.");
res.status(200).send(course);
});
});
I have already referenced the User model in the controller where that code ^ belongs as so var User = require('../user/User');
I believe that is needed to pull this off. If you have any questions, please let me know as I'm not the best at explaining things like this.
Hope someone can help me out!
Thanks.
// Creates a new course
router.post('/create', function (req, res) {
Course.create({
courseID : req.body.courseID,
courseName : req.body.courseName,
courseDesc : req.body.courseDesc,
coursePicture : req.body.coursePicture,
teacher : req.body.id, // find this user
students: req.body.students,
attendance: req.body.attendance
},
function (err, course) {
User.findById(req.body.id, function(err, user) {
user.update({
$push: {
courses: course._id
}
}, function(err) {
if (err) return res.status(500).send("There was a problem adding the information to the database.");
res.status(200).send(course);
})
})
});
});
This is an issue of database design. There should only be one place where information about a course is stored, the Courses table, and the Users table should know nothing about courses. There should be a table the relates a course to a user: a UserCourseRelations table.
I would strongly avoid the approach of storing an array of courseIds that a user is related in the user table as this is unnecessary coupling and so is not good database design. Also, it'll bog down reads to your Users table as those arrays grow on every row.
Here's how I would approach this. Note that some of this code uses ES6 syntax. The following code is untested, but should work. Take a look:
Create CourseSchema and CourseModel
var CourseSchema = new mongoose.Schema({
courseID: {
type: Number,
unique: true
},
courseName: String,
courseDesc: {
type: String,
default: "No course description provided."
},
teacherId: {
type: mongoose.Schema.Types.ObjectId,
}
coursePicture: {
type: String,
required: false
},
students: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Student'
}
]
})
CourseSchema.statics.createNew = function(data, callback) {
// do some verification here
// insert the new course
return new this(data).save((err, dbCourse) => {
if (err) {
return callback(err)
}
UserCourseRelationSchema.insertNew('teacher', userId, courseID, (err, dbUserCourseRelation) => {
if (err) {
return callback(err)
}
// done. return the new course
callback(null, dbCourse)
})
})
CourseSchema.statics.getByIds = function(courseIDs, callback) {
// find all of the courses where the courseID is in the courseIDs array
// see https://docs.mongodb.com/manual/reference/operator/query/in/
this.find({courseID: {$in: courseIDs}}, (err, courses) => {
if (err) {
// something went wrong
return callback(err)
}
callback(null, courses)
})
}
}
let CourseModel mongoose.model('courses', CourseSchema);
Create UserCourseRelationSchema and UserCourseRelationModel that relates a course to a user and vice versa
var UserCourseRelationSchema = new mongoose.Schema({
userId: {
type: String,
required: true,
},
courseID: {
type: Number,
required: true,
},
type: {
type: String,
enum: ['teacher', 'student'],
required: true,
},
});
UserCourseRelationSchema.statics.createNew = function(type, courseID, userId, callback) {
// do some verification here. I suggest making sure this relation doesn't already exist
// insert the new course
return new this({
courseID: courseID,
userId: userId,
type: type,
}).save((err, dbUserCourseRelation) => {
if (err) {
return callback(err)
}
// return the new relation
callback(null, dbRelation)
})
}
UserCourseRelationSchema.statics.getTeacherRelationCourseIdsByUserId = function(userId, callback) {
let query = this.find({userId: userId, type: 'teacher'})
query.distinct('courseID') // get an array of only the distinct courseIDs
query.exec((err, courseIDs) => {
if (err) {
// something went wrong
return callback(err)
}
callback(null, courseIDs)
})
}
let UserCourseRelationModel = mongoose.model('user_course_relations', UserCourseRelationSchema);
Create UserSchema and UserModel
var UserSchema = new mongoose.Schema({
firstName: String,
lastName: String,
email: String,
password: String
});
UserSchema.statics.getAllCoursesById = function(userId, callback) {
// get the relations for the courses the user is a teacher of
UserCourseRelationModel.getTeacherRelationCourseIdsByUserId(userId, (err, courseIDs) => {
// get the courses by the returned coursIDs
CourseModel.getByIds(courseIDs, (err, courses) => {
if (err) {
// something went wrong
return callback(err)
}
callback(nul, courses)
})
})
}
let UserModel = mongoose.model('users', UserSchema);
// -- create the router
// Creates a new course
router.post('/create', function (req, res) {
CourseModel.createNew({
courseID : req.body.courseID,
courseName : req.body.courseName,
courseDesc : req.body.courseDesc,
coursePicture : req.body.coursePicture,
teacher : req.body.id,
students: req.body.students
}, function (err, course) {
if (err) return res.status(500).send("There was a problem adding the information to the database.");
res.status(200).send(course);
});
});
// -- done
I also suggest using promises if possible as it makes all of this logic much simpler.

$addToSet to an array but it gives me null

so basically I've a wish list and I've bunch of products that I want to add inside the the wish list products array using a put request (I'm using postman btw).
This is the wish list schema, and yes I know that the document's name in the db is "whishlist"....I hate typos
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = mongoose.Schema.Types.ObjectId;
var whishList = new Schema({
title: {type: String, default: "Cool whishlist"},
products:[{type: ObjectId, ref:'Product'}]
});
module.exports = mongoose.model('WhishList', whishList);
This is the products schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var product = new Schema({
title: String,
price: Number,
likes: {type: Number, default: 0}
});
module.exports = mongoose.model('Product', product);
and now this is the code that I am trying to run
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://localhost/swag-shop');
var Product = require('./model/product');
var wishList = require('./model/wishlist');
app.put('/wishlist/product/add', function(request, response){
Product.find({_id: request.body.productId}, function(err, product){
if(err) {
response.status(500).send({err: "could not add item to wishlist"});
}else{
wishList.update({_id: request.body.wishlistId},{$addToSet: {products: product._id}}, function(err, wishlist){
if(err){
response.status(500).send({err: "could not add item to wishlist /update/"});
}else{
response.send(wishlist);
}
});
}
});
I really can't see where is the problem I tried deleting the document and posting it again but I had the same problem.
Thanks in advance
The issue is that the result from Product.find() is an array of Mongoose documents if the query matches any documents in the collection instead of a single document which you want.
Thus the expression {$addToSet: {products: product._id}} resolves to {$addToSet: {products: undefined}} because product is an array and product._id is undefined. Take this simple example
var product = [{ '_id': 1 }];
console.log(product._id) // logs undefined
To remedy this problem, you can either access the only element in the array as
wishList.update(
{ '_id': request.body.wishlistId },
{ '$addToSet': { 'products': product[0]._id} },
function(err, wishlist) { ... }
);
Or use the findOne() method which returns a single document when querying the product:
Product.findOne({ '_id': request.body.productId }, function(err, product) {
if(err) {
response.status(500).send({err: "could not add item to wishlist"});
} else {
wishList.update(
{ '_id': request.body.wishlistId },
{ '$addToSet': { 'products': product._id } },
function(err, wishlist) { ... }
);
}
});
The findById() method is also useful in this case i.e.
Product.findById(request.body.productId, function(err, product) {
if(err) {
response.status(500).send({err: "could not add item to wishlist"});
} else {
wishList.update(
{ '_id': request.body.wishlistId },
{ '$addToSet': { 'products': product._id } },
function(err, wishlist) { ... }
);
}
});

Sails.js: Inserting one-to-many associated models to MongoDB

I am trying to take advantage of the Waterline ORM in Sails.js to build an example app that has a model called 'Category'. Because a category can have multiple sub categories, I have the following one-to-many association for this model:
module.exports = {
adapter: 'mongo',
// adapter: 'someMysqlServer',
attributes: {
categoryTitle: {
type: 'string',
required: true
},
parentCat: {
model: 'category'
},
subCategories: {
collection: 'category',
via: 'parentCat'
},
articles: {
collection: 'article',
via: 'category',
required: false
}
}
};
In the CategoryController.js, I have the create method that first tries to see if the new category has a parent category assigned to it; however, I feel the code is quite messy, and the parentCat in Mongodb is always empty even if I tried to assign a parent category in the form submission. So I am wondering if this is the right way to do it:
create: function(req, res, next) {
var params = req.allParams();
// set parent category if exists
if (params.parentCat) {
Category.findOne({categoryTitle : params.parentCat})
.exec(function(err, category) {
if (err) {
return false; //not found
} else {
params.parentCat = category.id; //found the parent category
console.log('parent cat id is: ', category.id);
}
});
}
Category.create(params, function(err, newCategory) {
if (err) {
return next(err);
} else {
console.log('new category created');
}
console.log('successfully added the category: ' + newCategory.categoryTitle)
res.redirect('/category');
}); // create the category
}
The issue of your code is the callback.
I created a new version of code with the async feature (which is already in your sails app), hope it will help you.
create: function(req, res, next) {
var params = req.allParams();
async.waterfall([
function(callback) {
// set parent category if exists
if (params.parentCat) {
Category.findOne({
categoryTitle: params.parentCat
})
.exec(function(err, category) {
if (err) {
return false; //not found
}
params.parentCat = category.id; //found the parent category
console.log('parent cat id is: ', category.id);
callback(null, params);
});
} else {
callback(null, params);
}
},
function(params, callback) {
Category.create(params, function(err, newCategory) {
if (err) {
return next(err);
}
console.log('successfully added the category: ' + newCategory.categoryTitle);
callback(null, newCategory);
}); // create the category
}
], function(err, result) {
console.dir(result);
res.redirect('/category');
});
}

Categories