getting sum of list in list of documents - mongodb

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.

Related

Fetching sum of rows for a type of column value in mongodb as a single output

I am trying to get the sum of field 'score.number' based on the type of a column value work.type in MongoDB. It should fetch sum as 25 for 'hw' ,and 'cw' as 5 as a single output for the student 'A'. Is there a way to achieve it using mongodb queries ? I tried the $group as well but it doesn't seem to fetch the worktype and the sum for each worktype against it for a single student record 'A'.
Expected Output:
after $match you should use $group like this
db.collection.aggregate([
{
$match: {
student: {
$in: [
"A"
]
},
"work.type": {
$in: [
"hw",
"cw"
]
}
}
},
{
"$group": {
"_id": {
"worktype": "$work.type",
"student": "$student"
},
"workScore": {
"$sum": "$score.number"
}
}
}
])
https://mongoplayground.net/p/qzghM5KoAbp
Able to get the sum with these two
$match{
'student': {'$in': ['A']},
"work.type": {'$in': ['hw', 'cw']},
}
followed by
$group
{
_id: '$work.type',
totalAmount: { $sum: "$score.number" },
}
$match {'student': {
$in: [
"A"
]
},
"work.type": {
$in: [
"hw",
"cw"
]
}}
followed by
$group {
"_id": {
"worktype": "$work.type",
"student": "$student"
},
"workScore": {
"$sum": "$score.number"
}
}
followed by
$group {"_id": {
"student": "$_id.student"
},
'list': {'$push': {'worktype':"$_id.worktype", 'workScore': "$workScore" }},
}
Solved output:
Solves the issue.

Count number of rows and get only the last row in 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"
}
}
}
}
])

How do you get the middle result from a mongodb query

I have a MongoDB database, which has a collection that contains all of the addresses from a country. Sometimes when I execute a query on that I have a chance that I receive about 200 results (house numbers within that street). I want to get the middle item of that result.
When I do that in my coding like this for example:
const result = Address.find({ street: "fooStreet" })
// results in an array with a length of let's say 200 (could also be 20, 49, 103, etc) items
I could split it in my coding like below:
const middleIndex = Math.round(result.length / 2);
const house = result[middleIndex];
But this means that the other records go to waste and use unnecessary bandwidth + computing power which should be handled by the database. Since the database OS is optimized for working with collections etc, I was wondering if I could achieve the same result in a mongodb query? See pseudo below:
db.getCollection("addresses")
.find({ street: "fooStreet" })
.helpMeHere()
// ^ do something to get the middle result from the N items
You can do as below
db.collection.aggregate([
{ //Any match condition
$match: {}
},
{
$group: {//get the total matching result
"_id": null,
data: {
$push: "$$ROOT"
},
count: {
$sum: 0.5
}
}
},
{
$project: {//get the second half
"result": {
"$slice": [
"$data",
{
"$toInt": {
"$multiply": [//Negating results records from the last
{
"$toInt": "$count"
},
-1
]
}
}
]
}
}
}
])
playground
To get one element:
playground
db.collection.aggregate([
{
$match: {}
},
{
$group: {
"_id": null,
data: {
$push: "$$ROOT"
},
count: {
$sum: 0.5
}
}
},
{
$project: {
"result": {
"$arrayElemAt": [//array access
"$data",
{
"$toInt": "$count"
}
]
}
}
}
])

mongodb aggregation query for field value length's sum

Say, I have following documents:
{name: 'A', fav_fruits: ['apple', 'mango', 'orange'], 'type':'test'}
{name: 'B', fav_fruits: ['apple', 'orange'], 'type':'test'}
{name: 'C', fav_fruits: ['cherry'], 'type':'test'}
I am trying to query to find the total count of fav_fruits field on overall documents returned by :
cursor = db.collection.find({'type': 'test'})
I am expecting output like:
cursor.count() = 3 // Getting
Without much idea of aggregate, can mongodb aggregation framework help me achieve this in any way:
1. sum up the lengths of all 'fav_fruits' field: 6
and/or
2. unique 'fav_fruit' field values = ['apple', 'mango', 'orange', 'cherry']
You need to $project your document after the $match stage and use the $size operator which return the number of items in each array. Then in the $group stage you use the $sum accumulator operator to return the total count.
db.collection.aggregate([
{ "$match": { "type": "test" } },
{ "$project": { "count": { "$size": "$fav_fruits" } } },
{ "$group": { "_id": null, "total": { "$sum": "$count" } } }
])
Which returns:
{ "_id" : null, "total" : 6 }
To get unique fav_fruits simply use .distinct()
> db.collection.distinct("fav_fruits", { "type": "test" } )
[ "apple", "mango", "orange", "cherry" ]
Do this to get just the number of fruits in the fav_fruits array:
db.fruits.aggregate([
{ $match: { type: 'test' } },
{ $unwind: "$fav_fruits" },
{ $group: { _id: "$type", count: { $sum: 1 } } }
]);
This will return the total number of fruits.
But if you want to get the array of unique fav_fruits along with the total number of elements in the fav_fruits field of each document, do this:
db.fruits.aggregate([
{ $match: { type: 'test' } },
{ $unwind: "$fav_fruits" },
{ $group: { _id: "$type", count: { $sum: 1 }, fav_fruits: { $addToSet: "$fav_fruits" } } }
])
You can try this. It may helpful to you.
db.collection.aggregate([{ $match : { type: "test" } }, {$group : { _id : null, count:{$sum:1} } }])

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