How to find average in mongoose aggregation - javascript

I am aggregating data from 3 collections -
Users -
[
{
"_id": "5bc425bb3e99c8269ce51faa",
"fname": "Akash",
"salary": 25000,
"age": 23,
"__v": 0
},
{
"_id": "5bc425e23e99c8269ce51fab",
"fname": "Aditya",
"salary": 20000,
"age": 28,
"__v": 0
}
]
Balance -
[
{
"_id": "5bc4349adcc885e047054466",
"fname": "Akash",
"balance": 4000
},
{
"_id": "5bc434ffdcc885e0470544c2",
"fname": "Aditya",
"balance": 8000
}
]
dept collection -
[
{
"_id": "5bc46167b365918881aa3bbf",
"fname": "Akash",
"dept": "IT"
},
{
"_id": "5bc4620fb365918881aa3c1b",
"fname": "Apurva",
"dept": "Chemical"
}
]
I want to aggregriated result as -
[
{
"_id": {
"fname": "Aditya",
"balance": 8000,
"dept": "Medical",
"salary": 20000
},
"avg": 20000
},
{
"_id": {
"fname": "Apurva",
"balance": 1000,
"dept": "Chemical",
"salary": 22000
},
"avg": 22000
}
]
My query -
User.aggregate([
{
$lookup: {
from: "balance",
localField: "fname",
foreignField: "fname",
as: "result"
}
},
{
$unwind: "$result"
},
{
$lookup: {
from: "dept",
localField: "fname",
foreignField: "fname",
as: "deptresult"
}
},
{
$unwind: "$deptresult"
},
{ $sort: { salary: -1 } },
// { $limit: 1 },
{
$project: {
fname: 1,
salary: 1,
"result.balance": 1,
"deptresult.dept": 1,
avg: { $avg: "$salary" }
}
}
It shows remaining result but it does now display calculated average.
Instead of average , it simply prints salary . Please someone guide me on this.
I am using mongoose ORM.Rest of the code is running fine.

You can use below aggregation
User.aggregate([
{ "$facet": {
"projection": [
{ "$lookup": {
"from": "balance",
"localField": "fname",
"foreignField": "fname",
"as": "result"
}},
{ "$lookup": {
"from": "dept",
"localField": "fname",
"foreignField": "fname",
"as": "deptresult"
}},
{ "$unwind": "$deptresult" },
{ "$unwind": "$result" },
{ "$sort": { salary: -1 } },
{ "$project": {
"fname": 1,
"salary": 1,
"result.balance": 1,
"deptresult.dept": 1
}}
],
"averageSalary": [
{ "$group": {
"_id": null,
"avg": { "$avg": "$salary" }
}}
]
}}
])

Related

MongoDB : - Merge object with same key into one

I am trying to merge an object inside an array with the same date but with a different key name for the status key.
I have 2 collections users and canteens
The query I am trying to get the result but am not able to figure out how to merge the object with the same Date
OUTPUT
User.aggregate([
{ $sort: { workerId: 1 } },
{
$lookup: {
from: "canteens",
localField: "_id",
foreignField: "employeeId",
pipeline: [
{
$match: {
Date: {
$gte: new Date(fromDate),
$lte: new Date(toDate),
},
},
},
{
$project: {
Date: 1,
status: 1,
},
},
],
as: "canteens",
},
},
{
$project: {
_id: 1,
workerId: 1,
workerFirstName: 1,
workerSurname: 1,
workerDepartment: 1,
workerDesignation: 1,
locationName: 1,
canteenData: "$canteens",
},
},
]);
[
{
"_id": "60e6fd3616dd663e84a925e2",
"workerFirstName": "Firstaname",
"workerSurname": "lastname",
"workerId": "1",
"locationName": "location",
"workerDesignation": "designation",
"workerDepartment": "department",
"canteenData": [
{
"_id": "63b285b9e92eee614feb7be1",
"status": "LUNCH",
"Date": "2023-01-02T00:00:00.000Z"
},
{
"_id": "63b2db8db10c24487201e0a2",
"status": "DINNER",
"Date": "2023-01-02T00:00:00.000Z"
},
{
"_id": "63b39b247adbeb50bfbe3503",
"status": "BREAK FAST",
"Date": "2023-01-03T00:00:00.000Z"
},
{
"_id": "63b3d248c076184fb07ff2c4",
"status": "LUNCH",
"Date": "2023-01-03T00:00:00.000Z"
},
{
"_id": "63b42b8ccb57a4cb7af34015",
"status": "DINNER",
"Date": "2023-01-03T00:00:00.000Z"
},
{
"_id": "63b4ef71e038498fe6634506",
"status": "BREAK FAST",
"Date": "2023-01-04T00:00:00.000Z"
}
]
},
{
"_id": "60e6fd3616dd663e84a925e2",
"workerFirstName": "Firstaname1",
"workerSurname": "lastname1",
"workerId": "2",
"locationName": "location",
"workerDesignation": "designation",
"workerDepartment": "department",
"canteenData": [
{
"_id": "63b285b9e92eee614feb7be1",
"status": "LUNCH",
"Date": "2023-01-02T00:00:00.000Z"
},
{
"_id": "63b2db8db10c24487201e0a2",
"status": "DINNER",
"Date": "2023-01-02T00:00:00.000Z"
},
{
"_id": "63b39b247adbeb50bfbe3503",
"status": "BREAK FAST",
"Date": "2023-01-03T00:00:00.000Z"
},
{
"_id": "63b3d248c076184fb07ff2c4",
"status": "LUNCH",
"Date": "2023-01-03T00:00:00.000Z"
},
{
"_id": "63b42b8ccb57a4cb7af34015",
"status": "DINNER",
"Date": "2023-01-03T00:00:00.000Z"
},
{
"_id": "63b4ef71e038498fe6634506",
"status": "BREAK FAST",
"Date": "2023-01-04T00:00:00.000Z"
}
]
}
]
The output I am trying to get
[
{
"_id": "60e6fd3616dd663e84a925e2",
"workerFirstName": "Firstanem",
"workerSurname": "lastname",
"workerId": "1",
"locationName": "location",
"workerDesignation": "designation",
"workerDepartment": "department",
"canteenData": [
{
"_id": "63b285b9e92eee614feb7be1",
"status1": "LUNCH",
"status2": "DINNER",
"Date": "2023-01-02T00:00:00.000Z"
},
{
"_id": "63b39b247adbeb50bfbe3503",
"status1": "BREAK FAST",
"status2": "LUNCH",
"status3": "DINNER",
"Date": "2023-01-03T00:00:00.000Z"
},
{
"_id": "63b4ef71e038498fe6634506",
"status1": "BREAK FAST",
"Date": "2023-01-04T00:00:00.000Z"
}
]
},
{
"_id": "60e6fd3616dd663e84a925e2",
"workerFirstName": "Firstanem1",
"workerSurname": "lastname1",
"workerId": "2",
"locationName": "location",
"workerDesignation": "designation",
"workerDepartment": "department",
"canteenData": [
{
"_id": "63b285b9e92eee614feb7be1",
"status1": "LUNCH",
"status2": "DINNER",
"Date": "2023-01-02T00:00:00.000Z"
},
{
"_id": "63b39b247adbeb50bfbe3503",
"status1": "BREAK FAST",
"status2": "LUNCH",
"status3": "DINNER",
"Date": "2023-01-03T00:00:00.000Z"
},
{
"_id": "63b4ef71e038498fe6634506",
"status1": "BREAK FAST",
"Date": "2023-01-04T00:00:00.000Z"
}
]
}
]
One option is to add 2 steps into your $lookup pipeline aggregation:
{$group: {
_id: "$Date",
_idVal: {$first: "$_id"},
data: {$addToSet: "$status"}
}},
{$replaceRoot: {
newRoot: {
$mergeObjects: [
{_id: "$_idVal", Date: "$_id"},
{$arrayToObject: {
$reduce: {
input: "$data",
initialValue: [],
in: {$concatArrays: [
"$$value",
[{k: {$concat: [
"status",
{$toString: {$add: [{$size: "$$value"}, 1]}}
]},
v: "$$this"}]
]}
}
}}
]
}
}}
See how it works on the playground example
It's not easy to create status1, status2, ... variables dynamically + how do we know BREAK FAST should be status1 and not status2.
Alternative solution: We $group inside correlated subqueries and push all status values into an array
db.users.aggregate([
{
"$lookup": {
"from": "canteens",
"localField": "_id",
"foreignField": "employeeId",
pipeline: [
{
// Put your custom filters here
$match: {}
},
{
$group: {
_id: "$Date",
//pick "first" canteens _id
id: {
$first: "$_id"
},
status: {
$push: "$status"
}
}
},
{
$project: {
_id: "$id",
Date: "$_id",
status: 1
}
},
],
as: "canteenData",
}
}
])
MongoPlayground

How to group result of lookup aggregate results

I aggregate users document:
User.aggregate([
{
$match: { _id: req.params.id },
},
{
$lookup: {
from: 'moods',
localField: '_id',
foreignField: 'source.userId',
pipeline: [
{
$lookup: {
from: 'contactrequests',
localField: 'source.userId',
foreignField: 'source.userId',
let: {
// truncate timestamp to start of day
moodsDate: {
$dateTrunc: {
date: '$timestamp',
unit: 'day',
},
},
},
pipeline: [
{
$match: {
$expr: {
$eq: [
'$$moodsDate',
{
// truncate timestamp to start of day
$dateTrunc: {
date: '$timestamp',
unit: 'day',
},
},
],
},
},
},
],
as: 'contactRequests',
},
},
],
as: 'calendar',
},
},
]).exec()
There is final document what I get
{
"_id": "P4SpYVd1KjBaF4SKyVw0E",
"lastName": "Doe",
"login": "User-01",
"name": "John"
"calendar": [
{
"_id": "62a351e33859aaf975c63323",
"source": {
"userId": "P4SpYVd1KjBaF4SKyVw0E",
"deviceId": "Pacjent-141214"
},
"timestamp": "2022-06-07T12:44:13.333Z",
"mood": "good",
"contactRequests": []
},
{
"_id": "62a351f43859aaf975c63327",
"source": {
"userId": "P4SpYVd1KjBaF4SKyVw0E",
"deviceId": "Pacjent-141214"
},
"timestamp": "2022-06-09T12:44:13.333Z",
"mood": "middle",
"contactRequests": [
{
"timestamp": "2022-06-09T12:44:13.333Z",
"source": {
"deviceId": "Pacjent-141214",
"userId": "P4SpYVd1KjBaF4SKyVw0E"
},
"resolve": false,
"_id": "62a351ff3859aaf975c63329",
},
]
}
]
},
This is what I would to get. This is more clean and readable.
{
"_id": "P4SpYVd1KjBaF4SKyVw0E",
"login": "User-01",
"name": "John",
"lastName": "Doe",
"calendar": [
{
"timestamp": "2022-06-11T12:44:13.333Z"
"mood": {
"source": {
"userId": "P4SpYVd1KjBaF4SKyVw0E",
},
"timestamp": "2022-06-11T12:44:13.333Z",
"mood": "bad",
"_id": "62a352b83859aaf975c6332d",
},
"contactRequest": [
{
"timestamp": "2022-06-11T15:25:13.333Z",
"source" : {
"userId":"P4SpYVd1KjBaF4SKyVw0E"
},
"resolve": true,
"_id": "62a351ff3859aaf975c63329"
},
{
"timestamp": "2022-06-11T18:23:13.333Z",
"source" : {
"userId":"P4SpYVd1KjBaF4SKyVw0E"
},
"resolve": false,
"_id": "62a351ff3859aaf975c63329"
},
]
}
}
]
}
To achive that I've used $group parameter, but at some point I have to declare which field should be fetch to result document and I have problem with contatRequest fields.
{
$group: {
_id: {
$dateToString: {
format: '%Y-%m-%d',
date: '$timestamp',
},
},
mood: {
$push: {
_id: '$_id',
source: '$source',
type: '$mood',
timestamp: '$timestamp',
},
},
contactRequest: {
$push: {
_id: '$contactRequest._id',
source: '$contactRequest.source',
resolve: '$contactRequest.resolve',
timestamp: '$contactRequest.timestamp',
},
},
},
},
{
$project: {
_id: 0,
timestamp: '$_id',
mood: 1,
contactRequest: 1,
},
},
Sample database/collections/aggregation pipeline at mongoplayground.net.

join object from another documnt with localfield key

i have a competition doc with field teams array of object with _id of team and a score doc with teamId field
competitions.teams = [{_id: 100,..}, {..}]
score.teamId = 100
when aggregatig score i want to group it to the competition teams but imm getting all team inside the group innstead of matching id
sample document https://mongoplayground.net/p/yJ34IBnnuf5
db.scores.aggregate([
{
"$match": {
"type": "league"
}
},
{
"$lookup": {
"from": "competitions",
"localField": "competitionId",
"foreignField": "_id",
"as": "comp"
}
},
{
"$unwind": {
"path": "$comp",
"preserveNullAndEmptyArrays": true
}
},
{
"$project": {
"comp.teams": 1,
"teamId": 1
}
},
{
"$group": {
"_id": "$teamId",
"results": {
"$push": "$comp.teams"
}
}
}
])
returns all team in group instead of matched teamid
{
"_id" : 100
"results" : [
{
"_id": 100,
"name": "team 1"
},
{
"_id": 101,
"name": "team 2"
}
]
}
{
"_id" 101
"results" : [
{
"_id": 100,
"name": "team 1"
},
{
"_id": 101,
"name": "team 2"
}
]
}
this is the result im trying to accomplish please guide me
{
"_id" : 100
"results" : [
{
"_id": 100,
"name": "team 1"
}
]
}
{
"_id" 101
"results" : [
{
"_id": 101,
"name": "team 2"
}
]
}
what should i do i've read the docs this seems to be the way?
Demo - https://mongoplayground.net/p/ETeroLftcZZ
You have to add $unwind: { "path": "$comp.teams" }
and after that group by { $group: { "_id": "$comp.teams._id" ... }
db.scores.aggregate([
{ $match: { "type": "league" } },
{ $lookup: { "from": "competitions", "localField": "competitionId", "foreignField": "_id", "as": "comp" } },
{ $unwind: { "path": "$comp", "preserveNullAndEmptyArrays": true } },
{ $unwind: { "path": "$comp.teams", "preserveNullAndEmptyArrays": true }},
{ $group: { "_id": "$comp.teams._id", "results": { $push: "$comp.teams" } } }
])
Demo with more data - https://mongoplayground.net/p/b41Ch5ge2Wp

Wrong Result by $lookup mongodb

I am using $lookup to get the data by joining data from two or three collections, Below is my aggregate 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"] } } },
{
"$lookup": {
"from": "users",
"let": { "developers": "$developers" },
"pipeline": [
{ "$match": { "$expr": { "$in": ["$_id", "$$developers"] } } },
{ "$project":{"firstName":1,"lastName":1}}
],
"as": "developers"
}
},
{
"$lookup": {
"from": "billing_accounts",
"let": { "upworkId": "$upworkId" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$_id", "$$upworkId"] } } },
{"$project":{"name":1,"username":1}}
],
"as": "upworkId"
}
},
{
"$project": {
"projectName": 1, "upworkId": 1, "status": 1, "developers": 1, "hoursApproved": 1
}
}
],
"as": "project"
}}
],
"as": "projects"
}
}
])
And it is giving me the below result:
[
{
"_id": "5c188a9959f6cf1258f4cb01",
"projects": [
{
"_id": "5c188a9959f6cf1258f4cb01",
"totalHours": 8,
"project": [
{
"_id": "5c188a9959f6cf1258f4cb01",
"hoursApproved": 192,
"developers": [
{
"_id": "5c0a29e597e71a0d28b910aa",
"lastName": "kumar",
"firstName": "Amit"
}
],
"projectName": "Jims fitness",
"status": "ongoing",
"upworkId": [
{
"_id": "5c17a1cec1a7681f7c54bb2d",
"name": "Heena Ln",
"username": "heena_ln"
}
]
}
]
}
]
},
{
"_id": "5c17a253c1a7681f7c54bb2f",
"projects": []
}
]
But what i want to get is:
[
{
"_id": "5c188a9959f6cf1258f4cb01",
"projects": [
{
"_id": "5c188a9959f6cf1258f4cb01",
"totalHours": 0,
"project": [
{
"_id": "5c188a9959f6cf1258f4cb01",
"hoursApproved": 192,
"developers": [
{
"_id": "5c0a29e597e71a0d28b910aa",
"lastName": "kumar",
"firstName": "Amit"
}
],
"projectName": "Project1",
"status": "ongoing",
"upworkId": [
{
"_id": "5c17a1cec1a7681f7c54bb2d",
"name": "Heena Ln",
"username": "heena_ln"
}
]
}
]
}
]
},
{
"_id": "5c17a253c1a7681f7c54bb2f",
"projects": [
{
"_id": "5c17a253c1a7681f7c54bb2f",
"totalHours": 0,
"project": [
{
"_id": "5c17a253c1a7681f7c54bb2f",
"hoursApproved": 192,
"developers": [
{
"_id": "5c0a29e597e71a0d28b910a9",
"lastName": "kumar",
"firstName": "Rajat"
}
],
"projectName": "project2",
"status": "ongoing",
"upworkId": [
{
"_id": "5c17a1cec1a7681f7c54bb2d",
"name": "Heena Ln",
"username": "heena_ln"
}
]
}
]
}
]
}
]
As you can see that now i have totalHours equals to 0 instead of empty array and have the project details.
Actually I have four collections: projects, worksheets, users and billings and i am executing aggregate query on the projects collection to get the projects of a project manager and for this i am also joining worksheets collection to get the data for how many hours the employees worked on this project, because worksheets collection contains the projectId, userId and hours.
Query: You can see in the result that, i am getting the empty array of projects, this is because i don't have any record of second project projectId into the worksheet collection, so for this it is giving me empty array, but i want to get the projects details as it is and totalHours equals to 0.

