How do I sort data by date (json) - node.js

I want to sort json data by date , Actual data hava 1 ~ 31
example data:
{
Detail: { '60': 100, '68': 20 },
Time: 2020-01-02T07:08:41.059Z,
No: 'aaa'
},
{
Detail: { '60': 300, '68': 20 },
Time: 2020-01-02T09:10:21.059Z,
No: 'bbb'
},
{
Detail: { '60': 10 },
Time: 2020-01-11T07:08:45.521Z,
No: 'ccc'
}
How do I sort by Date like this :
{
"2":{
"aaa": { '60': 100, '68': 20 }
"bbb": { '60': 300, '68': 20 }
}
"11":{
"ccc": { '60': 100 },
}
}
Thanks

The following aggregation does that. Assuming that the Time field of type Date.
db.test.aggregate( [
{
$group: {
_id: { day: { $dayOfMonth: "$Time" } },
data:{ $push: { k: "$No", v: "$Detail" } }
}
},
{
$sort: { "_id.day": 1 }
},
{
$addFields: {
data: { $arrayToObject: "$data" },
day: "$_id.day"
}
},
{
$group: {
_id: null,
data: { $push: { k: { $toString: "$_id.day" }, v: "$data" } }
}
},
{
$project: {
_id: 0,
data: { $arrayToObject: "$data" }
}
},
{
$replaceRoot: { newRoot: "$data" }
}
] ).pretty()

Related

How to find difference between dates in different documents using Loopback 3 remote method in MongoDB?

Suppose my 2 documents are like this in mongodb. How do I find date difference between the fields "eventCreatedAt".
I am using loopback 3 remote method implementation for this.
[
{
"eventType": "offer",
"currentPrice": 3,
"seller": "Medicalstudent",
"buyer": "musiciann",
"eventCreatedAt": "2022-03-09T10:25:20.308Z",
"isExistingOffer": true,
"timeDiffOfPreTran": 1,
"id": "6228853556f56afb3d995d50",
"nftId": "622880fc56f56afb3d995d4f"
},
{
"eventType": "offer",
"currentPrice": 5,
"seller": "Medicalstudentss",
"buyer": "music",
"eventCreatedAt": "2022-03-09T10:25:20.308Z",
"isExistingOffer": false,
"timeDiffOfPreTran": 4,
"id": "622f2d8e550a568093b54c93",
"nftId": "622880fc56f56afb3d995d4f"
}
]
db.collection.aggregate([
{
$lookup: {
from: "nft",
localField: "nftId",
foreignField: "nftId",
as: "docs",
pipeline: [
{
$setWindowFields: {
partitionBy: "",
sortBy: { eventCreatedAt: 1 },
output: {
shift: {
$shift: {
output: "$eventCreatedAt",
by: -1,
default: "$$REMOVE"
}
}
}
}
},
{
$set: {
shift: {
$dateDiff: {
startDate: { $toDate: "$shift" },
endDate: { $toDate: "$eventCreatedAt" },
unit: "day"
}
}
}
}
]
}
}
])
mongoplayground
db.collection.aggregate([
{
$setWindowFields: {
partitionBy: "",
sortBy: { eventCreatedAt: 1 },
output: {
shift: {
$shift: {
output: "$eventCreatedAt",
by: -1,
default: "$$REMOVE"
}
}
}
}
},
{
$set: {
shift: {
$dateDiff: {
startDate: { $toDate: "$shift" },
endDate: { $toDate: "$eventCreatedAt" },
unit: "day"
}
}
}
}
])
mongoplayground

Fetching last 30 days of Mongo records, summing by date, and filling in missing dates in range

