Mongoose aggregating the documents, based on the time difference - node.js

I have a schema with following fields
var MySchema = new Schema({
roomId: { type: String, required: true, ref: 'Room' },
timestamp: Date,
currentTemp: Number,
outsideTemp: Number,
desiredTemp: Number
});
Now I want to aggregate the documents in this collection and return the response like
{
timestamp: [1,2,3,4,5,6,7,8,9,10..,24],
currentTemp: [34,45,.....],
outsideTemp: [14,45,.....],
desiredTemp: [34,45,.....]
}
I have some match condition and I am able to select the documents based on the match condition. Now I want to aggregate the selected documents and produce a result like above.
The timestamp value is bound by a lower bound and upper bound. The upper bound and lower bound is passed from the client and the aggregation should be based on the difference between the lower and upper. If the difference is
- 1 day : group by hour
- 3 days : group by 6 hrs
- 7 days : group by 12 hours
- < 30 days : group by day
- > 30 days : group by week
currentTemp is the average of all the documents currentTemp property in that time period. Similarly for outsideTemp and desiredTemp.
How can I do this ?
EDIT:
Sample data set
{
"_id": "5656bfd94d6f6304ff140000",
"roomId": "5656bc124d6f6335051b0000",
"timestamp": "2015-11-15T08:59:20Z",
"currentTemp": 10.55,
"outsideTemp": 43.83,
"desiredTemp": 21.32
}, {
"_id": "5656bfd94d6f6304ff150000",
"roomId": "5656bc124d6f633505200000",
"timestamp": "2015-06-01T06:33:49Z",
"currentTemp": 32.47,
"outsideTemp": 49.65,
"desiredTemp": 20.99
}, {
"_id": "5656bfd94d6f6304ff160000",
"roomId": "5656bc124d6f633505250000",
"timestamp": "2014-12-31T23:47:54Z",
"currentTemp": 35.69,
"outsideTemp": 29.91,
"desiredTemp": 20.15
}, {
"_id": "5656bfd94d6f6304ff170000",
"roomId": "5656bc124d6f6335051e0000",
"year": 2015,
"month": 3,
"day": 12,
"hour": 21,
"minute": 13,
"second": 38,
"inDST": true,
"timestamp": "2015-11-14T07:56:42Z",
"currentTemp": 27.65,
"outsideTemp": 41.4,
"desiredTemp": 24.68
}

Related

How to create graphs or historigrams with mongoDB

I need to make a graph where to show how many total users we have registered in a time interval. (The language is TypeScript but I can get an answer in another language or using the aggregate of mongoDB)
Example:
Day 1: 10 total users registered
Day 2: 139 ...
Day 3: 1230 ...
Day 4: 2838 ...
...
...
Current day: X number of users ... and so it would end.
It should be noted that all users have a field called createAt, which is of type date.
I tried to obtain the users by means of cubes but it is not an optimal solution.
const response = await this.userModel.aggregate([
{
$bucketAuto: {
groupBy: '$createdAt',
buckets: 4,
},
},
]);
console.log(response);
I have also thought about using mapReduce from mongoDB and pass the specified function to it. But in terms of performance, I would like to know if that could create the pipelines simply with aggregate. mapReduce would be a second option (slightly slower) and as a last option to get all the users (only with the CreateAt field) and process them in my backend.
Thank you in advance for your answers
Update
I also mean that with autoBucket it orders them by non-specific time intervals, it basically orders them by the number of users and groups them by the creation dates, also when i passed the dates to mongodb, with $bucket the result is not as expected
$bucketAuto Option
$bucket option
input Example:
const list = [
{
"createdAt": "2021-08-30T23:47:16.663Z",
"_id": "612d6e044007a95446848cef"
},
{
"createdAt": "2021-08-31T04:18:11.820Z",
"_id": "612dad830541fa001bb63671"
},
{
"createdAt": "2021-08-31T04:18:47.794Z",
"_id": "612dada70541fa001bb63674"
},
{
"createdAt": "2021-08-31T04:20:14.415Z",
"_id": "612dadfe0541fa001bb63678"
},
{
"createdAt": "2021-08-31T04:22:45.580Z",
"_id": "612dae950541fa001bb63682"
},
{
"createdAt": "2021-08-31T11:24:28.471Z",
"_id": "612e116c0541fa001bb63688"
},
{
"createdAt": "2021-08-31T18:47:09.452Z",
"_id": "612e792dba2a3e1d081c9f3d"
}
];

