Mongo db project a field not in the group - mongodb

I'm wondering if I can project field is not exist in the group.
For example I have the below collections and I want the name of students who has highest soccer across all students
{ "_id" : 1, "Snmae" : Alex, "score" : 97}
{ "_id" : 2, "Snmae" : Sara, "socre" : 97 }
{ "_id" : 3, "Snmae" : Sam, "socre" : 93 }
{ "_id" : 4, "Snmae" : Dan, "socre" : 77 }
db.stuudent.aggregate(
{$project:{_id:0,sname:1,score:1}},
{ $group : { _id : "", High_Score: { $max: "$score" }}}
);
The desire output is
Sname: Alex , score: 97
Sname: Sara , score: 97

Data:
{ "_id" : 1.0, "Sname" : "Alex", "score" : 97.0 },
{ "_id" : 2.0, "Sname" : "Sara", "score" : 97.0 },
{ "_id" : 3.0, "Sname" : "Sam", "score" : 93.0 },
{ "_id" : 4.0, "Sname" : "Dan", "score" : 77.0 }
Query:
db.collection.aggregate([
{
$group: {
_id: "$score",
"names": { $push: "$Sname" }
}
},
{ $sort: { "_id": -1 } },
{ $limit: 1},
{ $unwind: "$names" },
{
$project: {
"Sname": "$names",
"score": "$_id",
"_id": 0
}
}
])
Explanation:
$group - groups the students by score.
$sort - sorts the documents by score in the descending direction.
$limit - takes only first document (document with highest score value).
$unwind - splits "names" array into separated documents.
$project - generates the final results (documents with defined shape).

Related

Mongo $group and $count, and then $sort the result

