Find sum of fields inside array in MongoDB - mongodb

I have a data as follows:
> db.PQRCorp.find().pretty()
{
"_id" : 0,
"name" : "Ancy",
"results" : [
{
"evaluation" : "term1",
"score" : 1.463179736705023
},
{
"evaluation" : "term2",
"score" : 11.78273309957772
},
{
"evaluation" : "term3",
"score" : 6.676176060654615
}
]
}
{
"_id" : 1,
"name" : "Mark",
"results" : [
{
"evaluation" : "term1",
"score" : 5.89772766299929
},
{
"evaluation" : "term2",
"score" : 12.7726680028769
},
{
"evaluation" : "term3",
"score" : 2.78092882672992
}
]
}
{
"_id" : 2,
"name" : "Jeff",
"results" : [
{
"evaluation" : "term1",
"score" : 36.78917882992872
},
{
"evaluation" : "term2",
"score" : 2.883687879200287
},
{
"evaluation" : "term3",
"score" : 9.882668212003763
}
]
}
What I want to achieve is ::Find employees who failed in aggregate (term1 + term2 + term3)
What I am doing and eventually getting is:
db.PQRCorp.aggregate([
{$unwind:"$results"},
{ $group: {_id: "$id",
'totalTermScore':{ $sum:"$results.score" }
}
}])
OUTPUT:{ "_id" : null, "totalTermScore" : 90.92894831067625 }
Simply I am getting a output of a flat sum of all scores. What I want is, to sum terms 1 , 2 and 3 separately for separate employees.
Please can someone help me. I am new to MongoDB (quite evident though).

You do not need to use $unwind and $group here... A simple $project query can $sum your entire score...
db.PQRCorp.aggregate([
{ "$project": {
"name": 1,
"totalTermScore": {
"$sum": "$results.score"
}
}}
])

Related

what't the meaning of mongo $minKey?

this page :
https://docs.mongodb.com/manual/reference/operator/query/type/
{ "date": new Date(1393804800000), "grade": MaxKey(), "score": 2 },
when i show Maxkey() in mongo shell:
MaxKey().help
The MaxKey BSON Class.
For more information on usage: https://mongodb.github.io/node-mongodb-native/3.6/api/MaxKey.html
how can I understand it ?
should I compare it with "$lt" or "$gt" like this ?
db.test.find({"grades.grade": {"$gt":"a"}})
MinKey and MaxKey are MongoDB internal types. Their purpose is to represent the theoretical extremes.
MinKey is less than any value, and MaxKey is greater than any value, regardless of type.
See Comparison/Sort Order
I think minKey() or maxKey() is just a special value which can only be queried by { $type : "maxKey" }
If data is below,
{
"_id" : 2,
"grades" : [
{
"date" : ISODate("2014-03-03T00:00:00.000Z"),
"grade" : { "$maxKey" : 1 },
"score" : 2
}, {
"date" : ISODate("2013-01-24T00:00:00.000Z"),
"grade" : { "$maxKey" : 1 },
"score" : 3
}
]
}
Use,
db.test.find({"grades.grade": {"$gt":"A"}})
Will return nothing.
But if use,
db.test.find({"grades.grade" : { $type : "maxKey" }})
Will return,
{
"_id" : 2,
"grades" : [
{
"date" : ISODate("2014-03-03T00:00:00.000Z"),
"grade" : { "$maxKey" : 1 },
"score" : 2
}, {
"date" : ISODate("2013-01-24T00:00:00.000Z"),
"grade" : { "$maxKey" : 1 },
"score" : 3
}
]
}

MongoDB aggregate Query for sum

I have a collection as follows
{ "_id" : 0, "name" : "aimee Zank", "scores" :
[
{ "type" : "exam", "score" : 1.463179736705023 },
{ "type" : "quiz", "score" : 11.78273309957772 },
{ "type" : "homework", "score" : 6.676176060654615}
] }
{"_id" : 1, "name" : "Aurelia Menendez", "scores" :
[
{ "type" : "exam", "score" : 60.06045071030959 },
{ "type" : "quiz", "score" : 52.79790691903873 },
{ "type" : "homework", "score" : 71.761334391544 }
] }
{"_id" : 2, "name" : "Corliss Zuk", "scores" :
[
{ "type" : "exam", "score" : 67.03077096065002 },
{ "type" : "quiz", "score" : 6.301851677835235 },
{ "type" : "homework", "score" : 20.18160621941858}
] }
Now i want the sum of all the scores of each type for respective students
for example for student aimee zank i want the sum of scores for exam+quiz+homework.
I have tried this
db.collection.aggregate(
[
{
$group:
{
_id: "$name",
total: { $sum: "$scores.score" },
}
}
]
)
and this
db.scores.aggregate(
[
{ $project: { name: 1, total: { $add: [ "$scores.score" ] } } }
]
)
But i could not find a solution
Can someone please help me with the query?
After finding no help on stackoverflow and only discouraging people in the group, i have found a solution on my own and it is just one part of the solution of i was searching for:
db.scores.aggregate(
[
{ $unwind : "$scores"},
{ $group:
{
_id: "$name",
total: { $sum: "$scores.score" }
}
}
]
)

