Aggregate $filter Between Elements Matching Conditions - mongodb

Data:
[
{
"_id" : ObjectId("597c48d222b29fc421e82d20"),
"Date" : "12/06/2017",
"Country" : "DEMO",
"RiderId" : "DEMO",
"VehicleId" : "DEMO",
"StartAddress" : "Colombo",
"StartLocation" : [
6.9270974,
79.8612478
],
"EndAddress" : "Kegalle,",
"EndLocation" : [
7.2476005,
80.3483415
],
"DepartureAddress" : "Dellogistics International (Pvt) Ltd, Colombo 04",
"DepartureLocation" : [
6.8824893,
79.8620031
],
"ArrivalAddress" : "Osro, Kegalle",
"ArrivalLocation" : [
7.2476005,
80.3483415
],
"IsLadiesOnly" : false,
"Notes" : "\"I'm travalleing from Bambalapitiya to Kegalle, small luggages are ok\"",
"Luggage" : 2.0,
"Detours" : 1.0,
"Route" : {
"Bounds" : {
"NorthEast" : [
7.2916216,
80.6341326
],
"SouthWest" : [
6.9270974,
79.8607731
]
},
"Legs" : [
{
"LegId" : 0.0,
"Distance" : 40033.0,
"Duration" : 4725.0,
"Price" : "240",
"StartAddress" : "Colombo",
"StartLocation" : [
6.9270974,
6.9270974
],
"EndAddress" : "Nittambuwa",
"EndLocation" : [
7.1420863,
80.1038061
],
"Ancestors" : []
},
{
"LegId" : 1.0,
"Distance" : 18008.0,
"Duration" : 1850.0,
"Price" : "480",
"StartAddress" : "Nittambuwa",
"StartLocation" : [
7.1420863,
80.1038061
],
"EndAddress" : "Warakapola",
"EndLocation" : [
7.2268383,
80.1959644
],
"Ancestors" : [
"Colombo"
]
},
{
"LegId" : 2.0,
"Distance" : 22478.0,
"Duration" : 2208.0,
"Price" : "720",
"StartAddress" : "Warakapola",
"StartLocation" : [
7.2268383,
80.1959644
],
"EndAddress" : "Kegalle",
"EndLocation" : [
7.2514362,
80.3466076
],
"Ancestors" : [
"Colombo",
"Nittambuwa"
]
}
]
},
"CreatedDate" : "2017-01-06T07:00:00.000Z",
"ModifiedDate" : "01/06/2017 12:30",
"Points" : "123"
}]
And the query is :
db.trips.aggregate([
{$match: { "Route.Legs": { $elemMatch: { "StartAddress": "Nittambuwa","Ancestors":{$nin:["Kegalle"]}} }, "Route.Legs.EndAddress":"Kegalle" }},
{ $project: {
RiderId: "$RiderId",
Legs: {
$filter: {
input: "$Route.Legs",
as: "leg",
cond: {
"$and": [
{ "$gte": [ "$$leg.LegId", <**Get the leg id by passing the start address**> ] },
{ "$lte": [ "$$leg.LegId", 3 ] }
]
}
}
}
}}
])
Basicaly inside the condition i need to query the collection and get the leg ID for comparison. i tried $where but it doesn't recognize the operator inside the condition.
Also why i cannot use $where, $nin inside the cond

