Problem with a Cast using Router.put with mongoDB in MERN app - node.js

I want to attach the router.put which will update the Boolean(isOn) in toggle button but firstly I wanted to try how it works and now I am facing the problem.
const express = require("express");
const router = express.Router();
const Buttons = require('../../models/Buttons');
// GET buttons
// This request works perfect
router.get('/', (req,res) => {
Buttons.find()
.sort({name: 1})
.then(buttons => res.json(buttons))
});
// PUT buttons
// This one doesnt work at all
router.put('/:name', function(req,res,next) {
Buttons.findByIdAndUpdate({name: req.params.name},
req.body).then(function(){
Buttons.findOne({name: req.params.name}).then(function(buttons){
res.send(buttons);
});
});
});
module.exports = router;
Model of buttons has only name: String, required: true and isOn: Boolean, required: true and data in db looks like that:
Can you tell me what did I do wrong here?
Code of Buttons modal :
const mongoose = require('mongoose');
const Schema = mongoose.Schema
const buttonSchema = new Schema ({
name: {
type: String,
required: true
},
isOn: {
type: Boolean,
required: true
}
});
module.exports = Buttons = mongoose.model("buttons", buttonSchema);

You ca only use findByIdAndUpdate when you want to update the document by matching the _id of the document
If you want to match the document by any other property (such as name in your case), you can use findOneAndUpdate
Write your query like this
router.put('/:name', function(req,res,next) {
Buttons.findOneAndUpdate({name: req.params.name},
req.body).then(function(){
Buttons.findOne({name: req.params.name}).then(function(buttons){
res.send(buttons);
});
});
});
Hope this helps