$lookup with deeply nested object

I am new to MongoDB and currently working on a recipe App for school which suggests diet plans. Therefore I need to join the "meal" ObjectId in the diet plan of the user (collection "Users") with the ObjectIds in the collections "Meals".
Afterwards I need to join an "ingredient" ObjectID in the "Meals" collection with the ID of the "ingredient" in the "Ingredients" collection. The problem is, that the "ingredient" ObjectID in collection "Meals" is situated in an Object with another integer variable "amount". This object is nested in an array called "ingredients" with many objects such as the one just described.
Below is my Structure:
Users
{
"_id": ObjectId("5b28cab902f28e18b863bd36"),
"username: "testUser1",
"password": "$2a$08$KjddpaSQPjp6aF/gseOhVeddYdqWJCJ4DpFwxfNgsk81G.0TOtN5i",
"dietPlans": Object
{
"dietPlanCurrent":Object
{
"monday":Object
{
"breakfast":Object
{
"meal": ObjectId("5b2b9a8bbda339352cc39ec4")
},
…
},
…
},
…
},
}
Meals
{
"_id" : ObjectId("5b2b9a8bbda339352cc39ec4"),
"name": "Gulasch-breakfast",
"cuisine": "International",
"ingredients":[
{
"ingredient": ObjectId("5b1ec0f939b55efcd4e28a2d"),
"amount": 20
},
{
"ingredient": ObjectId("5b1ec42474fc1f58d84264d4"),
"amount": 20
},
{
"ingredient": ObjectId("5b1ec42474fc1f58d84264d5"),
"amount": 20
},
…
],
"comments": [
…
]
}
Ingredients
{
{
"_id": ObjectId("5b1ec0f939b55efcd4e28a2d"),
"name": "Walnut",
"calories": 654
…
}
{
"_id": ObjectId("5b1ec0f939b55efcd4e28a3d"),
"name": "Apple",
"calories": 123
…
}
…
}
What I am trying to get is:
{
"_id": ObjectId("5b28cab902f28e18b863bd36"),
"username: "testUser1",
"password": "$2a$08$KjddpaSQPjp6aF/gseOhVeddYdqWJCJ4DpFwxfNgsk81G.0TOtN5i",
"dietPlans": Object
{
"dietPlanCurrent":Object
{
"Monday":Object
{
"breakfast":Object
{
"meal": ObjectId("5b2b9a8bbda339352cc39ec4")
"matchedIngredients": [
{
"_id": ObjectId("5b1ec0f939b55efcd4e28a2d"),
"name": "Walnut",
"calories": 654
…
}
…
]
},
…
},
…
},
…
},
}
My approach which is not working (only returning empty matchedIngredients Array)
{
$match: {
'_id': mongoose.Types.ObjectId(req.params.userId)
}
},
{
$lookup: {
from: 'meals',
localField: 'dietPlans.dietPlanCurrent.monday.breakfast.meal',
foreignField: '_id',
as: "dietPlans.dietPlanCurrent.monday.breakfast.mealObject"
}
},
{
$unwind: {
path: "$dietPlans.dietPlanCurrent.monday.breakfast.mealObject",
preserveNullAndEmptyArrays: true
}
},
{
$unwind: {
path: "$dietPlans.dietPlanCurrent.monday.breakfast.mealObject.ingredients",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: 'ingredients',
localField: 'dietPlans.dietPlanCurrent.monday.breakfast.mealObject.ingredients.ingredient',
foreignField: '_id',
as: "dietPlans.dietPlanCurrent.monday.breakfast.matchedIngredients"
}
}
Help is very much appreciated. I already checked out this approach, but it somehow didn't work:
Approach that didn't work for me
Thank you very much!
What you are trying to do is not possible with mongodb version 3.4 but if you upgrade to 3.6 then you can try below aggregation
db.collection.aggregate([
{ "$match": { "_id": mongoose.Types.ObjectId(req.params.userId) } },
{ "$lookup": {
"from": Meals.collection.name,
"let": { "meal_id": "$dietPlans.dietPlanCurrent.monday.breakfast.meal" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$meal_id" ] } } },
{ "$unwind": "$ingredients" },
{ "$lookup": {
"from": Ingredients.collection.name,
"let": { "ingredient_id": "$ingredients.ingredient" },
"pipeline": [
{ "$match": { "$expr": { "$eq": [ "$_id", "$$ingredient_id" ] } } }
],
"as": "matchedIngredients"
}},
{ "$unwind": "$ingredients.matchedIngredients" },
{ "$group": {
"_id": "$_id",
"name": { "$first":"$name" },
"cuisine": { "$first":"$cuisine" },
"ingredients": { "$push":"$ingredients" }
}}
],
"as": "dietPlans.dietPlanCurrent.monday.breakfast.mealObject"
}},
{ "$unwind": "$dietPlans.dietPlanCurrent.monday.breakfast.mealObject" }
])

Categories

Resources