MongoDb-Need Count of all documents before grouping in aggregate query - node.js

I want count of all documents before doing grouping as show in my Query. The purpose of this if ai get count of all documents then I can calculate Percentage(%) of each individual students count. I tried with $count but after using that I'm not able to group my query. Is there any way 1st I can have counts of all documents after using $match function then store it to another variable without losing data.
This is the Query
const data = await AttendanceSchema.aggregate([
{ $match: { subjectID: mongoose.Types.ObjectId(`${req.params.Sid}`) } },
// Before Group I also want count of all documents
{ $group: { _id: "$studentID", count: { $sum: 1 } } },
{
$lookup: {
from: "students",
localField: "_id",
foreignField: "_id",
as: "student",
},
},
{
$project: {
"student.createdAt": 0,
"student.updatedAt": 0,
"student.__v": 0,
"student.password": 0,
},
},
]);
The data I'm getting from this query
{
"data": [
{
"_id": "635d40803352895afffdc294",
"count": 3,
"student": [
{
"_id": "635d40803352895afffdc294",
"name": "D R",
"email": "d#gmail.com",
"number": "9198998888",
"rollNumber": 202,
"departmentID": "635a8ca21444a47d65d32c1a",
"classID": "635a92141a081229013255b4",
"position": "Student"
}
]
},
{
"_id": "635eb8898dea5f437789b751",
"count": 4,
"student": [
{
"_id": "635eb8898dea5f437789b751",
"name": "V R",
"email": "v#gmail.com",
"number": "9198998899",
"rollNumber": 203,
"departmentID": "635a8ca21444a47d65d32c1a",
"classID": "635a92141a081229013255b4",
"position": "Student"
}
]
}
]
}
The desired output:
"data": [
{
"_id": "635d40803352895afffdc294",
"totalCount: 7, //This is what I want
"count": 3,
"student": [
{
"_id": "635d40803352895afffdc294",
"name": "D R",
"email": "d#gmail.com",
"number": "9198998888",
"rollNumber": 202,
"departmentID": "635a8ca21444a47d65d32c1a",
"classID": "635a92141a081229013255b4",
"position": "Student"
}
]
},
{
"_id": "635eb8898dea5f437789b751",
"totalCount: 7, //This is what I want
"count": 4,
"student": [
{
"_id": "635eb8898dea5f437789b751",
"name": "V R",
"email": "v#gmail.com",
"number": "9198998899",
"rollNumber": 203,
"departmentID": "635a8ca21444a47d65d32c1a",
"classID": "635a92141a081229013255b4",
"position": "Student"
}
]
}
]
}

You could add a few more stages to you aggregation pipeline to output what you want.
Here are the extra stages:
{
"$group": {
"_id": null,
"totalCount": {"$sum": "$count"},
"docs": {"$push": "$$ROOT"}
}
},
{
"$project": {
"docs": {
"$map": {
"input": "$docs",
"as": "doc",
"in": {
"$mergeObjects": [
"$$doc",
{"totalCount": "$totalCount"}
]
}
}
}
}
},
{"$unwind": "$docs"},
{"$replaceWith": "$docs"}
Try it on mongoplayground.net.

Related

How to filter the response from mongoDB, so nested arrays will include only items that meet a condition?

