I get a document from a mongodb which contains an array with comments for that document. In the comment is the _id of the user which wrote the comment.
I now need to get the username based on the _id of the user, but I'm running into several problems.
I have the following code which, obviously, doesn't work, but I hope it can give you an idea of what I'm trying to accomplish.
//MORE CODE... (No need to show this here, just a promise, some try catch and so on)
let article = await Article.findOne({_id:articleid})
for(var i = 0; i<=article.comment.length-1; i++){
User.findOne({_id:article.comment[i].user}).then((user)=>{
article.comment[i].username = user.username
})
}
return resolve(article)
I looked up several documentations but wasn't able to find a working solution. I tried using Promise.all, played around with a lot of async, await, tried to add a counter into the for-loop and resolve the promise after the loop finished but nothing worked so far.
This is what the article looks like in my db
{
"_id" : ObjectId("5c18c1cbc47e5e29d42e4b0e"),
"completed" : false,
"completedAt" : null,
"comment" : [
{
"_id" : ObjectId("5c18c95e328c8319ac07d817"),
"comment" : "This is a comment",
"rating" : [ ],
"user" : ObjectId("5c18b76e73236d2168eda2b4")
},
{
"_id" : ObjectId("5c18fb578de5741f20a4e2bd"),
"comment" : "Another comment",
"rating" : [ ],
"user" : ObjectId("5c18b76e73236d2168eda2b4")
}
]
}
I'm rather new to nodejs and mongodb aswell so I hope you can help a newbie like me.
Thank you for your Help
There are serveral approaches you can use here based on your convenience
Using async await
let article = await Article.findOne({ _id: articleid }).lean().exec()
await Promise.all(
article.comment.map(async(obj) => {
const user = await User.findOne({ _id: obj.user })
obj.username = user.username
})
)
console.log(article)
Using $lookup aggregation 3.6
Since mongodb has its own powerfull $lookup aggregation operator to join multiple collection and probably the better approach without any iteration
Article.aggregate([
{ "$match": { "_id": mongoose.Types.ObjectId(articleid) }},
{ "$unwind": "$comment" },
{ "$lookup": {
"from": "users",
"let": { "userId": "$comment.user" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$$userId", "$_id"] }}}
],
"as": "comment.user"
}},
{ "$unwind": "$comment.user" },
{ "$group": {
"_id": "$_id",
"comment": { "$push": "$comment" },
"completed": { "$first": "$completed" },
"completedAt": { "$first": "$completedAt" }
}}
])
Using $lookup aggregation 3.4
Article.aggregate([
{ "$match": { "_id": mongoose.Types.ObjectId(articleid) }},
{ "$unwind": "$comment" },
{ "$lookup": {
"from": "users",
"localField": "comment.user",
"foreignField": "_id",
"as": "comment.user"
}}
{ "$unwind": "$comment.user" },
{ "$group": {
"_id": "$_id",
"comment": { "$push": "$comment" },
"completed": { "$first": "$completed" },
"completedAt": { "$first": "$completedAt" }
}}
])
You can try like the following way
const d = {
"_id" : ObjectId("5c18c1cbc47e5e29d42e4b0e"),
"completed" : false,
"completedAt" : null,
"comment" : [
{
"_id" : ObjectId("5c18c95e328c8319ac07d817"),
"comment" : "This is a comment",
"rating" : [ ],
"user" : ObjectId("5c18b76e73236d2168eda2b4")
},
{
"_id" : ObjectId("5c18fb578de5741f20a4e2bd"),
"comment" : "Another comment",
"rating" : [ ],
"user" : ObjectId("5c18b76e73236d2168eda2b4")
}
]
}
d.comment.forEach( async (obj, index) => {
await new Promise((res) => {
obj.counter = index;
res();
})
});
console.log(d);
For reference please take a look on following link
Asycn/Await using forEach
Related
Here is my code, I have two collections called cart and product, First I want to take the id of the product from the cart and then from the product collection get the product details
collection cart
{
"_id" : ObjectId("62f8086e29c549f34ab89df7"),
"user" : ObjectId("62f3f8600e93c17d1c25c2ed"),
"product" : [
ObjectId("62f391b9482a375c4f83de8e"),
ObjectId("62f39121482a375c4f83de8d"),
ObjectId("62f39200482a375c4f83de8f")
]
}
collection product
{
"_id" : ObjectId("62f39121482a375c4f83de8d"),
"name" : "iphone 15",
"category" : "mobiles",
"price" : "125",
"description" : "fastest iphone"
}
{
"_id" : ObjectId("62f391b9482a375c4f83de8e"),
"name" : "OnePlus Nord",
"category" : "mobile",
"price" : "40000",
"description" : "budget phone of OnePlus"
}
{
"_id" : ObjectId("62f39200482a375c4f83de8f"),
"name" : "Samsung M33",
"category" : "mobile",
"price" : "25000",
"description" : "mid range mobile"
}
Here is the function for getting the details
getcartProducts:(userId)=>{
return new Promise(async(resolve,reject)=>{
let cartItem=await db.get().collection(collection.CART_COLLECTION).aggregate([
{
"$match":{user:objectId(userId)}
},
{
"$lookup":{
"from":collection.PRODUCT_COLLECTIONS,
"let":{"prodList":'$product'},
pipeline:[
{
"$match":{ "$expr":{ "$in":["_id","$$prodList"],
},
},
},
],
"as":"cartItems"
}
}
]
).toArray()
resolve(cartItem)
})
}
Finally the function is called
router.get('/cart',verifyLogin,async (req,res)=>{
let products=await userHelpers.getcartProducts(req.session.user._id)
console.log(products)
res.render('user/cart')
})
The output is: Showing null array in cartItems: []
[
{
_id: new ObjectId("62f8086e29c549f34ab89df7"),
user: new ObjectId("62f3f8600e93c17d1c25c2ed"),
product: [
new ObjectId("62f391b9482a375c4f83de8e"),
new ObjectId("62f39121482a375c4f83de8d"),
new ObjectId("62f39200482a375c4f83de8f"),
new ObjectId("62f39121482a375c4f83de8d")
],
cartItems: []
}
]
Use $_id instead of _id in $in operator. Mongo Playground
// This is the raw query
db.carts.aggregate([
{
"$match": {
"user": ObjectId("62f3f8600e93c17d1c25c2ed")
}
},
{
"$lookup": {
"from": "products",
"as": "cartItems",
"let": {
"prodList": "$product"
},
pipeline: [
{
"$match": {
"$expr": {
"$in": [
"$_id", // note the change here, use $ for matching field in lookup collection
"$$prodList" // use $$ for matching variable
],
},
},
},
],
}
}
])
Change the following line in your example code:
"$match":{ "$expr":{ "$in":["_id","$$prodList"],
To:
"$match":{ "$expr":{ "$in":["$_id","$$prodList"],
This aggregation pipeline cane be simplified to: Mongo Playground
[
{
"$match": {
"user": ObjectId("62f3f8600e93c17d1c25c2ed")
}
},
{
"$lookup": {
"from": "products",
"as": "cartItems",
"localField": "product",
"foreignField": "_id"
}
}
]
I have a query that is running fine, now i have requirement to filter some data that is inside array. I don't know how to do that. Below is my code. Please guide me where i am wrong.
Request data
[
'Online Casino/Betting',
'Other ',
'Prefer not to say ',
'Do you really care ? :) ',
'Spend time with friends'
]
Database Data
"interests" : [
{
"name" : "Computers/internet",
"_id" : ObjectId("60752406d8e7213e6b5306de"),
"id" : NumberInt(1)
},
{
"name" : "Astrology/Spiritualism",
"_id" : ObjectId("60752406d8e7213e6b5306df"),
"id" : NumberInt(3)
},
{
"name" : "Cars & motorbikes",
"_id" : ObjectId("60752406d8e7213e6b5306e0"),
"id" : NumberInt(2)
}
],
Query
if (filterData.interests != undefined && filterData.interests.length > 0) {
interests = {
interests: { $elemMatch: { $and: [{ name: filterData.interests }] } }
}
}
User.aggregate([
coordinatesCondition,
{
$match: {
$and: [
exerciseHabitsCondition,
interests
],
},
},
{
$sort: lastActivity,
},
{ $limit: skip + 12 },
{ $skip: skip },
{
$lookup: {
from: "favorites",
localField: "_id",
foreignField: "favorites.favoriteUserId",
as: "favUsers",
},
},
])
Any solution appreciated!
as per my understanding you want to match the result with interests in the req data.
I am sharing a simple update, that can work well for you.
if (filterData.interests != undefined && filterData.interests.length > 0) {
interestsQuery = {
'interests.name': { $in: filterData.interests } }
}
}
User.aggregate([
coordinatesCondition,
{
$match: {
$and: [
exerciseHabitsCondition,
interestsQuery
],
},
},
{
$sort: lastActivity,
},
])
After many many tries, I can't have a nice conditional aggregation of my collections.
I use two collections :
races which have a collection of reviews.
I need to obtain for my second pipeline only the reviews published.
I don't want to use a $project.
Is it possible to use only the $match ?
When I use localField, foreignField, it works perfect, but I need to filter only the published reviews.
I struggled so much on this, I don't understand why the let don't give me the foreignKey.
I tried : _id, $reviews, etc..
My $lookup looks like this :
{
$lookup: {
from: "reviews",
as: "reviews",
let: { reviewsId: "$_id" },
pipeline: [
{
$match: {
$expr: {
$and: [
// If I comment the next line, it give all the reviews to all the races
{ $eq: ["$_id", "$$reviewsId"] },
{ $eq: ["$is_published", true] }
]
}
}
}
]
// localField: "reviews",
// foreignField: "_id"
}
},
Example of a race :
{
"description":"Nice race",
"attendees":[
],
"reviews":[
{
"$oid":"5c363ddcfdab6f1d822d7761"
},
{
"$oid":"5cbc835926fa61bd4349a02a"
}
],
...
}
Example of a review :
{
"_id":"5c3630ac5d00d1dc26273dab",
"user_id":"5be89576a38d2b260bfc1bfe",
"user_pseudo":"gracias",
"is_published":true,
"likes":[],
"title":"Best race",
"__v":10,
...
}
I will become crazy soon :'(...
How to accomplish that ?
Your problem is this line:
{ $eq: ["$is_published", true] }
You are using this document _id field to match the reviews one.
The correct version looks like this:
(
[
{
"$unwind" : "$reviews"
},
{
"$lookup" : {
"from" : "reviews",
"as" : "reviews",
"let" : {
"reviewsId" : "$reviews"
},
"pipeline" : [
{
"$match" : {
"$expr" : {
"$and" : [
{
"$eq" : [
"$_id",
"$$reviewsId"
]
},
{ $eq: ["$is_published", true] }
]
}
}
}
]
}
}
],
);
and now if your want to restore the old structure add:
{
$group: {
_id: "$_id",
reviews: {$push: "$reviews"},
}
}
First you have to take correct field to get the data from the referenced collection i.e. reviews. And second you need to use $in aggregation operator as your reviews field is an array of ObjectIds.
db.getCollection('races').aggregate([
{ "$lookup": {
"from": "reviews",
"let": { "reviews": "$reviews" },
"pipeline": [
{ "$match": {
"$expr": { "$in": [ "$_id", "$$reviews" ] },
"is_published": true
}}
],
"as": "reviews"
}}
])
Suppose I have some MongoDB Event documents, each of which has a number of sessions which take place on different dates. We might represent this as:
db.events.insert([
{
_id: '5be9860fcb16d525543cafe1',
name: 'Past',
host: '5be9860fcb16d525543daff1',
sessions: [
{ date: new Date(Date.now() - 1e8 ) },
{ date: new Date(Date.now() + 1e8 ) }
]
}, {
_id: '5be9860fcb16d525543cafe2',
name: 'Future',
host: '5be9860fcb16d525543daff2',
sessions: [
{ date: new Date(Date.now() + 2e8) },
{ date: new Date(Date.now() + 3e8) }
]
}
]);
I'd like to find all Events which have not yet had their first session. So I'd like to find 'Future' but not 'Past'.
At the moment I'm using Mongoose and Express to do:
Event.aggregate([
{ $unwind: '$sessions' }, {
$group: {
_id: '$_id',
startDate: { $min: '$sessions.date' }
}
},
{ $sort:{ startDate: 1 } }, {
$match: { startDate: { $gte: new Date() } }
}
])
.then(result => Event.find({ _id: result.map(result => result._id) }))
.then(event => Event.populate(events, 'host'))
.then(events => res.json(events))
But I feel like I'm making heavy weather of this. Two hits on the database (three if you include the populate statement) and a big, complicated aggregate statement.
Is there a simpler way to do this? Ideally one which only involves one trip to the database.
You could use $reduce to fold the array and find if any of of the elements have a past session.
To illustrate this, consider running the following aggregate pipeline:
db.events.aggregate([
{ "$match": { "sessions.date": { "$gte": new Date() } } },
{ "$addFields": {
"hasPastSession": {
"$reduce": {
"input": "$sessions.date",
"initialValue": false,
"in": {
"$or" : [
"$$value",
{ "$lt": ["$$this", new Date()] }
]
}
}
}
} },
//{ "$match": { "hasPastSession": false } }
])
Based on the above sample, this will yield the following documents with the extra field
/* 1 */
{
"_id" : "5be9860fcb16d525543cafe1",
"name" : "Past",
"host" : "5be9860fcb16d525543daff1",
"sessions" : [
{
"date" : ISODate("2019-01-03T12:04:36.174Z")
},
{
"date" : ISODate("2019-01-05T19:37:56.174Z")
}
],
"hasPastSession" : true
}
/* 2 */
{
"_id" : "5be9860fcb16d525543cafe2",
"name" : "Future",
"host" : "5be9860fcb16d525543daff2",
"sessions" : [
{
"date" : ISODate("2019-01-06T23:24:36.174Z")
},
{
"date" : ISODate("2019-01-08T03:11:16.174Z")
}
],
"hasPastSession" : false
}
Armed with this aggregate pipeline, you can then leverage $expr and use the pipeline expression as your query in the find() method (or using the aggregate operation above but with the $match pipeline step at the end enabled) as
db.events.find(
{ "$expr": {
"$eq": [
false,
{ "$reduce": {
"input": "$sessions.date",
"initialValue": false,
"in": {
"$or" : [
"$$value",
{ "$lt": ["$$this", new Date()] }
]
}
} }
]
} }
)
which returns the document
{
"_id" : "5be9860fcb16d525543cafe2",
"name" : "Future",
"host" : "5be9860fcb16d525543daff2",
"sessions" : [
{
"date" : ISODate("2019-01-06T23:24:36.174Z")
},
{
"date" : ISODate("2019-01-08T03:11:16.174Z")
}
]
}
You don't need to use $unwind and $group to find the $min date from the array. You can directly use $min to extract the min date from the session array and then use $lookup to populate the host key
db.events.aggregate([
{ "$match": { "sessions.date": { "$gte": new Date() }}},
{ "$addFields": { "startDate": { "$min": "$sessions.date" }}},
{ "$match": { "startDate": { "$gte": new Date() }}},
{ "$lookup": {
"from": "host",
"localField": "host",
"foreignField": "_id",
"as": "host"
}},
{ "$unwind": "$host" }
])
Is it possible you can just reach into the sessions of each event, and pull back each event where all session dates are only in the future? Something like this? Might need tweaking..
db.getCollection("events").aggregate(
[
{$match:{'$and':
[
{'sessions.date':{'$gt': new Date()}},
{'sessions.date':{ '$not': {'$lt': new Date()}}}
]
}}
]
);
This is my appointment collection:
{ _id: ObjectId("518ee0bc9be1909012000002"), date: ISODate("2013-05-13T22:00:00Z"), patient:ObjectId("518ee0bc9be1909012000002") }
{ _id: ObjectId("518ee0bc9be1909012000002"), date: ISODate("2013-05-13T22:00:00Z"), patient:ObjectId("518ee0bc9be1909012000002") }
{ _id: ObjectId("518ee0bc9be1909012000002"), date: ISODate("2013-05-13T22:00:00Z"), patient:ObjectId("518ee0bc9be1909012000002") }
I used aggregate to get the following result
{date: ISODate("2013-05-13T22:00:00Z"),
patients:[ObjectId("518ee0bc9be1909012000002"),ObjectId("518ee0bc9be1909012000002"),ObjectId("518ee0bc9be1909012000002")] }
like this:
Appointments.aggregate([
{$group: {_id: '$date', patients: {$push: '$patient'}}},
{$project: {date: '$_id', patients: 1, _id: 0}}
], ...)
How can I populate the patient document
I trued this but it doesn't work ... Appointments.find({}).populate("patient").aggregate....
In other words, can i use populate and aggregate at the same statement
any help please
With the latest version of mongoose (mongoose >= 3.6), you can but it requires a second query, and using populate differently. After your aggregation, do this:
Patients.populate(result, {path: "patient"}, callback);
See more at the Mongoose API and the Mongoose docs.
Edit: Looks like there's a new way to do it in the latest Mongoose API (see the above answer here: https://stackoverflow.com/a/23142503/293492)
Old answer below
You can use $lookup which is similar to populate.
In an unrelated example, I use $match to query for records and $lookup to populate a foreign model as a sub-property of these records:
Invite.aggregate(
{ $match: {interview: req.params.interview}},
{ $lookup: {from: 'users', localField: 'email', foreignField: 'email', as: 'user'} }
).exec( function (err, invites) {
if (err) {
next(err);
}
res.json(invites);
}
);
You have to do it in two, not in one statement.
In async await scenario, make sure await until populate.
const appointments = await Appointments.aggregate([...]);
await Patients.populate(appointments, {path: "patient"});
return appointments;
or (if you want to limit)
await Patients.populate(appointments, {path: "patient", select: {_id: 1, fullname: 1}});
You can do it in one query like this:
Appointments.aggregate([{
$group: {
_id: '$date',
patients: {
$push: '$patient'
}
}
},
{
$project: {
date: '$_id',
patients: 1,
_id: 0
}
},
{
$lookup: {
from: "patients",
localField: "patient",
foreignField: "_id",
as: "patient_doc"
}
}
])
populate basically uses $lookup under the hood.
in this case no need for a second query.
for more details check MongoDB aggregation lookup
Perform a Join with $lookup
A collection orders contains the following documents:
{ "_id" : 1, "item" : "abc", "price" : 12, "quantity" : 2 }
{ "_id" : 2, "item" : "jkl", "price" : 20, "quantity" : 1 }
{ "_id" : 3 }
Another collection inventory contains the following documents:
{ "_id" : 1, "sku" : "abc", description: "product 1", "instock" : 120 }
{ "_id" : 2, "sku" : "def", description: "product 2", "instock" : 80 }
{ "_id" : 3, "sku" : "ijk", description: "product 3", "instock" : 60 }
{ "_id" : 4, "sku" : "jkl", description: "product 4", "instock" : 70 }
{ "_id" : 5, "sku": null, description: "Incomplete" }
{ "_id" : 6 }
The following aggregation operation on the orders collection joins the documents from orders with the documents from the inventory collection using the fields item from the orders collection and the sku field from the inventory collection:
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}
])
The operation returns the following documents:
{
"_id" : 1,
"item" : "abc",
"price" : 12,
"quantity" : 2,
"inventory_docs" : [
{ "_id" : 1, "sku" : "abc", description: "product 1", "instock" : 120 }
]
}
{
"_id" : 2,
"item" : "jkl",
"price" : 20,
"quantity" : 1,
"inventory_docs" : [
{ "_id" : 4, "sku" : "jkl", "description" : "product 4", "instock" : 70 }
]
}
{
"_id" : 3,
"inventory_docs" : [
{ "_id" : 5, "sku" : null, "description" : "Incomplete" },
{ "_id" : 6 }
]
}
Reference $lookup
Short answer:
You can't.
Long answer:
In the Aggregation Framework, the returned fields are built by you, and you're able to "rename" document properties.
What this means is that Mongoose can't identify that your referenced documents will be available in the final result.
The best thing you can do in such a situation is populate the field you want after the query has returned. Yes, that would result in two DB calls, but it's what MongoDB allows us to do.
Somewhat like this:
Appointments.aggregate([ ... ], function( e, result ) {
if ( e ) return;
// You would probably have to do some loop here, as probably 'result' is array
Patients.findOneById( result.patient, function( e, patient ) {
if ( e ) return;
result.patient = patient;
});
});
domain.Farm.aggregate({
$match: {
"_id": mongoose.Types.ObjectId(farmId)
}
}, {
$unwind: "$SelfAssessment"
}, {
$match: {
"SelfAssessment.questionCategoryID": QuesCategoryId,
"SelfAssessment.questionID": quesId
}
},function(err, docs) {
var options = {
path: 'SelfAssessment.actions',
model: 'FarmAction'
};
domain.Farm.populate(docs, options, function (err, projects) {
callback(err,projects);
});
});
results i got action model populate
{ "error": false, "object": [
{
"_id": "57750cf6197f0b5137d259a0",
"createdAt": "2016-06-30T12:13:42.299Z",
"updatedAt": "2016-06-30T12:13:42.299Z",
"farmName": "abb",
"userId": "57750ce2197f0b5137d2599e",
"SelfAssessment": {
"questionName": "Aquatic biodiversity",
"questionID": "3kGTBsESPeYQoA8ae2Ocoy",
"questionCategoryID": "5aBe7kuYWIEoyqWCWcAEe0",
"question": "Waterways protected from nutrient runoff and stock access through fencing, buffer strips and off stream watering points",
"questionImage": "http://images.contentful.com/vkfoa0gk73be/4pGLv16BziYYSe2ageCK04/6a04041ab3344ec18fb2ecaba3bb26d5/thumb1_home.png",
"_id": "57750cf6197f0b5137d259a1",
"actions": [
{
"_id": "577512c6af3a87543932e675",
"createdAt": "2016-06-30T12:38:30.314Z",
"updatedAt": "2016-06-30T12:38:30.314Z",
"__v": 0,
"Evidence": [],
"setReminder": "",
"description": "sdsdsd",
"priority": "High",
"created": "2016-06-30T12:38:30.312Z",
"actionTitle": "sdsd"
}
],
"answer": "Relevant"
},
"locations": []
} ], "message": "", "extendedMessage": "", "timeStamp": 1467351827979 }
I see that there are many answers, I am new to mongoldb and I would like to share my answer too.
I am using aggregate function along with lookup to populate the patients.
To make it easy to read I have changed the names of the collections and fields.
Hope it's helpful.
DB:
db={
"appointmentCol": [
{
_id: ObjectId("518ee0bc9be1909012000001"),
date: ISODate("2013-05-13T22:00:00Z"),
patientId: ObjectId("518ee0bc9be1909012000001")
},
{
_id: ObjectId("518ee0bc9be1909012000002"),
date: ISODate("2013-05-13T22:00:00Z"),
patientId: ObjectId("518ee0bc9be1909012000002")
},
{
_id: ObjectId("518ee0bc9be1909012000003"),
date: ISODate("2013-05-13T22:00:00Z"),
patientId: ObjectId("518ee0bc9be1909012000003")
}
],
"patientCol": [
{
"_id": ObjectId("518ee0bc9be1909012000001"),
"name": "P1"
},
{
"_id": ObjectId("518ee0bc9be1909012000002"),
"name": "P2"
},
{
"_id": ObjectId("518ee0bc9be1909012000003"),
"name": "P3"
},
]
}
Aggregate Query using lookup:
db.appointmentCol.aggregate([
{
"$lookup": {
"from": "patientCol",
"localField": "patientId",
"foreignField": "_id",
"as": "patient"
}
}
])
Output:
[
{
"_id": ObjectId("518ee0bc9be1909012000001"),
"date": ISODate("2013-05-13T22:00:00Z"),
"patient": [
{
"_id": ObjectId("518ee0bc9be1909012000001"),
"name": "P1"
}
],
"patientId": ObjectId("518ee0bc9be1909012000001")
},
{
"_id": ObjectId("518ee0bc9be1909012000002"),
"date": ISODate("2013-05-13T22:00:00Z"),
"patient": [
{
"_id": ObjectId("518ee0bc9be1909012000002"),
"name": "P2"
}
],
"patientId": ObjectId("518ee0bc9be1909012000002")
},
{
"_id": ObjectId("518ee0bc9be1909012000003"),
"date": ISODate("2013-05-13T22:00:00Z"),
"patient": [
{
"_id": ObjectId("518ee0bc9be1909012000003"),
"name": "P3"
}
],
"patientId": ObjectId("518ee0bc9be1909012000003")
}
]
Playground:
mongoplayground.net
I used lookup instead, and it worked well. See the code snipped below.
Post.aggregate([
{
$group: {
// Each `_id` must be unique, so if there are multiple
// posts with the same category, MongoDB will increment `count`.
_id: '$category',
count: { $sum: 1 }
}
},
//from: is collection name in MongoDB, localField are primary and foreign keys in Model.
{$lookup: {from: 'categories', localField: '_id', foreignField:'_id', as: 'category'}}
]).then(categoryCount => {
console.log(categoryCount);
let json = [];
categoryCount.forEach(cat => {
console.log(json);
});