Get count on several fields using mongoose aggregation - mongodb

I have two collections in my MongoDB employee and employeeData i need to get some statics information from DB.
total employees who were not deleted.
total employees who have security access and are not deleted.
total employees still active;
this is my Employee collection sample document
{
_id:'5ec25e74d028af28343f1061'
isDeleted:false
securityAccess:true
details:'60475b7a93ac45d64a5957b0'
}
this is EmployeeData collection document
{
_id:'60475b7a93ac45d64a5957b0'
emplyeeId:'5ec25e74d028af28343f1061'
isActive:'active',
salary:225543.00,
department:'sales'
}
I need to get this data from one query using some kind of aggregations but I'm not much familiar with the MongoDB queries.
the expected result looks like this.
Total Employees | Active Employees | Security Access
10 5 2

$match to check isDeleted condition
$lookup with EmployeeData
$group by null
get total employees count,
count total security access if securityAccess is true
count total active employees if isActive is 'active'
db.Employee.aggregate([
{ $match: { isDeleted: false } },
{
$lookup: {
from: "EmployeeData",
localField: "_id",
foreignField: "emplyeeId",
as: "activeEmployees"
}
},
{
$group: {
_id: null,
totalEmployees: { $sum: 1 },
securityAccess: {
$sum: {
$cond: ["$securityAccess", 1, 0]
}
},
activeEmployees: {
$sum: {
$cond: [
{ $eq: [{ $first: "$activeEmployees.isActive" }, "active"] },
1,
0
]
}
}
}
}
])
Playground

Related

How to write a query that will return the count of objects in table 2 that have a certain field matching table 1's ID in MongoDB Atlas?

I have two tables, Users and Rides.
I want to return the first and last name of all Users that have 0 rides in the Rides table.
A User object is set up with the following fields:
_id:
StravaConnect:Object {
AthleteID: 123
}
FirstName:
LastName:
A Ride object is set up with the following fields:
_id:
AthleteID: 123
Length:
How can I aggregate this data to show a list of all users where there are 0 rides where Ride.AthleteID == User.StravaConnect.AthleteID?
Note: I am using MongoDB Compass
Try this pipeline:
$lookup to get users' rides
$match documents where $size of rides array if greater than 0
$project required fields
[
{
"$lookup": {
"from": "rides",
"localField": "StravaConnect.AthleteID",
"foreignField": "AthleteID",
"as": "rides"
}
},
{
"$match": {
$expr: {
"$gt": [
{
"$size": "$rides"
},
0
]
}
}
},
{
"$project": {
"FirstName": 1,
"LastName": 1
}
}
]
Mongo Playground

MongoDB - How to select data that has a field equals to the minimum field value

I'm new to MongoDB and I want to select all users having the minimum age.
Something like this:
db.users.find({age: {$min: age}})
Seems really basic but I can't find how to do it.
$gorup by age and make array of users
$sort by _id means age in ascending order
$limit 1 document
db.users.aggregate([
{
$group: {
_id: "$age",
users: { $push: "$$ROOT" }
}
},
{ $sort: { _id: 1 } },
{ $limit: 1 }
])
Playground

How to make a query in two different collections in mongoDB? (without using ORM)

Suppose, In MongoDB i have two collections. one is "Students" and the another is "Course".
Student have the document such as
{"id":"1","name":"Alex"},..
and Course has the document such as
{"course_id":"111","course_name":"React"},..
and there is a third collection named "students-courses" where i have kept student's id with their corresponding course id. Like this
{"student_id":"1","course_id":"111"}
i want to make a query with student's id so that it gives the output with his/her enrolled course. like this
{
"id": "1",
"name":"Alex",
"taken_courses": [
{"course_id":"111","course_name":"React"},
{"course_id":"112","course_name":"Vue"}
]
}
it will be many to many relationship in MongoDB without using ORM. How can i make this query?
Need to use $loopup with pipeline,
First $group by student_id because we are going to get courses of students, $push all course_id in course_ids for next step - lookup purpose
db.StudentCourses.aggregate([
{
$group: {
_id: "$student_id",
course_ids: {
$push: "$course_id"
}
}
},
$lookup with Student Collection and get the student details in student
$unwind student because its an array and we need only one from group of same student record
$project required fields
{
$lookup: {
from: "Student",
localField: "_id",
foreignField: "id",
as: "student"
}
},
{
$unwind: "$student"
},
{
$project: {
id: "$_id",
name: "$student.name",
course_ids: 1
}
},
$lookup Course Collection and get all courses that contains course_ids, that we have prepared in above $group
$project the required fields
course details will store in taken_courses
{
$lookup: {
from: "Course",
let: {
cId: "$course_ids"
},
pipeline: [
{
$match: {
$expr: {
$in: [
"$course_id",
"$$cId"
]
}
}
},
{
$project: {
_id: 0
}
}
],
as: "taken_courses"
}
},
$project details, removed not required fields
{
$project: {
_id: 0,
course_ids: 0
}
}
])
Working Playground: https://mongoplayground.net/p/FMZgkyKHPEe
For more details related syntax and usage, check aggregation

