mongodb getting an array inside a nested array - mongodb

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

Related

Get all possible combinations from array in MongoDB aggregation 🚀

How to do aggregation ($group) by the same values ​​from the array? Not all at once, but few or all, if any. I can do $group by one word, but I also need all possible variations...
Collection example:
{"keywords": ["gta", "distribution", "keys"]}
{"keywords": ["gta", "online", "moto", "races"]}
{"keywords": ["gta", "online", "samp"]}
Result example:
"gta" - 3 matches
"online" - 2 matches
"gta online" - 2 matches
You could use $reduce to extract all combinations of pairs from an array. I've started from this post and I've added the current item, $unwind the
initial array and count the items :
db.test.aggregate([
{
$project: {
pairs: {
$reduce: {
input: { $range: [0, { $size: "$keywords" }] },
initialValue: [],
in: {
$concatArrays: [
"$$value",
[[{ $arrayElemAt: ["$keywords", "$$this"] }]],
{
$let: {
vars: { i: "$$this" },
in: {
$map: {
input: { $range: [{ $add: [1, "$$i"] }, { $size: "$keywords" }] },
in: [{ $arrayElemAt: ["$keywords", "$$i"] }, { $arrayElemAt: ["$keywords", "$$this"] }]
}
}
}
}
]
}
}
}
}
}, {
$unwind: "$pairs"
}, {
$group: {
_id: "$pairs",
count: { $sum: 1 }
}
}
])
Output :
{ "_id" : [ "online", "samp" ], "count" : 1 }
{ "_id" : [ "gta", "samp" ], "count" : 1 }
{ "_id" : [ "online", "races" ], "count" : 1 }
{ "_id" : [ "moto", "races" ], "count" : 1 }
{ "_id" : [ "gta", "keys" ], "count" : 1 }
{ "_id" : [ "races" ], "count" : 1 }
{ "_id" : [ "gta", "distribution" ], "count" : 1 }
{ "_id" : [ "samp" ], "count" : 1 }
{ "_id" : [ "distribution", "keys" ], "count" : 1 }
{ "_id" : [ "gta" ], "count" : 3 }
{ "_id" : [ "online" ], "count" : 2 }
{ "_id" : [ "keys" ], "count" : 1 }
{ "_id" : [ "gta", "online" ], "count" : 2 }
{ "_id" : [ "moto" ], "count" : 1 }
{ "_id" : [ "online", "moto" ], "count" : 1 }
{ "_id" : [ "distribution" ], "count" : 1 }
{ "_id" : [ "gta", "moto" ], "count" : 1 }
{ "_id" : [ "gta", "races" ], "count" : 1 }
If you need more combinations, you may need to update the $reduce stage above

Aggregate $filter Between Elements Matching Conditions

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.

Conditional Count in Subarray Using mongoDB