MongoDB: Sort in combination with Aggregation group

I have a collection called transaction with below documents,
/* 0 */
{
"_id" : ObjectId("5603fad216e90d53d6795131"),
"statusId" : "65c719e6727d",
"relatedWith" : "65c719e67267",
"status" : "A",
"userId" : "100",
"createdTs" : ISODate("2015-09-24T13:15:36.609Z")
}
/* 1 */
{
"_id" : ObjectId("5603fad216e90d53d6795134"),
"statusId" : "65c719e6727d",
"relatedWith" : "65c719e6726d",
"status" : "B",
"userId" : "100",
"createdTs" : ISODate("2015-09-24T13:14:31.609Z")
}
/* 2 */
{
"_id" : ObjectId("5603fad216e90d53d679512e"),
"statusId" : "65c719e6727d",
"relatedWith" : "65c719e6726d",
"status" : "C",
"userId" : "100",
"createdTs" : ISODate("2015-09-24T13:13:36.609Z")
}
/* 3 */
{
"_id" : ObjectId("5603fad216e90d53d6795132"),
"statusId" : "65c719e6727d",
"relatedWith" : "65c719e6726d",
"status" : "D",
"userId" : "100",
"createdTs" : ISODate("2015-09-24T13:16:36.609Z")
}
When I run the below Aggregation query without $group,
db.transaction.aggregate([
{
"$match": {
"userId": "100",
"statusId": "65c719e6727d"
}
},
{
"$sort": {
"createdTs": -1
}
}
])
I get the result in expected sorting order. i.e Sort createdTs in descending order (Minimal result)
/* 0 */
{
"result" : [
{
"_id" : ObjectId("5603fad216e90d53d6795132"),
"createdTs" : ISODate("2015-09-24T13:16:36.609Z")
},
{
"_id" : ObjectId("5603fad216e90d53d6795131"),
"createdTs" : ISODate("2015-09-24T13:15:36.609Z")
},
{
"_id" : ObjectId("5603fad216e90d53d6795134"),
"createdTs" : ISODate("2015-09-24T13:14:31.609Z")
},
{
"_id" : ObjectId("5603fad216e90d53d679512e"),
"createdTs" : ISODate("2015-09-24T13:13:36.609Z")
}
],
"ok" : 1
}
If I apply the below aggregation with $group, the resultant is inversely sorted(i.e Ascending sort)
db.transaction.aggregate([
{
"$match": {
"userId": "100",
"statusId": "65c719e6727d"
}
},
{
"$sort": {
"createdTs": -1
}
},
{
$group: {
"_id": {
"statusId": "$statusId",
"relatedWith": "$relatedWith",
"status": "$status"
},
"status": {$first: "$status"},
"statusId": {$first: "$statusId"},
"relatedWith": {$first: "$relatedWith"},
"createdTs": {$first: "$createdTs"}
}
}
]);
I get the result in inverse Order i.e. ** Sort createdTs in Ascending order**
/* 0 */
{
"result" : [
{
"_id" : ObjectId("5603fad216e90d53d679512e"),
"createdTs" : ISODate("2015-09-24T13:13:36.609Z")
},
{
"_id" : ObjectId("5603fad216e90d53d6795134"),
"createdTs" : ISODate("2015-09-24T13:14:31.609Z")
},
{
"_id" : ObjectId("5603fad216e90d53d6795131"),
"createdTs" : ISODate("2015-09-24T13:15:36.609Z")
},
{
"_id" : ObjectId("5603fad216e90d53d6795132"),
"createdTs" : ISODate("2015-09-24T13:16:36.609Z")
}
],
"ok" : 1
}
Where am I wrong ?
The $group stage doesn't insure the ordering of the results. See here the first paragraph.
If you want the results to be sorted after a $group, you need to add a $sort after the $group stage.
In your case, you should move the $sort after the $group and before you ask the question : No, the $sort won't be able to use an index after the $group like it does before the $group :-).
The internal algorithm of $group seems to keep some sort of ordering (reversed apparently), but I would not count on that and add a $sort.
You are not doing anything wrong here, Its a $group behavior in Mongodb
Lets have a look in this example
Suppose you have following doc in collection
{ "_id" : 1, "item" : "abc", "price" : 10, "quantity" : 2, "date" : ISODate("2014-01-01T08:00:00Z") }
{ "_id" : 2, "item" : "jkl", "price" : 20, "quantity" : 1, "date" : ISODate("2014-02-03T09:00:00Z") }
{ "_id" : 3, "item" : "xyz", "price" : 5, "quantity" : 5, "date" : ISODate("2014-02-03T09:05:00Z") }
{ "_id" : 4, "item" : "abc", "price" : 10, "quantity" : 10, "date" : ISODate("2014-02-15T08:00:00Z") }
{ "_id" : 5, "item" : "xyz", "price" : 5, "quantity" : 10, "date" : ISODate("2014-02-15T09:05:00Z") }
{ "_id" : 6, "item" : "xyz", "price" : 5, "quantity" : 5, "date" : ISODate("2014-02-15T12:05:10Z") }
{ "_id" : 7, "item" : "xyz", "price" : 5, "quantity" : 10, "date" : ISODate("2014-02-15T14:12:12Z") }
Now if you run this
db.collection.aggregate([{ $sort: { item: 1,date:1}} ] )
the output will be in ascending order of item and date.
Now if you add group stage in aggregation pipeline it will reverse the order.
db.collection.aggregate([{ $sort: { item: 1,date:1}},{$group:{_id:"$item"}} ] )
Output will be
{ "_id" : "xyz" }
{ "_id" : "jkl" }
{ "_id" : "abc" }
Now the solution for your problem
change "createdTs": -1 to "createdTs": 1 for group