Find object from a collection according to created date

I have a collection ticket_masters,which contain createdAt field and it store the date and time .
[
{
"_id": "5e78f2ddc0e09128e81db47a",
"NAME": "Jasin",
"PHONE": "2252545414",
"MAIL": "sdsdm#m.com",
"createdAt": "2020-03-23T17:33:17.470Z",
"updatedAt": "2020-03-23T17:33:17.470Z",
"__v": 0
}
]
Now i want find records according to createAt field from the user collection. Already tried with the following code snippet.
db.getCollection('ticket_masters').find({
"createdAt" : '2020-03-17T18:30:00.237+00:00'
})
Output :
Fetched 0 record(s) in 1ms
But zero records found as per the above code snippet.Kindly help me to resolve issues
Thank you
By specifying the Date type.
The $eq operator matches documents where the value of a field equals the specified value.
// Date and time
db.getCollection('ticket_masters').aggregate([
{
$match: {
"createdAt": {$eq: new Date('2020-03-17T18:30:00.237+00:00')}
}
},
])
OR
// Only Date
db.getCollection('ticket_masters').aggregate([
{
$match: {
"createdAt": {"$gte": new Date("2020-03-17"), $lt : new Date("2020-03-18") }
}
},
])
The dates you saved were new Date(), which includes the time components. To query those times you need to create a date range that includes all moments in a day
Using momentjs
Example:
// start today
var start = moment().startOf('day');
// end today
var end = moment(today).endOf('day');
{ createdAt: { '$gte': start, '$lte': end }
$gt Matches values that are greater than a specified value.
$lt Matches values that are less than a specified value.
For more information: https://docs.mongodb.com/manual/reference/operator/query-comparison/
Hope this will help you.
db.getCollection('ticket_masters').find({
"createdAt" : ISODate("2020-03-17T18:30:00.237+00:00")
})
or you can try this.
db.getCollection('ticket_masters').aggregate([
{
$match: {
"createdAt":ISODate("2020-03-17T18:30:00.237+00:00")
}
},
])

how to get the document details according to the field value in mongodb aggregate

I have a collection named users
var UserSchema = new Schema({
name: String,
age: Number,
points: {type: Number, default: 0}
})
all users have some different points like 5, 10, 20, 50
so i want to count the number of users having 5 points, 10 points etc, and want to show the counted users details also, like who are those users which are having 5 points, 10 points etc.
how to write query for that in $aggregate
You can write a group stage and push all the values you need using the $push operator
db.collection.aggregate([
{
"$group": {
"_id": "$points",
"details": {
"$push": {
"name": "$name",
"age": "$age"
}
}
}
}
])
In the above example, I've grouped according to points and for each group, you'll get an array containing the name and age of the people having those points

MongoDB - get documents with max attribute per group in a collection

My data looks like this:
session, age, firstName, lastName
1, 28, John, Doe
1, 21, Donna, Keren
2, 32, Jenna, Haze
2, 52, Tommy, Lee
..
..
I'd like to get all the rows which are the largest (by age) per session. So So for the above input my output would look like:
sessionid, age, firstName, lastName
1, 28, John, Doe
2, 52, Tommy, Lee
because John has the largest age in the session = 1 group and Tommy has the largest age on the session=2 group.
I need to export the result to a file (csv) and it may contain lots of records.
How can I achieve this?
MongoDB aggregation offers the $max operator, but in your case you want the "whole" record as it is. So the appropriate thing to do here is $sort and then use the $first operator within a $group statement:
db.collection.aggregate([
{ "$sort": { "session": 1, "age": -1 } },
{ "$group": {
"_id": "$session",
"age": { "$first": "$age" },
"firstName": { "$first" "$firstName" },
"lastName": { "$first": "$lastName" }
}}
])
So the "sorting" gets the order right, and the "grouping" picks the first occurrence within the "grouping" key where those fields exist.
Mostly $first here because the $sort is done in reverse order. You can also use $last when in an ascending order as well.
You could try the below aggregation query which uses max attribute: http://docs.mongodb.org/manual/reference/operator/aggregation/max/
db.collection.aggregate([
$group: {
"_id": "$session",
"age": { $max: "$age" }
},
{ $out : "max_age" }
])
The results should be outputted to the new collection max_age and then you could dump it into a csv.
Note: it will give only the session and max age and will not return other fields.

Querying mongodb for dups but allow certain duplicates based on timestamps

So I have a set of data that have timestamps associated with it. I want mongo to aggregate the ones that have duplicates within a 3 min timestamp. I'll show you an example of what I mean:
Original Data:
[{"fruit" : "apple", "timestamp": "2014-07-17T06:45:18Z"},
{"fruit" : "apple", "timestamp": "2014-07-17T06:47:18Z"},
{"fruit" : "apple", "timestamp": "2014-07-17T06:55:18Z"}]
After querying, it would be:
[{"fruit" : "apple", "timestamp": "2014-07-17T06:45:18Z"},
{"fruit" : "apple", "timestamp": "2014-07-17T06:55:18Z"}]
Because the second entry was within the 3 min bubble created by the first entry. I've gotten the code so that it aggregates and removed dupes that have the same fruit but now I only want to combine the ones that are within the timestamp bubble.
We should be able to do this! First lets split up an hour in 3 minute 'bubbles':
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57]
Now to group these documents we need to modify the timestamp a little. As far I as know this isn't currently possible with the aggregation framework so instead I will use the group() method.
In order to group fruits within the same time period we need to set the timestamp to the nearest minute 'bubble'. We can do this with timestamp.minutes -= (timestamp.minutes % 3).
Here is the resulting query:
db.collection.group({
keyf: function (doc) {
var timestamp = new ISODate(doc.timestamp);
// seconds must be equal across a 'bubble'
timestamp.setUTCSeconds(0);
// round down to the nearest 3 minute 'bubble'
var remainder = timestamp.getUTCMinutes() % 3;
var bubbleMinute = timestamp.getUTCMinutes() - remainder;
timestamp.setUTCMinutes(bubbleMinute);
return { fruit: doc.fruit, 'timestamp': timestamp };
},
reduce: function (curr, result) {
result.sum += 1;
},
initial: {
sum : 0
}
});
Example results:
[
{
"fruit" : "apple",
"timestamp" : ISODate("2014-07-17T06:45:00Z"),
"sum" : 2
},
{
"fruit" : "apple",
"timestamp" : ISODate("2014-07-17T06:54:00Z"),
"sum" : 1
},
{
"fruit" : "banana",
"timestamp" : ISODate("2014-07-17T09:03:00Z"),
"sum" : 1
},
{
"fruit" : "orange",
"timestamp" : ISODate("2014-07-17T14:24:00Z"),
"sum" : 2
}
]
To make this easier you could precompute the 'bubble' timestamp and insert it into the document as a separate field. The documents you create would look something like this:
[
{"fruit" : "apple", "timestamp": "2014-07-17T06:45:18Z", "bubble": "2014-07-17T06:45:00Z"},
{"fruit" : "apple", "timestamp": "2014-07-17T06:47:18Z", "bubble": "2014-07-17T06:45:00Z"},
{"fruit" : "apple", "timestamp": "2014-07-17T06:55:18Z", "bubble": "2014-07-17T06:54:00Z"}
]
Of course this takes up more storage. However, with this document structure you can use the aggregate function[0].
db.collection.aggregate(
[
{ $group: { _id: { fruit: "$fruit", bubble: "$bubble"} , sum: { $sum: 1 } } },
]
)
Hope that helps!
[0] MongoDB aggregation comparison: group(), $group and MapReduce

Resources