Multiple grouping in mongodb - mongodb

Sample Colloection Data :
{
"_id" : ObjectId("5f30df23243ffsdfwer3d14568bf"),
"value" : {
"busId" : 200.0,
"status" : {
"code" : {
"id" : 1.0,
"key" : "2100",
"value" : "Complete"
}
}
}
}
My Query does provides the right result, but would like to squeeze the output more by using multiple grouping or $project or any other aggregators.
mongo Query:
db.suraj_coll.aggregate([
{
$addFields: {
"value.available": {
$cond: [
{
$in: [
"$value.status.code.value",
[
"Accept",
"Complete"
]
]
},
"Approved",
"Rejected"
]
}
}
},
{
"$group": {
"_id": {
busID: "$value.busId",
status: "$value.available"
},
"subtotal": {
$sum: 1
}
}
}
])
Output:
/* 1 */
{
"_id" : {
"busID" : 200.0,
"status" : "Approved"
},
"subtotal" : 3.0
}
/* 2 */
{
"_id" : {
"busID" : 200.0,
"status" : "Rejected"
},
"subtotal" : 1.0
}
Is it possible to squeeze the output more by using any further grouping ?
Output should look like below
{
"_id" : {
"busID" : 200.0,
"Approved" : 3.0
"Rejected" : 1.0
}
}
tried with $project, by keeping the count in a doc , but couldn't place the count against Approve or Rejected.
Any suggestion would be great.

You can use more two pipelines after your query,
$group by busID and push status and count in status
$project to convert status array to object using $arrayToObject and merge with busID using $mergeObjects
{
$group: {
_id: "$_id.busID",
status: {
$push: {
k: "$_id.status",
v: "$subtotal"
}
}
}
},
{
$project: {
_id: {
$mergeObjects: [
{ busID: "$_id" },
{ $arrayToObject: "$status" }
]
}
}
}
Playground

Related

MongoDB - Group by and count value, but treat per record as one

I want to group by and count follow_user.tags.tag_id per record, so no matter how many times the same tag_id show up on the same record, it only counts as 1.
My database structure looks like this:
{
"external_userid" : "EXID1",
"follow_user" : [
{
"userid" : "USERID1",
"tags" : [
{
"tag_id" : "TAG1"
}
]
},
{
"userid" : "USERID2",
"tags" : [
{
"tag_id" : "TAG1"
},
{
"tag_id" : "TAG2"
}
]
}
]
},
{
"external_userid" : "EXID2",
"follow_user" : [
{
"userid" : "USERID1",
"tags" : [
{
"tag_id" : "TAG2"
}
]
}
]
}
Here's my query:
[
{ "$unwind": "$follow_user" }, { "$unwind": "$follow_user.tags" },
{ "$group" : { "_id" : { "follow_user᎐tags᎐tag_id" : "$follow_user.tags.tag_id" }, "COUNT(_id)" : { "$sum" : 1 } } },
{ "$project" : { "total" : "$COUNT(_id)", "tagId" : "$_id.follow_user᎐tags᎐tag_id", "_id" : 0 } }
]
What I expected:
{
"total" : 1,
"tagId" : "TAG1"
},
{
"total" : 2,
"tagId" : "TAG2"
}
What I get:
{
"total" : 2,
"tagId" : "TAG1"
},
{
"total" : 2,
"tagId" : "TAG2"
}
$set - Create a new field follow_user_tags.
1.1. $setUnion - To distinct the value from the Result 1.1.1.
1.1.1. $reduce - Add the value of follow_user.tags.tag_id into array.
$unwind - Deconstruct follow_user_tags array field to multiple documents.
$group - Group by follow_user_tags and perform total count via $sum.
$project - Decorate output document.
db.collection.aggregate([
{
$set: {
follow_user_tags: {
$setUnion: {
"$reduce": {
"input": "$follow_user.tags",
"initialValue": [],
"in": {
"$concatArrays": [
"$$value",
"$$this.tag_id"
]
}
}
}
}
}
},
{
$unwind: "$follow_user_tags"
},
{
$group: {
_id: "$follow_user_tags",
total: {
$sum: 1
}
}
},
{
$project: {
_id: 0,
tagId: "$_id",
total: 1
}
}
])
Sample Mongo Playground

Aggregate Query geting count of most recent element from nest subdocuments

