I'm having a problem applying a sort to an aggregation grouping. My raw data looks like the following:
{
"_id" : ObjectId("52deab2fe4b0a491abb54108"),
"type" : "build",
"time" : ISODate("2014-01-21T17:15:27.471Z"),
"data" : {
"buildNumber" : 43,
"buildDuration" : 997308,
"buildProjectName" : "TestABC",
"buildResult" : "SUCCESS"
}
}
I would like to sort this first by buildProjectName and then date. Here is my query:
db.builds.aggregate([
{ $group: {
_id: {
month: { $month: "$time" },
day: { $dayOfYear: "$time" },
year: { $year: "$time" },
buildProjectName: "$data.buildProjectName",
},
buildDuration: { $avg: "$data.buildDuration" }
} },
{ $sort: {buildProjectName: 1, year: 1, month: 1, day: 1} }
])
I've tried switching the order of the sort (i.e.: buildProjectName, day, month, year), but I always get the same result with the dates out of order:
{
"result" : [
{
"_id" : {
"month" : 1,
"day" : 20,
"year" : 2014,
"buildProjectName" : "TestABC"
},
"buildDuration" : 1170723.5
},
{
"_id" : {
"month" : 1,
"day" : 21,
"year" : 2014,
"buildProjectName" : "TestABC"
},
"buildDuration" : 2284863.3333333335
},
{
"_id" : {
"month" : 1,
"day" : 17,
"year" : 2014,
"buildProjectName" : "TestABC"
},
"buildDuration" : 2234662
}
],
"ok" : 1
}
The fields you're sorting on are part of the _id so you need to include that in your $sort field names:
db.builds.aggregate([
{ $group: {
_id: {
month: { $month: "$time" },
day: { $dayOfYear: "$time" },
year: { $year: "$time" },
buildProjectName: "$data.buildProjectName",
},
buildDuration: { $avg: "$data.buildDuration" }
} },
{ $sort: {
'_id.buildProjectName': 1,
'_id.year': 1,
'_id.month': 1,
'_id.day': 1
} }
])
Related
My documents are stored like this and no, i can't change them:
{
"_id" : ObjectId("5ea773f219d60c4f1629203a"),
"direction" : 135,
"latitude" : -3.744851,
"longitude" : -38.545571,
"metrictimestamp" : "20180201025959",
"odometer" : 55697826,
"routecode" : 0,
"speed" : 3,
"deviceid" : 134680,
"vehicleid" : 32040
}
I need a group by vehicleid and only the day of year from this "metrictimestamp", and count how many documents are with the same vehicle and day, ideas??
I would say your metrictimestamp probably would contains first few characters 20180201 as YYYYMMDD, So using $substrbytes in aggregation you can get month, day, year from the string. Try below query :
db.collection.aggregate([
{
$addFields: {
day: { $toInt: { $substrBytes: [ "$metrictimestamp", 6, 2 ] } }, // $toInt can be optional
month: { $toInt: { $substrBytes: [ "$metrictimestamp", 4, 2 ] } },
year: { $toInt: { $substrBytes: [ "$metrictimestamp", 0, 4 ] } }
}
},
{
$group: {
_id: { vehicleid: "$vehicleid", day: "$day", year: "$year" },
count: { $sum: 1 }
}
}
])
Test : mongoplayground
Hello I am working with the reporting api which will going to use in highcharts and I am new to mongodb.
Below is my aggregation query (suggest me modification) :
db.product_sold.aggregate({
$group: {
_id: { year: { $year: "$solddate" }, month: { $month: "$solddate" }, productid: "$productid" },
totalQty: { $sum: "$qty" }
}
})
Output:
{
"_id" : {
"year" : NumberInt(2019),
"month" : NumberInt(2),
"productid" : "11"
},
"totalQty" : 6.0
}
// ----------------------------------------------
{
"_id" : {
"year" : NumberInt(2019),
"month" : NumberInt(2),
"productid" : "14"
},
"totalQty" : 7.0
}
// ----------------------------------------------
{
"_id" : {
"year" : NumberInt(2019),
"month" : NumberInt(1),
"productid" : "13"
},
"totalQty" : 3.0
}
// ----------------------------------------------
{
"_id" : {
"year" : NumberInt(2019),
"month" : NumberInt(2),
"productid" : "10"
},
"totalQty" : 6.0
}
// ----------------------------------------------
{
"_id" : {
"year" : NumberInt(2018),
"month" : NumberInt(2),
"productid" : "12"
},
"totalQty" : 5.0
}
// ----------------------------------------------
{
"_id" : {
"year" : NumberInt(2019),
"month" : NumberInt(2),
"productid" : "15"
},
"totalQty" : 8.0
}
// ----------------------------------------------
{
"_id" : {
"year" : NumberInt(2019),
"month" : NumberInt(1),
"productid" : "11"
},
"totalQty" : 2.0
}
// ----------------------------------------------
What I want in output is something like :
status: 200,
msg: "SUCCESS"
data: [{
1:[
{
"productid": 11,
"totalQty": 3
},
{
"productid": 12,
"totalQty": 27
}
],
2:[
{
"productid": 11,
"totalQty": 64
},
{
"productid": 12,
"totalQty": 10
}
]
}]
For reference attaching the image of the collection
Is there any way to achieve it using aggregation or anything else or I will have to do it manually by code ?
You can append below aggreagation stages to your current pipeline:
db.product_sold.aggregate([
// your current $group stage
{
$group: {
_id: "$_id.month",
docs: { $push: { productid: "$_id.productid", totalQty: "$totalQty" } }
}
},
{
$project: {
_id: 0,
k: { $toString: "$_id" },
v: "$docs"
}
},
{
$group: {
_id: null,
data: { $push: "$$ROOT" }
}
},
{
$project: {
_id: 0,
data: { $arrayToObject: "$data" }
}
}
])
The idea here is that you can use $group with _id set to null to get all the data into single document and then use $arrayToObject to get month number as key and all the aggregates for that month as value.
This is what my collection looks like.
{ "name" : "Lorem", "createdate" : ISODate("2018-09-12T04:02:41.208Z")},
{ "name" : "Impusom", "createdate" : ISODate("2018-09-12T04:02:41.208Z")}
{ "name" : "Lorem", "createdate" : ISODate("2018-11-01T04:02:41.208Z")}
{ "name" : "Damut", "createdate" : ISODate("2018-11-12T04:02:41.208Z")}
{ "name" : "Remetat", "createdate" : ISODate("2019-01-01T04:02:41.208Z")}
I want to extract distinct createdate values so I can mark them on a calendar widget. I wish to ignore time.
Here is the pipeline which is I am currently using.
db.mycollection.aggregate({
'$project': {
'y': {
'$year': '$createdate'
},
'm': {
'$month': '$createdate'
},
'd': {
'$dayOfMonth': '$createdate'
}
}
}, {
'$group': {
'_id': {
'year': '$y',
'month': '$m',
'day': '$d'
}
}
}, {
'$sort': {
'_id.year': 1,
'_id.month': 1,
'_id.day': 1
},
})
The result looks like this
{
"_id" : {
"year" : 2019,
"month" : 1,
"day" : 1
}
},
{
"_id" : {
"year" : 2018,
"month" : 11,
"day" : 12
}
},
{
"_id" : {
"year" : 2018,
"month" : 11,
"day" : 1
}
},
{
"_id" : {
"year" : 2018,
"month" : 9,
"day" : 12
}
}
I wish to improve the query and limit data to a particular year. e.g. 2018
Hence I tried adding a match condition before $project
{ '$match': { "createdate":{ "$gte": ISODate("2017-12-31T18:30:00.000Z"), "$lt": ISODate("2018-12-31T18:30:00.000Z") } }
},
The result is limited to year 2018 but then $group stops working hence I get duplicated records for dates.
I tried moving the $match option after $sort which allows $group to work but won't apply $match condition.
I would really appreciate a nudge in the right direction.
Update ---
So the above query with $match condition is working in mongoplayground.net
But not with my mongoose code.
const pipeline = [{
"$match": {
"createdate": {
"$gte": moment().startOf('year'),
"$lt": moment().endOf('year'),
}
}
},{
'$project': {
'y': {
'$year': '$createdate'
},
'm': {
'$month': '$createdate'
},
'd': {
'$dayOfMonth': '$createdate'
}
}
}, {
'$group': {
'_id': {
'year': '$y',
'month': '$m',
'day': '$d'
}
}
}, {
'$sort': {
'_id.year': 1,
'_id.month': 1,
'_id.day': 1
},
}];
Collection.aggregate(pipeline, (err, dates) => { res.json({ data: dates }); });
The response returns empty array.
Change your $match section to:
"$match": {
"createdate": {
"$gte": moment().startOf('year').toDate(),
"$lt": moment().endOf('year').toDate(),
}
}
And see if this helps. I do not think moment would work as a valid date parameter
I've got the following query that works fine. However adding a sort to the aggregation pipeline does not actually sort it. It does change the order of the results but does not properly sort.
Any idea why it refuses to sort the results by date?
db.calls.aggregate({
$match: {
'_id.date': {
$gte: ISODate('2014-04-13T00:00:00.000Z'),
$lte: ISODate('2014-04-20T00:00:00.000Z')
}
}
}, {
$project: {
calls: 1,
year: {
$year: '$_id.date'
},
month: {
$month: '$_id.date'
},
day: {
$dayOfMonth: '$_id.date'
}
}
}, {
$group: {
_id: {
name: "$_id.name",
day: '$day',
year: '$year',
month: '$month'
},
date: {
'$first': '$_id.date'
},
calls: {
'$sum': '$calls'
}
}
}, {
$group: {
_id: '$_id.name',
records: {
$addToSet: {
date: '$date',
calls: '$calls'
}
}
}
}, {
$sort: {
'records.date': 1
}
})
Outputs:
/* 0 */
{
"result" : [
{
"_id" : "a",
"records" : [
{
"date" : ISODate("2014-04-13T00:00:00.000Z"),
"calls" : 522
},
{
"date" : ISODate("2014-04-16T00:00:00.000Z"),
"calls" : 1523
},
{
"date" : ISODate("2014-04-20T00:00:00.000Z"),
"calls" : 57
},
{
"date" : ISODate("2014-04-14T00:00:00.000Z"),
"calls" : 1540
},
{
"date" : ISODate("2014-04-15T00:00:00.000Z"),
"calls" : 1592
},
{
"date" : ISODate("2014-04-17T00:00:00.000Z"),
"calls" : 1466
},
{
"date" : ISODate("2014-04-18T00:00:00.000Z"),
"calls" : 1003
},
{
"date" : ISODate("2014-04-19T00:00:00.000Z"),
"calls" : 623
}
]
},
{
"_id" : "b",
"records" : [
{
"date" : ISODate("2014-04-15T00:00:00.000Z"),
"calls" : 102
},
{
"date" : ISODate("2014-04-17T00:00:00.000Z"),
"calls" : 97
},
{
"date" : ISODate("2014-04-16T00:00:00.000Z"),
"calls" : 116
},
{
"date" : ISODate("2014-04-13T00:00:00.000Z"),
"calls" : 118
},
{
"date" : ISODate("2014-04-14T00:00:00.000Z"),
"calls" : 142
},
{
"date" : ISODate("2014-04-19T00:00:00.000Z"),
"calls" : 68
},
{
"date" : ISODate("2014-04-18T00:00:00.000Z"),
"calls" : 100
},
{
"date" : ISODate("2014-04-20T00:00:00.000Z"),
"calls" : 6
}
]
},
{
"_id" : "c",
"records" : [
{
"date" : ISODate("2014-04-17T00:00:00.000Z"),
"calls" : 137130
},
{
"date" : ISODate("2014-04-14T00:00:00.000Z"),
"calls" : 139497
},
{
"date" : ISODate("2014-04-13T00:00:00.000Z"),
"calls" : 92166
},
{
"date" : ISODate("2014-04-18T00:00:00.000Z"),
"calls" : 123129
},
{
"date" : ISODate("2014-04-15T00:00:00.000Z"),
"calls" : 146390
},
{
"date" : ISODate("2014-04-20T00:00:00.000Z"),
"calls" : 4515
},
{
"date" : ISODate("2014-04-16T00:00:00.000Z"),
"calls" : 141792
},
{
"date" : ISODate("2014-04-19T00:00:00.000Z"),
"calls" : 104847
}
]
}
],
"ok" : 1
}
Document structure:
{
"_id" : {
"date" : ISODate("2014-04-08T12:00:00.000Z"),
"name" : "a"
},
"calls" : 515
}
Fixed by doing a $sort before the last group and switching out $addToSet with $push so the order is preserved.
db.calls.aggregate({
$match: {
'_id.date': {
$gte: ISODate('2014-04-13T00:00:00.000Z'),
$lte: ISODate('2014-04-20T00:00:00.000Z')
}
}
}, {
$project: {
calls: 1,
year: {
$year: '$_id.date'
},
month: {
$month: '$_id.date'
},
day: {
$dayOfMonth: '$_id.date'
}
}
}, {
$group: {
_id: {
name: "$_id.name",
day: '$day',
year: '$year',
month: '$month'
},
date: {
'$first': '$_id.date'
},
calls: {
'$sum': '$calls'
}
}
}, {
$sort: {
date: 1
}
}, {
$group: {
_id: '$_id.name',
records: {
$push: {
date: '$date',
calls: '$calls'
}
}
}
})
I have a MongoDB query I am trying to figure out. I'd like to group my data by date and one other field (portfolio) and get the counts for each buildResult in that grouping.
Sample data looks like this:
{
"_id" : ObjectId("52dea764e4b0a491abb54102"),
"type" : "build",
"time" : ISODate("2014-01-21T16:59:16.642Z"),
"data" : {
"buildNumber" : 35,
"buildDuration" : 1034300,
"portfolio" : "Shared",
"buildResult" : "FAILURE"
}
}
{
"_id" : ObjectId("52dea7b7e4b0a491abb54103"),
"type" : "build",
"time" : ISODate("2014-01-21T17:00:39.617Z"),
"data" : {
"buildNumber" : 13,
"buildDuration" : 1186028,
"portfolio" : "Sample",
"buildResult" : "SUCCESS"
}
}
The output I am shooting for would be something like this:
{
"result" : [
{
"_id" : {
"month" : 2,
"day" : 28,
"year" : 2014,
"portfolio" : "Shared"
},
"aborted" : 3,
"failure" : 0,
"unstable" : 0,
"success" : 34
},
{
"_id" : {
"month" : 2,
"day" : 28,
"year" : 2014,
"portfolio" : "Sample"
},
"aborted" : 3,
"failure" : 2,
"unstable" : 0,
"success" : 37
}
],
"ok" : 1
}
My current query is:
db.builds.aggregate([
{ $match: { "data.buildResult" : { $ne : null} }},
{ $group: {
_id: {
month: { $month: "$time" },
day: { $dayOfMonth: "$time" },
year: { $year: "$time" },
portfolio: "$data.portfolio",
},
aborted: { $sum: { "$data.buildResult": "ABORTED" } },
failure: { $sum: { "$data.buildResult": "FAILURE" } },
unstable: { $sum: { "$data.buildResult": "UNSTABLE" } },
success: { $sum: { "$data.buildResult": "SUCCESS" } }
} },
{ $sort: { "_id.day": 1, "_id.month": 1, "_id.year": 1 } }
])
I have tried many variations with the following lines including $match, $in and other operators. Any help would be very appreciated.
aborted: { $sum: { "$data.buildResult": "ABORTED" } },
failure: { $sum: { "$data.buildResult": "FAILURE" } },
unstable: { $sum: { "$data.buildResult": "UNSTABLE" } },
success: { $sum: { "$data.buildResult": "SUCCESS" } }
To achieve that you can use the $cond and $eq operators like this:
aborted: {$sum: {$cond : [{$eq : ["$data.buildResult", "ABORTED"]}, 1, 0]}}
Edit:
As noted by Neil Lunn in the comments, the $cond here is irrelevant because the $eq operator already returns 0 or 1.
aborted: {$sum: {$eq : ["$data.buildResult", "ABORTED"]}}