I have a Mongo database filled with "Events" records, that look like this:
{
timestamp: 2022-03-15T22:11:34.711Z,
_id: new ObjectId("62310f16b0d71321e887a905")
}
Using a NodeJs server, I need to fetch the last 30 days of Events, grouped/summed by date, and any dates within that 30 days with no records need to be filled with 0.
Using this code I can get the correct events, grouped/summed by date:
Event.aggregate( [
{
$match: {
timestamp: {
$gte: start,
$lte: end,
}
}
},
{
$project: {
date: {
$dateToParts: { date: "$timestamp" }
},
}
},
{
$group: {
_id: {
date: {
year: "$date.year",
month: "$date.month",
day: "$date.day"
}
},
"count": { "$sum": 1 }
}
}
] )
This will return something like this:
[
{
"_id": {
"date": {
"year": 2022,
"month": 3,
"day": 14
}
},
"count": 3
},
{
"_id": {
"date": {
"year": 2022,
"month": 3,
"day": 15
}
},
"count": 8
},
]
I also have this Javascript code to generate the last 30 days of dates:
const getDateRange = (start, end) => {
const arr = [];
for(let dt = new Date(start); dt <= end; dt.setDate(dt.getDate() + 1)){
arr.push(new Date(dt));
}
return arr;
};
const subtractDays = (date, days) => {
return new Date(date.getTime() - (days * 24 * 60 * 60 * 1000));
}
const end = new Date();
const start = subtractDays(end, 30);
const range = getDateRange(start, end);
Which returns something like this:
[
2022-03-09T01:13:10.769Z,
2022-03-10T01:13:10.769Z,
2022-03-11T01:13:10.769Z,
2022-03-12T01:13:10.769Z,
2022-03-13T01:13:10.769Z,
...
]
It seems like I have all the pieces, but I'm having trouble putting all this together to do what I need in an efficient way. Any push in the right direction would be appreciated.
Whenever one has to work with date/time arithmetic then I recommend a library like moment.js
const end = moment().startOf('day').toDate();
const start = moment().startOf('day').subtract(30, 'day').toDate();
In MongoDB version 5.0 you can use $dateTrunc(), which is shorter than $dateToParts and { year: "$date.year", month: "$date.month", day: "$date.day" }
You need to put all data in an array ({$group: {_id: null, data: { $push: "$$ROOT" }}) and then at missing elements with $ifNull:
event.aggregate([
{
$match: {
timestamp: { $gte: start, $lte: end }
}
},
{
$group: {
_id: { $dateTrunc: { date: "$timestamp", unit: "day" } },
count: { $sum: 1 }
}
},
{ $project: {timestamp: "$_id", count: 1, _id: 0} },
{
$group: {
_id: null,
data: { $push: "$$ROOT" }
}
},
{
$set: {
data: {
$map: {
input: { $range: [0, 30] },
as: "i",
in: {
$let: {
vars: {
day: { $dateAdd: { startDate: start, amount: "day", unit: "$$i" } }
},
in: {
$ifNull: [
{
$first: {
$filter: {
input: "$data",
cond: { $eq: ["$$this.timestamp", "$$day"] }
}
}
},
{ timestamp: "$$day", count: 0 }
]
}
}
}
}
}
}
},
{ $unwind: "$data" }
])
$range operator supports only integer values, that's the reason for using $let. Otherwise, if you prefer to use the external generated range, it would be
{
$set: {
data: {
$map: {
input: range,
as: "day",
in: {
$ifNull: [
{
$first: {
$filter: {
input: "$data",
cond: { $eq: ["$$this.timestamp", "$$day"] }
}
}
},
{ timestamp: "$$day", count: 0 }
]
}
}
}
}
}
And for MongoDB version 5.1 you may have a look at $densify
Use aggregation stage densify if you're using MongoDB version 5.1 or later. But for lower version, below query can be used.
db.collection.aggregate([
{
$match: {
timestamp: {
$gte: {
"$date": "2022-03-01T00:00:00.000Z"
},
$lte: {
"$date": "2022-03-31T23:59:59.999Z"
},
}
}
},
{
$project: {
date: {
$dateToParts: {
date: "$timestamp"
}
},
}
},
{
$group: {
_id: {
date: {
year: "$date.year",
month: "$date.month",
day: "$date.day"
}
},
"count": {
"$sum": 1
}
}
},
{
"$group": {
"_id": null,
"originData": {
"$push": "$$ROOT"
}
}
},
{
"$project": {
"_id": 0,
"data": {
"$concatArrays": [
{
"$map": {
"input": {
"$range": [
0,
30,
1
]
},
"in": {
"$let": {
"vars": {
"date": {
"$add": [
{
"$date": "2022-03-01T00:00:00.000Z"
},
{
"$multiply": [
"$$this",
86400000
]
}
]
}
},
"in": {
"_id": {
"date": {
"day": {
"$dayOfMonth": "$$date"
},
"month": {
"$month": "$$date"
},
"year": {
"$year": "$$date"
}
}
},
"count": 0
}
}
}
}
},
"$originData"
]
}
}
},
{
"$unwind": "$data"
},
{
$group: {
_id: {
date: {
year: "$data._id.date.year",
month: "$data._id.date.month",
day: "$data._id.date.day"
}
},
"count": {
"$sum": "$data.count"
}
}
},
{
"$sort": {
"_id.date.year": 1,
"_id.date.month": 1,
"_id.date.day": 1
}
}
])
Link to online playground. https://mongoplayground.net/p/5I0I04HoHXm

How to find the latest date in nested array of objects (MongoDB)

I am trying to find the latest "order" in "orders" array in the whole collection (Not only in the one object).
Data:
[
{
_id: 1,
orders: [
{
title: 'Burger',
date: {
$date: '2021-07-18T13:12:08.717Z',
},
},
],
},
{
_id: 2,
orders: [
{
title: 'Salad',
date: {
$date: '2021-07-18T13:35:01.586Z',
},
},
],
},
];
Code:
var restaurant = await Restaurant.findOne({
'orders.date': 1,
});
Rather simple:
db.collection.aggregate([
{ $project: { latest_order: { $max: "$orders.date" } } }
])
If you like to get the full order use this:
db.collection.aggregate([
{
$project: {
latest_order: {
$first: {
$filter: {
input: "$orders",
cond: { $eq: [ "$$this.date", { $max: "$orders.date" } ] }
}
}
}
}
},
{ $sort: { "latest_order.date": 1 } },
{ $limit: 1 }
])
Mongo Playground
You have to use aggregation for that
db.collection.aggregate([
{ $unwind: "$orders" },
{ $sort: { "orders.date": -1 } },
{ $limit: 1 },
{
"$group": {
"_id": "$_id",
"orders": { "$first": "$orders" }
}
}
])
Working Mongo playground

How to sum up one record per day for one month?

I’m trying to do to sum up values in a month, but i only want to sum 1 entry per day for 1 month, i tried to use $limit before the group but only get 1 entry return.
Thanks.
[
{
$match:
{
"fPort": 60,
"rxInfo.time":
{
"$gte": "2020-12-01T00:00:00.000000Z",
"$lte": "2020-12-31T59:59:59.935Z"
}
}
},
{ $limit: '1' }, ///// This only returns 1 record and not 1 per day.
{
$group:
{
_id: "9cd9cb00000124c1",
"Total":
{
"$sum": "$object.TotalDaily"
}
}
}
]
I managed with the following query.
[
{
$match:
{
"fPort": 60,
"rxInfo.time":
{
"$gte": "2020-12-01T00:00:00.000000Z",
"$lte": "2020-12-31T59:59:59.99"
}
}
},
{
$project:
{
_id:1,
year:
{
$year:
{
$dateFromString:
{
dateString:
{"$arrayElemAt": [ "$rxInfo.time", 0 ] }
}
}
},
month:
{
$month:
{
$dateFromString:
{
dateString:
{"$arrayElemAt": [ "$rxInfo.time", 0 ] }
}
}
},
day:
{
$dayOfMonth:
{
$dateFromString:
{
dateString:
{"$arrayElemAt": [ "$rxInfo.time", 0 ] }
}
}
},
sum : "$object.TotalDaily"
}
},
{
$group:{ _id:{year:"$year",month:"$month",day:"$day"},TotalPerDay:{$sum:"$sum"}}
},
{
$group: { _id: "9cd9cb00000124c1","MonthlyTotal":{"$sum": "$TotalPerDay"},
}
}
]

Group data of linked document in aggregate

We currently have the following code to group all documents from a collection by creation date:
this.aggregate( [
{ $match: { language: options.criteria.language, status: 1, type:{ $ne: "challenge" } }},
{ $group: {
_id: {
y: { '$year': '$created' },
m: { '$month': '$created' },
d: { '$dayOfMonth': '$created' },
h: { '$hour': '$created' },
min: { '$minute': '$created' },
s: { '$second': '$created' }
},
count: { $sum : 1 }
}},
{$project: {
date: "$_id", // so this is the shorter way
count: 1,
_id: 0
}},
{ $sort: { "date": 1 } }
], function(err, result){
if(err) {
return callback(err);
}
callback(null, result);
});
However, we now would like to group the results based on the start date instead of the creation date. The start date is not a field of the current collection, but it is a field of the currentRevision object, that is linked in this collection.
I tried this:
this.aggregate( [
{ $match: { language: options.criteria.language, status: 1, type:{ $ne: "challenge" } }},
{ $group: {
_id: {
y: { '$year': '$currentRevision.start_date' },
m: { '$month': '$currentRevision.start_date' },
d: { '$dayOfMonth': '$currentRevision.start_date' },
h: { '$hour': '$currentRevision.start_date' },
min: { '$minute': '$currentRevision.start_date' },
s: { '$second': '$currentRevision.start_date' }
},
count: { $sum : 1 }
}},
{$project: {
date: "$_id", // so this is the shorter way
count: 1,
_id: 0
}},
{ $sort: { "date": 1 } }
], function(err, result){
if(err) {
return callback(err);
}
callback(null, result);
});
but that just gives me an error: "failed to query db"
any idea on how to solve this?

Resources