Reputation: 1526
I hope the snippets below make my question more clearer. I can fetch an object that has embedded an array of category objects. From the category objects I want to show only certain properties.
So this is what I get now:
{
"photo": [],
"_id": "5ec55ae39eere8ert85bf8fe749",
"title": "Blog about coding",
"description": "Hi, I want to share with you my incredible journey as a software developer...",
"rate": 3,
"category": [
{
"_id": "5ec55ae39aaa78985f8fe74a",
"name": "programming",
"display_name": "Programming"
}
],
"createdAt": "2020-05-20T16:29:23.982Z",
"updatedAt": "2020-05-20T16:29:23.982Z",
"__v": 0
}
With the following code:
/* GET blog with id */
router.get("/:id", async (req, res, next) => {
try {
const blog = await Blog.findById(req.params.id); // How to filter out fields of objects in category array?
res.send(blog);
} catch(ex) {
res.status(400).send(ex);
}
});
This is what I actually want:
{
"photo": [],
"_id": "5ec55ae39eere8ert85bf8fe749",
"title": "Blog about coding",
"description": "Hi, I want to share with you my incredible journey as a software developer...",
"rate": 3,
"category": [
{
"display_name": "Programming"
}
],
"createdAt": "2020-05-20T16:29:23.982Z",
"updatedAt": "2020-05-20T16:29:23.982Z",
"__v": 0
}
So, in category I want only the "display_name"
How can I get this. I thought I saw a solution before, but I can't find it anymore.
Upvotes: 1
Views: 812
Reputation: 17915
Since you're using .findById()
which is mongoose wrapper to .findOne({_id: ObjectId()})
which offloads burden of converting input string to ObjectId()
, but there is nothing much .find()
's can help on projection, You need to list all the fields as like given below :
/** .findById(inputString, projection) */
Blog.findById(req.params.id,
{
"category.display_name": 1,
"title": 1,
"description": 1,
"rate": 1,
"createdAt": 1,
"updatedAt": 1
})
/** Or */
Blog.findById(req.params.id,
{
"category._id": 0,
"category.name": 0,
})
Test : As playground can't use .findById
test it using .find()
: mongoplayground
But if you lean towards using aggregation you can do lot more in projection :
const mongoose = require('mongoose');
/* GET blog with id */
router.get("/:id", async (req, res, next) => {
try {
const blog = await Blog.aggregate([{$match : {_id : mongoose.Types.ObjectId(req.params.id)}},
{ $addFields: { category: { $map: { input: "$category", in: { display_name: "$$this.display_name" } } } } }
]);
res.send(blog);
} catch(ex) {
res.status(400).send(ex);
}
});
Test : mongoplayground
Note : This aggregation query iterates thru category
array and keep only display_name
in each object, Use this pipeline only if you need to exclude a lot of fields in category
objects. I would suggest to use second option in .findById()
cause you no need to convert string
to ObjectId()
and an aggregation pipeline just to exclude fields name
and _id
inside objects of category array.
Upvotes: 1
Reputation: 132
You could also create a schema for your Blog collection and then customize the toJSON method for deleting the properties you want to hide like this:
BlogSchema.methods.toJSON = function callback() {
const blog = this;
const blogObject = blog.toObject();
delete blogObject._id;
delete blogObject.name;
return blogObject;
};
I use this approach to hide sensible data such as passwords when sending back the users their info!
Upvotes: 2