You generally need to apply the condition by nesting another $filter within the outer $filter which looks up the matching array entry and matches the conditions. Then applying $map and $arrayElemAt to actually supply the "LegId" value for the $gte condition:
db.trips.aggregate([
{ "$match": {
"Route.Legs": {
"$elemMatch": {
"StartAddress": "Nittambuwa",
"Ancestors":{ "$nin": [ "Kegalle" ] }
}
},
"Route.Legs.EndAddress":"Kegalle"
}},
{ "$project": {
"RiderId": 1,
"Legs": {
"$filter": {
"input": "$Route.Legs",
"as": "l",
"cond": {
"$and": [
{ "$gte": [
"$$l.LegId",
{ "$arrayElemAt": [
{ "$map": {
"input": {
"$filter": {
"input": "$Route.Legs",
"as": "l",
"cond": {
"$and": [
{ "$eq": [ "$$l.StartAddress", "Nittambuwa" ] },
{ "$eq": [
{ "$size": {
"$setIntersection": [ [ "Kegalle" ], "$$l.Ancestors" ]
}},
0
]}
]
}
}
},
"as": "l",
"in": "$$l.LegId"
}},
0
]}
]},
{ "$lte": [ "$$l.LegId", 3 ] }
]
}
}
}
}}
])
You cannot really apply such things as $indexOfArray here to fetch the array index, because of the "multiple conditions" required to match the array element.
Whilst there is not a "direct" equivalence to $nin as a "logical operator" for an aggregation condition, you can use alternatives. Here I apply $setIntersection as it's probably the best to compare two "unique" arrays. The logic says when the $size of the "intersection" is 0, there there was no match. Thus meeting the same criteria as $nin.
Of course the "multiple" conditions are all wrapped by $and, since that is what it does.
The result of course filters the array items between the "LegId" value that matched the conditions, and the "end" value supplied, which is 3:
/* 1 */
{
"_id" : ObjectId("597c48d222b29fc421e82d20"),
"RiderId" : "DEMO",
"Legs" : [
{
"LegId" : 1.0,
"Distance" : 18008.0,
"Duration" : 1850.0,
"Price" : "480",
"StartAddress" : "Nittambuwa",
"StartLocation" : [
7.1420863,
80.1038061
],
"EndAddress" : "Warakapola",
"EndLocation" : [
7.2268383,
80.1959644
],
"Ancestors" : [
"Colombo"
]
},
{
"LegId" : 2.0,
"Distance" : 22478.0,
"Duration" : 2208.0,
"Price" : "720",
"StartAddress" : "Warakapola",
"StartLocation" : [
7.2268383,
80.1959644
],
"EndAddress" : "Kegalle",
"EndLocation" : [
7.2514362,
80.3466076
],
"Ancestors" : [
"Colombo",
"Nittambuwa"
]
}
]
}
All copied and pasted from the data supplied and results of the statement issued here.

Related

mongodb getting an array inside a nested array

I'm still new to mongodb, I have this basic enrollment system data:
{ "_id" : ObjectId("62277d92a561e550d5ec73ca"), "sid" : 1, "sname" : "sad", "semail" : "dsa", "scourse" : "it", "enrolled" : [ { "subjid" : 3 } ] }
{ "_id" : ObjectId("6227875bdbcc41a56a863697"), "sid" : 2, "sname" : "daws", "semail" : "dws", "scourse" : "cs", "enrolled" : [ { "subjid" : 1, "grades" : [ { "prelim" : "A", "midterm" : "B", "prefinal" : "B", "final" : "A" } ] }, { "subjid" : 2, "grades" : [ { "prelim" : "D", "midterm" : "A", "prefinal" : "B", "final" : "F" } ] } ] }
I want display the grades of sid 2 who has enrolled subjid 1.
I tried using this aggregation line:
db.students2.aggregate( [{"$match":{"sid":{"$eq":2},"enrolled.subjid":{"$eq":2}}}, {$group: {_id:'$enrolled.subjid[1]', prelim:{$first:'$enrolled.grades.prelim'},midterm:{$first:'$enrolled.grades.midterm'},prefinal:{$first:'$enrolled.grades.prefinal'},"final":{$first:'$enrolled.grades.final'} } } ])
but this was the result:
{ "_id" : [ ], "prelim" : [ [ "A" ], [ "D" ] ], "midterm" : [ [ "B" ], [ "A" ] ], "prefinal" : [ [ "B" ], [ "B" ] ], "final" : [ [ "A" ], [ "F" ] ] }
I only wanted to get the grades of subjid 1 but it also got the grades of subjid 2
Maybe you need something like this:
db.collection.aggregate([
{
"$match": {
"sid": 2,
"enrolled.subjid": 1
}
},
{
"$addFields": {
"enrolled": {
"$filter": {
"input": "$enrolled",
"as": "en",
"cond": {
$eq: [
"$$en.subjid",
1
]
}
}
}
}
},
{
$unwind: "$enrolled"
},
{
$unwind: "$enrolled.grades"
},
{$limit:1}
,
{
$project: {
_id: "$enrolled.subjid",
prelim: "$enrolled.grades.prelim",
midterm: "$enrolled.grades.midterm",
prefinal: "$enrolled.grades.prefinal",
"final": "$enrolled.grades.final"
}
}
])
Explained:
Match the necessary documents (sid=2,subjid=1)
Filter only the enrolled elements based on subjid ( subjid=1 )
unwind the two array
limit to the first result document available only in case there is more.
project the necesary fields
playground

