Count number of rows and get only the last row in MongoDB - mongodb

I have a collection of posts as follows:
{
"author": "Rothfuss",
"text": "Name of the Wind",
"likes": 1007,
"date": ISODate("2013-03-20T11:30:05Z")
},
{
"author": "Rothfuss",
"text": "Doors of Stone",
"likes": 1,
"date": ISODate("2051-03-20T11:30:05Z")
}
I want to get the count of each author's posts and his/her last post.
There is a SQL answer for the same question here. I try to find its MongoDB alternative.
I ended up this query so far:
db.collection.aggregate([
{
"$group": {
"_id": "$author",
"count": {
"$sum": 1
},
"lastPost": {
"$max": {
"_id": "$date",
"post": "$text"
}
}
}
}
])
which seems to work, but its different runs generate different results. It can be tested here in Mongo playground.
I don't understand how to use $max to select another property from the document containing the maximum. I am new to MongoDB, so describing the basics is also warmly appreciated.
extra question
Is it possible to limit $sum to only add posts with likes more than 100?

its different runs generate different results.
I don't understand how to use $max to select another property from the document containing the maximum.
The $max does not work in multiple fields, and also it is not effective in that field that having text/string value.
It will select any of the properties from a group of posts, it will different every time.
So the accurate result you can add new stage $sort before $group stage, to sort by date in descending order, and in the group stage you can select a value by $first operator,
{ $sort: { date: -1 } },
{
$group: {
_id: "$author",
count: { $sum: 1 },
date: { $first: "$date" },
post: { $first: "$text" }
}
}
Is it possible to limit $sum to only add posts with likes more than 100?
There is two meaning of your requirement, I am not sure which is you are asking but let me give both the solutions,
If you only don't want to count posts in count but you want to get it as the last post's date and text if it is.
$cond check condition if likes is greater than 100 then count 1 otherwise count 0
db.collection.aggregate([
{ $sort: { date: -1 } },
{
$group: {
_id: "$author",
count: {
$sum: {
$cond: [{ $gt: ["$likes", 100] }, 1, 0]
}
},
date: { $first: "$date" },
post: { $first: "$text" }
}
}
])
Playground
If you don't want to count and also don't want the last post if it is.
You can add a $match stage at the first stage to check greater than condition, and your final query would be,
db.collection.aggregate([
{ $match: { likes: { $gt: 100 } } },
{ $sort: { date: -1 } },
{
$group: {
_id: "$author",
count: { $sum: 1 },
date: { $first: "$date" },
post: { $first: "$text" }
}
}
])
Playground

Your query looks ok to me, adding a $match stage can filter out the posts if not likes > 100. (you can also do it in $sum, with $cond but there is no need here)
Query
$max accumulator can be used for documents also
Here you can see how MongoDB compares documents
mongoplayground has a problem and loses the order of fields in the documents(behaves likes they are are hashmaps when they are not) (test it in your driver also)
Test code here
db.collection.aggregate([
{
"$match": {
"likes": {
"$gt": 100
}
}
},
{
"$group": {
"_id": "$author",
"count": {
"$sum": 1
},
"lastPost": {
"$max": {
_id: "$date",
post: "$text"
}
}
}
}
])

Related

getting sum of list in list of documents

Consider this mongo document, an order with an internal list of products with their counts:
{
ordernumber: "1234"
detail: [
{ "number": "987",
"count": 10 },
{ "number": "654",
"count": 5 }
]
}
How do we get the sum of all counts with mongodb shell? I always get zero for sum and dont know what to pass for _id.
db.preorders.aggregate([ { $match: {} }, { $group: { _id: "$_id", total: { $sum: "$detail.count" } } }])
You can do a $unwind first, then $group on null.
Here is the Mongo Playground for your reference.

How can query in MongoDB that count number of 2 equal field?

I have a collection in MongoDB for my survey results(name=surveyresults). I want to have a query that gives me the number of correct answers based on category, for example, category "Bee" number of correct answers 10.
I tried different ways but these are not results that I want.
I have searched and found this post Group count with MongoDB using aggregation framework useful but not working for me.
This is part of my data in my surveyResults collection :
[{"_id":"0eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOjE5LCJpYXQiOjE1MjQwMDgzOTl9.2YvhnXtCD7-fm4B14k10m6NF7xuv7moCTbekVekkbvY","category":"Wasp","photo":"A_wasp_565","description":"","answer":"Bee","__v":0},{"_id":"1eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOjE5LCJpYXQiOjE1MjQwMDgzOTl9.2YvhnXtCD7-fm4B14k10m6NF7xuv7moCTbekVekkbvY","category":"Wasp","photo":"A_Pompilid_wasp_007","description":"","answer":"Wasp","__v":0},{"_id":"2eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOjE5LCJpYXQiOjE1MjQwMDgzOTl9.2YvhnXtCD7-fm4B14k10m6NF7xuv7moCTbekVekkbvY","category":"Wasp","photo":"wasp_248","description":"","answer":"Wasp","__v":0},{"_id":"3eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOjE5LCJpYXQiOjE1MjQwMDgzOTl9.2YvhnXtCD7-fm4B14k10m6NF7xuv7moCTbekVekkbvY","category":"Fly","photo":"A_butterfly_291","description":"kjlkjlkjlk","answer":"Moth/Butterfly","__v":0},
I want result like this :
[{"category":"Fly","count":3, "correct":1},{"category":"Wasp","count":3, "correct":1},{"category":"Moth/Butterfly","count":4, "correct":2},{"category":"Bee","count":3, "correct":1}]
Now I have these two queries but not giving me correct results :
1.
SurveyResults.aggregate([
{ $group: {
_id: { answer: '$answer', category: '$category' }
}},
{ $group: {
_id: '$_id.answer',
answer_correct: { $sum: 1 }
}},
{ $project: {
_id: 0,
answer: '$_id',
answer_correct: 1
}}
]).exec(callback);
2.
SurveyResults.aggregate([
{
$group:{
_id:"$answer",
count: { $sum : {$cond : { if: { $eq: ["answer", "$category"]}, then: 1, else: 0} }}
}
}]).exec(callback);
Also, I can have the number of answers based on the category by this query:
SurveyResults.aggregate([
{
$group:{
_id:"$answer",
count: { $sum : 1 }
}
}]).exec(callback);
Results:
[{"_id":"Don't know","count":2},{"_id":"Fly","count":3},{"_id":"Wasp","count":3},{"_id":"Moth/Butterfly","count":4},{"_id":"Bee","count":3}]
Here's what you want:
SurveyResults.aggregate([
$group: {
_id: "$category",
"count": { $sum: 1 }, // simply count all questions per category
"correct": {
$sum: { // and sum up the correct ones in a field called "correct"
$cond: [ // ...where "correct ones" means
{ $eq: [ "$category", "$answer" ] }, // that "category" needs to match "answer"
1,
0
]
}
}
}
}, {
$project: { // this is just to effectively rename the "_id" field into "category" - may or may not be needed
_id: 0,
"category": "$_id",
"count": "$count",
"correct": "$correct"
}
}]).exec(callback);

