I am trying to update certain info in a user collection, when the user is visiting a page.
But my method doesn't work. Can anyone help to get it fixed.
app.get('/add-your-accommodation/apartment-type', (req, res, next) => {
if (req.isAuthenticated()) {
res.render('apartment-type.ejs')
} else {
res.render('login.ejs')
}
var id = req.params.id
if(mongoose.Types.ObjectId.isValid(id)) {
User.findByIdAndUpdate(id, {$set: {accomtype: 'house'}},{new: true})
}
});
Your req.params.id is undefined since there is no mention of it in the route path. You can do this,
app.get('/add-your-accommodation/apartment-type', (req, res) => {
if (!req.isAuthenticated()) {
return res.render('login.ejs')
}
res.render('apartment-type.ejs')
var id = req.user._id //since you're using passport (LocalStrategy)
if(mongoose.Types.ObjectId.isValid(id)) {
User.findByIdAndUpdate(id, {$set: {accomtype: 'house'}})
}
})
Now when you call your API, do it like this,
GET /add-your-accommodation/apartment-type
I agree with #kedar-sedai, when you update/change something in your DB, you should not use a GET request. A good practise would be to use the PUT method, even if you have nothing to pass in the body. It makes it easier for you and other developers to understand what your code does at a glance.
Here are 4 HTTP requests that will work in most of the use cases :
GET
You want to retrieve information from your DB (ex: get users, get all the apartment types...)
POST
You want to add information (ex: register user, add an apartment, ...), or send information using the body of the POST request (ex: login, ...)
PUT
You want to update a value (ex: change username, change an apartment type, ...)
DELETE
You simply want to delete something in your DB (ex: delete a user...)
Try findOneAndUpdate. Also, use callback in your query function for getting the error or result.
app.get('/add-your-accommodation/apartment-type/:id', (req, res, next) => {
if (req.isAuthenticated()) {
res.render('apartment-type.ejs')
} else {
res.render('login.ejs')
}
var id = req.params.id
if(mongoose.Types.ObjectId.isValid(id)) {
User.findOneAndUpdate({_id: mongoose.Types.ObjectId(id)}, { $set: { accomtype:'house' } },(err, result)=>{
if (err) throw new Error(err);
console.log(result)
return
})
}
});
Related
I've recently started using the MEAN stack and stumbled upon some errors while trying to work with my MongoDB database. I connected to the database successfully, implemented my CRUD routes, and I get wrong values for anything besides the find() method (which returns all the documents in my collection without any problem). The findOne() looks like this for example:
router.route(server.get("/company/:id", (request, response) => {
const companyId = request.params.id;
console.log("Showing company with id: " + companyId)
dbCollection.findOne({ _id: mongodb.ObjectId(companyId) }, (error, result) => {
if (error) throw error;
// return company
response.json(result);
});
}));
The result after making a get request via Postman is null
The insertOne() looks like this:
router.route(server.post("/company/add", (request, response) => {
const company = request.body;
dbCollection.insertOne(company, (error, result) => {
if (error) throw error;
// return updated list
dbCollection.find().toArray((_error, _result) => {
if (_error) throw _error;
response.json(_result);
});
});
}));
It adds one document to the database with the ID that it creates for itself, but for some reason it doesn't take in the body data (2 string elements { "name": "xy", "type": "company" })
And last but not least, the deleteOne():
router.route(server.delete("/company/delete/:id", (req, res) => {
const companyId = req.param.id;
console.log("Delete company with id: ", companyId);
dbCollection.deleteOne({ _id: mongodb.ObjectId(companyId) }, function(err, result) {
if (err) throw err;
// send back entire updated list after successful request (optional)
dbCollection.find().toArray(function(_err, _result) {
if (_err) throw _err;
res.json(_result);
});
});
}));
For some reason it deletes the very first document in the collection, but not the one that is entered with the corresponding ID.
If anyone could help me out with this it would be awesome. Thank you in advance!
Edit 1:
Adding a new document to the collection via Postman
Collection after the add
Edit 2:
Get request via ID and response (returns null)
Console output:
Showing company with id: 5e63db861dd0ce2418ce423d
Edit 3:
Corrected the code for the findOne() and deleteOne() methods.
When you try with _id you need to convert the string(request.params.id) to ObjectId().
Convert string to ObjectID in MongoDB - whoami
I am trying to batch delete using deleteMany via Mongoose. Currently I have a few rows with check-boxes and a submit button which POSTs an array of IDs to my deleteMany endpoint like this,
router.get('/list/batchDelete', secured()).delete(function(req, res) {
Booking.deleteMany(
{
_id: {$in: [req.params.ids]},
},
function(err, rowsToDelete) {
if (!err) {
res.send(rowsToDelete);
res.redirect('/list');
} else {
res.send(err);
console.log('Error in batch delete :' + err);
}
},
);
});
I can see the req.params.ids payload.
POSThttp://localhost:8000/list/batchDelete
[HTTP/1.1 404 Not Found 34ms]
Request payload
ids%5B%5D=5e1da4db2f11682b506fc6c8&ids%5B%5D=5e1da522becbb13f24748012&ids%5B%5D=5e1da57a5c7f911db82e5731
But I keep getting Cannot POST /list/batchDelete
Please, what am I missing?
I referred to:
Mongoose Delete Many by Id
Mongoose Docs: Query.prototype.deleteMany()
UPDATE:
I added a post route like this, which now produces 200OK but in the browser JSON view and still no change to the dataset.
router.post('/list/batchDelete', function(req, res) {
const ids = req.body.ids;
res.send(ids);
res.redirect('/list');
});
Use your route like this
router.post('/list/batchDelete', async (req, res) {
const {ids} = req.body;
await Booking.deleteMany(
{
_id: {$in: ids},
})
return res.send('record deleted');
});
Use postman for api call
Call should be POST and on /list/batchDelete route
Body should contain array of ids
e.g {"ids":['id1','id2']}
This will solve your problem of deleting records.
I have a API, which allows a user to add a intervalValue - which will be used in my program to determine how often my program runs. I am using node js, express and mongodb in my project.
This is the API which allows a user to add a value:
router.post('/', function(req, res, next) {
intervalValue.create(req.body, function(err, post) {
if (err) {
console.log(err);
} else {
res.json(post);
}
});
});
And this is the schema for it:
var intervalValueSchema = new mongoose.Schema({
milliseconds: {
type: Number,
min: 15000
}
}, {
capped: {
size: 1024,
max: 1,
autoIndexId: true
}
});
From my understanding, this schema will only allow milliseconds value larger than 15000 milliseconds, and because it is capped, it will only allow one document in the collection.
THE AIM : add a interval value, and then only be able to modify that value - i.e. it will not be allowed to be deleted, and no more will be able to be added. Hence I need it to be limited to one document.
However with the current code, I am able to add multiple documents to this collection (even though when I do isCapped() I get true returned), and when I update the value I can insert a value less than 15000 - which should not be allowed.
This is the update API:
router.put('/updateValue/:id', function(req, res, next) {
intervalPoll.findByIdAndUpdate(req.params.id, req.body, {
new: true
}, function(err, post) {
if (err) {
console.log(err);
} else {
res.json(post);
}
});
});
What am I doing wrong - I am a beginner with mongo so any tips would be appreciated. Also, is there a better way to achieve my aim?
Thanks!
I wouldn't expose the ID of that value and I would not implement the PUT handler with :id (which is not convenient anyway, because user has to know the ID which once set will never change and be only one).
I would implement two endpoints:
router.get('/interval', ...
and
router.post('/interval', ...
# or:
router.put('/interval', ...
The get handler would search the database for any document and if present return its value. If there is no such document it would return some default value.
The post handler would first verify the value and then modify a document if it exists or insert it if it doesn't.
I think that in this case it would be much easier to check for that number in your handler then to fight with Mongoose to do what you need. This is a specific use case.
Udate
Here are some examples:
Schema can be:
mongoose.Schema({
milliseconds: Number
});
And the handlers would be something like this - let's say that your model is called Interval:
var defaultInterval = {milliseconds: 15000};
router.get('/interval', function(req, res, next) {
Interval.find().exec(function (err, intervals) {
if (err || intervals.length === 0) {
res.json(defaultInterval);
} else {
res.json({milliseconds: intervals[0].milliseconds});
}
});
});
router.put('/interval', function(req, res, next) {
var milliseconds = // get it from request
if (!milliseconds || milliseconds < 15000) {
// respond with error
} else {
Interval.findOneAndUpdate(
{}, {milliseconds: milliseconds}, {upsert: true},
function(err, interval){
if (err) {
// respond with error
} else {
// respond with success
}
}
);
}
});
This is not tested and you need to fill in the blanks but you get the idea.
I'm using nodejs Express. I would like to have a router that receive id or username as request param as below:
router.get('/:id?/:username?', authMiddleware, function (req, res, next) {
models
.User
.findAll({
where: {
$or: {
id: req.params['id'],
username: req.params['username']
}
}
})
.then(function (rows) {
res.send(rows);
})
}
However, express seems to not understand my request on:
http://localhost:3000/api/1.0/users//david
Which I would like to query user by username instead of id
The only way works right now is defining a route as: /:id_or_username as below:
router.get('/:id_or_username', function (req, res, next) {
models
.User
.findAll({
where: {
$or: {
id: req.params['id_or_username'],
username: req.params['id_or_username']
}
}
})
.then(function (rows) {
res.send(rows);
})
}
But it is kind of dirty and I'm not happy with that code.
Is that possible to define router in this case using REST style instead of :idOrName param?
Assuming your id is an int, you can do something like this:
router.get('/:id_or_username', function (req, res, next) {
var where = {}
var param = req.params['id_or_username']
if (isNaN(param)) {
where.username = param
} else {
where.id = param
}
models.User.findAll({where}).then(function (rows) {
res.send(rows)
})
}
Now you can send the id or the username using the same paramether
I am not sure how your system is setup but this sounds like a better use for a req.query instead of req.param.
You should use req.query when you are not sure what the input might be, and use req.param when passing information you are sure will be there. There is probably a way to do this with req.param but I am not sure I can think of a reason why you'd want to do it that way.
This way you could do an action like:
action = "/users/"+ id ? id : username
and then req.query for it.
I'm working with NodeJS + Mongoose and I'm trying to populate an array of objects and then send it to the client, but I can't do it, response is always empty because it is sent before forEach ends.
router.get('/', isAuthenticated, function(req, res) {
Order.find({ seller: req.session.passport.user }, function(err, orders) {
//handle error
var response = [];
orders.forEach(function(doc) {
doc.populate('customer', function(err, order) {
//handle error
response.push(order);
});
});
res.json(response);
});
});
Is there any way to send it after the loop has finished?
Basically, you could use any solution for async control flow management like async or promises (see laggingreflex's answer for details), but I would recommend you to use specialized Mongoose methods to populate the whole array in one MongoDB query.
The most straightforward solution is to use Query#populate method to get already populated documents:
Order.find({
seller: req.session.passport.user
}).populate('customer').exec(function(err, orders) {
//handle error
res.json(orders);
});
But if, for some reason, you can't use this method, you could call Model.populate method yourself to populate an array of already fetched docs:
Order.populate(orders, [{
path: 'customer'
}], function(err, populated) {
// ...
});
One solution is to use Promises.
var Promise = require('bluebird');
Promise.promisifyAll(Order);
router.get('/', isAuthenticated, function(req, res) {
Order.findAsync({ seller: req.session.passport.user })
.then(function(orders) {
return Promise.all(orders.map(function(doc){
return Promise.promisify(doc.populate).bind(doc)('customer');
}));
}).then(function(orders){
// You might also wanna convert them to JSON
orders = orders.map(function(doc){ return doc.toJSON() });
res.json(orders);
}).catch(function(err){
//handle error
});
});
BlueBird's .promisifyAll creates an …Async version of all functions of an object, which saves you an extra step in configuring the initial promise. So instead of Order.find I used Order.findAsync in above example