My documents look like this
{
"_id": {
"$oid": "62825f71005ce00c5f0235c1"
},
"user": "jon",
"roles": {
"User": 2001,
},
"STOCK ": [
{
"sku": "BLACK-M",
"productname": "BLACK",
"sendout": 0,
"recived": 1,
"totalinstock": 40,
"location": "B32",
"_id": {
"$oid": "62826016005ce00c5f0235c8"
}
},
{
"sku": "CJNS",
"productname": "89796363",
"sendout": 0,
"recived": 45,
"totalinstock": 0,
"location": "B232",
"_id": {
"$oid": "62836f2d56b4f1ac79c99b8d"
}
}
],
"ORDERS": [
{
"date": {
"$date": "2022-06-02T15:23:58Z"
},
"OrderNumber": "745607",
"City": "xxxxx",
"Address": "yyyyyy",
"Phone": "8678678678",
"Name": "Holly ",
"Trackingnumber": 40,
"ZipCode": 00000,
"Province": "New ",
"Quantity": [
1
],
"Product_Name": [
" pants pants"
],
"SKU": [
"CJNS"
],
"_id": {
"$oid": "6298d61ba6eeec72b78332a7"
}
},
{
"date": {
"$date": "2022-06-02T15:23:58Z"
},
"OrderNumber": "748840",
"City": "xxxxx",
"Address": "yyyyyy",
"Phone": "354353454",
"Name": "Michael",
"Trackingnumber": 0,
"ZipCode": 00000,
"Province": "North",
"Quantity": [
1
],
"Product_Name": [
" pants pants"
],
"SKU": [
"CJNS"
],
"_id": {
"$oid": "6298d61ba6eeec72b78332a9"
}
}
]
}
I successful to return all the objects in STOCK or all the objects in ORDERS
Through this query
const foundUser= await User.find({"user":req.body.user},("Orders") ).exec()
Now I want to filter the response to include only items where "Trackingnumber" is different from 0
For the sample data I expect to receive only
{
"date": {
"$date": "2022-06-02T15:23:58Z"
},
"OrderNumber": "748840",
"City": "xxxxx",
"Address": "yyyyyy",
"Phone": "354353454",
"Name": "Michael",
"Trackingnumber": 0,
"ZipCode": 00000,
"Province": "North",
"Quantity": [
1
],
"Product_Name": [
" pants pants"
],
"SKU": [
"CJNS"
],
"_id": {
"$oid": "6298d61ba6eeec72b78332a9"
}
}
You can use an aggregation pipeline with a $filter for this:
db.collection.aggregate([
{
$match: {
"user": "jon"
}
},
{
$project: {
ORDERS: {
$filter: {
input: "$ORDERS",
as: "item",
cond: {$ne: ["$$item.Trackingnumber", 0]}
}
}
}
}
])
Playground example
User.find({"Orders" : {"Trackingnumber": 0} })

How to filter results from collection the $lookup in mongoose

i want to filter the result as the following in mongodb. I use $lookup to populate the result from another collection. Please check my following code
This code below is what i get
{
"_id": "5f3d563122de0730d0f6a754",
"barcode": "1234",
"productname": "Lays Packet",
"brandName": "Lays",
"productSize": "12",
"price": "12",
"quant": "12",
"imageurl": "http://localhost:3000/images/imageurl-1597855281050.jpg",
"remaining": "12",
"creator": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0,
"source": [
{
"_id": "5f3a9bbc325a074240a1a815",
"firstname": "test",
"lastname": "test",
"storename": "test",
"gst": "test",
"phoneNumber": 1,
"email": "1#demo.com",
"address1": "test",
"address2": "test",
"city": "test",
"state": "test",
"country": "test",
"zip": "1",
"password": "1",
"usertype": 3,
"unique_SHOP": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0
}
]
},
How to retrieve only unique_SHOP and zip from source listing.I want result like the one below with one or more fields
{
"_id": "5f3d563122de0730d0f6a754",
"barcode": "1234",
"productname": "Lays Packet",
"brandName": "Lays",
"productSize": "12",
"price": "12",
"quant": "12",
"imageurl": "http://localhost:3000/images/imageurl-1597855281050.jpg",
"remaining": "12",
"creator": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0,
"source": [
{
"zip": "1",
"unique_SHOP": "3d943b957fb5db510d824c5cbd6e8f7d",
}
]
},
The query i use
List.aggregate([
{$match:
{ productname: { $regex: req.params.query,$options: "i" }}
},
{ $lookup:{
from: "suppliers",
localField: "creator",
foreignField: "unique_SHOP",
as: "source"
}
},
])
You can try $lookup with pipeline,
$match condition of creator id
$project to display required fields
{
$lookup: {
from: "suppliers",
as: "source",
let: { creator: "$creator" },
pipeline: [
{
$match: {
$expr: { $eq: ["$$creator", "$_id"] }
}
},
{
$project: {
_id: 0,
zip: 1,
unique_SHOP: 1
}
}
]
}
}
Playground

