Updating Document with Embedded Document in Mongoose not working - node.js

I have a collections called classroom which already have a record. I want to update the document with Embedded Document called timetable. When i perform update operation, this a displayed in console { ok: 0, n: 0, nModified: 0 }. And document are not updating.
Classroom Schema
var ClassroomSchema = new mongoose.Schema ({
classroom_name: {
type:String,
required:true,
unique: true,
},
classroom_blok:{
type:String,
required:true,
},
classroom_floor: {
type:String,
required:true,
},
timetable:
{
timeslot: {
required: true,
'type': String,
},
subject :{
type: mongoose.Schema.Types.ObjectId,
ref: 'Subject'
},
teacher :{
type: mongoose.Schema.Types.ObjectId,
ref: 'Teacher'
},
day :{
type:String,
required: true,
},
year :{
type:String,
required: true,
},
session :{
type:String,
required:true,
}
}
});
Update Operation
router.post('/timetable_morning', function (req, res, next) {
if (
req.body.teacher &&
req.body.timeslot,
req.body.subject,
req.body.classroom,
req.body.session,
req.body.day) {
var timetableData = {
teacher: req.body.teacher,
timeslot: req.body.timeslot,
subject: req.body.subject,
classroom: req.body.classroom,
year: currentYear,
day: req.body.day,
session: req.body.session
}
//use schema.create to insert data into the db
var timetableData1 = {
teacher: req.body.teacher,
timeslot: req.body.timeslot,
subject: req.body.subject,
classroom: req.body.classroom,
year: currentYear,
day: req.body.day,
session: req.body.session
}
Classroom.updateOne({_id:req.body.classroom},timetableData1,function(err,classroom){
if(!err){
console.log(classroom);
return res.redirect('/timetable');
}
});
} else {
var err = new Error('All fields have to be filled out');
err.status = 400;
return next(err);
}
});
Example of Existing Document
{
"_id" : ObjectId("5ee3943833325c210c9a7fee"),
"classroom_name" : "6 Musytari",
"classroom_blok" : "A",
"classroom_floor" : "2",
"__v" : 0
}

your handler function can be like this
router.post('/timetable_morning/:classroom_id', function (req, res, next) {
let dataObj = {
subject: req.body.subject,
teacher: req.body.teacher,
timeslot: req.body.timeslot,
year: currentYear,
day: req.body.day,
session: req.body.session
}
db['Classroom'].updateOne({_id: req.params.classroom_id}, {timetable: dataObj}).then(data => {
if (data.nModified !== 0) {
console.log("updated successfully")
} else {
console.log("Something went wrong")
}
}).catch(err => {
console.log(err)
})})
Note
please make a note here the field classroom_id should be none other then the mongodb ObjectID that you will pass as a param to your route handler function
I also noticed that your payload is having one more field classroom while updating the document which is causing the issues because you are trying to update the value which the timetable embedded document does not have.

Related

How to update a field-List in MongoDB using Mongoose and Nodehs?