I have a simple table with ranked users...
User:
{
"_id" : "aaa",
"rank" : 10
},
{
"_id" : "bbb",
"rank" : 30
},
{
"_id" : "ccc",
"rank" : 20
},
{
"_id" : "ddd",
"rank" : 30
},
{
"_id" : "eee",
"rank" : 30
},
{
"_id" : "fff",
"rank" : 10
}
And I would like to count how many have each rank, and then sort them with highest to lowest count
So I can get this result:
Result:
{
"rank" : 30,
"count": 3
},
{
"rank" : 10,
"count": 2
},
{
"rank" : 20,
"count": 1
}
I tried different things but cant seem to get the correct output
db.getCollection("user").aggregate([
{
"$group": {
"_id": {
"rank": "$rank"
},
"count": { "$sum": 1 }
},
"$sort": {
"count" : -1
}
])
I hope this is possible to do.
You can count and then sort them by aggregation in mongodb
db.getCollection('users').aggregate(
[
{
$group:
{
_id: "$rank",
count: { $sum: 1 }
}
},
{ $sort : { count : -1} }
]
)
Working example
https://mongoplayground.net/p/aM3Ci3GACjp
You don't need to add additional group or count stages when you can do it in one go -
db.getCollection("user").aggregate([
{
$sortByCount: "$rank"
}
])

How to use $unwind and $match with MongoDB?

I have a document of the following format:
{
"P": {
"Workspaces": [
{
"Key": "Value1",
"Size": 2.27,
"Status": 'something'
},
{
"Key": "Value2",
"Size": 3.27,
"Status": 'somethingelse'
}
]
}
}
The following query returns the average correctly.
db.collection.aggregate([
{ $unwind: "$P.Workspaces" },
{ $group: { _id: "$P.Workspaces.Key", average: { $avg: "$P.Workspaces.Size" } } }
])
I am trying to add a match to filter the status as shown below. However I am not getting no result even though there are documents with matching status. I am trying to filter the results before taking the average. Am I missing something here?
db.collection.aggregate([
{ $unwind: "$P.Workspaces" },
{ $match: { "P.Workspaces.Status":'something'}},
{ $group: { _id: "$P.Workspaces.Key", average: { $avg: "$P.Workspaces.Size" } } }
])
db.articles.aggregate(
[ { $match : { author : "dave" } } ]
);
The examples use a collection named articles with the following documents:
{ "_id" : ObjectId("512bc95fe835e68f199c8686"), "author" : "dave", "score" : 80, "views" : 100 } { "_id" : ObjectId("512bc962e835e68f199c8687"), "author" : "dave", "score" : 85, "views" : 521 } { "_id" : ObjectId("55f5a192d4bede9ac365b257"), "author" : "ahn", "score" : 60, "views" : 1000 } { "_id" : ObjectId("55f5a192d4bede9ac365b258"), "author" : "li", "score" : 55, "views" : 5000 } { "_id" : ObjectId("55f5a1d3d4bede9ac365b259"), "author" : "annT", "score" : 60, "views" : 50 }

MongoDB: Aggregation Group operation over a dynamic key-value pair

I have a simple document with one field as a key-value pair. I want to just perform a group operation in Aggregation over those keys and add their values. But the keys in the pair are not fixed and can be anything.
Here is a sample document.
{
_id: 349587843,
matchPair: {
3 : 21,
9 : 4,
7 : 32
}
},
{
_id: 349587478,
matchPair: {
7 : 11,
54 : 32,
9 : 7,
2 : 19
}
}
And I want a result something like the following.
{
_id : 3,
count : 21
},
{
_id : 9,
count : 11
},
{
_id : 7,
count : 43
},
{
_id : 54,
count : 32
},
{
_id : 2,
count : 19
}
I have the following query in mind and tried using $unwindoperation but it doesn't work probably because "matchPair" isn't an array and I don't know what to specify for the $sumoperation.
db.MatchPairs.aggregate([
{ "$unwind" : "$matchPair" },
{ "$group" : {
_id: "$matchPair",
count : { $sum : $matchPair }
} }
]);
I could also try Map-Reduce but for that too I need to emit() keys and values by name.
I'm sure there's a simple solution to this but I can't figure it out.
:
You could start by projecting and reshaping the matchPair field with $objectToArray
New in version 3.4.4.
{
$project: {
matchPair: { $objectToArray: '$matchPair' }
}
}
which would give
{
matchPair: [{ k: 3, v: 21 }, { k: 9, v: 4 }, ...]
}
Then $unwind based on matchPair
{
$unwind: '$matchPair'
}
which would give
{
matchPair: { k: 3, v: 21 }
}
Then $project
{
$project: {
_id: '$matchPair.k',
count: '$matchPair.v'
}
}
That should give the output you want. Altogether would be
.aggregate([
{
$project: {
matchPair: { $objectToArray: '$matchPair' }
}
},
{ $unwind: '$matchPair' },
{
$project: {
_id: '$matchPair.k',
count: '$matchPair.v'
}
}
])
In the mongoDb documentation for $unwind:
Deconstructs an array field from the input documents to output a
document for each element.
So you have to change your schema for something like:
{
"_id" : ObjectId("5880b57b039a3c89c1db145a"),
"matchPair" : [
{
"_id" : "3",
"count" : 21
},
{
"_id" : "9",
"count" : 4
},
{
"_id" : "7",
"count" : 32
}
]
},
{
"_id" : ObjectId("5880b58c039a3c89c1db145b"),
"matchPair" : [
{
"_id" : "7",
"count" : 11
},
{
"_id" : "54",
"count" : 32
},
{
"_id" : "9",
"count" : 7
},
{
"_id" : "2",
"count" : 19
}
]
}
Then doing:
db.MatchPairs.aggregate([
{ $unwind : "$matchPair" }
]);
will return:
{
"_id" : ObjectId("5880b57b039a3c89c1db145a"),
"matchPair" : {
"_id" : "3",
"count" : 21
}
},
{
"_id" : ObjectId("5880b57b039a3c89c1db145a"),
"matchPair" : {
"_id" : "9",
"count" : 4
}
},
{
"_id" : ObjectId("5880b57b039a3c89c1db145a"),
"matchPair" : {
"_id" : "7",
"count" : 32
}
},
{
"_id" : ObjectId("5880b58c039a3c89c1db145b"),
"matchPair" : {
"_id" : "7",
"count" : 11
}
},
{
"_id" : ObjectId("5880b58c039a3c89c1db145b"),
"matchPair" : {
"_id" : "54",
"count" : 32
}
},
{
"_id" : ObjectId("5880b58c039a3c89c1db145b"),
"matchPair" : {
"_id" : "9",
"count" : 7
}
},
{
"_id" : ObjectId("5880b58c039a3c89c1db145b"),
"matchPair" : {
"_id" : "2",
"count" : 19
}
}
Then you just have to do your grouping.

Mongo db skip-limit work before match after unwind

This is my query:
db.getCollection('grades').
aggregate([{ "$match" : { "class_id" : 28, "student_id" : 0 } },
{ "$unwind" : "$scores" },
{ "$match" : { "scores.type" : "homework" } },
{ "$skip" : 3 }, { "$limit" : 3 },
{ "$group" : { "_id" : { "id" : "$_id" }, "scores" : { "$push" : "$scores" } } },
{ "$project" : { "_id" : "$_id.id", "scores" : 1 } }])
scores - is a nested array of objects. Score object - {type: "someType", score: someScore}. This query returns one document.
The problem: array of scores has 6 objects and 4 of them have type homework.
The result, what I've received: http://prntscr.com/bq217r
The original document: http://prntscr.com/bq23bv
Why skip-limit performed before match operator? How can I fix it?
As per attached screenshot everything looks OK.
we have 4 elements 1 2 3 4, then we are skipping 3, so we get 4 at the end... and 53 is the value :-)
btw your skip/limit is after $match

Aggregating an array of values in MongoDB

I have an array of values I want to group by the id of, and get the sum of all the values. Currently I'm trying:
db.entries.aggregate(
[
{ $match: {"user": "John" } },
{ "$unwind": '$games'},
{ $group: {
"_id": "$games.id",
"score": { "$sum": "$games.score"} }},
])
The data looks like:
{
"user":"john",
"games":[
{
"id":123,
"score":123
}
]
},
{
"user":"john",
"games":[
{
"id":123,
"score":123
}
]
},
{
"user":"john",
"games":[
{
"id":256,
"score":256
}
]
}
In this example, for "John" I would like to get the total of all the unique game scores. I should get (123 + 256) in this example.
However, right now I'm getting the sum of individual games, not all of them as a total.
I have here:
> db.entries.find();
{ "_id" : ObjectId("56a9610cca390d7e14a54486"), "user" : "john", "games" : [ { "id" : 123, "score" : 123 } ] }
{ "_id" : ObjectId("56a9610cca390d7e14a54487"), "user" : "john", "games" : [ { "id" : 123, "score" : 123 } ] }
{ "_id" : ObjectId("56a9610cca390d7e14a54488"), "user" : "john", "games" : [ { "id" : 256, "score" : 256 } ] }
> db.entries.aggregate([ {$match: {user: "john"}}, {$unwind: "$games"}, {$group: {_id: "$games.id", score: {$sum: "$games.score"}}} ]);
{ "_id" : 256, "score" : 256 }
{ "_id" : 123, "score" : 246 }
What’s the problem?