Issue with to get data from MongoDB aggregate

I want data from places where userid is equal to the body.user._id
PLACE DATA
{
"_id": "5e8ddd0f7f5b174290bbefc8",
"type": 0,
"location": [
24.8757396,
67.3464698
],
"title": "E 22, Steel Town Karachi, Karachi City, Sindh, Pakistan",
"googlePlaceId": "ChIJlbvA8BIzsz4Rlh7w_fKfwus",
"createdDate": "2020-04-08T14:17:51.869Z",
"__v": 0,
},
{
"_id": "5e8de4204396310017564a2b",
"type": 0,
"location": [
24.910688,
67.0310973
],
"title": "Nazimabad, Karachi, Pakistan",
"googlePlaceId": "ChIJzZudRr0_sz4R81KZ48Ylk3Q",
"createdDate": "2020-04-08T14:48:00.557Z",
"__v": 0,
},
{
"_id": "5e8de4364396310017564a2d",
"type": 0,
"location": [
24.9180271,
67.0970916
],
"title": "Gulshan-e-Iqbal, Karachi, Pakistan",
"googlePlaceId": "ChIJsda_CLg4sz4RIghXwgIae5k",
"createdDate": "2020-04-08T14:48:22.979Z",
"__v": 0,
},
{
"_id": "5e8dea79894854524cc554e0",
"type": 0,
"location": [
24.9343322,
67.177173
],
"title": "Malir Cantt Check Post No 6, Malir Link to Super Highway, Karachi, Pakistan",
"googlePlaceId": "ChIJJ7BbsyQ4sz4RvpkV9Ig_aU4",
"createdDate": "2020-04-08T15:15:05.360Z",
"__v": 0,
}**
Visited Places DATA
{
"_id":"5e90998f8bc84d0017a6d2f3",
"visitingNo":"0",
"_userId":"5e8f3ef5434f5800170c7169"
"_placeId":"5e908fdb8bc84d0017a6d2e8"
"createdDate":"2020-04-10T16:06:39.231+00:00"
"__v":"0"
},
{
"_id":"5e90998f8bc84d0017a6d2f3",
"visitingNo":"0",
"_userId":"5e8f3ef5434f5800170c7169"
"_placeId":"5e908fdb8bc84d0017a6d2e8"
"createdDate":"2020-04-10T16:06:39.231+00:00"
"__v":"0"
},
{
"_id":"5e90998f8bc84d0017a6d2f3",
"visitingNo":"0",
"_userId":"5e8f3ef5434f5800170c7169"
"_placeId":"5e908fdb8bc84d0017a6d2e8"
"createdDate":"2020-04-10T16:06:39.231+00:00"
"__v":"0"
},
MY CODE
const palace = placeData.aggregate([
{
$lookup:{
from: `${visitplaceData}`,
// localField: "_id",
// foreignField: "_placeId",
let : {
"placeId" : "$_id"
},
pipeline : [
{ $sort: { visitingNo : -1 } },
{
$match : {
$expr : {
$and : [
{$eq: [ "$_placeId", "$$placeId" ]},
{"_userId": body.user._id}
]
}
}
},
],
as: "places"
}
}
])

How do I merge these two separate aggregate queries into a single query?

