MongoDB grouping and max item? - mongodb

For the life of me, I can't figure out how to write this query in MongoDB!
I need to return rows from a query where the max "Revision" is the row returned for an item
I have two parts:
Return a count without actually getting the data.
Return all the data (not necessarily in order by column A but would
be helpful) based on the grouping.
Here's a sample set (In our real world data, we have ~40 columns and potentially millions of rows):
A B C D Revision
--------+-------+-------+-------+------------
Item1 100 200 300 1
Item1 111 222 333 2
Item2 200 500 800 1
Item2 222 555 888 2
Item2 223 556 889 3
Item3 300 600 900 1
Item4 400 700 1000 1
What I need to be returned:
The returned count: 4
The returned data:
A B C D Revision
--------+-------+-------+-------+--------
Item1 111 222 333 2
Item2 223 556 889 3
Item3 300 600 900 1
Item4 400 700 1000 1
I've been trying so many combination or $group and $count and even tried to use
SQL to MongoDB query translation tools but can't seem to make it work.
Please help and thanks in advance!!

Try below query :
db.collection.aggregate([
{
$group: {
_id: "$A",
A: { $first: "$A" },
B: { $max: "$B" },
C: { $max: "$C" },
D: { $max: "$D" },
Revision: { $max: "$Revision" },
}
},
{
$project: { _id: 0 },
},
{
$group: {
_id: "",
count: { $sum: 1 },
data: { $push: "$$ROOT" }
}
}
]);
Test : mongoplayground
So in the data field you'll have actual documents where as count represent no.of unique 'A''s in collection.
In other way if you don't want result to be in that way you can just try below query :
db.collection.aggregate([
{
$group: {
_id: "$A",
A: { $first: "$A" },
B: { $max: "$B" },
C: { $max: "$C" },
D: { $max: "$D" },
Revision: { $max: "$Revision" },
}
},
{
$project: { _id: 0 },
}
])
Test : mongoplayground
It would return an array of objects cause aggregation will always return a cursor which will be resolved to an array in code then you can just do .length on array to get total count instead of using second $group stage in first query to get just the total count this is another option to do.

Related

How to search in MongoDB an element depending on the previous one?

I'm having to deal with a query that is kind of strange. I'm creating an app for boat tracking: I have a collections of documents with the timestamp and the Port ID where it was the board at that moment.
After sorting all the documents of this collection by the timestamp descending, I need to grab the elements that have the same Port ID in that range of time.
For example:
timestamp | port_id
2021-11-10T23:00:00.000Z | 1
2021-11-10T22:00:00.000Z | 1
2021-11-10T21:00:00.000Z | 1
2021-11-10T20:00:00.000Z | 2
2021-11-10T19:00:00.000Z | 2
2021-11-10T18:00:00.000Z | 2
2021-11-10T17:00:00.000Z | 1
2021-11-10T16:00:00.000Z | 1
2021-11-10T15:00:00.000Z | 1
Having this data (sorted by timestamp), I would have to grab the first 3 documents. The way I'm doing this now, is grabbing 2000 documents and implementing a filter function in the application level.
Another approch would be grabbing the first element, and then filtering by that port id, but that returns me 6 elements, not the first 3.
Do you know any way to perform a query like this in Mongo? Thanks!
Use $setWindowFields
db.collection.aggregate([
{
$setWindowFields: {
partitionBy: "",
sortBy: { timestamp: -1 },
output: {
c: {
$shift: {
output: "$port_id",
by: -1,
default: "Not available"
}
}
}
}
},
{
$set: {
c: {
$cond: {
if: { $eq: [ "$port_id", "$c" ] },
then: 0,
else: 1
}
}
}
},
{
$setWindowFields: {
partitionBy: "",
sortBy: { timestamp: -1 },
output: {
c: {
$sum: "$c",
window: { documents: [ "unbounded", "current" ] }
}
}
}
},
{
$match: { c: 1 }
},
{
$unset: "c"
}
])
mongoplayground

Problem with an aggregated query in mongoDB

