Getting null array as result in mongodb aggregate pipeline - node.js

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"
}
}
]

Related

How can i do elemMatch inside array using mongodb?

I have below user details in my bookings collection
{
"_id" : ObjectId("609a382b589346973c84c6fe"),
"Name" : "abc",
"UserId":1
"Status" : "Pending",
"BookingData" : {
"Date" : ISODate("2021-04-30T04:00:00.000Z"),
"info" : [],
"BookingDataMethod" : "avf",
"Message" : null,
"products" : [
{
"_id" : ObjectId("60a4e92775e5de3570578820"),
"ProductName" : "Test1",
"ProductID" : ObjectId("60a4e92475e5de357057880a"),
"IsDeliveryFailed" : "Yes"
},
{
"_id" : ObjectId("60a4e92775e5de357057881f"),
"ProductName" : "Test2",
"ProductID" : ObjectId("60a4e92475e5de357057880d")
}
],
}
}
I have prepared a query for the below conditions and when I run the below query I should get the "UserId":1 documents but I got 0 records
condition 1: products should not be null
condition 2: ProductID should exist in the products array and should not be null
condition 3: IsDeliveryFailed should not be "Yes"
Based on the above user only one product got delivery failed(IsDeliveryFailed": "Yes") so when I run this query it should return "UserId":1 document. if both products "IsDeliveryFailed": "Yes" then
we should not get this user
Query
db.getCollection('bookings').find({
"$and": [
{ "BookingData.products": { $ne: [] } },
{ "BookingData.products": {"$elemMatch":{ "ProductID": { "$exists": true ,$ne: null } }} },
{ "BookingData.products": {"$elemMatch":{ "IsDeliveryFailed": { $ne: 'Yes' } }} }
]
})
Could someone please tell me the issue on the above query or please help me to prepare a query for the above condition?
I think you can do it with aggregations
db.collection.aggregate([
{
$match: {
"BookingData.products": { "$exists": true }
}
},
{
$set: {
"BookingData.products": {
"$filter": {
"input": "$BookingData.products",
"cond": {
$and: [
{ $ne: [ "$$this.ProductID", undefined ] },
{ $ne: [ "$$this._id", null ] },
{ $ne: [ "$$this.IsDeliveryFailed", "Yes" ] }
]
}
}
}
}
},
{
$match: {
$expr: {
$ne: [ "$BookingData.products", [] ]
}
}
}
])
Working Mongo playground

MongoDB : add New Field to existing sub document after $look stage or merge lookup response to main document

I want new field "isActive" inside modifierStatus sub document which is coming from modifieritems collection.
modifieritems collection :
{
"_id" : ObjectId("5e6a5a0e6d40624b12453a67"),
"modifierName" : "xxx",
"isActive" : 1
}
{
"_id" : ObjectId("5e6a5a0e6d40624b12453a6a"),
"modifierName" : "yyy",
"isActive" : 0
}
favoritedrinks collection :
{
"alcoholName" : "whiskey",
"modifierList" : [{
"_id" : ObjectId("5e6a5a0e6d40624b12453a61"),
"modifierId" : ObjectId("5e6a5a0e6d40624b12453a67"),
"modifierName" : "xxx",
}
{
"_id" : ObjectId("5e6a5a0e6d40624b12453a66"),
"modifierId" : ObjectId("5e6a5a0e6d40624b12453a6a"),
"modifierName" : "yyy",
}]
}
my query is :
db.getCollection('favoritedrinks').aggregate([
{ "$sort": { "alcoholName": 1 } },
{"$lookup": {
"from": "modifieritems",
localField: 'modifierList.modifierId',
foreignField: '_id',
as: 'modifier'
}},
{
$project:{
"alcoholName" : "$alcoholName",
"modifierStatus":"$modifier",
}
},
]);
But my expected result :
{
"alcoholName" : "Whiskey",
"modifierStatus" : [
{
"_id" : ObjectId("5e6a5a0e6d40624b12453a61"),
"modifierId" : ObjectId("5e6a5a0e6d40624b12453a67"),
"modifierName" : "xxx",
"isActive" : 1,
},
{
"_id" : ObjectId("5e6a5a0e6d40624b12453a66"),
"modifierId" : ObjectId("5e6a5a0e6d40624b12453a6a"),
"modifierName" : "yyy",
"isActive" : 0,
}
]
}
anyone please help me?
Try this query :
Update with new requirement :
db.favoritedrinks.aggregate([
{
"$sort": {
"alcoholName": 1
}
},
{
"$lookup": {
"from": "modifieritems",
localField: "modifierList.modifierId",
foreignField: "_id",
as: "modifierStatus"
}
},
{
$addFields: {
modifierStatus: {
$map: {
input: "$modifierList",
as: "m",
in: {
$mergeObjects: [
{
$arrayElemAt: [ /** As filter would only get one object (cause you'll have only one matching doc in modifieritems coll for each "modifierList.modifierId", So getting first element out of array, else you need to take this array into an object & merge that field to particular object of 'modifierList') */
{
$filter: {
input: "$modifierStatus",
cond: {
$eq: [
"$$this._id",
"$$m.modifierId"
]
}
}
},
0
]
},
"$$m"
]
}
}
}
}
},
{
$project: {
modifierStatus: 1,
alcoholName: 1,
_id: 0
}
}
])
Test : MongoDB-Playground
Old :
db.favoritedrinks.aggregate([
{
"$sort": {
"alcoholName": 1
}
},
{
$lookup: {
from: "modifieritems",
let: {
id: "$modifierList.modifierId"
},
pipeline: [
{
$match: { $expr: { $in: ["$_id", "$$id"] } }
},
/** Adding a new field modifierId(taken from _id field of modifieritems doc)
* to each matched document from modifieritems coll */
{
$addFields: {
modifierId: "$_id"
}
}
],
as: "modifierStatus"
}
},
/** By mentioning 0 to particular fields to remove them & retain rest all other fields */
{
$project: {
modifierList: 0,
_id: 0
}
}
])
Test : MongoDB-Playground
When you want $project to include a field's current value while keeping the same field name, you need only specify :1. When you use "$field" you are explicitly setting the value, which will overwrite any existing value.
Try making your projection:
{
$project:{
"alcoholName" : 1,
"modifier.isActive": 1,
"modifier.modifierName": 1
}
}

How to get inbox's coversation's users and its last messages in NodeJS with MongoDb

MongoDb User Collection
Think that you are User 1. In the inbox page, I want to get the conversation's last message. I may sent the last message or receive the last message from a user. The last message will be shown in inbox like this:
Query Result Shold Be Like This
[
{
"_id": "user2",
"username": "user2",
"lastMessage": "3"
},
{
"_id": "user3",
"username": "user3",
"lastMessage": "2"
}
]
User 1 Document on MongoDb
{
"_id" : ObjectId("user1"),
"username" : "user1",
"inbox" : [
{
"from" : {
"user" : {
"id" : ObjectId("user2")
}
},
"message" : "1",
"received_at" : ISODate("2019-04-27")
},
{
"from" : {
"user" : {
"id" : ObjectId("user3")
}
},
"message" : "2",
"received_at" : ISODate("2019-05-1")
}
]
}
User 2 Document on MongoDb
{
"_id" : ObjectId("user2"),
"username" : "user2",
"inbox" : [
{
"from" : {
"user" : {
"id" : ObjectId("user1")
}
},
"message" : "3",
"received_at" : ISODate("2019-04-29")
}
]
}
User 3 Document on MongoDb
{
"_id" : ObjectId("user3"),
"username" : "user3",
"inbox" : [
{
"from" : {
"user" : {
"id" : ObjectId("user1")
}
},
"message" : "4",
"received_at" : ISODate("2019-04-30")
}
]
}
What query I have to use for this problem ?
You can use below aggregation:
db.col.aggregate([
{
$unwind: "$inbox"
},
{
$addFields: {
participants: [ "$_id", "$inbox.from.user.id" ]
}
},
{
$match: { participants: "user1" }
},
{
$addFields: {
participants: {
$filter: {
input: "$participants",
cond: {
$ne: [ "$$this", "user1" ]
}
}
}
}
},
{
$unwind: "$participants"
},
{
$sort: { "inbox.received_at": -1 }
},
{
$group: {
_id: "$participants",
lastMessage: { $first: "$inbox.message" }
}
}
])
The challenge here is that you need to analyse an array which might contain for instance [user1, user2] or [user2, user1] and both should be considered as the same grouping key.
To do that you can introduce participants array in order to filter out all the messages that do not belong to user1 and then remove user1 from that array (using $filter) so that you can group by second user.
The point is that you run $unwind to get single document per message and then $sort them so that you can run $group with $first to get the most recent one
Mongo Playground

NodeJS callback after multiple async-functions in for-loop

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

how to use populate and aggregate in same statement?

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);
});

Resources