Finding intersection between two object arrays based on field

In mongo collection I have documents of following structure.
{
"_id" : "Suzuki",
"qty" : 10,
"plates" : [
{
"rego" : "1QX-WA-123",
"date" : 1516374000000.0
},
{
"rego" : "1QX-WA-456",
"date" : 1513369800000.0
}
],
"accounts" : [
{
"_id" : "23kpi9MD4KnTvnaW7",
"createdAt" : 1513810712802.0,
"date" : 1503446400000.0,
"type" : "Suzuki",
"rego" : "1QX-WA-123",
},
{
"_id" : "2Wqrd4yofvLmqLm5H",
"createdAt" : 1513810712802.0,
"date" : 1501632000000.0,
"type" : "Suzuki",
"rego" : "1QX-WA-111",
}
]
}
I am trying to filter objects in accounts array so that it contains only those objects whose rego exists in plates array.
I tried following query, however, it throws an error: all operands of $setIntersection must be arrays. One argument if of type object.
db.getCollection('dummy').aggregate([{
$project: {
plates: 1,
accounts: 1,
intersect: {
$setIntersection: [
{ $arrayElemAt: [ "$plates", 0 ] },
{ $arrayElemAt: [ "$accounts", 4 ] }
]
}
}
}])
The expected output I am looking for is:
{
"_id" : "Suzuki",
"qty" : 10,
"plates" : [
{
"rego" : "1QX-WA-123",
"date" : 1516374000000.0
},
{
"rego" : "1QX-WA-456",
"date" : 1513369800000.0
}
],
"accounts" : [
{
"_id" : "23kpi9MD4KnTvnaW7",
"createdAt" : 1513810712802.0,
"date" : 1503446400000.0,
"type" : "Suzuki",
"rego" : "1QX-WA-123",
}
]
}
So there are a couple of ways, but what you really are after is simply to $filter instead.
Using $in would likely be the first choice:
db.getCollection('dummy').aggregate([
{ "$addFields": {
"accounts": {
"$filter": {
"input": "$accounts",
"cond": {
"$in": [ "$$this.rego", "$plates.rego" ]
}
}
}
}}
])
Or if you don't have MongoDB 3.4 at least, then using $anyElementTrue:
db.getCollection('dummy').aggregate([
{ "$project": {
"qty": 1,
"plates": 1,
"accounts": {
"$filter": {
"input": "$accounts",
"as": "acc",
"cond": {
"$anyElementTrue": {
"$map": {
"input": "$plates.rego",
"as": "rego",
"in": { "$eq": [ "$$rego", "$$acc.rego" ] }
}
}
}
}
}
}}
])
Or even $setIsSubset:
db.getCollection('dummy').aggregate([
{ "$project": {
"qty": 1,
"plates": 1,
"accounts": {
"$filter": {
"input": "$accounts",
"as": "acc",
"cond": {
"$setIsSubset": [ ["$$acc.rego"], "$plates.rego" ]
}
}
}
}}
])
It's really not a $setIntersection for this type of operation, since that would need a comparison on "just the field values" as a "set", and the output is really just "that" and not the "objects".
You could do something silly with matching array indexes to the produced "set" positions:
db.getCollection('dummy').aggregate([
{ "$addFields": {
"accounts": {
"$map": {
"input": { "$setIntersection": ["$plates.rego", "$accounts.rego"] },
"in": {
"$arrayElemAt": [
"$accounts",
{ "$indexOfArray": [ "$accounts.rego", "$$this" ] }
]
}
}
}
}}
])
But in reality you probably really just want the $filter result as being far more practical. And if you want that output as a "set" then you can simply wrap the $filter output with a $setDifference or like operator to make the entries "unique".
In all variations these return:
{
"_id" : "Suzuki",
"qty" : 10.0,
"plates" : [
{
"rego" : "1QX-WA-123",
"date" : 1516374000000.0
},
{
"rego" : "1QX-WA-456",
"date" : 1513369800000.0
}
],
"accounts" : [
{
"_id" : "23kpi9MD4KnTvnaW7",
"createdAt" : 1513810712802.0,
"date" : 1503446400000.0,
"type" : "Suzuki",
"rego" : "1QX-WA-123"
}
]
}
Showing the items in the "accounts" array "filtered" as matching the respective "rego" amounts from the "plates" array.