Hello dear Stackoverflow team.
I am trying to patch a user, which can handle several "devices". I am using nodeJs with Express and Mongoose (MongoDB). My User model is the following:
const userSchema = new Schema({
name: {type: String, required: true},
lastname: {type: String, required: true},
email: {type: String, required: true, trim: true, lowercase: true, unique:
true},
password: {type: String, required: true, minlength: 5},
userTyp: {type: String, required: true,
enum: {values: ['Administrator', 'Doctor','Patient','Optiker'], message:
'{VALUE} is not supported' }},
image: {type: String},
devices: [ {device: {type: Schema.Types.ObjectId, ref: "Device"}} ]
});
and I want to have something like this everytime i do a patch:
{
"user": {
"_id": "6138cd30ffc5239bba72e6c0",
"name": "Fernando",
"lastname": "Gonzalez",
"email": "f.gonzalez#unitransferklinik.de",
"password": "Hol087+/*",
"userTyp": "Administrator",
"image": "sdsadsadsa/asdfasdas",
"devices": [
{
"device": "6138c7587ab4b5fc4d369230"
},
{
"device": "6138c7587ab4b5fc4d365210"
}
],
}
}
How can I implement in my function:
const updateUser = async (req, res, next) => {
const { name, lastname, email, password, userTyp, device } = req.body;
const userID = req.params.userID;
let updatedUser;
try {
updatedUser = await User.findById(userID);
}catch(err){
console.log(err);
return next(new HttpError('Something happend.', 500));
}
updatedUser.name = name;
updatedUser.devices = [device, ...updatedUser.devices];
try{
updatedUser.save();
}catch (err) {
return next(new HttpError('It could not uodate device.', 500));
}
});
res.status(200).json({user: updatedUser.toObject( {getters: true} )});
};
In easy words, I want to updated the list everytime that i do a patch with a new device, and I can fetch later all the device list per user.
Thanks a lot!
regards,
Eliot
You can use findOneAndUpdate function of mongoose library
const dynamicModel = libMongoose.model(collection_name, userSchema);
var filter = { userID: req.params.userID };
var update = { name: name, devices : [...device, ...updatedUser.devices]};
//callback approach
dynamicModel.findOneAndUpdate(filter, update, (err, resp) => {
if(err) {
console.log("Error while updating record " + JSON.stringify(err));
}
if(!resp) {
console.log("Couldn't find record");
} else {
console.log("Updated data to DB");
}
});
You can also refer here for async await, Hope this helps!

mongoose: create new element in X collection, update another in Y collection