MongoDB aggregate $lookup to return unmatched items from the main collection

I would like unmatched data from the USERS collection. Here I have two collections 1) USERS, 2) COMPANY.I am able to get the matched data from both USERS using aggregate function. but in this case I want data from USERS table which are not assigned to a company.
USERS table
{
_id: "AAA",
fullName:"John Papa"
},
{
_id: "BBB",
fullName:"Robin Son"
}
COMPANY table
{
_id: "1sd1s",
Name:"Lumbar Company"
User:"AAA"
},
{
_id: "23s1dfs3",
Name:"Patricia"
User:"AAA"
}
$lookup works like LEFT OUTER JOIN so it will remove empty array when there's no match. Then you can use $size to get only empty arrays:
db.users.aggregate([
{
$lookup: {
from: "company",
localField: "_id",
foreignField: "User",
as: "companies"
}
},
{
$match: {
$expr: {
$eq: [ { "$size": "$companies" }, 0 ]
}
}
}
])
Mongo Playground

Poor lookup aggregation performance

I have two collections
Posts:
{
"_Id": "1",
"_PostTypeId": "1",
"_AcceptedAnswerId": "192",
"_CreationDate": "2012-02-08T20:02:48.790",
"_Score": "10",
...
"_OwnerUserId": "6",
...
},
...
and users:
{
"_Id": "1",
"_Reputation": "101",
"_CreationDate": "2012-02-08T19:45:13.447",
"_DisplayName": "Geoff Dalgas",
...
"_AccountId": "2"
},
...
and I want to find users who write between 5 and 15 posts.
This is how my query looks like:
db.posts.aggregate([
{
$lookup: {
from: "users",
localField: "_OwnerUserId",
foreignField: "_AccountId",
as: "X"
}
},
{
$group: {
_id: "$X._AccountId",
posts: { $sum: 1 }
}
},
{
$match : {posts: {$gte: 5, $lte: 15}}
},
{
$sort: {posts: -1 }
},
{
$project : {posts: 1}
}
])
and it works terrible slow. For 6k users and 10k posts it tooks over 40 seconds to get response while in relational database I get response in a split second.
Where's the problem? I'm just getting started with mongodb and it's quite possible that I messed up this query.
from https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/
foreignField Specifies the field from the documents in the from
collection. $lookup performs an equality match on the foreignField to
the localField from the input documents. If a document in the from
collection does not contain the foreignField, the $lookup treats the
value as null for matching purposes.
This will be performed the same as any other query.
If you don't have an index on the field _AccountId, it will do a full tablescan query for each one of the 10,000 posts. The bulk of the time will be spent in that tablescan.
db.users.ensureIndex("_AccountId", 1)
speeds up the process so it's doing 10,000 index hits instead of 10,000 table scans.
In addition to bauman.space's suggestion to put an index on the _accountId field (which is critical), you should also do your $match stage as early as possible in the aggregation pipeline (i.e. as the first stage). Even though it won't use any indexes (unless you index the posts field), it will filter the result set before doing the $lookup (join) stage.
The reason why your query is terribly slow is that for every post, it is doing a non-indexed lookup (sequential read) for every user. That's around 60m reads!
Check out the Pipeline Optimization section of the MongoDB Aggregation Docs.
First use $match then $lookup. $match filter the rows need to be examined to $lookup. It's efficient.
as long as you're going to group by user _AccountId, you should do the $group first by _OwnerUserId then lookup only after filtering accounts having 10<postsCount<15 this will reduce lookups:
db.posts.aggregate([{
$group: {
_id: "$_OwnerUserId",
postsCount: {
$sum: 1
},
posts: {
$push: "$$ROOT"
} //if you need to keep original posts data
}
},
{
$match: {
postsCount: {
$gte: 5,
$lte: 15
}
}
},
{
$lookup: {
from: "users",
localField: "_id",
foreignField: "_AccountId",
as: "X"
}
},
{
$unwind: "$X"
},
{
$sort: {
postsCount: -1
}
},
{
$project: {
postsCount: 1,
X: 1
}
}
])