Conditionally Count Value from Array into New Field

Consider the following data:
{
"_id" : ObjectId("592ffb3d257acc76fc0eecd7"),
"primaryProcessName" : "BI",
"dateTimeStamp" : ISODate("2017-06-01T11:32:12.834+0000"),
"tag" : [
{
"key" : "processname",
"value" : "NEUpdateService",
"value_original" : "NEUpdateService"
},
{
"key" : "processstageid",
"value" : "inprocess",
"value_original" : "InProcess"
},
]
}
{
"_id" : ObjectId("592ffb3d257acc76fc0eecdd"),
"primaryProcessName" : "BI",
"dateTimeStamp" : ISODate("2017-06-01T11:32:13.345+0000"),
"tag" : [
{
"key" : "processname",
"value" : "CommissionPaymentSend",
"value_original" : "CommissionPaymentSend"
},
{
"key" : "processstageid",
"value" : "faulted",
"value_original" : "Faulted"
},
]
}
{
"_id" : ObjectId("592ffb3d257acc76fc0eece4"),
"primaryProcessName" : "BI",
"dateTimeStamp" : ISODate("2017-06-01T11:32:13.745+0000"),
"tag" : [
{
"key" : "processname",
"value" : "commonbusinessintegratorservice",
"value_original" : "CommonBusinessIntegratorService"
},
{
"key" : "processstageid",
"value" : "inprocess",
"value_original" : "InProcess"
},
]
}
{
"_id" : ObjectId("592ffb3d257acc76fc0eecea"),
"primaryProcessName" : "BI",
"dateTimeStamp" : ISODate("2017-06-01T11:32:13.876+0000"),
"tag" : [
{
"key" : "processname",
"value" : "commonbusinessintegratorservice",
"value_original" : "CommonBusinessIntegratorService"
},
{
"key" : "processstageid",
"value" : "inprocess",
"value_original" : "InProcess"
},
]
}
{
"_id" : ObjectId("592ffb3e257acc76fc0eecf1"),
"primaryProcessName" : "BI",
"dateTimeStamp" : ISODate("2017-06-01T11:32:14.193+0000"),
"tag" : [
{
"key" : "processname",
"value" : "SmartComplianceMessenger",
"value_original" : "SmartComplianceMessenger"
},
{
"key" : "processstageid",
"value" : "complete",
"value_original" : "Complete"
},
]
}
I am trying to write a query to aggregate this data to show in the following format:
{
"Total" : 1982, "InProcess" : 991, "Complete" : 991, "Faulted" : 0,
"name" : "SmartComplianceMessenger",
"displayName" : "SmartComplianceMessenger",
"drillDownUrl" : "process/forprimary/name/SmartComplianceMessenger"
},
{
"Total" : 122333, "InProcess" : 56375, "Complete" : 54856, "Faulted" : 11102,
"name" : "NEUpdateService",
"displayName" : "NEUpdateService",
"drillDownUrl" : "process/forprimary/name/NEUpdateService"
},
....
This is what I have so far:
db.ActivityNotice.aggregate([
{$match: {
dateTimeStamp: {
$gte: ISODate("2017-06-01T11:00:00.000Z")
, $lt: ISODate("2017-06-01T11:45:00.000Z")
}
}},
{$group :
{
_id: {process: "$primaryProcessName"} //, status:"$processStageId"
, Total:{$sum:1}
, InProcess: {$sum:0}// { $sum: {$cond: [{$eq: ["$processStageId","InProcess"]},1,0]}}
, Complete: {$sum:0} // { $sum: {$cond: [{$eq: ["$processStageId","Complete"]},1,0]}}
, Faulted: {$sum:0} // { $sum: {$cond: [{$eq: ["$processStageId","Faulted"]},1,0]}}
, Test: { $sum: {$cond: [{$eq: ["tag.key","processstageid"]},1,0]}}
}},
{$project: {
_id: 0,
name: "$_id.process", displayName: "$_id.process",
drillDownUrl: { $concat: [ "process/forprimary/name/", "$_id.process" ] },
Total: 1, InProcess: 1 , Complete: 1, Faulted: 1, Test: 1
}}
])
The challenge I am facing is selecting the value for the "processname" key from tags into a new field, called processName and the value for "processtageid" into a new field so I can do the sum on those values.
Any help would be greatly appreciated.
You want $filter and $size for the most efficient way:
{ "$group": {
"_id": "$primaryProcessName",
"Total": { "$sum": 1 },
"InProcess": {
"$sum": {
"$size": {
"$filter": {
"input": "$tag",
"as": "t",
"cond": {
"$and": [
{ "$eq": [ "$$t.key", "processstageid" ] },
{ "$eq": [ "$$t.value","inprocess"] }
]
}
}
}
}
},
"Complete": {
"$sum": {
"$size": {
"$filter": {
"input": "$tag",
"as": "t",
"cond": {
"$and": [
{ "$eq": [ "$$t.key", "processstageid" ] },
{ "$eq": [ "$$t.value","complete"] }
]
}
}
}
}
},
"Faulted": {
"$sum": {
"$size": {
"$filter": {
"input": "$tag",
"as": "t",
"cond": {
"$and": [
{ "$eq": [ "$$t.key", "processstageid" ] },
{ "$eq": [ "$$t.value","faulted"] }
]
}
}
}
}
}
}}
$filter has it's own condition for which we can use $and to match the multiple conditions of different properties of the array element. This reduces the array to only the entries that match, where you can then take the $size

