I am saving a document in mongodb using mongoose and I wanna send data to the user by excluding some fields like _id.
router.post('/', async (req, res) => { // create category with an item
try {
const category = new Category({
name: req.body.name,
icon_url: req.body.icon_url,
items_quantity: req.body.items.length
})
const data = await category.save();
res.send(data);
} catch (err) {
debug(err.message);
}
})
I want to exclude _id property. I have searched for this but couldn't find the solution. someone suggested to use Node Package underscore but how to do it without it. Someone suggested about lean() and I couldn't understand, how to use it in my case.
To do that, I just delete this field before sending to client side like this :
router.post('/', async (req, res) => { // create category with an item
try {
const category = new Category({
name: req.body.name,
icon_url: req.body.icon_url,
items_quantity: req.body.items.length
})
const data = await category.save();
data._id = null;
res.send(data);
} catch (err) {
debug(err.message);
}
})
Then _id field isn't sent.
Hope it helps.
You can use this bro.
I hope it helps :)
let obj = {
_id: '475947598475947957jdhfjkdhjkghdfkjhgjkhdyfer9t8',
name: 'John Doe'
}
delete obj['_id'];
console.log(obj);
Related
I'm making a Forum application with my first Node Express Mongo API. Posts have an array of comments, and I am trying to delete a comment, based in it's id.
My method returns without errors, but the comment is never deleted.
Deleting method:
export const deleteComment = async (req, res) => {
const commentId = req.body._id
const post = await Post.findById(req.params.id)
const _id = post.id
try {
await Post.updateOne(
{ _id },
{
$pull:
{
comments: { _id: commentId }
}
}
)
res.status(200).json({ message: 'Success' })
} catch (error) {
res.status(400).json({ error: error.message })
}
}
The model looks like the following:
Previously, when using the updateOne method, it for some reason had issues finding the id of the model to update, but I have now used the same formula as before, so I don't see that as being an issue.
Any guesses of what I can do to fix this?
I have been trying to access a collection that was dynamically created in the database through {$out: "Accepted"} in nodejs using mongoose. the collection was successfully created.
from the controller:
exports.accepted = async (req, res, next)=>{
await Pupil.aggregate([{$match: {status: "Accepted"}}, {$out: "Accepted" }])
**Accepted.find({}).then(result=>{
res.render('home/accepted', {results: result, pageTitle: 'accepted page'})
}).catch(error=>{
console.log(error)*emphasized text*
});**
}
I want to retrieve the documents in that 'Accepted' collection and render them to the ejs file.
the error message is:
Accepted.find({}).then(result=>{
^
ReferenceError: Accepted is not defined at exports.accepted...
can someone please help me out?
Thanks!
Welcome to StackOverflow #Jerevick
There are two possible cases here:
Case 1 - You don't need to write to a new collection
You just want to find documents of accepted pupils and render them in the EJS template, in which case:
A) You don't need to write your documents into a new collection, you can just get them from the result and pass them to your template.
exports.accepted = async (req, res, next) => {
try {
const result = await Pupil.aggregate([{ $match: { status: 'Accepted' } }]);
res.render('home/accepted', { results: result, pageTitle: 'accepted page' });
} catch (err) {
console.log(err);
}
};
B) You don't even need the aggregation framework, you can just do a simple find:
exports.accepted = async (req, res, next) => {
try {
const result = await Pupil.find({ status: 'Accepted' });
res.render('home/accepted', { results: result, pageTitle: 'accepted page' });
} catch (err) {
console.log(err);
}
};
Case 2 - you need to write to a new collection, and your example was just to simplify
If that's the case, it's important to emphasize the difference between mongoose and MongoDB. Mongoose is a wrapper around the native MongoDB driver to help with casting, and provide a nicer API.
When you add a new collection to the database using the $out stage, the mongoose isn't aware of it and it doesn't assign a model for it and has no idea what kind of data live there, in which case you would need to bypass mongoose and use the native MongoDB driver directly.
I highly advise against this approach, since you'd be giving up all the convenience mongoose provides, don't take this approach unless you really know what you're doing, there's probably a better solution than using the native driver directly.
exports.accepted = async (req, res, next) => {
try {
await Pupil.aggregate([{ $match: { status: 'Accepted' } }, { $out: 'Accepted' }]);
const acceptedCollection = mongoose.connection.collection('Accepted');
const result = await acceptedCollection.find({}).toArray();
res.render('home/accepted', { results: result, pageTitle: 'accepted page' });
} catch (err) {
console.log(err);
}
};
Here's a full reproduction script you can play with:
65209755.js
'use strict';
const mongoose = require('mongoose');
const { Schema } = mongoose;
const assert = require('assert');
run().catch(console.error);
async function run () {
await mongoose.connect('mongodb://localhost:27017/test', {
useNewUrlParser: true,
useUnifiedTopology: true
});
await mongoose.connection.dropDatabase();
const studentSchema = new Schema({
name: String,
status: String
});
const User = mongoose.model('User', studentSchema);
await User.insertMany([
{ name: 'Hafez1', status: 'Accepted' },
{ name: 'Hafez2', status: 'Accepted' },
{ name: 'Hafez3', status: 'Accepted' },
{ name: 'Hafez4', status: 'Rejected' },
{ name: 'Hafez5', status: 'Rejected' }
]);
await User.aggregate([
{ $match: { status: 'Accepted' } },
{ $out: 'acceptedstudents' }
]);
const db = mongoose.connection;
const acceptedStudents = await db.collection('acceptedstudents').find({}).toArray();
assert.deepStrictEqual(
acceptedStudents.map(student => student.name),
['Hafez1', 'Hafez2', 'Hafez3']
);
console.log('All assertions passed.');
}
Output
$ node 65209755.js
All assertions passed.
GoodDay Experts,
I've tried following code but it did not work, and it gives me null value.. maybe my routes are wrong but basically it works the way on other routes... and here is my backend for delete case: manage.js/actions
export const removeRecipient = (payload) => async (dispatch) => {
try {
const res = await axios.delete(
`${_config.MAT_URL}/api/1/customer/delete`,
payload
);
dispatch({
type: DELETE_CUSTOMER,
payload: res.data,
});
} catch (err) {
dispatch({
type: POST_ERROR,
payload: { err },
});
}
};
and for my routes which is the mongoose query for findOneAndDelete, under customer.js :
router.delete("/delete", (req, res) => {
Customer.findOneAndDelete({ _id: req.params.id }, (err, Customer) => {
if (!err) {
res.json({ msg: "customer deleted", deleted: Customer });
} else {
console.log("Error removing :" + err);
}
});
});
And for the front end im using "AiOutlineDelete" which was coded as :
const handleDelete = (id) => {
console.log('delete')
removeRecipient(id)
}
<a
id={`delete-${rowIndex}`}
className="anchor-action-delete"
href="#foo"
onClick={(e) => {
e.preventDefault();
handleDelete(row);
}}>
thanks have a great day
There are 2 problems in your code:
req.params.id is meant for urls of the form /delete/:id which is obviously not your route, you should change it to req.query.id instead which matches query parameters in the url such as /delete?id=123.
The default type of _id is ObjectId, under the assumption you did not change this you need to cast your req.query.id which is type string to ObjectId.
It looks like you're using mongoose so here's mongoose syntax:
const mongoose = require("mongoose");
router.delete("/delete", (req, res) => {
Customer.findOneAndDelete({ _id: new mongoose.Types.ObjectId(req.query.id) }, (err, Customer) => {
if (!err) {
res.json({ msg: "customer deleted", deleted: Customer });
} else {
console.log("Error removing :" + err);
}
});
});
For nodejs native Mongo package:
import {ObjectId} from "mongodb";
...
new ObjectId(req.query.id)
I dont see you sent the id to the backend but you are trying to retrieve it from req.params.id try passing the id like "delete/:id" at the end of the link and specify this in the routes aswell.
if that doesnt fix try the below code this for routes
if nothing works check this, In the component you need to send the id(object id) but i see "row" what is the value of row? if the row value is not the id in the database then it wont delete. if this your issue try inspecting the code by keeping breakpoints or write a console.log() to check the value of "row" .
try {
const removedProject = await Customer.remove({
_id: req.params.id
})
res.json(removedProject)
} catch (err) {
res.json({
message: err
})
}
For update I use the following code that works:
router.put('/:id', async (req, res) => {
const { error } = validateProduct(req.body);
if (error) return res.status(400).send(error.details[0].message);
const product = await Product.findByIdAndUpdate(req.params.id,
{
name: req.body.name,
description: req.body.description,
category: req.body.category,
tags: req.body.tags,
withdrawn: req.body.withdrawn,
extraData: {
brand: req.body.extraData.brand,
quantity: req.body.extraData.quantity,
type: req.body.extraData.type
}
},
{new: true}
);
if (!product) return res.status(404).send('The product with the given ID was not found.');
res.send(product);
});
What I want to do is to create a Patch operation that updates only certain fields and not all of them as the update above. These fields are not standard but they are one of the above fields of the update operation.
You can try this snippet (didn't test it locally though). The idea is to only update those fields in Product, which were mentioned in req.body. Be sure your validator is secure, otherwise you can end up with nosql injection.
router.put('/:id', async (req, res) => {
const { error } = validateProduct(req.body);
if (error) return res.status(400).send(error.details[0].message);
const product = await Product.findById(req.params.id).exec();
if (!product) return res.status(404).send('The product with the given ID was not found.');
let query = {$set: {}};
for (let key in req.body) {
if (product[key] && product[key] !== req.body[key]) // if the field we have in req.body exists, we're gonna update it
query.$set[key] = req.body[key];
const updatedProduct = await Product.updateOne({_id: req.params.id}, query}).exec();
res.send(product);
});
Also I'm sure you can leverage lodash in the line, where I you use a for-in loop ;)
The downside of this approach is that it takes 2 queries to mongo, because you need a real document to compare the thing. Also update operation doesn't trigger post save hooks of your model. If you need them - you should findById() first, update necessary fields and then hit .save() on the very document you found.
Hope it helps
You could do this as well:
// define your middlewares here
// validateObjectId.js
const mongoose = require('mongoose');
module.exports = function (req, res, next) {
if (!mongoose.Types.ObjectId.isValid(req.params.id))
return res.status(404).send("Invalid ID.");
next();
}
// 404.js
module.exports = function (str, id) {
if (!str || !id) throw new Error('the string name and id must be defined');
return `The ${str} with the given ID (${id}) was not found`;
}
// import your middlewares into your product route
const validateObjectId = require('../middleware/validateObjectId'); // respect your middleware path
const fourOfour = require('../middleware/404'); // respect your middleware path
router.put('/:id', validateObjectId, async (req, res) => {
const { error } = validateProduct(req.body);
const { name, description, category, tags, withdrawn, extraData, } = req.body
if (error) return res.status(400).send(error.details[0].message);
let product = await Product.findById(req.params.id).exec();
if (!product) return res.status(404).send(fourOfour("Product", req.params.id));
product = await Product.findByIdAndUpdate(req.params.id, {
name: name || product.name,
description: description || product.description,
category: category || product.category,
withdrawn: withdrawn || product.withdrawn,
extraData: extraData || product.extraData
}, { new: true });
product = await product.save();
if (!product) return res.status(404).send(fourOfour("Product", req.params.id));
res.send(product);
});
I have the following Users and Posts schemas, which Users has the following data type:
postCreated:{
type: Array,
default: []
}
And the server route for Posts is:
server.post('/posts',(req,res,next) => {
const {username,title,categories,content} = req.body;
const post = new Posts({
username,
title,
categories,
content
});
post.save().then((doc) => {
console.log(doc);
res.send(doc);
User.findOneAndUpdate({username}, {$push: {'postCreated': doc}});
})
.catch((e) => {
return next(new errors.BadRequestError(e.message));
});
});
Which saves the newly created post into the database and update the 'postCreated' property in the User schema. However, my current code does not work, is there any way that I could do pushing that newly created post into the 'Post Created' Array?
Thank you for your help.
See if this will work for you:
server.post('/posts',(req,res,next) => {
const {username,title,categories,content} = req.body;
const post = new Posts({
username,
title,
categories,
content
});
return post.save()
.then(doc => User.findOneAndUpdate({username}, {$push: {'postCreated': doc}})
.then(() => res.send(doc)))
.catch(e => {
return next(new errors.BadRequestError(e.message));
});
});
You ware missing the returns for the post.save as well as for the User.findOneAndUpdate. Also your the res.send(doc) should probably be the last thing in your function.