I have a large dataset that I'd like to use to describe performance of a test. The database is as follows:
db.test.insertMany({
"_id" : ObjectId("58e574a768afb6085ec3a388"),
"tests" : [
{
"name" : "2",
"evaluation" : [
{
"aHigh" : [1,2],
"aLow" : [ ],
"zHigh" : [ ],
"zLow" : [1,3]
},
{
"aHigh" : [1,4],
"aLow" : [2],
"zHigh" : [ 3],
"zLow" : [ ]
},
{
"aHigh" : [ ],
"aLow" : [1,2,3],
"zHigh" : [1,2,3,4],
"zLow" : [ ]
},]
}
]
},
{
"_id" : ObjectId("58eba09e51f7f631dd24aa1c"),
"tests" : [
{
"name" : "2",
"evaluation" : [
{
"aHigh" : [2],
"aLow" : [3 ],
"zHigh" : [ ],
"zLow" : [1,2,3,4]
},
{
"aHigh" : [ ],
"aLow" : [ ],
"zHigh" : [ ],
"zLow" : [3,4]
},
{
"aHigh" : [1,2],
"aLow" : [3,4],
"zHigh" : [ ],
"zLow" : [1,2,3,4]
},]
}
]
})
I have a set of conditional logic that I need to apply and count the number of outcomes. Essentially, I need to know how many evaluations meet a certain criteria. The logic is:
case1: if the `$size` of the array `aHigh` is `$gte` to 3 AND
if the `$size` of the array `aLow` is `$lt` to 1
then AHI
case2: if the `$size` of the array `aLow` is `$gte` to 3 AND
if the `$size` of the array `aHigh` is `$lt` to 1
then ALO
case3: if the `$sum` of `$size` of the array `aHigh` and the `$size` of the array `aLow` is `$gte` to 3
then AVR
My expected output is essentially a list. I want something to the effect of
{ "_id" : "AHI", "count" : 5 }
{ "_id" : "ALO", "count" : 15 }
{ "_id" : "AVR", "count" : 8 }
{ "_id" : "ZHI", "count" : 4 }
{ "_id" : "ZLO", "count" : 11 }
{ "_id" : "ZVR", "count" : 10 }
I cannot change the structure of the database. I have been using aggregate to get information. This code does not get me what I want, but it's what I've come up with so far.
db.test.aggregate([
{ $unwind: "$tests" },
    { $unwind: "$tests.evaluation" },
    { $unwind: "$tests.evaluation.aHigh"},
    { $unwind: "$tests.evaluation.aLow"},
{ $project: {
"results" :
{
$switch: {
branches: [
{
case: { $and : [ { $gte : [ { $size : "$tests.evaluation.aHigh" }, 1 ] },
{ $lt : [ { $size : "$tests.evaluation.aLow" }, 1 ] }
] },
then: "AHI"
},
{
case: { $and : [ { $gte : [ { $size : "$tests.evaluation.aLow" }, 1 ] },
{ $lt : [ { $size : "$tests.evaluation.aHigh" }, 1 ] }] },
then: "ALO"
},
{
case: { $gte : [ {$sum: [ {$size : "$tests.evaluation.aHigh" } , { $size : "$tests.evaluation.aLow" } ] }, 1 ] },
then: "AVR"
}
],
default: ""
}
}}}
])
edit:
I'd like to now be able to do the same thing for both the a and for the z.
You have one too many $unwind.
Updated your aggregation query to remove extra $unwindstage and added $group to count the results from $project stage.
db.test.aggregate([
{ $unwind: "$tests" },
{ $unwind: "$tests.evaluation" },
{ $project: {
"results" : {
$switch: {
branches: [
{
case: { $and : [ { $gte : [ { $size : "$tests.evaluation.aHigh" }, 1 ] },
{ $lt : [ { $size : "$tests.evaluation.aLow" }, 1 ] }
] },
then: "AHI"
},
{
case: { $and : [ { $gte : [ { $size : "$tests.evaluation.aLow" }, 1 ] },
{ $lt : [ { $size : "$tests.evaluation.aHigh" }, 1 ] }] },
then: "ALO"
},
{
case: { $gte : [ {$sum: [ {$size : "$tests.evaluation.aHigh" } , { $size : "$tests.evaluation.aLow" } ] }, 1 ] },
then: "AVR"
}
],
default: ""
}
}}},
{ $group:{_id:"$results", count:{$sum:1}}}
])

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
]
}
}
}
}

How do I create nested aggregations with count on MongoDB?