multiple group in mongodb

My collection look likes this.
{
"_id" : ObjectId("572c4ed33c1b5f51215219a8"),
"name" : "This is an angular course, and integeration with php",
"description" : "After we connected we can query or update the database just how we would using the mongo API with the exception that we use a callback. The format for callbacks is always callback(error, value) where error is null if no exception has occured. The update methods save, remove, update and findAndModify also pass the lastErrorObject as the last argument to the callback function.",
"difficulty_level" : "Beginner",
"type" : "Fast Track",
"tagged_skills" : [
{
"_id" : "5714e894e09a0f7d804b2254",
"name" : "PHP"
},
{
"_id" : "5717355806313b1f1715fa50",
"name" : "c++"
},
{
"_id" : "5715025bc2c5dbb4675180da",
"name" : "java"
},
{
"_id" : "5714f188ec325f5359979e33",
"name" : "symphony"
}
]}
I want to group by the collection on the basis of type,difficulty level and tagged skills and also get the count in a single query.
I am not been able to add skills count.
My query is as follows:-
db.course.aggregate([
{$unwind:"$tagged_skills"},
{$group:{
_id:null,
skills: { $addToSet: "$tagged_skills.name" },
Normal_df:{$sum:{
"$cond": [
{ "$eq":[ "$difficulty_level","Normal"] },
1,
0
]
}},
Beginner_df:{$sum:{
"$cond": [
{ "$eq":[ "$difficulty_level","Beginner"] },
1,
0
]
}},
Intermediate_df:{$sum:{
"$cond": [
{ "$eq":[ "$difficulty_level","Intermediate"] },
1,
0
]
}},
Advanced_df:{$sum:{
"$cond": [
{ "$eq":[ "$difficulty_level","Advanced"] },
1,
0
]
}},
Fast_Track_type:{$sum:{
"$cond": [
{ "$eq":[ "$type","Fast Track"] },
1,
0
]
}},
Normal_type:{$sum:{
"$cond": [
{ "$eq":[ "$type","Normal"] },
1,
0
]
}},
Beginner_type:{$sum:{
"$cond": [
{ "$eq":[ "$type","Beginner"] },
1,
0
]
}},
Normal_Track_type:{$sum:{
"$cond": [
{ "$eq":[ "$type","Normal Track"] },
1,
0
]
}},
}}
])
The result is as follows:-
{
"_id" : null,
"skills" : [
"SQL",
"PHP",
"java",
"Angular Js",
"Laravel 23",
"c++",
"Node Js",
"symphony",
"Mysql",
"Express Js",
"JAVA"
],
"Normal_df" : 1,
"Beginner_df" : 14,
"Intermediate_df" : 7,
"Advanced_df" : 2,
"Fast_Track_type" : 8,
"Normal_type" : 6,
"Beginner_type" : 1,
"Normal_Track_type" : 9
}
I also want to get all skills with their count.
To get all the skills with their count, you need to first get a list of all the skills. You can obtain this list with running a distinct command on the approapriate fields. With this list you can then construct the appropriate $group pipeline document that will use the $sum and $cond operators.
Consider the following use case:
var difficultyLevels = db.course.distinct("difficulty_level"),
types = db.course.distinct("type"),
skills = db.course.distinct("tagged_skills.name"),
unwindOperator = { "$unwind": "$tagged_skills" },
groupOperator = {
"$group": {
"_id": null,
"skills": { "$addToSet": "$tagged_skills.name" }
}
};
difficultyLevels.forEach(function (df){
groupOperator["$group"][df+"_df"] = {
"$sum": {
"$cond": [ { "$eq": ["$difficulty_level", df] }, 1, 0]
}
}
});
types.forEach(function (type){
groupOperator["$group"][type.replace(" ", "_")+"_type"] = {
"$sum": {
"$cond": [ { "$eq": ["$type", type] }, 1, 0]
}
}
});
skills.forEach(function (skill){
groupOperator["$group"][skill] = {
"$sum": {
"$cond": [ { "$eq": ["$tagged_skills.name", skill] }, 1, 0]
}
}
});
//printjson(groupOperator);
db.course.aggregate([unwindOperator, groupOperator]);
In the first line, we obtain an array with the difficulty levels by running the distinct command on the difficulty_level field
db.course.distinct("difficulty_level")
This will produce the array
var difficultyLevels = ["Normal", "Beginner", "Intermediate", "Advanced"]
Likewise, the preceding distinct operations will return the list of possible unique values for that key.
After getting these lists, you can then create the pipeline objects using the forEach() method to populate the document keys for each given item in the list. You can then use the resulting document, which will look like this
printjson(groupOperator);
{
"$group" : {
"_id" : null,
"skills" : {
"$addToSet" : "$tagged_skills.name"
},
"Beginner_df" : {
"$sum" : {
"$cond" : [
{
"$eq" : [
"$difficulty_level",
"Beginner"
]
},
1,
0
]
}
},
"Intermediate_df" : {
"$sum" : {
"$cond" : [
{
"$eq" : [
"$difficulty_level",
"Intermediate"
]
},
1,
0
]
}
},
"Fast_Track_type" : {
"$sum" : {
"$cond" : [
{
"$eq" : [
"$type",
"Fast Track"
]
},
1,
0
]
}
},
"PHP" : {
"$sum" : {
"$cond" : [
{
"$eq" : [
"$tagged_skills.name",
"PHP"
]
},
1,
0
]
}
},
"c++" : {
"$sum" : {
"$cond" : [
{
"$eq" : [
"$tagged_skills.name",
"c++"
]
},
1,
0
]
}
},
"java" : {
"$sum" : {
"$cond" : [
{
"$eq" : [
"$tagged_skills.name",
"java"
]
},
1,
0
]
}
},
"symphony" : {
"$sum" : {
"$cond" : [
{
"$eq" : [
"$tagged_skills.name",
"symphony"
]
},
1,
0
]
}
},
"C#" : {
"$sum" : {
"$cond" : [
{
"$eq" : [
"$tagged_skills.name",
"C#"
]
},
1,
0
]
}
},
"Scala" : {
"$sum" : {
"$cond" : [
{
"$eq" : [
"$tagged_skills.name",
"Scala"
]
},
1,
0
]
}
},
"javascript" : {
"$sum" : {
"$cond" : [
{
"$eq" : [
"$tagged_skills.name",
"javascript"
]
},
1,
0
]
}
}
}
}