Using Sum with Last mongodb

UseCase: I have the following data:
{"accountNumber":"1-1", "details":["version":{ "number": "1","accountGroup":"1", "editable":"false" , "amount":100 }]}
{"accountNumber":"1-2", "details":[version":{ "number": "2", "accountGroup":"1", "editable":"false" , "amount":200}]}
{"accountNumber":"2-1", "details":[version":{ "number": "1", "accountGroup":"2", "editable":"false", "amount":200 }]}
Where: my document is account. Each record has a accountGroup (1, 2). A group can have multiple versions. AccountNumber is being initialized by the combination of AccountGroup & version
I want to get the latest version of the account (accountNumber 1-2 & 2-1) along with the sum of their amount.
Expected output:
{accountNumber:2-1}, {accountNumber: 1-2}, total: 400 (sum of amount of the latest versions of the account group)
I am using the following query:
db.getCollection('account').aggregate([
{ "$sort": { "accountNumber": 1 } },
{ "$unwind": "$details"},
{ "$group": {
"_id": "$details.version.accountGroup",
"Latestversion": { "$last": "$$ROOT" },
"total": {
$sum: "$details.version.amount"
}
}
}])
It gets the sum of the all the versions which belongs to a group.
Current output:
{"accountNumber": "1-2", total: 300}, {"accountNumber":"2-1", total: 200}
I am new to Mongodb, any help is appreciated. Looking forward for a response.
You will need two $group stages.
First $group to find the latest document for each account group and second $group to sum amount from latest document.
Something like
aggregate([
{ "$sort": { "accountNumber": 1 } },
{ "$unwind": "$details"},
{ "$group": {
"_id": "$details.version.accountGroup",
"latest": { "$last": "$$ROOT" }
}
},
{ "$group": {
"_id": null,
"accountNumbers": { $push:"$latest.accountNumber" },
"total": { $sum: "$latest.details.version.amount" }
}
}
])
You can update your structure to below and remove $unwind.
{"accountNumber":"1-1", detail:{"number": "1","accountGroup":"1", "editable":"false" , "amount":100 }}

Mongodb - count of items using addToSet

I grouped by organization and used $addToSet to show the distinct machineIds associated with that organization. I would like to get the count of machineIds for each organization. However the code below is returning a count of all machineIds, not the count of distinct ones. Is there another way to get the total unique machineIds?
db.getCollection('newcollections').aggregate([{
$group: {
_id: {
organization: "$user.organization"
},
machineId: {
"$addToSet": "$user.machineId"
},
count: {
$sum: 1
}
}
}])
You need to use $size operator in projection like following:
db.collection.aggregate([{
$group: {
_id: {
organization: "$user.organization"
},
machineId: {
"$addToSet": "$user.machineId"
}
}
}, {
$project: {
"organization": "$_id.organization",
"machineId": 1,
"_id": 0,
"size": {
$size: "$machineId"
}
}
}])

MongoDB using $sort on aggregation not sorting

I'm doing the course of MongoDB and I'm on the first exercise of week 5. The first exercise consists on getting the author who has more comments.
The first thing I did was check how looks the data and after that I started writing the query and that's what I got:
db.posts.aggregatae([
{ $unwind: "$comments" },
{ $group:
{
_id: "$author",
num_posts:{ $sum:1 }
}
},
{ $sort:
{ "num_posts": -1 }
}
]);
The query works and counts the num of comments correctly but when I try to sort the results it didn't work. I tried to change the $group stage to this:
{ $group:
{ _id: "$author" },
num_posts:{ $sum:1 }
}
But I get the error:
Error: command failed: {
"errmsg" : "exception": A pipeline state specification object must contain exactly
one field.", "code" : 16435, "ok" : 0
The problem with your query is you are grouping by a non-existing key, you need to group by the comments' author key to get the author (from the embedded comments subdocuments array) with the most number of comments as follows:
db.posts.aggregate([
{ "$unwind": "$comments"},
{
"$group": {
"_id": "$comments.author",
"num_posts": { "$sum": 1 }
}
},
{
"$sort": { "num_posts": -1 }
},
{ "$limit": 1 }
]);