I am learning MongoDB in order to see if it matches our needs.
Currently we use heavily aggregations, so I am testing the flexibility of the Aggregation Framework.
I started with this hierarchy
db.companytest3.insert({"name":"A", age:7})
db.companytest3.insert({"name":"B", age:17, owner:"A"})
db.companytest3.insert({"name":"C", age:12, owner:"A"})
db.companytest3.insert({"name":"D", age:7, owner:"B"})
db.companytest3.insert({"name":"E", age:13, owner:"B"})
db.companytest3.insert({"name":"F", age:23, owner:"C"})
So I have:
db.companytest3.find()
{ "_id" : ObjectId("5457c2c0fa82c305e0b80006"), "name" : "A", "age" : 7 }
{ "_id" : ObjectId("5457c2cafa82c305e0b80007"), "name" : "A", "age" : 7 }
{ "_id" : ObjectId("5457c2d0fa82c305e0b80008"), "name" : "B", "age" : 17, "owner" : "A" }
{ "_id" : ObjectId("5457c2d6fa82c305e0b80009"), "name" : "C", "age" : 12, "owner" : "A" }
{ "_id" : ObjectId("5457c2ddfa82c305e0b8000a"), "name" : "D", "age" : 7, "owner" : "B" }
{ "_id" : ObjectId("5457c2e4fa82c305e0b8000b"), "name" : "E", "age" : 13, "owner" : "B" }
{ "_id" : ObjectId("5457c2eafa82c305e0b8000c"), "name" : "F", "age" : 23, "owner" : "C" }
My goal is to aggregate the children using their ages, so I have something like this:
{
"_id" : null,
"children" : [
{
"range:" : "lower than 10",
total: 1,
names: ["A"]
}
{
"range:" : "higher than 10",
total: 0,
names: []
}
],
"total" : 1
}
{
"_id" : "A",
"children" : [
{
"range:" : "lower than 10",
total: 0,
names: []
}
{
"range:" : "higher than 10",
total: 2,
names: ["C","B"]
}
],
"total" : 1
}
{
"_id" : "B",
"children" : [
{
"range:" : "lower than 10",
total: 1,
names: ["D"]
}
{
"range:" : "higher than 10",
total: 13,
names: ["E"]
}
],
"total" : 1
}
{
"_id" : "C",
"children" : [
{
"range:" : "lower than 10",
total: 0,
names: []
}
{
"range:" : "higher than 10",
total: 1,
names: ["F"]
}
],
"total" : 1
}
I feel I am getting near, I've got this query:
db.companytest3.aggregate(
{ $project: {
"_id": 0,
"range": {
$concat: [{
$cond: [ { $lte: ["$age", 10] }, "até 10", "" ]
}, {
$cond: [ { $gte: ["$age", 11] }, "mais de 10", "" ]
}]
},
"owner": "$owner",
"name" : "$name"
}
},
{
$group: {
_id: { owner: "$owner", range: "$range" },
children: { $addToSet: { name: "$name", range: "$range"} } ,
total: { $sum: 1}
}
},
{
$group: {
_id: { owner:"$_id.owner" },
children: { $addToSet: "$children" }
}
}
)
which gives me the following output:
{ "_id" : { "owner" : null }, "children" : [ [ { "name" : "A", "range" : "até 10" } ] ] }
{ "_id" : { "owner" : "A" }, "children" : [ [ { "name" : "C", "range" : "mais de 10" }, { "name" : "B", "range" : "mais de 10" } ] ] }
{ "_id" : { "owner" : "B" }, "children" : [ [ { "name" : "D", "range" : "até 10" } ], [ { "name" : "E", "range" : "mais de 10" } ] ] }
{ "_id" : { "owner" : "C" }, "children" : [ [ { "name" : "F", "range" : "mais de 10" } ] ] }
Now I am having issues to group the items by owner and keep sum the total, I am stuck and I do not know how to proceed. I've been trying many diferent alternatives using groups variations but I do not feel they are worth posting here.
How can I change my current query so I group the children by range and add the count?
thanks! :D
It should be possible in earlier versions, but even basically looking at how you want to manipulate the result, the simplest way I can see is with the help of some operators introduced in MongoDB 2.6.
db.companytest3.aggregate([
{ "$group": {
"_id": "$owner",
"lowerThanTenNames": {
"$addToSet": {
"$cond": [
{ "$lte": [ "$age", 10 ] },
"$name",
false
]
}
},
"lowerThanTenTotal": {
"$sum": {
"$cond": [
{ "$lte": [ "$age", 10 ] },
1,
0
]
}
},
"moreThanTenNames": {
"$addToSet": {
"$cond": [
{ "$gte": [ "$age", 11 ] },
"$name",
false
]
}
},
"moreThanTenTotal": {
"$sum": {
"$cond": [
{ "$gte": [ "$age", 11 ] },
1,
0
]
}
}
}},
{ "$project": {
"children": {
"$map": {
"input": { "$literal": ["L", "M"] },
"as": "el",
"in": {
"$cond": [
{ "$eq": [ "$$el", "L" ] },
{
"range": { "$literal": "lower than 10" },
"total": "$lowerThanTenTotal",
"names": {
"$setDifference": [
"$lowerThanTenNames",
[false]
]
}
},
{
"range": { "$literal": "higher than 10" },
"total": "$moreThanTenTotal",
"names": {
"$setDifference": [
"$moreThanTenNames",
[false]
]
}
}
]
}
}
},
"total": { "$add": [ "$lowerThanTenTotal", "$moreThanTenTotal" ]},
}},
{ "$sort": { "_id": 1 } }
])
Basically you want to separate these out into two sets of results for each grouping, being one for each age range. Due to the use of conditional operators, the "names" sets then need to be filtered for any false values where the conditions did not match.
The other thing that needs to be done is to coerce these results from separate fields into an array. The $map operator makes this simple by just providing a two element template with effectively "A/B" choices to do the re-mapping.
Since we had discrete fields here before they were re-mapped onto an array, you can just supply each "total" field as an argument to $add in order to get the combined total.
Produces exactly this:
{
"_id" : null,
"children" : [
{
"range" : "lower than 10",
"total" : 1,
"names" : ["A"]
},
{
"range" : "higher than 10",
"total" : 0,
"names" : [ ]
}
],
"total" : 1
}
{
"_id" : "A",
"children" : [
{
"range" : "lower than 10",
"total" : 0,
"names" : [ ]
},
{
"range" : "higher than 10",
"total" : 2,
"names" : ["C","B"]
}
],
"total" : 2
}
{
"_id" : "B",
"children" : [
{
"range" : "lower than 10",
"total" : 1,
"names" : ["D"]
},
{
"range" : "higher than 10",
"total" : 1,
"names" : ["E"]
}
],
"total" : 2
}
{
"_id" : "C",
"children" : [
{
"range" : "lower than 10",
"total" : 0,
"names" : [ ]
},
{
"range" : "higher than 10",
"total" : 1,
"names" : ["F"]
}
],
"total" : 1
}