Get Count of specific field mongodb - mongodb

I am using below query to get combined data from users and project collections:
db.collection.aggregate([
{
"$group": {
"_id": "$userId",
"projectId": { "$push": "$projectId" }
}
},
{
"$lookup": {
"from": "users",
"let": { "userId": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$userId" ] }}},
{ "$project": { "firstName": 1 }}
],
"as": "user"
}
},
{ "$unwind": "$user" },
{
"$lookup": {
"from": "projects",
"let": { "projectId": "$projectId" },
"pipeline": [
{ "$match": { "$expr": { "$in": [ "$_id", "$$projectId" ] }}},
{ "$project": { "projectName": 1 }}
],
"as": "projects"
}
}
])
and it results like below:
[
{
"_id": "5c0a29e597e71a0d28b910aa",
"projectId": [
"5c0a2a8897e71a0d28b910ac",
"5c0a4083753a321c6c4ee024"
],
"user": {
"_id": "5c0a29e597e71a0d28b910aa",
"firstName": "Amit"
},
"projects": [
{
"_id": "5c0a2a8897e71a0d28b910ac",
"projectName": "LN-PM"
},
{
"_id": "5c0a4083753a321c6c4ee024",
"projectName": "fallbrook winery"
}
]
},
{
"_id": "5c0a29c697e71a0d28b910a9",
"projectId": [
"5c0a4083753a321c6c4ee024"
],
"user": {
"_id": "5c0a29c697e71a0d28b910a9",
"firstName": "Rajat"
},
"projects": [
{
"_id": "5c0a4083753a321c6c4ee024",
"projectName": "fallbrook winery"
}
]
}
]
Now i have another table "Worksheets" and want to include hours field in projects Array, which will be calculated from the worksheets table by specifying the projectId which is _id in the projects array. It will be find in worksheet table and hours will be incremented how many times this _id has in worksheets table. Below is my worksheet collection:
{
"_id" : ObjectId("5c0a4efa91b5021228681f7a"),
"projectId" : ObjectId("5c0a4083753a321c6c4ee024"),
"hours" : 8,
"userId" : ObjectId("5c0a29c697e71a0d28b910a9"),
"__v" : 0
}
{
"_id" : ObjectId("5c0a4f4191b5021228681f7c"),
"projectId" : ObjectId("5c0a2a8897e71a0d28b910ac"),
"hours" : 6,
"userId" : ObjectId("5c0a29e597e71a0d28b910aa"),
"__v" : 0
}
The result will look like below:
{
"_id": "5c0a29c697e71a0d28b910a9",
"projectId": [
"5c0a4083753a321c6c4ee024"
],
"user": {
"_id": "5c0a29c697e71a0d28b910a9",
"firstName": "Rajat"
},
"projects": [
{
"_id": "5c0a4083753a321c6c4ee024",
"projectName": "fallbrook winery",
"hours":8
}
]
}

You can use below aggregation
$lookup 3.6 nested syntax allows you to join nested collection inside the $lookup pipeline. You can perform all the aggregation inside the nested $lookup pipline
db.collection.aggregate([
{ "$group": {
"_id": "$userId",
"projectId": { "$push": "$projectId" }
}},
{ "$lookup": {
"from": "users",
"let": { "userId": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$userId" ] }}},
{ "$project": { "firstName": 1 }}
],
"as": "user"
}},
{ "$unwind": "$user" },
{ "$lookup": {
"from": "projects",
"let": { "projectId": "$projectId" },
"pipeline": [
{ "$match": { "$expr": { "$in": [ "$_id", "$$projectId" ] }}},
{ "$lookup": {
"from": "worksheets",
"let": { "projectId": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$projectId", "$$projectId" ] }}},
{ "$group": {
"_id": "$projectId",
"totalHours": { "$sum": "$hours" }
}}
],
"as": "workHours"
}}
{ "$project": {
"projectName": 1,
"hours": { "$arrayElemAt": ["$workHours.totalHours", 0] }
}}
],
"as": "projects"
}}
])

Related

$lookup in nested array

I need a MongoDB query to return the aggregation result from a collection of events, users and confirmations.
db.events.aggregate([
{
"$match": {
"_id": "1"
}
},
{
"$lookup": {
"from": "confirmations",
"as": "confirmations",
"let": {
"eventId": "$_id"
},
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$eventId",
"$$eventId"
]
}
}
},
{
"$lookup": {
"from": "users",
"as": "user",
"let": {
"userId": "$confirmations.userId"
},
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$_id",
"$$userId"
]
}
}
},
]
},
},
]
}
}
])
Desired
[
{
"_id": "1",
"confirmations": [
{
"_id": "1",
"eventId": "1",
"user": {
"_id": "1",
"name": "X"
},
"userId": "1"
},
{
"_id": "2",
"eventId": "1",
"user": {
"_id": "2",
"name": "Y"
},
"userId": "2"
}
],
"title": "foo"
}
]
Everything works except the embedded user in confirmations array. I need the output to show the confirmations.user, not an empty array.
Playgound: https://mongoplayground.net/p/jp49FW59WCv
You made mistake in variable declaration of inner $lookup. Try this Solution:
db.events.aggregate([
{
"$match": {
"_id": "1"
}
},
{
$lookup: {
from: "confirmations",
let: { "eventId": "$_id" },
pipeline: [
{
$match: {
"$expr": {
$eq: ["$eventId", "$$eventId"]
}
}
},
{
$lookup: {
from: "users",
let: { "userId": "$userId" },
pipeline: [
{
$match: {
$expr: {
$eq: ["$_id", "$$userId"]
}
}
}
],
as: "user"
}
},
{ $unwind: "$user" }
],
as: "confirmations"
}
}
])
Also instead of $unwind of user inside inner $lookup you can use:
{
$addFields: {
user: { $arrayElemAt: ["$user", 0] }
}
}
since $unwind will not preserve empty results from previous stage by default.