count documents by specific nested fields values with aggregation-framework in MongoDB

Here's my db.
{
"_id" : ObjectId("579cab6c6aba30f42a57a979"),
"iecode" : "P1111",
"country" : "India",
"totalTreatmentArms" : 3,
"treatmentArms" : [
{
"mechanismOrPkg" : "Mechanism",
"mechanism" : "mechanism1"
},
{
"mechanismOrPkg" : "Mechanism",
"mechanism" : "mechanism2"
},
{
"mechanismOrPkg" : "Package",
"mechanism" : "mechanism1"
}
]
}
{
"_id" : ObjectId("579cab7a6aba30f42a57a97a"),
"iecode" : "P1111",
"country" : "Canada",
"totalTreatmentArms" : 3,
"treatmentArms" : [
{
"mechanismOrPkg" : "Mechanism",
"mechanism" : "mechanism1"
},
{
"mechanismOrPkg" : "Mechanism",
"mechanism" : "mechanism2"
},
{
"mechanismOrPkg" : "Package",
"mechanism" : ""
}
]
}
{
"_id" : ObjectId("579cabac6aba30f42a57a97b"),
"iecode" : "P2222",
"country" : "India",
"totalTreatmentArms" : 1,
"treatmentArms" : [
{
"mechanismOrPkg" : "Package",
"mechanism" : ""
}
]
}
{
"_id" : ObjectId("579cabe76aba30f42a57a97c"),
"iecode" : "P3333",
"country" : "India",
"totalTreatmentArms" : 2,
"treatmentArms" : [
{
"mechanismOrPkg" : "Mechanism",
"mechanism" : "mechanism1"
},
{
"mechanismOrPkg" : "Package",
"mechanism" : ""
}
]
}
Please note that there are two records with iecode : P1111 and I want result distinguished on iecode so only one record will be considered(any one).
Now my requirement is I want count on field mechanismOrPkg. If it contains value Package then we will increment package with one. If the value is Mechanism then we will consider value of field mechanism and its respected values count will be considered.
So final result will be like this
{
"_id" : null,
"totalPackage" : 3,
"totalMechanism1" : 2,
"totalMechanism2" : 1
}
Please ask if it sounds confusing. Let me know even if this kind of aggregation is possible with query or I have to do server side filtering?
Thanks.
Edit
Possible values of mechanismOrPkg : Package or Mechanism
Possible values of mechanism : mechanism1 or mechanism2
Accomplishing the above would require using the $cond operator in the $sum accumulator operator. The $cond operator will evaluate a logical condition based on its first argument (if) and then returns the second argument where the evaluation is true (then) or the third argument where false (else). This converts the true/false logic into 1 and 0 numerical values that feed into $sum respectively. So for instance, when you want to aggregate the count for the "Package" value, the logic follows:
{
"$sum": {
"$cond": [ { "$eq": [ "$treatmentArms.mechanismOrPkg", "Package" ] }, 1, 0 ]
}
}
As a resulting pipeline, you need to run the aggregation operation
db.collection.aggregate([
{
"$group": {
"_id": "$iecode",
"treatmentArms": { "$first": "$treatmentArms" }
}
},
{ "$unwind": "$treatmentArms" },
{
"$group": {
"_id": null,
"totalPackage": {
"$sum": {
"$cond": [
{ "$eq": [ "$treatmentArms.mechanismOrPkg", "Package" ] },
1, 0
]
}
},
"totalMechanism1":{
"$sum": {
"$cond": [
{
"$and": [
{ "$eq": [ "$treatmentArms.mechanismOrPkg", "Mechanism" ] },
{ "$eq": [ "$treatmentArms.mechanism", "mechanism1" ] }
]
},
1,
0 ]
}
},
"totalMechanism2": {
"$sum": {
"$cond": [
{
"$and": [
{ "$eq": [ "$treatmentArms.mechanismOrPkg", "Mechanism" ] },
{ "$eq": [ "$treatmentArms.mechanism", "mechanism2" ] }
]
},
1,
0 ]
}
}
}
}
])
Sample Output
{
"_id" : null,
"totalPackage" : 3,
"totalMechanism1" : 2,
"totalMechanism2" : 1
}