Please add your id as well which you have to update in your database
Model.findByIdAndUpdate(id, updateObj, {new: true}, function(err, model) {...
This error occur because findByIdAndUpdate need id of an Object which we want to update so it shows ObjectId error. so pass your id from front end and use it in your back-end to update particulate data.
step 1 : you can create new endpoint for update-name
router.put('/update-name', function(req,res,next) {
//here you can access req.body data comes from front-end
// id = req.body.id and name = req.body.name then use it in your
Buttons.findByIdAndUpdate(id, { name : name }, {new: true}, function(err, model) {...
}
step 2 : try this endpoint /update-name and pass your data in Body from postman

Related

Mongoose MongoError : 11000

I sent a create post signal through Postman. a once time, the post signal was a successfully. but next time it was failed . error message is like this.
Error creating new record : {
"driver": true,
"name": "MongoError",
"index": 0,
"code": 11000,
"keyPattern": {
"RoutineParts.userId": 1
},
"keyValue": {
"RoutineParts.userId": null
}
}
i cant understand the error message,
my post code and user model code is like that ,
// it's post code.
router.post('/',(req,res)=>{
const newRecord = User ({
username : req.body.username,
email : req.body.email,
password : req.body.password
})
newRecord.save((err,docs)=>{
if(!err) {
res.send(docs)
}else {
console.log('Error creating new record : ' + JSON.stringify(err,undefined,2))
}
})
})
// it's user model
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema(
{
username : {type:String},
email: { type: String, required: true},
password: { type: String, required: true, trim: true },
created_at : { type: Date, default: Date.now },
updated_at : { type: Date, default: Date.now },
}
)
const User = mongoose.model('User', userSchema);
module.exports = { User }
i can't understand. In fact 'RoutineParts' is a one of model, but i didn't write user's documents. and, controller path is corrected in app.js
how can i solve it?
it's RoutineParts model
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema(
{
routine_name : {type:String},
userId : { type: String, required: true},
exercise_name : { type: String},
order : { type: Number},
}
)
const RoutineParts = mongoose.model('RoutineParts', userSchema);
module.exports = { RoutineParts }
and it's app.js contents
// Connecting data to the MongoDB through Mongoose
require('./db')
const express = require('express')
const app = express()
const PORT = 5000
const bodyParser = require('body-parser')
const userRoute = require('./controller/user')
const routineRoute = require('./controller/routine')
const RP_Route = require('./controller/routineParts')
const EX_Route = require('./controller/excersise')
app.use(bodyParser.json())
app.get("/", function (req, res) {
res.status(201).send("<h1>Hey guys! Hello World !!</h1>");
});
app.listen(PORT, function () {
console.log(`start express server on port ${PORT}`);
});
app.use('/Users', userRoute)
app.use('/Routine', routineRoute)
app.use('/Excersise', EX_Route)
app.use('/RoutineParts', RP_Route)
What's going on
I took a look at your full code. The only real problem, which also fits with the error message you are seeing, is the following :
In your ./models/routineParts.js you've set a unique field. In your case userid. E.g. if your create a new routineParts document with userid "AAA" you can not create another document with the same userid "AAA". In short this means, every user can only create 1 single routineParts document.
The first time you did a POST request to your route localhost:5000/RoutineParts it created the first routineParts document. After that, every request will fail, because it already created 1 routineParts document. ( Read here about unique index with mongoose )
See your ./controller/routineParts.js . If you try to do the same request with a different userid it should work.
How to fix
1 : Remove unique: true from your ./models/routineParts Schema.
2 : ⚡ Drop the index . Mongoose most-likey already registered this index and you have to drop it specifically. Otherwise it will always treat the userid field as unique.
3 : You are still in development, so it shouldn't hurt to also drop the RoutineParts collection.
4 : Restart the app and try to hit the POST localhost:5000/RoutineParts endpoint a couple of times. You now should be able to create multiple routineParts documents under the same user.
How to drop the index
That's a different story. This question should help you with that. They are also using mongoose. If your are using the latest version of mongoose there is a method called cleanIndexes. Use this method after you've removed the unique:true from your model.
For solve this isuse you must remove index duplicate email_1 from index menu

Cannot add property to mongoose document with findOneAndUpdate

My express app tries to record the login time of the user using Mongoose's findOneAndUpdate.
router.post('/login', passport.authenticate('local', {
failureFlash: true,
failureRedirect: '/'
}), async(req, res, next) => {
// if we're at this point in the code, the user has already logged in successfully.
console.log("successful login")
// save login time to database
const result = await User.findOneAndUpdate({ username: req.body.username }, { loginTime: Date.now() }, { new: true });
console.log(result);
return res.redirect('/battle');
})
The user document does not start out with a login time property. I'm expecting this code to insert that property for me.
The actual result is, the console shows the user document being printed out, but without any added login time property. How can I fix this so a login time property is inserted into the document? Is the only way to do it by defining a login time property in the original mongoose schema? And if so, doesn't that nullify the supposed advantage of NoSQL vs SQL in that it's supposed to allow new unexpected property types into your collections and documents?
I've found the answer for anyone who might come across the same problem. It is not at all possible to add a property to a Mongoose collection if it is not already defined in the Schema. So to fix it I added the property in the Schema.
In fact, you can add a new property that isn't defined in the schema, without modifying the schema. You need to set the flag strict to false to enable this mode. See document here.
The code below demonstrates what I said, feel free to runs it:
const mongoose = require('mongoose');
// connect to database
mongoose.connect('mongodb://localhost/test', { useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false });
// define the schema
const kittySchema = new mongoose.Schema({
name: String
// this flag indicate that the shema we defined is not fixed,
// document in database can have some fields that are not defined in the schema
// which is very likely
}, { strict: false });
// compile schema to model
const Kitten = mongoose.model('Kitten', kittySchema);
test();
async function test() {
// empty the database
await Kitten.deleteMany({});
// test data
const dataObject = { name: "Kitty 1" };
const firstKitty = new Kitten(dataObject);
// save in database
await firstKitty.save();
// find the kitty from database
const firstKittyDocument = await Kitten.findOne({ name: "Kitty 1" });
console.log("Mongoose document", firstKittyDocument);
// modify the kitty, add new property doesn't exist in the schema
const firstKittyDocumentModified = await Kitten.findOneAndUpdate(
{ _id: firstKittyDocument._id },
{ $set: { age: 1 } },
{ new: true }
);
console.log("Mongoose document updated", firstKittyDocumentModified);
// note : when we log the attribute that isn't in the schema, it is undefined :)
console.log("Age ", firstKittyDocumentModified.age); // undefined
console.log("Name", firstKittyDocumentModified.name); // defined
// for that, use .toObject() method to convert Mongoose document to javascript object
const firstKittyPOJO = firstKittyDocumentModified.toObject();
console.log("Age ", firstKittyPOJO.age); // defined
console.log("Name", firstKittyPOJO.name); // defined
}
The output:
Mongoose document { _id: 60d1fd0ac3b22b4e3c69d4f2, name: 'Kitty 1', __v: 0 }
Mongoose document updated { _id: 60d1fd0ac3b22b4e3c69d4f2, name: 'Kitty 1', __v: 0, age: 1 }
Age undefined
Name Kitty 1
Age 1
Name Kitty 1

Save _id current logged inuser to another mongodb collection

I want to save the user collection _id from the currently logged in user to the projects collection when a new project is saved to the database. The project gets saved but the createdby field isn't in the db after saving. I've followed this example: https://mongoosejs.com/docs/2.7.x/docs/populate.html
I found this to be the right way for referencing in several examples .
What am I missing?
project model
const mongoose = require('mongoose');
const projectSchema = new mongoose.Schema({
projectTitle:{
type:String,
required: true
},
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Users' ,
},
createdAt: {
type: Date,
default: Date.now,
required: true
},
});
module.exports = mongoose.model('Projects', projectSchema)
route post project form
// #desc Process project add form
// #route POST /project
router.post('/', ensureAuth, async (req, res) => {
req.body.user = req.user.id
await Project.create(req.body)
res.redirect('/')
})
Though not fully clear from your code snippets, my guess is you lack this piece of code in the controller:
// #desc Process project add form
// #route POST /project
router.post('/', ensureAuth, async (req, res) => {
req.body.user = req.user.id // not sure if this is necessary?
req.body.createdBy = req.user._id
await Project.create(req.body)
res.redirect('/')
})
When using mongoose.Schema.Types.ObjectId, .id is just a getter method that returns a string representation of the ObjectId (so basically ._id.toString()). To properly save the reference, you should save the ._id of the User document (i'm assuming in my code that req.user holds the full document so you can access the ObjectId).

Saving data to array in mongoose

Users are able to post items which other users can request. So, a user creates one item and many users can request it. So, I thought the best way would be to put an array of users into the product schema for who has requested it. And for now I just want to store that users ID and first name. Here is the schema:
const Schema = mongoose.Schema;
const productSchema = new Schema({
title: {
type: String,
required: true
},
category: {
type: String,
required: true
},
description: {
type: String,
required: true
},
userId: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
requests: [
{
userId: {type: Object},
firstName: {type: String}
}
],
});
module.exports = mongoose.model('Product', productSchema);
In my controller I am first finding the item and then calling save().
exports.postRequest = (req, res, next) => {
const productId = req.body.productId;
const userId = req.body.userId;
const firstName = req.body.firstName;
const data = {userId: userId, firstName: firstName};
Product.findById(productId).then(product => {
product.requests.push(data);
return product
.save()
.then(() => {
res.status(200).json({ message: "success" });
})
.catch(err => {
res.status(500).json({message: 'Something went wrong'});
});
});
};
Firstly, is it okay to do it like this? I found a few posts about this but they don't find and call save, they use findByIdAndUpdate() and $push. Is it 'wrong' to do it how I have done it? This is the second way I tried it and I get the same result in the database:
exports.postRequest = (req, res, next) => {
const productId = req.body.productId;
const userId = req.body.userId;
const firstName = req.body.firstName;
const data = {userId: userId, firstName: firstName};
Product.findByIdAndUpdate(productId, {
$push: {requests: data}
})
.then(() => {
console.log('succes');
})
.catch(err => {
console.log(err);
})
};
And secondly, if you look at the screen shot is the data in the correct format and structure? I don't know why there is _id in there as well instead of just the user ID and first name.
Normally, Developers will save only the reference of other collection(users) in the collection(product). In addition, you had saved username also. Thats fine.
Both of your methods work. But, second method has been added in MongoDB exactly for your specific need. So, no harm in using second method.
There is nothing wrong doing it the way you have done it. using save after querying gives you the chance to validate some things in the data as well for one.
and you can add additional fields as well (if included in the Schema). for an example if your current json return doesn't have a field called last_name then you can add that and save the doc as well so that's a benefit..
When using findById() you don't actually have the power to make a change other than what you program it to do
One thing I noticed.. In your Schema, after you compile it using mongoose.modal()
export the compiled model so that you can use it everywhere it's required using import. like this..
const Product = module.exports = mongoose.model('Product', productSchema);

Finding a MongoDB document by ObjectId with Mongoose

I am trying to update a document in MongoDB by finding it by the ObjectId. The work flow is as follows (this is for a blog).
Create a new post in MongoDB by passing a title and body. The ObjectId is automatically created.
Go to edit the post. It uses the ObjectId from the URL to grab it from the database and display it in the same new post form, just with the preexisting values.
When the submit button is clicked I want to find the document by the ObjectId and update the values in the database with those in the post form.
Step 1 & 2 work fine, but step 3 doesn't seem to be working. It redirects to the page I need it to. But the database has not been updated. It's the same value as it was before.
Here's the relevant code for the update post portion:
app.js
app.post "/office/post/:id/update", ensureAuthenticated, routes.updatePost
routes/index.js
mongoose = require 'mongoose'
ObjectId = mongoose.Types.ObjectId
Post = require '../models/Post'
...
updatePost: function(req, res) {
var o_id, the_id;
the_id = req.params.id;
console.log(the_id); // 510e05114e2fd6ce61000001
o_id = ObjectId.fromString(the_id);
console.log(o_id); // 510e05114e2fd6ce61000001
return Post.update({
"_id": ObjectId.fromString(the_id)
}, {
"title": "CHANGE"
}, res.redirect("/office/edit/posts"));
}
I'm using Express and Mongoose.
This is also the post model if that helps:
(function() {
var Post, Schema, mongoose;
mongoose = require('mongoose');
Schema = mongoose.Schema;
Post = new Schema({
title: String,
subhead: String,
body: String,
publish_date: {
type: Date,
"default": Date.now
},
mod_date: {
type: Date,
"default": Date.now
}
});
module.exports = mongoose.model('Post', Post);
}).call(this);
And here's the code for the edit blog post view:
app.js
app.get("/office/post/:id/edit", ensureAuthenticated, routes.editPost);
routes/index.js
editPost: function(req, res) {
return Post.findById(req.params.id, function(err, post) {
return res.render('edit-post', {
post: post,
title: post.title
});
});
}
The problem is how you call update
return Post.update({
"_id": ObjectId.fromString(the_id)
}, {
"title": "CHANGE"
}, res.redirect("/office/edit/posts"));
The last argument will actually redirect the page, whereas update expects a function to be called when the update is complete
You should pass in
return Post.update({
"_id": ObjectId.fromString(the_id)
}, {
"title": "CHANGE"
}, function(err, model) {
if (err) // handleerr
res.redirect("/office/edit/posts"));
});
That way, we only redirect once the model is successfully updated

Resources