Populate specific fields in $lookup

I am using aggregate to group and populate the result like below:
{
"$group": {
"_id": "$userId",
"projectId": { "$push": "$projectId" }
}
},
{
"$lookup": {
"from": "users",
"localField": "_id",
"foreignField": "_id",
"as": "user"
}
},
{ $unwind:"$user" },
{
"$lookup": {
"from": "projects",
"localField": "projectId",
"foreignField": "_id",
"as": "projects"
}
}
But i want to populate specific fields from that result For this I tried
$project, But it combining projectId into one array and projectName into another array.Below is my result json:
[
{
"_id": "5c0a29e597e71a0d28b910aa",
"projectId": [
"5c0a2a8897e71a0d28b910ac",
"5c0a4083753a321c6c4ee024"
],
"user": {
"_id": "5c0a29e597e71a0d28b910aa",
"firstName": "Amit"
"lastName": "kumar",
"type": "developer",
"status": "active"
},
"projects": [
{
"_id": "5c0a2a8897e71a0d28b910ac",
"skypeId": "",
"projectName": "LN-PM",
"status": "ongoing",
"assignId": "5c0a2a0a97e71a0d28b910ab"
},
{
"_id": "5c0a4083753a321c6c4ee024",
"skypeId": "",
"status": "pending",
"assignId": "5c0a2a0a97e71a0d28b910ab"
}
]
}
]
Now i want to get the only "firstName and _id" field from user field and "projectName and _id" field from the projects field
You can use below aggregation with mongodb 3.6 and above
With the newer $lookup syntax you can use $projection inside the $lookup pipeline
db.collection.aggregate([
{ "$group": {
"_id": "$userId",
"projectId": { "$push": "$projectId" }
}},
{ "$lookup": {
"from": "users",
"let": { "userId": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$userId" ] }}},
{ "$project": { "firstName": 1 }}
],
"as": "user"
}},
{ "$unwind": "$user" },
{ "$lookup": {
"from": "projects",
"let": { "projectId": "$projectId" },
"pipeline": [
{ "$match": { "$expr": { "$in": [ "$_id", "$$projectId" ] }}},
{ "$project": { "projectName": 1 }}
],
"as": "projects"
}}
])

MongoDB: Get all $matched elements individually from an array

I'm trying to get all matched elements individually, here is the sample data and the query.
// json
[
{
"name": "Mr Cool",
"ican": [
{
"subcategory": [
{
"id": "5bffdba824488b182ec86f8d", "name": "Cricket"
},
{
"id": "5bffdba824488b182ec86f8c", "name": "Footbal"
}
],
"category": "5bffdba824488b182ec86f88",
"name": "Sports"
}
]
}
]
// query
db.collection.aggregate([
{
"$match": {
"ican.subcategory.name": { $in: ["Cricket","Football"] }
}
},
{
"$project": { "_id": 1, "name": 1, }
}
])
I'm getting the combined result, I need the individual match record. I tried $all and $elementMatch but getting the same response. how can I get the results as below. I'm using $aggregate because I will be using $geoNear pipeline for getting the nearby users.
// current result
[
{
"_id": ObjectId("5a934e000102030405000000"),
"name": "Mr Cool"
}
]
// expected result
[
{
"_id": ObjectId("5a934e000102030405000000"),
"name": "Mr Cool",
"subcategory: "Cricket"
},
{
"_id": ObjectId("5a934e000102030405000000"),
"name": "Mr Cool",
"subcategory: "Footbal"
}
]
Thank you
Try this Mongo Playground
db.col.aggregate([
{"$unwind" : "$ican"},
{"$unwind" : "$ican.subcategory"},
{"$match" : {"ican.subcategory.name": { "$in": ["Cricket","Football"] }}},
{"$group" : {"_id" : null,"data" : {"$push" : {"_id" : "$_id","name" : "$name","subcategory" : "$ican.subcategory.name"}}}},
{"$unwind" : "$data"},
{"$replaceRoot" : {"newRoot" : "$data"}}
])
You can use below aggregation without the $unwind and for better performance
db.collection.aggregate([
{ "$match": { "ican.subcategory.name": { "$in": ["Cricket","Football"] }}},
{ "$project": {
"ican": {
"$reduce": {
"input": "$ican",
"initialValue": [],
"in": {
"$concatArrays": [
{ "$filter": {
"input": {
"$map": {
"input": "$$this.subcategory",
"as": "s",
"in": { "name": "$name", "subcategory": "$$s.name" }
}
},
"as": "fil",
"cond": { "$in": ["$$fil.subcategory", ["Football"]] }
}},
"$$value"
]
}
}
}
}},
{ "$unwind": "$ican" },
{ "$replaceRoot": { "newRoot": "$ican" }}
])

