I am trying to get a set of results in MongoDB (version 4.2) using only MongoDB Query Language where a specific date is greater than 30 days before today (that is, I need the data from the last 30 days, starting at 00:00h of the day 30 days ago).
I have tried this, but it does not yield any results:
{
date: {
$gte: {
$dateFromParts: {
year: {
$year: new Date()
},
month: {
$month: new Date()
},
day: {
$add: [
{$dayOfMonth: new Date()},
-30
]
}
}
}
}
}
Obviously, I checked that there were results when I entered the date directly this way (being today the 13th of December of 2022):
{date: {$gte: ISODate('2022-11-13')}}
Thank you for the help!
UPDATE 1:
Thanks to #wernfried-domscheit, I came to this solution, although it is very slow. If there was a solution that does not involve using $expr...
{
$expr: {
$gte: [
"$date",
{
$dateFromParts: {
year: {
$year: new Date()
},
month: {
$month: new Date()
},
day: {
$add: [
{
$dayOfMonth: Date()
},
-30
]
}
}
}
]
}
}
Try this one:
db.collection.find({
$expr: {
$gt: [
"$date",
{
$dateSubtract: {
startDate: { $dateTrunc: { date: ISODate(), unit: "day" } },
unit: "day",
amount: 30
}
}
]
}
})
For older version of MongoDB, use
db.collection.find({
$expr: {
$gt: [
"$date",
{
$subtract: [
{
$dateFromParts: {
year: { $year: ISODate() },
month: { $month: ISODate() },
day: { dayOfMonth: ISODate() }
}
},
1000 * 60 * 60 * 24 * 30
]
}
]
}
})
I'm trying to sum the 'emsion_co2' values by day. in order to know in what days the value is higher than 420, but my query is not working.
this is the database
db.test_sensores.insert([{"timestamp":"2020-07-01T00:00:00Z","dia":01,"sensor_id":1,"location_id":1,"medidas":[{"tipo_medida":"Temperatura","valor":16.61,"unidad":"ºC"},{"tipo_medida":"Humedad_relativa","valor":83.74,"unidad":"%"}]},
{"timestamp":"2020-07-01T00:00:00Z","dia":01,"sensor_id":2,"location_id":1,"medidas":[{"tipo_medida":"Emision_CO2","valor":1.572,"unidad":"gCO2/m2"},{"tipo_medida":"Consumo_electrico","valor":0.00188,"unidad":"kWh/m2"}]},
{"timestamp":"2020-07-01T00:15:00Z","dia":01,"sensor_id":1,"location_id":1,"medidas":[{"tipo_medida":"Temperatura","valor":15.75,"unidad":"ºC"},{"tipo_medida":"Humedad_relativa","valor":83.08,"unidad":"%"}]},
{"timestamp":"2020-07-01T00:15:00Z","dia":01,"sensor_id":2,"location_id":1,"medidas":[{"tipo_medida":"Emision_CO2","valor":1.626,"unidad":"gCO2/m2"},{"tipo_medida":"Consumo_electrico","valor":0.00146,"unidad":"kWh/m2"}]},
{"timestamp":"2020-07-10T00:30:00Z","dia":10,"sensor_id":1,"location_id":1,"medidas":[{"tipo_medida":"Temperatura","valor":14.08,"unidad":"ºC"},{"tipo_medida":"Humedad_relativa","valor":90.05,"unidad":"%"}]},
{"timestamp":"2020-07-10T00:30:00Z","dia":10,"sensor_id":2,"location_id":1,"medidas":[{"tipo_medida":"Emision_CO2","valor":1.614,"unidad":"gCO2/m2"},{"tipo_medida":"Consumo_electrico","valor":0.00173,"unidad":"kWh/m2"}]},
{"timestamp":"2020-07-01T00:45:00Z","dia":01,"sensor_id":1,"location_id":1,"medidas":[{"tipo_medida":"Temperatura","valor":10.62,"unidad":"ºC"},{"tipo_medida":"Humedad_relativa","valor":92.27,"unidad":"%"}]},
{"timestamp":"2020-07-01T00:45:00Z","dia":01,"sensor_id":2,"location_id":1,"medidas":[{"tipo_medida":"Emision_CO2","valor":1.59,"unidad":"gCO2/m2"},{"tipo_medida":"Consumo_electrico","valor":0.00196,"unidad":"kWh/m2"}]},
{"timestamp":"2020-07-01T00:00:00Z","dia":01,"sensor_id":1,"location_id":2,"medidas":[{"tipo_medida":"Temperatura","valor":22.08,"unidad":"ºC"},{"tipo_medida":"Humedad_relativa","valor":34.92,"unidad":"%"}]},
{"timestamp":"2020-07-01T00:00:00Z","dia":01,"sensor_id":2,"location_id":2,"medidas":[{"tipo_medida":"Emision_CO2","valor":2.055,"unidad":"gCO2/m2"},{"tipo_medida":"Consumo_electrico","valor":0.00269,"unidad":"kWh/m2"}]},
{"timestamp":"2020-07-01T00:15:00Z","dia":01,"sensor_id":1,"location_id":2,"medidas":[{"tipo_medida":"Temperatura","valor":21.12,"unidad":"ºC"},{"tipo_medida":"Humedad_relativa","valor":37.7,"unidad":"%"}]},
{"timestamp":"2020-07-01T00:15:00Z","dia":01,"sensor_id":2,"location_id":2,"medidas":[{"tipo_medida":"Emision_CO2","valor":2.102,"unidad":"gCO2/m2"},{"tipo_medida":"Consumo_electrico","valor":0.00272,"unidad":"kWh/m2"}]},
{"timestamp":"2020-07-10T00:30:00Z","dia":10,"sensor_id":1,"location_id":2,"medidas":[{"tipo_medida":"Temperatura","valor":24.26,"unidad":"ºC"},{"tipo_medida":"Humedad_relativa","valor":52.52,"unidad":"%"}]},
{"timestamp":"2020-07-10T00:30:00Z","dia":10,"sensor_id":2,"location_id":2,"medidas":[{"tipo_medida":"Emision_CO2","valor":2.023,"unidad":"gCO2/m2"},{"tipo_medida":"Consumo_electrico","valor":0.00255,"unidad":"kWh/m2"}]},
{"timestamp":"2020-07-01T00:45:00Z","dia":01,"sensor_id":1,"location_id":2,"medidas":[{"tipo_medida":"Temperatura","valor":24.51,"unidad":"ºC"},{"tipo_medida":"Humedad_relativa","valor":34.1,"unidad":"%"}]},
{"timestamp":"2020-07-01T00:45:00Z","dia":01,"sensor_id":2,"location_id":2,"medidas":[{"tipo_medida":"Emision_CO2","valor":1.957,"unidad":"gCO2/m2"},{"tipo_medida":"Consumo_electrico","valor":0.00215,"unidad":"kWh/m2"}]}])
And the next query is what I was trying, but the result is wrong because there is not showing the sum, just appear in 0 the values.
db.test_sensores.aggregate([{$match:{'medidas.tipo_medida':'Emision_CO2'}},{$group:{_id:'$dia', acumulado_emisiones_CO2:{$sum:'$medidas.0.valor'}}}])
In my query is missing how to find the days where emision_co2 value > 420 too, I was thinking manually but if there is an automated way, please let me know. Thank you
Unsure why you attempt to group by dia,
$match
$set - Set timestamp to Date object, medidas_Emision_CO2_valor for sum all the values in medidas array by filtering ($filter) the document with tipo_medida is "Emision_CO2" via $reduce.
$group - Group by date, month, and year of timestamp and sum the medidas_Emision_CO2_valor values as acumulado_emisiones_CO2.
$match - Filter the documents with acumulado_emisiones_CO2 is greater than ($gt) 420.
db.test_sensores.aggregate([
{
$match: {
"medidas.tipo_medida": "Emision_CO2"
}
},
{
$set: {
timestamp: {
$toDate: "$timestamp"
},
medidas_Emision_CO2_valor: {
$reduce: {
input: {
$filter: {
input: "$medidas",
cond: {
$eq: [
"$$this.tipo_medida",
"Emision_CO2"
]
}
}
},
initialValue: 0,
in: {
$sum: [
"$$value",
"$$this.valor"
]
}
}
}
}
},
{
$group: {
_id: {
day: {
"$dayOfMonth": "$timestamp"
},
month: {
"$month": "$timestamp"
},
year: {
$year: "$timestamp"
}
},
acumulado_emisiones_CO2: {
$sum: "$medidas_Emision_CO2_valor"
}
}
},
{
$match: {
"acumulado_emisiones_CO2_valor": {
$gt: 420
}
}
}
])
Sample Mongo Playground
I come from the world of relational databases and am having difficulty with a MongoDB query.
In my database I have telemetry documents with minute-by-minute records for each monitored system.
I'm tending to do a query that groups records by hour.
Below is a demonstration of the structure of my documents:
{
_id: ObjectId("61847b83618fc8a6fb368ab7"),
system_code: 22,
date: ISODate("2021-08-01T00:00:01.000Z"),
value1: 22.2372973251,
value2: 20
}
Here's the structure of the query I'm trying to run:
db.telemetry.aggregate([
{
$match: {
$and: [
{ system_code: 22 },
{ date: { $gte: ISODate("2021-08-01 00:00:00") } },
{ date: { $lte: ISODate("2021-08-01 02:00:00") } }
]
}
},
{
$addFields: {
italianDate: {
$dateToString: {
format: "%d/%m/%Y %H:00:00",
date: "$date"
}
}
}
},
{
$sort: {
italianDate: 1
}
},
{
$group: {
_id: {
year: { $year: "$date" },
month: { $month: "$date" },
day: { $dayOfMonth: "$date" },
hour: { $hour: "$date" }
}
}
}
]);
The SQL query with PostgreSQL is exactly this:
SELECT TO_CHAR(t.date, 'YYYY-MM-DD HH24:00:00') AS italianDate, AVG(value1), AVG(value2) FROM telemetry t WHERE t.system_code = 22 AND t.date BETWEEN '2021-08-01 00:00:00' AND '2021-08-01 02:00:00' GROUP BY italianDate ORDER BY italianDate ASC
Thank you so much if you can help me.
You are actually pretty close. You just need to put the $avg inside your $group stage. Another thing is you need to pay attention to the date formats. Refer to this official MongoDB document for the formats.
db.telemetry.aggregate([
{
$match: {
system_code: 22,
date: {
$gte: ISODate("2021-08-01T00:00:00Z"),
$lte: ISODate("2021-08-01T02:00:00Z")
}
}
},
{
$addFields: {
italianDate: {
$dateToString: {
format: "%Y-%m-%d %H:00:00",
date: "$date"
}
}
}
},
{
$group: {
_id: "$italianDate",
avgValue1: {
$avg: "$value1"
},
avgValue2: {
$avg: "$value2"
}
}
},
{
$sort: {
_id: -1
}
}
])
Here is the Mongo playground for your reference.
We can use $add to add Dates in the aggregation pipe. Is there a way to add a specific number of time units (ie days months or years) except milliseconds?
The current way to add 3 years is date: { $add: [ "$date", 3*365*24*60*60000 ] }
My expected syntax format is date: { $add: [ "$date", { $years:3 } ] } which is not working
As mentioned in my comment, you will get this function Mongo 5.0
In the meantime you can use this workaround:
db.collection.aggregate([
{
$addFields: {
new_date: {
$dateFromParts: {
year: { $add: [{ $year: "$date" }, 3] },
month: { $month: "$date" },
day: { $dayOfMonth: "$date" },
hour: { $hour: "$date" },
minute: { $minute: "$date" },
second: { $second: "$date" },
millisecond: { $millisecond: "$date" }
}
}
}
}
])
I am spinning my wheels on this. I am needing to find all documents within a collection that have a timestamp ("createdTs") that have a 3 second or less difference (to be clear: month/day/time/year all the same, save those few seconds). An example of createdTs field (it's type Date): 2021-04-26T20:39:01.851Z
db.getCollection("CollectionName").aggregate([
{ $match: { memberId: ObjectId("1234") } },
{
$project:
{
year: { $year: "$createdTs" },
month: { $month: "$createdTs" },
day: { $dayOfMonth: "$createdTs" },
hour: { $hour: "$createdTs" },
minutes: { $minute: "$createdTs" },
seconds: { $second: "$createdTs" },
milliseconds: { $millisecond: "$createdTs" },
dayOfYear: { $dayOfYear: "$createdTs" },
dayOfWeek: { $dayOfWeek: "$createdTs" },
week: { $week: "$createdTs" }
}
}
])
I've tried a lot of different variances. Where I'm struggling is how to compare these findings to one another. I'd also prefer to just search the entire collection and not match on the "memberId" field, just collect any documents that have less than a 3 second createdTs difference, and group/display those.
Is this possible? Newer to Mongo, and spun my wheels on this for two days now. Any advice would be greatly appreciated, thank you!
I saw this on another post, but not sure how to utilize it since I'm wanting to compare the same field:
db.collection.aggregate([
{ "$project": {
"difference": {
"$divide": [
{ "$subtract": ["$logoutTime", "$loginTime"] },
60 * 1000 * 60
]
}
}},
{ "$group": {
"_id": "$studentID",
"totalDifference": { "$sum": "$difference" }
}},
{ "$match": { "totalDifference": { "$gte": 20 }}}
])
Also am trying...
db.getCollection("CollectionName").aggregate([
{ $match: { memberId: ObjectId("1234") } },
{
$project:
{
year: { $year: "$createdTs" },
month: { $month: "$createdTs" },
total:
{ $add: ["$year", "$month"] }
}
}
])
But this returns a total of null. Not sure if it's because $year and $month are difference? The types are both int32, so I thought that'd work. Was wondering if there's a way to compare if all the fields are 0, then if seconds is not/difference is $gte 3 when using $group, could go from there.