MongoDB Aggregation - return default value for documents that don't match query

I'm having trouble figuring out the right aggregation pipe operations to return the results I need.
I have a collection similar to the following :-
{
"_id" : "writer1",
"Name" : "writer1",
"Website" : "website1",
"Reviews" : [
{
"Film" : {
"Name" : "Jurassic Park",
"Genre" : "Action"
},
"Score" : 4
},
{
"Technology" : {
"Name" : "Mad Max",
"Genre" : "Action"
},
"Score" : 5
}
]
}
{
"_id" : "writer2",
"Name" : "writer2",
"Website" : "website1",
"Reviews" : [
{
"Technology" : {
"Name" : "Mad Max",
"Genre" : "Action"
},
"Score" : 5
}
]
}
And this is my aggregation so far : -
db.writers.aggregate([
{ "$unwind" : "$Reviews" },
{ "$match" : { "Reviews.Film.Name" : "Jurassic Park" } },
{ "$group" : { "_id" : "$Website" , "score" : { "$avg" : "$Reviews.Score" },
writers :{ $push: { name:"$Name", score:"$Reviews.Score" } }
}}
])
This returns only writers who have a review of the matching film and also only websites that have at least 1 writer who has reviewed the film,
however, I need to return all websites containing a list of their all writers, with a score of 0 if they haven't written a review for the specified film.
so, I am currently getting : -
{ "_id" : "website1", "score" : 4, "writers" : [ { "name" : "writer1", "score" : 4 } ] }
When I actually need : -
{ "_id" : "website1", "score" : 2, "writers" : [ { "name" : "writer1", "score" : 4 },{ "name" :"writer2", "score" : 0 } ] }
Can anyone point me in the right direction?
Cheers

Can I use "$first" operator on two fields in a "$group" operation in mongo db?

Consider the dataset
{ "_id" : { "$oid" : "aaa" }, "student_id" : 0, "type" : "exam", "score" : 54.6535436362647 }
{ "_id" : { "$oid" : "bbb" }, "student_id" : 0, "type" : "quiz", "score" : 31.95004496742112 }
{ "_id" : { "$oid" : "ccc" }, "student_id" : 0, "type" : "homework", "score" : 14.8504576811645 }
{ "_id" : { "$oid" : "ddd" }, "student_id" : 0, "type" : "homework", "score" : 63.98402553675503 }
{ "_id" : { "$oid" : "eee" }, "student_id" : 1, "type" : "exam", "score" : 74.20010837299897 }
{ "_id" : { "$oid" : "fff" }, "student_id" : 1, "type" : "quiz", "score" : 96.76851542258362 }
{ "_id" : { "$oid" : "ggg" }, "student_id" : 1, "type" : "homework", "score" : 21.33260810416115 }
{ "_id" : { "$oid" : "hhh" }, "student_id" : 1, "type" : "homework", "score" : 44.31667452616328 }
Say, for each student, I need to find minimum score and the corresponding document_id(_id).
Here is my pipeline
pipeline = [
{"$sort":{"student_id":1,"score":1 } },
{"$group": {"_id":"$student_id","mscore":{"$first":"$score"},"docid":{"$first":"$_id"} } },
{"$sort":{"_id":1}},
{"$project":{"docid":1,"_id":0}}
]
While this is working fine, I am not sure whether it is because I have the right query or whether it is because of way data is stored.
Here is my stragery
Sort by student_id, score
Group by student_id and do first on score, it will give student_id, min_score
Now, I need the doc_id(_id) also for this min_score, so I am using first on that field also. Is that correct?
Let's say after the sort, I need the entire first document, so should I apply first on each and every field or is there other way to do this?
To get the entire first document after sorting, apply the $first operator on the system variable $$ROOT which references the root document, i.e. the top-level document, currently being processed in the $group operator pipeline stage. Your pipeline would look like this:
var pipeline = [
{
"$sort": { "score": 1 }
},
{
"$group": {
"_id": "$student_id",
"data": { "$first": "$$ROOT" }
}
},
{
"$project": {
"_id": "$data._id",
"student_id": "$data.student_id",
"type": "$data.type",
"lowest_score": "$data.score"
}
}
]