I have a mongodb database with many users and one of the subdocuments I track is file uploads and their statuses through a review process. Every file upload will have an attachment status eventually. I want to be able to pull some metrics to get the total of the current statuses for each uploaded file. I started building an aggregate query that pulls the latest attachment subdocument status from each file uploaded and count them.
The data structure is as follows:
"userName": "johnDoe",
"email": "johnDoe#gmail.com",
"uploads" : [
{
"_id" : ObjectId("adh12451e0012ce9da0"),
"fileName" : "TestDoc.txt",
"fileType" : "text/plain",
"created" : ISODate("2021-01-06T15:26:14.166Z"),
"attachmentStatus" : [ ]
},
{
"_id" : ObjectId("5ff5d6c066cacc0012ed655a"),
"fileName" : "testerABC.txt",
"fileType" : "text/plain",
"created" : ISODate("2021-01-06T15:26:56.027Z"),
"attachmentStatus" : [
{
"_id" : ObjectId("60884f733f88bd00129b9ad4"),
"status" : "Uploaded",
"date" : ISODate("2021-04-22T02:23:00Z")
},
{
"_id" : ObjectId("60884f733f88bd00129b9ad5"),
"status" : "Processing",
"date" : ISODate("2021-04-26T04:54:00Z")
}
]
},
{
"_id" : ObjectId("6075c82a19fdcc0012f81907"),
"fileName" : "Test file.docx",
"fileType" : "application/word",
"created" : ISODate("2021-04-13T16:34:50.955Z"),
"attachmentStatus" : [
{
"_id" : ObjectId("72844f733f88bd11479b9ad7"),
"status" : "Uploaded",
"date" : ISODate("2021-04-23T03:42:00Z")
},
{
"_id" : ObjectId("724986d73f88bd00147c9wt8"),
"status" : "Completed",
"date" : ISODate("2021-04-24T01:37:00Z")
}
]
}
]
"userName": "janeDoe",
"email": "janeDoe#gmail.com",
"uploads" : [
{
"_id" : ObjectId("ej9784652h0012ce9da0"),
"fileName" : "myResume.txt",
"fileType" : "text/plain",
"created" : ISODate("2021-02-13T12:36:14.166Z"),
"attachmentStatus" : [
{
"_id" : ObjectId("15dhdf6f88bd00147c9wt8"),
"status" : "Completed",
"date" : ISODate("2021-04-24T01:37:00Z")
}
]
},
How can I pull the latest attachment status out for each file uploaded and then summarize the statuses?
I want something like this:
{ "status" : "Completed", "Count" : 2 }
{ "status" : "Processing", "Count" : 1 }
...
I get very close with this Aggregate query, but it will grab each and every status and not just the the single most current Status for each file. (one current status per file).
db.myDB.aggregate([
{
"$match" : {
"uploads.attachmentStatus": {
"$elemMatch": { "status": { "$exists": true } }
}
}
},
{ $unwind: "$uploads"},
{ $unwind: "$uploads.attachmentStatus"},
{
$sortByCount: "$uploads.attachmentStatus.status"
},
{
$project: {
_id:0,
status: "$_id",
Count: "$count"
}
}
]).pretty();
Any suggestions?
Demo - https://mongoplayground.net/p/zzOR9qhqny0
{ $sort: { "uploads.attachmentStatus.date": -1 } }, to get the latest 1st
{ $group: { _id: "$uploads._id", status: { $first: "$uploads.attachmentStatus.status" } } } Group the records by uploads._id and take the top status (which is the latest status after the sort by date).
Query
{ $sort: { "uploads.attachmentStatus.date": -1 } },
{ $group: { _id: "$uploads._id", status: { $first: "$uploads.attachmentStatus.status" } } },
Complete query
db.collection.aggregate([
{ $match: { "uploads.attachmentStatus": { "$elemMatch": { "status": { "$exists": true } } } } },
{ $unwind: "$uploads" },
{ $unwind: "$uploads.attachmentStatus" },
{ $sort: { "uploads.attachmentStatus.date": -1 } },
{ $group: { _id: "$uploads._id", status: { $first: "$uploads.attachmentStatus.status" } } },
{ $sortByCount: "$status" },
{ $project: { _id: 0, status: "$_id", Count: "$count" } }
])

Sort documents by value in the last element of an array that matches filter. Mongodb

I have a collection of evaluationGroups with the following documents structure:
{
"_id" : ObjectId("60073749694fd4d81e4d677d"),
"AlertSettingId" : ObjectId("5ffddaaa0b1d2c30b191599a"),
"CurrentStatus" : "success",
"Evaluations" : [
{
"EvaluatedAt" : ISODate("2021-01-19T19:47:18.850Z"),
"ReferenceValue" : 1.0,
"Status" : "success"
},
{
"EvaluatedAt" : ISODate("2021-01-19T19:52:16.423Z"),
"ReferenceValue" : 1.0,
"Status" : "triggered"
},
{
"EvaluatedAt" : ISODate("2021-01-19T21:47:16.400Z"),
"ReferenceValue" : 1.0,
"Status" : "success"
}
]
}
{
"_id" : ObjectId("60085ec60a264ce3829a6335"),
"AlertSettingId" : ObjectId("5ffddaaa0b1d2c30b191599a"),
"CurrentStatus" : "triggered",
"Evaluations" : [
{
"EvaluatedAt" : ISODate("2021-01-20T18:03:01.040Z"),
"ReferenceValue" : 1.0,
"Status" : "noDataFound"
},
{
"EvaluatedAt" : ISODate("2021-01-20T22:04:43.983Z"),
"ReferenceValue" : 1.0,
"Status" : "triggered"
},
{
"EvaluatedAt" : ISODate("2021-01-20T22:39:43.978Z"),
"ReferenceValue" : 1.0,
"Status" : "triggered"
},
]
}
{
"_id" : ObjectId("60099092f7386972de3e8a05"),
"AlertSettingId" : ObjectId("5ffddaaa0b1d2c30b191599a"),
"CurrentStatus" : "success",
"Evaluations" : [
{
"EvaluatedAt" : ISODate("2021-01-21T14:32:48.697Z"),
"ReferenceValue" : 1.0,
"Status" : "noDataFound"
},
{
"EvaluatedAt" : ISODate("2021-01-21T14:37:44.929Z"),
"ReferenceValue" : 1.0,
"Status" : "triggered"
},
{
"EvaluatedAt" : ISODate("2021-01-21T14:42:44.928Z"),
"ReferenceValue" : 1.0,
"Status" : "triggered"
},
{
"EvaluatedAt" : ISODate("2021-01-21T15:17:46.052Z"),
"ReferenceValue" : 1.0,
"Status" : "success"
}
]
}
What I need to do is to sort all evaluation groups by the latest evaluation inside Evaluations (using EvaluatedAt property as the sort), but that the evaluation has also status triggered.
So, to sum up, I have to sort the groups, by the latest triggered Evaluation date.
I was looking at the question: Mongodb: sort documents by value in the last element of an array
And I liked this response of how to sort by last item, (because latest evaluations are at the end of the array in my case):
db.collection.aggregate([
{
$addFields: {
lastSent: {
$let: {
vars: {
last: {
$arrayElemAt: [ "$messages", -1 ]
}
},
in: "$$last.commData.sent.dateTime"
}
}
}
},
{ $sort: { lastSent: 1 } },
{ $project: { lastSent: 0 } }
])
But I would need to also filter evaluations by status "triggered" before getting the latest one.
How can achieve this using MongoDB aggregate query?
You can use $filter operator,
$filter to filter Evaluations array on the base of Status
$max to get latest EvaluatedAt form filtered result
db.collection.aggregate([
{
$addFields: {
lastSent: {
$let: {
vars: {
filtered: {
$filter: {
input: "$Evaluations",
cond: { $eq: ["$$this.Status", "triggered"] }
}
}
},
in: { $max: "$$filtered.EvaluatedAt" }
}
}
}
},
{ $sort: { lastSent: 1 } },
{ $project: { lastSent: 0 } }
])
Playground

How to get percentage total of data with group by date in MongoDB

How to get percentage total of data with group by date in MongoDB ?
Link example : https://mongoplayground.net/p/aNND4EPQhcb
I have some collection structure like this
{
"_id" : ObjectId("5ccbb96706d1d47a4b2ced4b"),
"date" : "2019-05-03T10:39:53.108Z",
"id" : 166,
"update_at" : "2019-05-03T10:45:36.208Z",
"type" : "image"
}
{
"_id" : ObjectId("5ccbb96706d1d47a4b2ced4c"),
"date" : "2019-05-03T10:39:53.133Z",
"id" : 166,
"update_at" : "2019-05-03T10:45:36.208Z",
"type" : "image"
}
{
"_id" : ObjectId("5ccbb96706d1d47a4b2ced4d"),
"date" : "2019-05-03T10:39:53.180Z",
"id" : 166,
"update_at" : "2019-05-03T10:45:36.208Z",
"type" : "image"
}
{
"_id" : ObjectId("5ccbb96706d1d47a4b2ced4e"),
"date" : "2019-05-03T10:39:53.218Z",
"id" : 166,
"update_at" : "2019-05-03T10:45:36.208Z",
"type" : "image"
}
And I have query in mongodb to get data of collection, how to get percentage of total data. in bellow example query to get data :
db.name_collection.aggregate(
[
{ "$match": {
"update_at": { "$gte": "2019-11-04T00:00:00.0Z", "$lt": "2019-11-06T00:00:00.0Z"},
"id": { "$in": [166] }
} },
{
"$group" : {
"_id": {
$substr: [ '$update_at', 0, 10 ]
},
"count" : {
"$sum" : 1
}
}
},
{
"$project" : {
"_id" : 0,
"date" : "$_id",
"count" : "$count"
}
},
{
"$sort" : {
"date" : 1
}
}
]
)
and this response :
{
"date" : "2019-11-04",
"count" : 39
},
{
"date" : "2019-11-05",
"count" : 135
}
how to get percentage data total from key count ? example response to this :
{
"date" : "2019-11-04",
"count" : 39,
"percentage" : "22%"
},
{
"date" : "2019-11-05",
"count" : 135,
"percentage" : "78%"
}
You have to group by null to get total count and then use $map to calculate the percentage. $round will be a useful operator in such case. Finally you can $unwind and $replaceRoot to get back the same number of documents:
db.collection.aggregate([
// previous aggregation steps
{
$group: {
_id: null,
total: { $sum: "$count" },
docs: { $push: "$$ROOT" }
}
},
{
$project: {
docs: {
$map: {
input: "$docs",
in: {
date: "$$this.date",
count: "$$this.count",
percentage: { $concat: [ { $toString: { $round: { $multiply: [ { $divide: [ "$$this.count", "$total" ] }, 100 ] } } }, '%' ] }
}
}
}
}
},
{
$unwind: "$docs"
},
{
$replaceRoot: { newRoot: "$docs" }
}
])
Mongo Playground

How to group by multiple object elements

I have sample data like below
[
{
brand:"iphone",
category:"mobile"
},
{
brand:"iphone",
category:"laptop"
},
{
brand:"lenova",
category:"laptop"
}
]
and expecting result as
[
{
brand:"iphone",
count:2
},
{
brand:"lenova",
count:1
},
{
category:"laptop",
count:2
},
{
category:"mobile",
count:1
}
]
Here I want group by same object with multiple fields and get there count. Can any one please let me how to do that in the mongoose.
I am not familiarised with Mongoose. Just tried in Mongoshell
db.getCollection('test').aggregate([
{
$group:{
_id:"$brand",
brand:{$first:"$brand"},
category:{$first:"$category"}
}
},
{$project:{_id:0}}
])
Possible only by using two queries.
Group By Brand
db.getCollection('pages').aggregate([
{
$group: {_id: "$brand", category: { $push: "$category" }}
},
{
$project : {
_id : 0, brand : "$_id", count : {$size : "$category"}
}
},
{ $unwind: { path: "$category", preserveNullAndEmptyArrays: true } }
])
Result:-
/* 1 */
{
"brand" : "lenova",
"count" : 1
}
/* 2 */
{
"brand" : "iphone",
"count" : 2
}
Group By Category
db.getCollection('pages').aggregate([
{
$group: {
_id: "$category", brand: { $push: "$brand" },
}
},
{
$project : {
_id : 0, category : "$_id", count : {$size : "$brand"}
}
},
{ $unwind: { path: "$brand", preserveNullAndEmptyArrays: true } },
])
Result:-
/* 1 */
{
"category" : "laptop",
"count" : 2
}
/* 2 */
{
"category" : "mobile",
"count" : 1
}
Merge them for the required output.
We can use $facet to run parallel aggregation on data.
The following query can get us the expected output:
db.collection.aggregate([
{
$facet:{
"brand_group":[
{
$group:{
"_id":"$brand",
"brand":{
$first:"$brand"
},
"count":{
$sum:1
}
}
},
{
$project:{
"_id":0
}
}
],
"category_group":[
{
$group:{
"_id":"$category",
"category":{
$first:"$category"
},
"count":{
$sum:1
}
}
},
{
$project:{
"_id":0
}
}
]
}
},
{
$project:{
"array":{
$concatArrays:["$brand_group","$category_group"]
}
}
},
{
$unwind:"$array"
},
{
$replaceRoot:{
"newRoot":"$array"
}
}
]).pretty()
Data set:
{
"_id" : ObjectId("5da5c0d0795c8651a7f508c2"),
"brand" : "iphone",
"category" : "mobile"
}
{
"_id" : ObjectId("5da5c0d0795c8651a7f508c3"),
"brand" : "iphone",
"category" : "laptop"
}
{
"_id" : ObjectId("5da5c0d0795c8651a7f508c4"),
"brand" : "lenova",
"category" : "laptop"
}
Output:
{ "brand" : "lenova", "count" : 1 }
{ "brand" : "iphone", "count" : 2 }
{ "category" : "laptop", "count" : 2 }
{ "category" : "mobile", "count" : 1 }