I have posted two questions concerning aggregation queries in mongo.
Question 1 Gets all the chores for a specific person
Question 2 Gets all the chores for all people on a specific ordinal (day)
Now, however, I would like to combine both of those queries into a single query. Returning only the chores for a specific person on a specific day.
Here is what I have thus far :
ChoreChart.aggregate([
{ "$match": { "affiliation": affiliation, "year": weekYear, "weekNumber": weekNumber } },
{ "$addFields": {
"chart": {
"$map": {
"input": "$chart",
"as": "cc",
"in": {
"_id": "$$cc._id",
"ordinal": "$$cc.ordinal",
"ordinalString": "$$cc.ordinalString",
"chorePerson": {
"$filter": {
"input": "$$cc.chorePerson",
"as": "dd",
"cond":
{
"$and": [
{"$eq": ["$$dd.personID", personID]},
{"$eq": ["$$cc.ordinal", ordinal ]}
] }
}
}
}
}
}
}}
])
This is what I get for an ordinal of 4, however, my output includes ordinals that I am not interested in.
[
{
"_id": "5e2d482cd8593e00162d0568",
"affiliation": "800_800",
"year": 2020,
"month": "January",
"weekNumber": 5,
"weekStart": "01/26/2020",
"weekEnd": "02/01/2020",
"chart": [
{
"_id": "5e330310c66e9e4084cda785",
"ordinal": 0,
"ordinalString": "Sunday",
"chorePerson": []
},
{
"_id": "5e330310c66e9e4084cda783",
"ordinal": 1,
"ordinalString": "Monday",
"chorePerson": []
},
{
"_id": "5e330310c66e9e4084cda780",
"ordinal": 2,
"ordinalString": "Tuesday",
"chorePerson": []
},
{
"_id": "5e330310c66e9e4084cda77e",
"ordinal": 3,
"ordinalString": "Wednesday",
"chorePerson": []
},
{
"_id": "5e330310c66e9e4084cda77c",
"ordinal": 4,
"ordinalString": "Thursday",
"chorePerson": [
{
"_id": "5e330310c66e9e4084cda77d",
"person": "Jo",
"personID": "5e2890268c63351b7c07dc26",
"phone": "8008008001",
"chore": "DC 1",
"choreID": "5e2929cf285338cb8cf375fc"
},
{
"_id": "5e330310c66e9e4084cda77e",
"person": "Jo",
"personID": "5e2890268c63351b7c07dc26",
"phone": "8008008001",
"chore": "DC 2",
"choreID": "5e2929cf285338cb8cf375fd"
}
]
},
{
"_id": "5e330310c66e9e4084cda77a",
"ordinal": 5,
"ordinalString": "Friday",
"chorePerson": []
},
{
"_id": "5e330310c66e9e4084cda778",
"ordinal": 6,
"ordinalString": "Saturday",
"chorePerson": []
}
],
"date": "2020-01-30T16:23:44.713Z",
"__v": 0
}
]
This is what I really want :
[
{
"_id": "5e2d482cd8593e00162d0568",
"affiliation": "800_800",
"year": 2020,
"month": "January",
"weekNumber": 5,
"weekStart": "01/26/2020",
"weekEnd": "02/01/2020",
"chart": [
{
"_id": "5e330310c66e9e4084cda77c",
"ordinal": 4,
"ordinalString": "Thursday",
"chorePerson": [
{
"_id": "5e330310c66e9e4084cda77d",
"person": "Jo",
"personID": "5e2890268c63351b7c07dc26",
"phone": "8008008001",
"chore": "DC 1",
"choreID": "5e2929cf285338cb8cf375fc"
},
{
"_id": "5e330310c66e9e4084cda77e",
"person": "Jo",
"personID": "5e2890268c63351b7c07dc26",
"phone": "8008008001",
"chore": "DC 2",
"choreID": "5e2929cf285338cb8cf375fd"
}
]
}
],
"date": "2020-01-30T16:23:44.713Z",
"__v": 0
}
]
You can try this :
ChoreChart.aggregate([
{ "$match": { "affiliation": affiliation, "year": weekYear, "weekNumber": weekNumber } }, {
"$addFields": {
"chart": {
"$map": {
/** $filter in input to retain only objects in array that match ordinal filter */
"input": { $filter: { input: '$chart', as: 'c', cond: { "$eq": ["$$c.ordinal", ordinal] } } },
"as": "cp",
"in": {
/** $mergeObjects to merge chorePerson filtered array field on each 'chart.chorePerson' */
$mergeObjects: ['$$cp', { chorePerson: { $filter: { input: '$$cp.chorePerson', as: 'each', cond: { "$eq": ["$$each.personID", personID] } } } }]
}
}
}
}
}
])
Test : MongoDB-Playground

