filter in aggregation and groupby to get values from given time interval - mongodb

in the below $aggregate query, want to add a filter with $gt and $lt
The mongo doc:
{
"_id" : ObjectId("599eb4fae0f86361c36b1c91"),
"device_id" : ObjectId("5993df1b9a5fea3183064e49"),
"updatedAt" : ISODate("2017-08-24T11:38:12.135Z"),
"power_data" : [
{
"timestamp" : ISODate("2017-08-24T11:14:04.256Z"),
"_id" : ObjectId("599eb4fdeea8c69622751de3"),
"pfactor" : 1,
"rpower" : 0,
"voltage" : 0,
"current" : 0,
"energy" : 0,
"power" : 0
},
{
"timestamp" : ISODate("2017-08-24T11:14:04.256Z"),
"_id" : ObjectId("599eb507eea8c69622751de4"),
"pfactor" : 1,
"rpower" : 0,
"voltage" : 0,
"current" : 0,
"energy" : 0,
"power" : 0
},
{
"timestamp" : ISODate("2017-08-24T11:14:04.256Z"),
"_id" : ObjectId("599eb511eea8c69622751de5"),
"pfactor" : 1,
"rpower" : 0,
"voltage" : 0,
"current" : 0,
"energy" : 0,
"power" : 0
},
],
"__v" : 0,
"createdAt" : ISODate("2017-08-24T11:14:05.946Z")
}
and here is the query :
aggregate([{
$match: { "device_id": { $in: [ObjectId("5993df1b9a5fea3183064e49")] } }
}, {
$project: {
"power_data": 1
}
}, {
$unwind: "$power_data"
},
{
$project: {
"power": "$power_data.power", "hours": { $dayOfMonth: "$power_data.timestamp" }
}
}, {
$group: {
"_id": "$hours", "avg_power": { $avg: "$power" }
}
}]
all i want is, by passing some date timestamp range, i will get the data for this time interval only, currently its calculating overall.
Thanks For you valuable time!

I believe you just need to add an additional $match stage after the $unwind.
Here's the full pipeline:
{
$match: { "device_id": { $in: [ObjectId("5993df1b9a5fea3183064e49")] } }
},
{
$project: { "power_data": 1 }
},
{
$unwind: "$power_data"
},
{
$match: {
"power_data.timestamp" : { $gt : ISODate("2017-08-23T12:00:00.000Z"), $lt: ISODate("2017-08-25T12:00:00.000Z")}
}
},
{
$project: { "power": "$power_data.power", "hours": { $dayOfMonth: "$power_data.timestamp" }
}
},
{
$group: { "_id": "$hours", "avg_power": { $avg: "$power" } }
}

Related

How to get percentage total of data with group by date in MongoDB

How to get percentage total of data with group by date in MongoDB ?
Link example : https://mongoplayground.net/p/aNND4EPQhcb
I have some collection structure like this
{
"_id" : ObjectId("5ccbb96706d1d47a4b2ced4b"),
"date" : "2019-05-03T10:39:53.108Z",
"id" : 166,
"update_at" : "2019-05-03T10:45:36.208Z",
"type" : "image"
}
{
"_id" : ObjectId("5ccbb96706d1d47a4b2ced4c"),
"date" : "2019-05-03T10:39:53.133Z",
"id" : 166,
"update_at" : "2019-05-03T10:45:36.208Z",
"type" : "image"
}
{
"_id" : ObjectId("5ccbb96706d1d47a4b2ced4d"),
"date" : "2019-05-03T10:39:53.180Z",
"id" : 166,
"update_at" : "2019-05-03T10:45:36.208Z",
"type" : "image"
}
{
"_id" : ObjectId("5ccbb96706d1d47a4b2ced4e"),
"date" : "2019-05-03T10:39:53.218Z",
"id" : 166,
"update_at" : "2019-05-03T10:45:36.208Z",
"type" : "image"
}
And I have query in mongodb to get data of collection, how to get percentage of total data. in bellow example query to get data :
db.name_collection.aggregate(
[
{ "$match": {
"update_at": { "$gte": "2019-11-04T00:00:00.0Z", "$lt": "2019-11-06T00:00:00.0Z"},
"id": { "$in": [166] }
} },
{
"$group" : {
"_id": {
$substr: [ '$update_at', 0, 10 ]
},
"count" : {
"$sum" : 1
}
}
},
{
"$project" : {
"_id" : 0,
"date" : "$_id",
"count" : "$count"
}
},
{
"$sort" : {
"date" : 1
}
}
]
)
and this response :
{
"date" : "2019-11-04",
"count" : 39
},
{
"date" : "2019-11-05",
"count" : 135
}
how to get percentage data total from key count ? example response to this :
{
"date" : "2019-11-04",
"count" : 39,
"percentage" : "22%"
},
{
"date" : "2019-11-05",
"count" : 135,
"percentage" : "78%"
}
You have to group by null to get total count and then use $map to calculate the percentage. $round will be a useful operator in such case. Finally you can $unwind and $replaceRoot to get back the same number of documents:
db.collection.aggregate([
// previous aggregation steps
{
$group: {
_id: null,
total: { $sum: "$count" },
docs: { $push: "$$ROOT" }
}
},
{
$project: {
docs: {
$map: {
input: "$docs",
in: {
date: "$$this.date",
count: "$$this.count",
percentage: { $concat: [ { $toString: { $round: { $multiply: [ { $divide: [ "$$this.count", "$total" ] }, 100 ] } } }, '%' ] }
}
}
}
}
},
{
$unwind: "$docs"
},
{
$replaceRoot: { newRoot: "$docs" }
}
])
Mongo Playground

Get average per year and label?

I can use this query to get the average sqmPrice for a myArea
db.getCollection('sold').aggregate([
{$match:{}},
{$group: {_id: "$myArea", "sqmPrice": {$avg: "$sqmPrice"} }}
])
Output:
[
{
"_id" : "Yttre Aspudden",
"sqmPrice" : 48845.7777777778
},
{
"_id" : "Hägerstensåsen",
"sqmPrice" : 120
}
]
I would like to group this by year, ideally an object that looks like this:
{
"Yttre Aspudden": {
2008: 1232,
2009: 1244
...
}
...
}
but the formatting is not the most important.
Here is a sample object, I would like to use soldDate:
{
"_id" : ObjectId("5beca41c78f21248ab47f4a6"),
"location" : {
"address" : {
"streetAddress" : "Ljusstöparbacken 26C"
},
"position" : {
"latitude" : 59.31427884,
"longitude" : 18.00892421
},
"namedAreas" : [
"Hägersten-Liljeholmen"
],
"region" : {
"municipalityName" : "Stockholm",
"countyName" : "Stockholms län"
},
"distance" : {
"ocean" : 3777
}
},
"listPrice" : 1895000,
"rent" : 1959,
"floor" : 1,
"livingArea" : 38.5,
"source" : {
"name" : "Fastighetsbyrån",
"id" : 1573,
"type" : "Broker",
"url" : "http://www.fastighetsbyran.se/"
},
"rooms" : 1.5,
"published" : ISODate("2018-11-02T20:55:19.000Z"),
"constructionYear" : 1959,
"objectType" : "Lägenhet",
"booliId" : 3278478,
"soldDate" : ISODate("2018-11-14T00:00:00.000Z"),
"soldPrice" : 2620000,
"soldPriceSource" : "bid",
"url" : "https://www.booli.se/annons/3278478",
"publishedDays" : 1735,
"soldDays" : 1747,
"daysUp" : 160,
"street" : "Ljusstöparbacken",
"streetYear" : "Ljusstöparbacken Hägersten-Liljeholmen 1959",
"yearDay" : 318,
"yearWeek" : 46,
"roughSize" : 40,
"sqmPrice" : 49221,
"myArea" : "Gröndal",
"hotlist" : true
}
You need to generate your keys dynamically so you have to use $arrayToObject. To build an object which aggregates the data you need three $group stages and to create new root of your document you can use $replaceRoot, try:
db.sold.aggregate([
{ $group: {_id: { area: "$myArea", year: { $year: "$soldDate" } }, "sqmPrice": {$avg: "$sqmPrice"} }},
{ $group: { _id: "$_id.area", avgs: { $push: { k: { $toString: "$_id.year" }, v: "$sqmPrice" } } } },
{ $group: { _id: null, areas: { $push: { k: "$_id", v: { $arrayToObject: "$avgs" } } } } },
{ $replaceRoot: { newRoot: { $arrayToObject: "$areas" } } }
])

Is there a way to group results from multiple documents when performing aggregation

I am new to mongo and trying to perform aggregation query to calculate min/max of timestamps for a given document.
Sample documents are below -
{
"_id" : ObjectId("5c9cd93adddca9ebb2b3fcba"),
"frequency" : 5,
"s_id" : "30081993",
"timestamp" : NumberLong(1546300800000),
"date" : ISODate("2019-01-01T00:00:00.000Z"),
"values" : {
"1547439900000" : {
"number_of_values" : 3,
"min_value" : 32.13,
"max_value" : 81.42
},
"1547440200000" : {
"number_of_values" : 3,
"min_value" : 48.08,
"max_value" : 84.52
},
"1547440500000" : {
"number_of_values" : 2,
"min_value" : 27.39,
"max_value" : 94.64
}
}
}
{
"_id" : ObjectId("5c9cd851dddca9ebb2b3f2ac"),
"frequency" : 5,
"s_id" : "27061995",
"timestamp" : NumberLong(1546300800000),
"date" : ISODate("2019-01-01T00:00:00.000Z"),
"values" : {
"1547539900000" : {
"number_of_values" : 31,
"min_value" : 322.13,
"max_value" : 831.42
},
"1547540200000" : {
"number_of_values" : 3,
"min_value" : 418.08,
"max_value" : 8114.52
},
"1547740500000" : {
"number_of_values" : 2,
"min_value" : 207.39,
"max_value" : 940.64
}
}
}
I have come up with the following query which works for a single document.
db.testdb.aggregate([
{
$match: {
"s_id": "30081993",
"frequency": 5,
}
},
{
$project: {
_id: 1,
valuesarray: {
$objectToArray: "$values"
}
}
},
{
$unwind: "$valuesarray"
},
{
$group: {
"_id": "",
"min_timestamp": {
$min: "$valuesarray.k"
},
"max_timestamp": {
$max: "$valuesarray.k"
}
}
}
]);
The output is below
{
"_id" : "",
"min_timestamp" : "1547439900000",
"max_timestamp" : "1547440500000"
}
I want an aggregation query which can calculate the max/min of timestamps but for multiple documents i.e I want to use a $in operator during the $match stage and get min/max of all s_id. Is this possible?
Expected :
{
"_id" : "30081993",
"min_timestamp" : "1547439900000",
"max_timestamp" : "1547440500000"
}
{
"_id" : "27061995",
"min_timestamp" : "1547539900000",
"max_timestamp" : "1547740500000"
}
Yes, only small changes are required to make this work for multiple documents.
In $match stage, specify your $in query:
$match: {
"s_id": { $in : [ "30081993", "27061995" ] },
"frequency": 5,
}
In $project stage, rename s_id to _id, to ensure we keep the s_id associated with each document:
$project: {
_id: "$s_id",
valuesarray: {
$objectToArray: "$values"
}
}
In $group stage, group by _id (originally s_id), to ensure we correctly group the timestamps together before calculating $min/$max:
$group: {
"_id": "$_id",
"min_timestamp": {
$min: "$valuesarray.k"
},
"max_timestamp": {
$max: "$valuesarray.k"
}
}
Whole pipeline:
db.testdb.aggregate([
{
$match: {
"s_id": { $in : [ "30081993", "27061995" ] },
"frequency": 5,
}
},
{
$project: {
_id: "$s_id",
valuesarray: {
$objectToArray: "$values"
}
}
},
{
$unwind: "$valuesarray"
},
{
$group: {
"_id": "$_id",
"min_timestamp": {
$min: "$valuesarray.k"
},
"max_timestamp": {
$max: "$valuesarray.k"
}
}
}
]);

MongoDB aggregate array of objects together by object id and count occurences

I'm trying to figure out what I'm doing wrong, I have collected the following, "Subset of data", "Desired output"
This is how my data objects look
[{
"survey_answers": [
{
"id": "9ca01568e8dbb247", // As they are, this is the key to groupBy
"option_answer": 5, // Represent the index of the choosen option
"type": "OPINION_SCALE" // Opinion scales are 0-10 (meaning elleven options)
},
{
"id": "ba37125ec32b2a99",
"option_answer": 3,
"type": "LABELED_QUESTIONS" // Labeled questions are 0-x (they can change it from survey to survey)
}
],
"survey_id": "test"
},
{
"survey_answers": [
{
"id": "9ca01568e8dbb247",
"option_answer": 0,
"type": "OPINION_SCALE"
},
{
"id": "ba37125ec32b2a99",
"option_answer": 3,
"type": "LABELED_QUESTIONS"
}
],
"survey_id": "test"
}]
My desired output is:
[
{
id: '9ca01568e8dbb247'
results: [
{ _id: 5, count: 1 },
{ _id: 0, count: 1 }
]
},
{
id: 'ba37125ec32b2a99'
results: [
{ _id: 3, count: 2 }
]
}
]
Active query
Model.aggregate([
{
$match: {
'survey_id': survey_id
}
},
{
$unwind: "$survey_answers"
},
{
$group: {
_id: "$survey_answers.option_answer",
count: {
$sum: 1
}
}
}
])
Current output
[
{
"_id": 0,
"count": 1
},
{
"_id": 3,
"count": 2
},
{
"_id": 5,
"count": 1
}
]
I added your records to my db. Post that I tried your commands one by one.
$unwind results you similar to -
> db.survey.aggregate({$unwind: "$survey_answers"})
{ "_id" : ObjectId("5c3859e459875873b5e6ee3c"), "survey_answers" : { "id" : "9ca01568e8dbb247", "option_answer" : 5, "type" : "OPINION_SCALE" }, "survey_id" : "test" }
{ "_id" : ObjectId("5c3859e459875873b5e6ee3c"), "survey_answers" : { "id" : "ba37125ec32b2a99", "option_answer" : 3, "type" : "LABELED_QUESTIONS" }, "survey_id" : "test" }
{ "_id" : ObjectId("5c3859e459875873b5e6ee3d"), "survey_answers" : { "id" : "9ca01568e8dbb247", "option_answer" : 0, "type" : "OPINION_SCALE" }, "survey_id" : "test" }
{ "_id" : ObjectId("5c3859e459875873b5e6ee3d"), "survey_answers" : { "id" : "ba37125ec32b2a99", "option_answer" : 3, "type" : "LABELED_QUESTIONS" }, "survey_id" : "test" }
I am not adding code for match since that is okay in your query as well
The grouping would be -
> db.survey.aggregate({$unwind: "$survey_answers"},{$group: { _id: { 'optionAnswer': "$survey_answers.option_answer", 'id':"$survey_answers.id"}, count: { $sum: 1}}})
{ "_id" : { "optionAnswer" : 0, "id" : "9ca01568e8dbb247" }, "count" : 1 }
{ "_id" : { "optionAnswer" : 3, "id" : "ba37125ec32b2a99" }, "count" : 2 }
{ "_id" : { "optionAnswer" : 5, "id" : "9ca01568e8dbb247" }, "count" : 1 }
You can group on $survey_answers.id to bring it into projection.
The projection is what you're missing in your query -
> db.survey.aggregate({$unwind: "$survey_answers"},{$group: { _id: { 'optionAnswer': "$survey_answers.option_answer", 'id':'$survey_answers.id'}, count: { $sum: 1}}}, {$project : {answer: '$_id.optionAnswer', id: '$_id.id', count: '$count', _id:0}})
{ "answer" : 0, "id" : "9ca01568e8dbb247", "count" : 1 }
{ "answer" : 3, "id" : "ba37125ec32b2a99", "count" : 2 }
{ "answer" : 5, "id" : "9ca01568e8dbb247", "count" : 1 }
Further you can add a group on id and add results to a set. And your final query would be -
db.survey.aggregate(
{$unwind: "$survey_answers"},
{$group: {
_id: { 'optionAnswer': "$survey_answers.option_answer", 'id':'$survey_answers.id'},
count: { $sum: 1}
}},
{$project : {
answer: '$_id.optionAnswer',
id: '$_id.id',
count: '$count',
_id:0
}},
{$group: {
_id:{id:"$id"},
results: { $addToSet: {answer: "$answer", count: '$count'} }
}},
{$project : {
id: '$_id.id',
answer: '$results',
_id:0
}})
Hope this helps.

MongoDB aggregation counts based on value

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