I have the following collection in mongodb:
IDcustomer. idServicerequired. ...
001. 13
002. 15
002. 19
002. 10
003. null
From this, i want to get the average number of services required by each customer (in this case, the output should be (1+3+0)/3 = 1.34)
I tried as follows, but in this way, for each customer that has required no service, it is counted 1, as if he had required one service, so the average is higher than expected (in this case it would be (1+3+1)/3=1.67)
first group, check condition if idServicerequired is null then count 0
second $group by null and average count
db.collection.aggregate([
{
$group: {
_id: "$idCustomer",
count: {
$sum: {
$cond: [{ $eq: ["$idServicerequired", null] }, 0, 1]
}
}
}
},
{
$group: {
_id: null,
count: { $avg: "$count" }
}
}
])
Playground

Mongodb aggregate $gt returns non matching records

I would like to count the sum of a field in my database.
I have this pipeline in mongodb:
{
match: {
'user1': user.id,
'unreadMessagesCount': { $exists: true, $gt: 0 },
}
},
{
group: {
objectId: null,
total: { $sum: "$unreadMessagesCount" },
count: { $sum: 1 }
}
}
The results returned is
{total: 3, count: 30}
The total is correct because I only have 3 records with 1 unreadMessagesCount each. But the count returned is 30 which is wrong. There should only be 3 records matched. When i remove group from pipeline, I get 30 records.

How to aggregate data which an array field sum is between two values?

I have two values which are minCount and maxCount.
In my model I have field which is called counts.Something like this.
{
createdAt: date
counts: [ 0,200,100] ==> Sum 300
},
{
createdAt: date
counts: [ 200,500,0] ==> Sum 700
},
{
createdAt: date
counts: [ 0,1100,100] ==> Sum 1200
},
I need to return sum of counts which sum of counts array elements are between minCount and MaxCount.
Exm:
minCount= 400
maxCount= 1300
Return
{
createdAt: date
total: 700
},
{
createdAt: date
total: 1200
},
I
I have createdAt dates between two dates like this in first step of pipe.
Record.aggregate ([
{
$match: {
createdAt: {
$gte: new Date (req.body.startDate),
$lte: new Date (req.body.endDate),
},
},
},
{}, ==> I have to get total counts with condition which I could not here.
])
I am almost new to aggreagate pipeline so please help.
Working example - https://mongoplayground.net/p/I6LOLhTA-yA
db.collection.aggregate([
{
"$project": {
"counts": 1,
"createdAt": 1,
"totalCounts": {
"$sum": "$counts"
}
}
},
{
"$match": {
"totalCounts": {
"$gte": 400,
"$lte": 1300
}
}
}
])

Aggregate counting logical values

Hello friend I am not friendly with mongodb aggregation I want is that I have array of object that contains subjects with its score for each question and I am using node js so I want is that full calculation with mongo query if possible that include subject name with its total score and count of attempt and not attempt my Json Array is as bellow
{
"examId": ObjectId("597367af7d8d3219d88c4341"),
"questionId": ObjectId("597368207d8d3219d88c4342"),
"questionNo": 1,
"subject": "Reasoning Ability",
"yourChoice": "A",
"correctMark": "1",
"attempt": true,
"notAttempt": false,
}
here in object one field is for correct marks subject are different an I want an output like
|Subject Name | Total attempts | total not attempts | total score |
| A | 5 | 3 | 10 |
| B | 10 | 5 | 25 |
I am trying with aggregation but not done yet I have tried this query
db.examscores.aggregate([
{ $group:{
_id:"$examId",
score: { $sum: '$correctMark' },
count: { $sum: 1 }
}}
])
Any one has idea how to achieve this type of output.
and if another way to achieve this using node than also good.
I have solved this here is my query
[
{ $match: { subject:'Reasoning Ability' } },
{
$group:
{
_id:{id:"$examId",subject:'$subject'},
totalAttempt: { $sum: {$cond : [ "$attempt", 1, 0 ]} },
totalNotAttempt: { $sum: {$cond : [ "$notAttempt", 1, 0 ]} },
markedForReview:{ $sum: {$cond : [ "$markedForReview", 1, 0 ]} },
answerAndMarkedForReview:{ $sum: {$cond : [ "$answerAndMarkedForReview", 1, 0 ]} },
score: { $sum: '$correctMark' },
count: { $sum: 1 }
}
}
]