Multiple groups and project using mongoose

I'm trying display aggregated data by a time range in Mongoose. My Data is like:
{company: "1" createdTime:"2017-01-01"}
{company: "1" createdTime:"2017-01-01"}
{company: "1" createdTime:"2017-01-02"}
{company: "2" createdTime:"2017-01-02"}
{company: "2" createdTime:"2017-01-03"}
{company: "3" createdTime:"2017-01-03"}
{company: "3" createdTime:"2017-01-03"}
I have created a query like:
{
"aggregate": [{
"$project": {
"frequency": {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$createdTime"
}
}
}
}, {
"$group": {
"_id": {
"date": "$frequency"
},
"count": {
"$sum": 1
}
}
}
]
}
and returns:
[
{
"_id": {
"date": "2017-01-01"
},
"count": 2
},
{
"_id": {
"date": "2017-01-02"
},
"count": 2
},
{
"_id": {
"date": "2017-01-03"
},
"count": 3
}
]
Which gives me the count for the overall occurrences for that particular date, which is fine.
I'm wanting to further break that down to give me the sum of occurrences per company for that date which should look something like this:
[
{
"_id": {
"date": "2017-01-01",
"companies": [{
"company": 1,
"count": 2
}]
},
"count": 2
},
{
"_id": {
"date": "2017-01-02",
"companies": [{
"company": 1,
"count": 1
},{
"company": 2,
"count": 1
}]
},
"count": 2
},
{
"_id": {
"date": "2017-01-03",
,
"companies": [{
"company": 2,
"count": 1
},{
"company": 3,
"count": 2
}]]
},
"count": 3
}
]
I've tried all sort of combinations but always get errors when i put in a second group and i can't work out how to do it?
EDIT
I wasn't able to use examples from the comments or they didn't work for my use case however, combining this information i came up with this:
{
"aggregate": [{
"$group": {
"_id": {
"company": "$company",
"date": {
"$dayOfMonth": "$createdTime"
},
"month": {
"$month": "$createdTime"
},
"year": {
"$year": "$createdTime"
}
},
"count": {
"$sum": 1
}
}
}, {
"$group": {
"_id": "$_id.company",
"events": {
"$push": {
"date": "$_id.date",
"month": "$_id.month",
"year": "$_id.year",
"count": "$count"
}
},
"totalEvents": {
"$sum": "$count"
}
}
}
]
}
which renders some results like:
[
{
"_id": "1",
"dateCount": [
{
"date": 3,
"month": 12,
"year": 2017,
"count": 40
},
{
"date": 10,
"month": 12,
"year": 2017,
"count": 8
}
]
}
]
Ideally the date would be yyyy-mm-dd but using the full date in the query would include the time also resulting in an entry for each time not date. I guess this will do for now, i can easily create a date from this albeit it's a little more cumbersome but it'll do until anything better comes along.
Edit 2
[{
"_id": ObjectId("5a38a03b33ccd28fb87f6dc9"),
"createdBy": ObjectId("5a2e203fa567425b2cb411fc"),
"appId": ObjectId("5a2e203fa567425b2cb411fb"),
"eventName": "event",
"userName": "person",
"userEmail": "company__29",
"organisation": "company",
"module": "module",
"createdTime": ISODate("2017-12-02T17:07:25.000Z")
"__v": 0
}
]
Query:
{
"aggregate": [{
"$project": {
"frequency": {
"$dateToString": {
"format": "%U",
"date": "$createdTime"
}
}
}
}, {
"$group": {
"_id": {
"frequency": "$frequency",
"organisation": "$organisation"
},
"count": {
"$sum": 1
}
}
}, {
"$group": {
"_id": {
"date": "$_id.frequency"
},
"count": {
"$sum": 1
},
"companies": {
"$push": {
"organisation": "$organisation",
"count": "$count"
}
}
}
}
]
}

Resources