const categorySlug = req.query.category;
const category = await Category.findOne({ slug: categorySlug });
const children = await Category.find({ parent: category._id });
Is it possible to combine those two queries into one? I tried doing something like
const children = await Category.aggregate([
{
$match: {
slug: categorySlug,
},
},
{
// somehow use the _id field from document fetched in first stage
}
]);
But I have no idea if this is even possible.
EDIT:
const document = await Category.aggregate([
{
$match: {
slug: categorySlug,
},
},
{
$lookup: {
from: 'categories',
localField: '_id',
foreignField: 'parent',
as: 'children',
},
},
]);
const desiredResult = document[0].children;
This works but is there a way to do this fully in the aggregation pipeline? Basically I want to get ONLY the "children" array, nothing else.
EDIT2: The result of above query is this array with an object which has an array of objects
[
{
"_id": "6051cacf8f974b3104715ec4",
"parent": null,
"products": [
"60515150d6ac5e08a4e796b4",
"6051b8b0f8c80835480b28d3"
],
"isParent": true,
"name": "Electronics",
"description": "All kinds of devices",
"createdAt": "2021-03-17T09:24:31.365Z",
"updatedAt": "2021-03-20T20:42:50.699Z",
"slug": "electronics",
"__v": 4,
"children": [
{
"_id": "6051cb588f974b3104715ec5",
"parent": "6051cacf8f974b3104715ec4",
"products": [],
"isParent": true,
"name": "Cameras",
"description": "description",
"createdAt": "2021-03-17T09:26:48.220Z",
"updatedAt": "2021-03-17T09:29:35.608Z",
"slug": "cameras",
"__v": 0
},
{
"_id": "6051cb5f8f974b3104715ec6",
"parent": "6051cacf8f974b3104715ec4",
"products": [
"60515150d6ac5e08a4e796b4",
"6051b8b0f8c80835480b28d3"
],
"isParent": true,
"name": "Computers",
"description": "description",
"createdAt": "2021-03-17T09:26:55.364Z",
"updatedAt": "2021-03-20T20:42:50.779Z",
"slug": "computers",
"__v": 4
},
{
"_id": "6051cb6f8f974b3104715ec7",
"parent": "6051cacf8f974b3104715ec4",
"products": [],
"isParent": false,
"name": "Car Electronics",
"description": "description",
"createdAt": "2021-03-17T09:27:11.108Z",
"updatedAt": "2021-03-17T09:27:11.108Z",
"slug": "car-electronics",
"__v": 0
},
{
"_id": "6051cb768f974b3104715ec8",
"parent": "6051cacf8f974b3104715ec4",
"products": [],
"isParent": false,
"name": "TV",
"description": "description",
"createdAt": "2021-03-17T09:27:18.422Z",
"updatedAt": "2021-03-17T09:27:18.422Z",
"slug": "tv",
"__v": 0
}
]
}
]
What I want to get is the whole "children" array inside that object
[
{
"_id": "6051cb588f974b3104715ec5",
"parent": "6051cacf8f974b3104715ec4",
"products": [],
"isParent": true,
"name": "Cameras",
"description": "description",
"createdAt": "2021-03-17T09:26:48.220Z",
"updatedAt": "2021-03-17T09:29:35.608Z",
"slug": "cameras",
"__v": 0
},
{
"_id": "6051cb5f8f974b3104715ec6",
"parent": "6051cacf8f974b3104715ec4",
"products": [
"60515150d6ac5e08a4e796b4",
"6051b8b0f8c80835480b28d3"
],
"isParent": true,
"name": "Computers",
"description": "description",
"createdAt": "2021-03-17T09:26:55.364Z",
"updatedAt": "2021-03-20T20:42:50.779Z",
"slug": "computers",
"__v": 4
},
{
"_id": "6051cb6f8f974b3104715ec7",
"parent": "6051cacf8f974b3104715ec4",
"products": [],
"isParent": false,
"name": "Car Electronics",
"description": "description",
"createdAt": "2021-03-17T09:27:11.108Z",
"updatedAt": "2021-03-17T09:27:11.108Z",
"slug": "car-electronics",
"__v": 0
},
{
"_id": "6051cb768f974b3104715ec8",
"parent": "6051cacf8f974b3104715ec4",
"products": [],
"isParent": false,
"name": "TV",
"description": "description",
"createdAt": "2021-03-17T09:27:18.422Z",
"updatedAt": "2021-03-17T09:27:18.422Z",
"slug": "tv",
"__v": 0
}
]
Like I mentioned this can be done with const desiredResult = document[0].children; but I would like to do this in the query itself.
EDIT3: It seems that aggregate will always return an array of documents even if there is only one document and I can choose how the documents will look like, however I can't only return a field itself, without the document. That's fine for what I'm trying to do - I can assign that document (or a specific field of it) to a variable.
Use https://mongoosejs.com/docs/populate.html
MongoDB has the join-like $lookup aggregation operator in versions >= 3.2. Mongoose has a more powerful alternative called populate(), which lets you reference documents in other collections.
Population is the process of automatically replacing the specified paths in the document with document(s) from other collection(s). We may populate a single document, multiple documents, a plain object, multiple plain objects, or all objects returned from a query. Let's look at some examples.
get the full array
{ $project: { _id:0, children: 1 } }
Use this for getting 1st element
Extract the first element from the array in the aggregation project pipeline
{ $project: { _id:0, children: { $arrayElemAt: [ "$children", 0 ] } } }
if(document && document[0] && document[0].children) { // your logic }
$arrayElemAt
$project
combined query
const document = await Category.aggregate([
{
$match: {
slug: categorySlug,
},
},
{
$lookup: {
from: 'categories',
localField: '_id',
foreignField: 'parent',
as: 'children',
},
},
{ $project: { _id:0, children: { $arrayElemAt: [ "$children", 0 ] } } }
]);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With