Populate to the deep level using $lookup mongodb

I am using $lookup to join two collections and get data from below query:
let condition = {status:{$ne:config.PROJECT_STATUS.completed}, assignId:mongoose.Types.ObjectId(req.params.id)};
Project.aggregate([
{
$match: condition
},
{
"$group":{
"_id": "$_id"
}
},
{
"$lookup": {
"from": "worksheets",
"let": { "projectId": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$projectId", "$$projectId" ] }}},
{ "$group": {_id:"$projectId", totalHours:{"$sum": "$hours"}}},
{
"$lookup": {
"from": "projects",
"let": { "projectId": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$projectId" ] }}},
{ "$project": { "projectName": 1,"upworkdId":1,"status":1,"developers":1,"hoursApproved":1 }}
],
"as": "project"
}
}
],
"as": "projects"
}
}
])
.then((data)=>{
res.json(data);
})
And above query is giving me below result:
[
{
"_id": "5c0a4083753a321c6c4ee024",
"projects": [
{
"_id": "5c0a4083753a321c6c4ee024",
"totalHours": 11,
"project": [
{
"_id": "5c0a4083753a321c6c4ee024",
"hoursApproved": 24,
"developers": [
"5c0a29c697e71a0d28b910a9"
],
"projectName": "fallbrook winery",
"status": "pending"
}
]
}
]
}
]
Now i want to populate the developers subfield inside the project Array. How can i modify the above code to get that.
You can use $lookup one more deep level with the developers array.
Something like this
Project.aggregate([
{ "$match": condition },
{ "$group": { "_id": "$_id" }},
{ "$lookup": {
"from": "worksheets",
"let": { "projectId": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$projectId", "$$projectId"] } } },
{ "$group": { "_id": "$projectId", "totalHours": { "$sum": "$hours" } }},
{ "$lookup": {
"from": "projects",
"let": { "projectId": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$_id", "$$projectId"] } } },
{ "$lookup": {
"from": "developers",
"let": { "developers": "$developers" },
"pipeline": [
{ "$match": { "$expr": { "$in": ["$_id", "$$developers"] } } },
],
"as": "developers"
}},
{ "$project": {
"projectName": 1, "upworkdId": 1, "status": 1, "developers": 1, "hoursApproved": 1
}}
],
"as": "project"
}}
],
"as": "projects"
}}
])

mongodb $lookup 3 level nested document

I'm new to mongo and struggling mightily with the following.
In my mongodb database, there are 3 collections and structured as below.
lv1:
{
"_id": ObjectId("58650f1abbf1cd8804d0abde"),
"name": "lv1_aaa"
}
lv2:
{
"_id": ObjectId("5ba45de41e78c7eb3fdfbfa6"),
"lv1_id": ObjectId("58650f1abbf1cd8804d0abde"),
"name": "lv2_bbb"
}
lv3:
{
"_id": ObjectId("5ba45de41e78c7eb3fdfbfa6"),
"lv1_id": ObjectId("58650f1abbf1cd8804d0abde"),
"lv2_id": ObjectId("58d8c3e1bbf1cd7436117bd6"),
"name": "lv3_ccc"
}
How can I get a data structure below use $lookup
[
{
"_id": ObjectId("58650f1abbf1cd8804d0abde"),
"name": "lv1_aaa",
"children": [
{
"_id": ObjectId("5ba45de41e78c7eb3fdfbfa6"),
"lv1_id": ObjectId("58650f1abbf1cd8804d0abde"),
"name": "lv2_bbb",
"children": [
{
"_id": ObjectId("5ba45de41e78c7eb3fdfbfa6"),
"lv1_id": ObjectId("58650f1abbf1cd8804d0abde"),
"lv2_id": ObjectId("58d8c3e1bbf1cd7436117bd6"),
"name": "lv3_ccc"
},
......
]
},
......
]
},
......
]
Any help would be greatly appreciated!
You can try below aggregation with mongodb 3.6 and above
db.lv1.aggregate([
{ "$sort": { _id: 1 } },
{ "$lookup": {
"from": "lv2",
"let": { "lv1_id": "$_id" },
"pipeline": [
{ "$sort": { index: 1 } },
{ "$match": { "$expr": { "$eq": [ "$lv1_id", "$$lv1_id" ] } } },
{ "$lookup": {
"from": "lv3",
"let": { "lv2_id": "$_id" },
"pipeline": [
{ "$sort": { index: 1 } },
{ "$match": { "$expr": { "$eq": [ "$lv2_id", "$$lv2_id" ] } } }
],
"as": "children"
}}
],
"as": "children"
}}
]);