I am trying to develop a CRUD app for users to store, add, delete and update recipes. It's built on MEVN stack. As I need to show the user, which recipes they have created, I am trying to create a recipe based on this model:
const RecipeSchema = new Schema({
title: {
type: String,
required: [true, 'Title of the recipe is required'],
},
category: {
type: Array,
required: [true, 'Category is required'],
},
description: {
type: String,
required: [true, 'Description is required'],
},
imgUrl: {
type: String,
required: [true, 'Image is required'],
},
ingredients: {
type: Array,
required: [true, 'Ingredients are required'],
},
timeOfPreparation: {
type: String,
required: true,
},
preparation: {
type: String,
required: true,
},
sourceName: {
type: String,
required: true,
},
sourceUrl: {
type: String,
required: true,
},
author: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }],
});
const Recipe = mongoose.model('Recipe', RecipeSchema);
module.exports = Recipe;
And at the same time update User model, based on this:
const UserSchema = Schema({
googleId: String,
name: String,
favorites: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' }],
authoredRecipes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' }],
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
In the controller, I have this method (as per #Stock Overflaw comment):
exports.create_new_recipe = (req, res, next) => {
Recipe.create(req.body)
.then(recipe => {
User.update(
{ _id: req.body.author },
{
$push: { authoredRecipes: recipe.id },
}
);
res.send(res.status);
})
.catch(error => {
res.status(500).json({ error });
});
};
This method is called when I go to /create endpoint. However, even though I do get all the correct ids (req.body.author and recipe.id), I cannot get this to work. In my mLab recipe collection the recipe is displayed correctly (all data that I have inserted with authorId), however in the User collection, the array of authoredRecipes stays empty.
How can I get mongoose to both create an object in one collection as well as update another object based on their ids?
The documentation for findByIdAndUpdate requires the _id field as its value, not an object:
User.findByIdAndUpdate(req.body.author, {
$push: { authoredRecipes: recipe.id }
});
// equivalent to the more general method:
User.findOneAndUpdate({ _id: req.body.author }, {
$push: { authoredRecipes: recipe.id }
});
// and if you don't need the modified document in your callback, this should be faster:
// EDIT: this is advised against (we should use a user object, not the collection)
User.update({ _id: req.body.author }, { // or updateOne
$push: { authoredRecipes: recipe.id }
});
Edit: a working, minimal example
Mind {new: true} maybe? Depending on how you test whether it works...
const mongoose = require('mongoose');
const fs = require('fs');
const userIdFile = './tmp.txt'; // just for this test
mongoose.connect('mongodb://localhost/meuh', {
useNewUrlParser: true, // removes a deprecation warning
useFindAndModify: false // removes another deprecation warning
});
// make schemas/models
const RecipeSchema = mongoose.Schema({
title: { type: mongoose.Schema.Types.String }
});
const UserSchema = mongoose.Schema({
name: { type: mongoose.Schema.Types.String },
data: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' }]
});
const RecipeModel = mongoose.model('Recipe', RecipeSchema);
const UserModel = mongoose.model('User', UserSchema);
// user precreation
// UserModel.create({
// name: 'me, myself and I'
// }).then((user) => {
// fs.writeFile(userIdFile, user.id, console.log.bind(null, 'error writing file:'));
// mongoose.connection.close();
// });
// return;
// fetch user
const userId = fs.readFileSync(userIdFile);
let pushedRecipeId; // to test everything went smooth
RecipeModel.create({
title: 'pasta solo'
}).then((recipe) => {
console.log('created recipe:', recipe);
pushedRecipeId = recipe.id;
return UserModel.findOneAndUpdate(
{ _id: userId },
{ $push: { data: recipe.id } },
{ new: true } // forces callback to be passed a fresh object
);
}).then((user) => {
console.log('updated user:', user);
console.log('izok:', !!~user.data.indexOf(pushedRecipeId));
mongoose.connection.close();
}).catch((err) => {
console.log('error', err);
mongoose.connection.close();
})
Example output I got:
# creating user (uncommented this part)
ubuntu#ubuntu-VirtualBox:~/web/test$ node .
error writing file: null
# calling for $push (re-commented user creation)
ubuntu#ubuntu-VirtualBox:~/web/test$ node .
created recipe: { _id: 5c72be7032bd2f1acad37c95, title: 'pasta solo', __v: 0 }
updated user: { data: [ 5c72be7032bd2f1acad37c95 ],
_id: 5c72be6a8143fd1aa9416d85,
name: 'me, myself and I',
__v: 0 }
izok: true
# again $push
ubuntu#ubuntu-VirtualBox:~/web/test$ node .
created recipe: { _id: 5c72c020c2ac7a1b8c65fa36, title: 'pasta solo', __v: 0 }
updated user: { data: [ 5c72be7032bd2f1acad37c95, 5c72c020c2ac7a1b8c65fa36 ],
_id: 5c72be6a8143fd1aa9416d85,
name: 'me, myself and I',
__v: 0 }
izok: true
# and again
ubuntu#ubuntu-VirtualBox:~/web/test$ node .
created recipe: { _id: 5c72c023bf62331b97ef096b, title: 'pasta solo', __v: 0 }
updated user: { data:
[ 5c72be7032bd2f1acad37c95,
5c72c020c2ac7a1b8c65fa36,
5c72c023bf62331b97ef096b ],
_id: 5c72be6a8143fd1aa9416d85,
name: 'me, myself and I',
__v: 0 }
izok: true
# end
ubuntu#ubuntu-VirtualBox:~/web/test$
I don't see what's wrong in your code, but at least you have something to compare with... hope this helps!

Issue with displaying data using mongodb

I have a web app where users can post items and it returns it in a table format. But I am wanting to create a section where a user can view their individual submissions. I have the first part working correctly. And it does show a list of user items. However when I am trying to only view that one persons submissions no data shows. When I console.log it I am getting my user data but I am not getting the item data. The item data just returns an empty array. I am not sure what all I need to post on here but I am going to show both Schemas and the route for listing the data.
UserSchema:
const UserSchema = new mongoose.Schema({
username: {
type: String,
trim: true,
unique: true,
required: true,
minlength: 3,
maxlength: 15
},
firstName: {
type: String,
required: true,
minlength: 3,
maxlength: 15
},
lastName: {
type: String,
required: true,
minlength: 3,
maxlength: 15
},
email: {
type: String,
unique: true,
required: true
},
items: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Items"
}
],
isAdmin: {
type: Boolean,
default: false
}
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
ItemSchema:
const ItemSchema = new mongoose.Schema({
name: {
type: String,
required: true,
minlength: 3,
maxlength: 20
},
description: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
image: String,
noImage: String,
createdAt: {
type: Date,
default: Date.now
},
createdBy: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
});
module.exports = mongoose.model("Items", ItemSchema);
Route:
router.get("/:id", middleware.isLoggedIn, function(req, res) {
User.findById(req.params.id, function(err, foundUser) {
if (err || !foundUser) {
req.flash("error", "Something went wrong");
res.render("index");
} else {
Item.find()
.where("creadtedBy.id")
.equals(foundUser._id)
.exec(function(err, items) {
if (err || !foundUser) {
req.flash("error", "Something went wrong");
res.render("index");
}
console.log("user" + foundUser);
console.log("items" + items);
res.render("inventory", {
user: foundUser,
items: items
});
});
}
});
});
So, what am I doing wrong here? ... Thanks
Here is the route that joins the user to the item:
router.post("/item/add", middleware.isLoggedIn, (req, res) => {
User.findById(req.user._id, (err, user) => {
upload(req, res, err => {
if (err) {
req.flash("error", "error uploading image");
return res.redirect("back");
}
var name = req.body.name;
if (typeof req.file !== "undefined") {
var image = "/uploads/" + req.file.filename;
} else {
image = "/uploads/no-img.PNG";
}
var description = req.body.description;
var price = req.body.price;
var createdBy = { id: req.user._id, username: req.user.username };
var newItem = {
name: name,
image: image,
description: description,
price: price,
createdBy: createdBy
};
Item.create(newItem, (err, newlyCreated) => {
if (err) {
return console.log(err);
} else {
user.items.push(newlyCreated);
user.save();
res.redirect("/products");
}
});
});
});
});
And here is my user info from mongo:
{
"_id" : ObjectId("5aea79207c1f272d186ab97a"),
"items" : [
ObjectId("5aea793b7c1f272d186ab97b")
],
"isAdmin" : true,
"username" : "testuser",
"firstName" : "Test",
"lastName" : "User",
"email" : "test#user.com",
"__v" : 1
}
And here is the Item data from mongo:
{
"_id" : ObjectId("5aea793b7c1f272d186ab97b"),
"createdBy" : {
"id" : ObjectId("5aea79207c1f272d186ab97a"),
"username" : "testuser"
},
"name" : "Test",
"image" : "/uploads/no-img.PNG",
"description" : "Item",
"price" : 1,
"createdAt" : ISODate("2018-05-03T02:51:39.818Z"),
"__v" : 0
}

How to obtain object id and also how to update

I am trying to obtain the object id for any article already in db so that I can validate that the article exists before comments are made.
The issue is on the router (/blog/article/comment). I cannot get the article object id from /blog/article/:postid. I want to pass this id to articleId like this:
articleId: req.params.postid
I have also tried:
articleId: req.article._id
model structure: comment.js
var mongoose = require('mongoose');
var CommentSchema = new mongoose.Schema({
content: { type: String },
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
articleId: { type: mongoose.Schema.Types.ObjectId, ref:'Article' },
dateCommented: { type: Date, default : Date.now }
});
Article model: article.js
var ArticleSchema = new mongoose.Schema({
category: { type: mongoose.Schema.Types.ObjectId, ref: 'Category' },
commentId:{type: mongoose.Schema.Types.ObjectId, ref:'Comment'},
title: String,
author: { type: mongoose.Schema.Types.ObjectId, ref: 'User'},
blog: [{
topic: { type: String, unique: false, lowercase: true },
body: { type: String, unique: false, lowercase: true },
tags: [ 'first', 'mongodb', 'express'],
created: Date,
modified: { type : Date, default : Date.now },
state: { type: String, unique: false, lowercase: true }
}]
});
main.js
router.param('postid', function(req, res, next, id) {
if (id.length !=24) return next(new Error ('The post id is not having the correct length'));
//articleId: req.param('postid'),
Article.findOne({ _id: ObjectId(id)}, function(err, article) {
if (err) return next(new Error('Make sure you provided correct post id'));
req.article = article;
next();
});
});
router.get('/blog/article/:postid', function (req, res, next) {
Article.findById({ _id: req.params.postid }, function (err, article) {
if (err) return next(err);
res.render('main/publishedArticle', {
article: article
});
});
});
router.post('/blog/article/comment', function(req, res, next) {
async.waterfall([
function(callback) {
var comment = new Comment({
articleId: req.params.postid,
content: req.body.content,
user: req.user._id
});
comment.save(function(err) {
if (err) return next (err);
req.flash('success', 'Thank you for your comment');
callback(err, comment);
});
},
function(comment) {
Article.update({_id : comment.articleId }, { $set: { commentId: {} }}, function(err, updated) {
if (updated) {
res.redirect('/')
}
});
}
]);
});
Another issue I have is how to update the commentId for each comment in the Article
Article.update({_id : comment.articleId }, { $set: { commentId: {} }}, function(err, updated)
Since the /blog/article/comment route is a post request. Just submit your articleId in the body of that request. You'll have to send it up from the client. You can access it with req.body.articleID (If that is what you call the variable).
See here for more info on POST requests in node.
For your second question:
Within your article schema you have commentId, That is a single record. What you want is an array of comments. Something like this:
comments: [{type: mongoose.Schema.Types.ObjectId, ref:'Comment'}]
Then within your code...
...
function(comment) {
//comment should contain all the comments
//Grab the article
Article.findOne({ _id: comment.articleId}, function(err, article){
//Go through all the comments in 'comment' compare them with the ones in artcle.comments.
//The ones that aren't already in the article object get put into newComments...
var newComments = [];
Article.update({ _id: comment.articleId }, { $addToSet: { comments: newComments } }, function(err, updated) {
if (updated) {
res.redirect('/')
}
});
});
}
...
I didn't fully implement the code, but it should get you off to the right start.
addToSet Documentation
Some more examples of add to set

Add unique id to nested array in user model Mongoosedb

Im trying to add a unique track id to a nested array in user favourites array inside the user model. New to this, so a little help would be great
User.js (model)
var UserSchema = new Schema({
name: String,
username: { type: String, required: true, index: { unique: true }},
password: { type: String, required: true, select: false },
favorites: [Track],
meta : [{
favorites_count : {
type: Number,
default: 0
},
friends_count: {
type: Number,
default: 0
}
}]
});
apiRouter.route('/users/:user_id/favorites/:track_id')
.post(function(req, res){
User.findByIdAndUpdate(req.params.user_id, {
$addToSet: {"favorites": {track_id: req.body.track_id}},
$inc: { "meta.favorites_count": 1 }
// $set: { "meta.favorites_count": 1}
},
{safe: true, upsert: true}, function(err, user) {
if (err) res.send(err);
res.json({ message: "Track Favorited" });
}
);
})
You should define your favorites in your schema like the following:
var mongoose = require('mongoose'),
ObjectId = mongoose.Types.ObjectId;
var UserSchema = new Schema({
name: String,
username: { type: String, required: true, index: { unique: true }},
password: { type: String, required: true, select: false },
favorites: [{ type : ObjectId, ref: 'Track' }],
meta : [{
favorites_count : {
type: Number,
default: 0
},
friends_count: {
type: Number,
default: 0
}
}]
});
And change your route to:
apiRouter.route('/users/:user_id/favorites/:track_id')
.post(function(req, res){
User.findByIdAndUpdate(req.params.user_id, {
$addToSet: {"favorites": req.body.track_id},
$inc: { "meta.favorites_count": 1 }
// $set: { "meta.favorites_count": 1}
},
{safe: true, upsert: true}, function(err, user) {
if (err) res.send(err);
res.json({ message: "Track Favorited" });
}
);
});
EDIT: Answer the question from your comments.
If the track id is already present in your favorites array then you should change the your query to like this:
var track_id = req.body.track_id;
User.findOneAndUpdate({
_id: req.params.user_id,
favorites: {$nin: [track_id]}
},{
$addToSet: {"favorites": track_id },
$inc: { "meta.favorites_count": 1 }
// $set: { "meta.favorites_count": 1}
},{
safe: true,
upsert: true
},function(err, user) {
if (err) res.send(err);
res.json({ message: "Track Favorited" });
}
);
So you should exclude your documents that already contains track_id in favorites array

Resources