Repetitive Query in MongoDB - javascript
I am creating a query in MongoDB which works correctly for me but in the end I have to do an extra grouping to get the response grouped by year. This is the query:
db.sales.aggregate([
{
$group: {
_id: {
month: {
$month: "$createdAt"
},
year: {
$year: "$createdAt"
},
dayOfWeek: {
$dayOfWeek: "$createdAt"
},
stringDay: {
$dateToString:
{ format: "%Y-%m-%d", date: "$createdAt"}
},
week: {
$isoWeek: "$createdAt"
}
},
total: { $sum: '$total'},
count: { $sum: 1 },
totalAverage: { $avg: '$total'}
}
},
{
$sort : {
"_id.month" : 1
}
},
{
$project: {
total: { $round: [ "$total", 2 ] },
year: "$_id.year",
date: "$_id.date",
week: "$_id.week",
numVentas: "$count",
month: "$_id.month",
dayOfWeek: "$_id.dayOfWeek",
stringDay:"$_id.stringDay",
count: "$count",
totalAverage: { $round: [ "$totalAverage", 2 ] },
stringMonth: {
$arrayElemAt: [
[
"",
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
],
"$_id.month"
]
},
stringWeek: {
$switch: {
branches:[
{ case: { $eq: ["$_id.dayOfWeek", 1] }, then: "Lunes" },
{ case: { $eq: ["$_id.dayOfWeek", 2] }, then: "Martes" },
{ case: { $eq: ["$_id.dayOfWeek", 3] }, then: "Miércoles" },
{ case: { $eq: ["$_id.dayOfWeek", 4] }, then: "Jueves" },
{ case: { $eq: ["$_id.dayOfWeek", 5] }, then: "Viernes" },
{ case: { $eq: ["$_id.dayOfWeek", 6] }, then: "Sábado" },
{ case: { $eq: ["$_id.dayOfWeek", 7] }, then: "Domingo" }
],
default: "Día desconocido"
}
}
}
},
{
$group: {
_id: { month: "$stringMonth", year: "$year"},
count: { $sum: "$count" },
total: { $sum: "$total" },
totalAverage: { $sum: "$totalAverage" },
sales: {
$push: {
numberDay: "$dayOfWeek",
stringWeek: "$stringWeek",
date: "$stringDay",
total: "$total",
count: "$count",
totalAverage: { $round: [ "$totalAverage", 2 ] }
}
}
}
},
{
$group: {
_id: "$_id.year",
monthsWithSales: { $sum: 1 },
count: { $sum: "$count" },
total: { $sum: "$total" },
totalAverage: { $sum: "$totalAverage" },
sales: {
$push: {
mes: "$_id.month",
count: "$count",
total: "$total",
totalAverage: "$totalAverage",
sales:"$sales"
}
}
}
}
])
And I get this response:
[
{
"_id": 2022,
"monthsWithSales": 4,
"count": 57,
"total": 22324.8,
"totalAverage": 7765.799999999999,
"sales": [
{
"mes": "Oct",
"count": 10,
"total": 1936,
"totalAverage": 1233.6,
"sales": [
{
"numberDay": 6,
"stringWeek": "Sábado",
"date": "2022-10-21",
"total": 526.8,
"count": 3,
"totalAverage": 175.6
},
{
"numberDay": 1,
"stringWeek": "Lunes",
"date": "2022-10-02",
"total": 85.6,
"count": 1,
"totalAverage": 85.6
},
{
"numberDay": 7,
"stringWeek": "Domingo",
"date": "2022-10-22",
"total": 526.8,
"count": 3,
"totalAverage": 175.6
},
{
"numberDay": 3,
"stringWeek": "Miércoles",
"date": "2022-10-04",
"total": 180,
"count": 1,
"totalAverage": 180
},
{
"numberDay": 4,
"stringWeek": "Jueves",
"date": "2022-10-12",
"total": 531.2,
"count": 1,
"totalAverage": 531.2
},
{
"numberDay": 3,
"stringWeek": "Miércoles",
"date": "2022-10-25",
"total": 85.6,
"count": 1,
"totalAverage": 85.6
}
]
},
{
"mes": "Nov",
"count": 7,
"total": 2205.2,
"totalAverage": 1014.8,
"sales": [
{
"numberDay": 4,
"stringWeek": "Jueves",
"date": "2022-11-02",
"total": 526.8,
"count": 3,
"totalAverage": 175.6
},
{
"numberDay": 6,
"stringWeek": "Sábado",
"date": "2022-11-25",
"total": 171.2,
"count": 2,
"totalAverage": 85.6
},
{
"numberDay": 7,
"stringWeek": "Domingo",
"date": "2022-11-12",
"total": 1507.2,
"count": 2,
"totalAverage": 753.6
}
]
},
{
"mes": "Dec",
"count": 33,
"total": 12587.6,
"totalAverage": 4074.5,
"sales": [
{
"numberDay": 3,
"stringWeek": "Miércoles",
"date": "2022-12-06",
"total": 850,
"count": 1,
"totalAverage": 850
},
{
"numberDay": 6,
"stringWeek": "Sábado",
"date": "2022-12-02",
"total": 8737.6,
"count": 25,
"totalAverage": 349.5
},
{
"numberDay": 7,
"stringWeek": "Domingo",
"date": "2022-12-10",
"total": 900,
"count": 1,
"totalAverage": 900
},
{
"numberDay": 1,
"stringWeek": "Lunes",
"date": "2022-12-04",
"total": 200,
"count": 1,
"totalAverage": 200
},
{
"numberDay": 2,
"stringWeek": "Martes",
"date": "2022-12-05",
"total": 500,
"count": 1,
"totalAverage": 500
},
{
"numberDay": 5,
"stringWeek": "Viernes",
"date": "2022-12-08",
"total": 250,
"count": 2,
"totalAverage": 125
},
{
"numberDay": 4,
"stringWeek": "Jueves",
"date": "2022-12-07",
"total": 250,
"count": 1,
"totalAverage": 250
},
{
"numberDay": 6,
"stringWeek": "Sábado",
"date": "2022-12-09",
"total": 900,
"count": 1,
"totalAverage": 900
}
]
},
{
"mes": "Sep",
"count": 7,
"total": 5596,
"totalAverage": 1442.8999999999999,
"sales": [
{
"numberDay": 2,
"stringWeek": "Martes",
"date": "2022-09-12",
"total": 5069.2,
"count": 4,
"totalAverage": 1267.3
},
{
"numberDay": 6,
"stringWeek": "Sábado",
"date": "2022-09-02",
"total": 526.8,
"count": 3,
"totalAverage": 175.6
}
]
}
]
},
{
"_id": 2021,
"monthsWithSales": 1,
"count": 2,
"total": 608,
"totalAverage": 608,
"sales": [
{
"mes": "Dec",
"count": 2,
"total": 608,
"totalAverage": 608,
"sales": [
{
"numberDay": 1,
"stringWeek": "Lunes",
"date": "2021-12-12",
"total": 171.2,
"count": 1,
"totalAverage": 171.2
},
{
"numberDay": 4,
"stringWeek": "Jueves",
"date": "2021-12-22",
"total": 436.8,
"count": 1,
"totalAverage": 436.8
}
]
}
]
}
]
It is correct, but as you can see at the end I make two groups to obtain the data grouped by year and then the sales array grouped by month.
Is there any way to improve this query without so much grouping?
Related
How can I sort and reduce the object base on this sample data from javascript array?
The date and time are integers, I would like to sort and reduce them base on up to date format like date should be up to date and military time should be up to date too. All values are distinct base on there object id let arr = [ {"id": 1, "date": 20220518, "time": 1700}, {"id": 1, "date": 20220518, "time": 1500}, {"id": 1, "date": 20220518, "time": 2200}, {"id": 1, "date": 20220517, "time": 2300}, {"id": 2, "date": 20220518, "time": 1500}, {"id": 2, "date": 20220516, "time": 2100}, {"id": 3, "date": 20220518, "time": 1400}, {"id": 3, "date": 20220517, "time": 1900}, {"id": 3, "date": 20220518, "time": 1900}, {"id": 4, "date": 20220517, "time": 1900}, {"id": 4, "date": 20220516, "time": 1300}, {"id": 5, "date": 20220516, "time": 1600} ] // Result should be like this let arr = [ {"id": 1, "date": 20220518, "time": 2200}, {"id": 2, "date": 20220518, "time": 1500}, {"id": 3, "date": 20220518, "time": 1900}, {"id": 4, "date": 20220517, "time": 1900}, {"id": 5, "date": 20220516, "time": 1600} ]
Use Array.prototype.filter() let arr = [ { id: 1, date: 20220518, time: 1700 }, { id: 1, date: 20220518, time: 1500 }, { id: 1, date: 20220518, time: 2200 }, { id: 1, date: 20220517, time: 2300 }, { id: 2, date: 20220518, time: 1500 }, { id: 2, date: 20220516, time: 2100 }, { id: 3, date: 20220518, time: 1400 }, { id: 3, date: 20220517, time: 1900 }, { id: 3, date: 20220518, time: 1900 }, { id: 4, date: 20220517, time: 1900 }, { id: 4, date: 20220516, time: 1300 }, { id: 5, date: 20220516, time: 1600 }, ]; const result = arr.filter( (item) => !arr.some( (i) => i.id === item.id && // assume the time is a 4 digits number i.date * 10000 + i.time > item.date * 10000 + item.time ) ); console.log(result); Use Array.prototype.reduce() let arr = [ { id: 1, date: 20220518, time: 1700 }, { id: 1, date: 20220518, time: 1500 }, { id: 1, date: 20220518, time: 2200 }, { id: 1, date: 20220517, time: 2300 }, { id: 2, date: 20220518, time: 1500 }, { id: 2, date: 20220516, time: 2100 }, { id: 3, date: 20220518, time: 1400 }, { id: 3, date: 20220517, time: 1900 }, { id: 3, date: 20220518, time: 1900 }, { id: 4, date: 20220517, time: 1900 }, { id: 4, date: 20220516, time: 1300 }, { id: 5, date: 20220516, time: 1600 }, ]; const result = arr.reduce((previousArray, currentItem) => { const item = previousArray.find((i) => i.id === currentItem.id); if (item) { if (currentItem.date > item.date) { item.date = currentItem.date; item.time = currentItem.time; } else if (currentItem.date === item.date && currentItem.time > item.time) { item.time = currentItem.time; } } else { previousArray.push(currentItem); } return previousArray; }, []); console.log(result);
There are so many ways I don't know about your data and what you have done so far. Simple logic is below you can filter the data as below or you can use jquery. If you want to sort the array then sort it before re-creating. //Raw data let arr = [{ "id": 1, "date": 20220518, "time": 1700 }, { "id": 1, "date": 20220518, "time": 1500 }, { "id": 1, "date": 20220518, "time": 2200 }, { "id": 1, "date": 20220517, "time": 2300 }, { "id": 2, "date": 20220518, "time": 1500 }, { "id": 2, "date": 20220516, "time": 2100 }, { "id": 3, "date": 20220518, "time": 1400 }, { "id": 3, "date": 20220517, "time": 1900 }, { "id": 3, "date": 20220518, "time": 1900 }, { "id": 4, "date": 20220517, "time": 1900 }, { "id": 4, "date": 20220516, "time": 1300 }, { "id": 5, "date": 20220516, "time": 1600 } ]; // Sort before creating the data arr.sort(function(a, b) { if (a.date >= b.date) { return 1; } else { return -1; } if (a.time >= b.time) { return 1; } else { return -1; } }); //Create the new array with required data let data = []; arr.forEach(function(e, i) { data[e.id] = e; }); console.log(data.filter(n => n));
Loop through object javascript
This is my response body. I want to select only Collaborator's "supportNotifications" key-value set to true, how can I do that? { "status": true, "date": "2022-04-06T16:33:13.630Z", "data": { "collaborators": [ { "name": "Paul", "lastCheckin": "2022-03-31T19:54:50.584Z", "sales": 1, "createdAt": "2022-03-30T14:47:48.478Z", "id": "62446d4ab7f4da001e8f40dc", "supportNotification": true, "collaboratorId": 1 }, { "name": "John", "lastCheckin": null, "sales": 0, "createdAt": "2022-03-01", "id": "62446ea4b7f4da001e8f40df", "supportNotification": false, "collaboratorId": 6 } ], "count": 2 } }
let response = { "status": true, "date": "2022-04-06T16:33:13.630Z", "data": { "collaborators": [ { "name": "Paul", "lastCheckin": "2022-03-31T19:54:50.584Z", "sales": 1, "createdAt": "2022-03-30T14:47:48.478Z", "id": "62446d4ab7f4da001e8f40dc", "supportNotification": true, "collaboratorId": 1 }, { "name": "John", "lastCheckin": null, "sales": 0, "createdAt": "2022-03-01", "id": "62446ea4b7f4da001e8f40df", "supportNotification": false, "collaboratorId": 6 } ], "count": 2 } }; const collaborators = response.data.collaborators.filter(c => c.supportNotification);
This will give you each collaborator object that has supportNotification = true. Is this the result you are trying to select? var body = { status: true, date: "2022-04-06T16:33:13.630Z", data: { collaborators: [ { name: "Paul", lastCheckin: "2022-03-31T19:54:50.584Z", sales: 1, createdAt: "2022-03-30T14:47:48.478Z", id: "62446d4ab7f4da001e8f40dc", supportNotification: true, collaboratorId: 1, }, { name: "John", lastCheckin: null, sales: 0, createdAt: "2022-03-01", id: "62446ea4b7f4da001e8f40df", supportNotification: false, collaboratorId: 6, }, ], count: 2, }, }; var result = []; body.data.collaborators.forEach((item) => { if (item.supportNotification === true) { result.push(item); } }); console.log(result);
Shape Tabular Data into Hierachy
I'd like to reshape this tabular data [{ "orderid": 1, "grandtotal": 100.00, "detailid": 11, "description": "apple" }, { "orderid": 1, "grandtotal": 100.00, "detailid": 12, "description": "orange" }, { "orderid": 2, "grandtotal": 0.00 }, { "orderid": 3, "grandtotal": 300.00, "detailid": 31, "description": "tea" }, { "orderid": 3, "grandtotal": 300.00, "detailid": 32, "description": "coffee" }] which is like this into master/slave hierarchy: [{ "orderid": 1, "grandtotal": 100.00, "details": [{ "detailid": 11, "description": "apple" }, { "detailid": 12, "description": "orange" } ] }, { "orderid": 2, "grandtotal": 0.00, "details": [] }, { "orderid": 3, "grandtotal": 300.00, "details": [{ "detailid": 31, "description": "tea" }, { "detailid": 32, "description": "coffee" } ] } ] How to do it rather than conventional for loop?
const reshape = (inputArray) => { const reducedArray = inputArray.reduce((resultedObject, { orderid, grandtotal, detailid, description }) => { let detail = null; if (detailid !== undefined || description !== undefined) detail = {detailid, description}; if (orderid in resultedObject && detail) { resultedObject[orderid].details.push(detail); } if (!(orderid in resultedObject)) { resultedObject[orderid] = {orderid, grandtotal, details: []}; detail && resultedObject[orderid].details.push(detail); } return resultedObject; }, {}); return Object.values(reducedArray); } reshape(data);
MongoDB Month wise grouping
Can Somebody Please help in getting the last 6 months count of my given data? This My Data { "_id" : ObjectId("59815d4704ca1760a45957ca"), "ticketId" :"TCKT0HF652Y", "createdAt" : ISODate("2020-06-02T05:03:57Z") }, { "_id" : ObjectId("59815d5404ca1760a45957cb"), "ticketId" :"TCKT0HF8849A", "createdAt" : ISODate("2020-06-02T05:04:11Z") }, { "_id" : ObjectId("5980191d04ca1760a45957cd"), "ticketId" :"TCKT0H4953Z", "createdAt" : ISODate("2020-05-01T06:00:46Z") }, { "_id" : ObjectId("59815d4704ca1760a45957ca"), "ticketId" :"TCKT0HF339Y", "createdAt" : ISODate("2020-05-02T05:03:57Z") }, { "_id" : ObjectId("59815d5404ca1760a45957cb"), "ticketId" :"TCKT0HF839A", "createdAt" : ISODate("2020-05-02T05:04:11Z") }, { "_id" : ObjectId("5980191d04ca1760a45957cd"), "ticketId" :"TCKT0HF9582Z", "createdAt" : ISODate("2020-04-01T06:00:46Z") } And My Query is Tickets.aggregate([ { $project: { count: {$sum: 1}, month: {$month: "$createdAt"}, year: {$year: "$createdAt"}, }, }, { $group: { _id: {month: "$month", year: "$year"}, total: {$sum: "$count"}, }, }, ]) And This is the result I am getting "data": [ { "_id": { "month": 6, "year": 2020 }, "total": 2 }, { "_id": { "month": 5, "year": 2020 }, "total": 3 }, { "_id": { "month": 4, "year": 2020 }, "total": 1 } ] My Requirement is to generate the last 6 months' data irrespective of whether the month has data or not. So the result I am expecting is "data": [ { "_id": { "month": 6, "year": 2020 }, "total": 2 }, { "_id": { "month": 5, "year": 2020 }, "total": 3 }, { "_id": { "month": 4, "year": 2020 }, "total": 1 }, { "_id": { "month": 3, "year": 2020 }, "total": 0 }, { "_id": { "month": 2, "year": 2020 }, "total": 0 }, { "_id": { "month": 1, "year": 2020 }, "total": 0 } ] And can we also display month names instead of month numbers?.
I don't think you you output Months names natively in MongoDB. I suggest Moment.js library. Could be this one: Tickets.aggregate([ { $group: { _id: { month: { $month: "$createdAt" }, year: { $year: "$createdAt" } }, total: { $sum: 1 }, }, } ]).forEach(function (row) { print(moment.utc(row._id.year + "-" + row._id.month, "YYYY-MM").format("MMMM YYYY") + ": " + row.total); })
Nested array filtering in lodash
I have a Json Like this - [ { "place": 1, "player": { "playerId": 733234, "firstName": "cheng", "lastName": "w", "country": { "countryId": 13, "name": "China", "abbreviation": "CHN" } }, "rounds": [ { "roundNumber": 1, "startHole": 1 }, { "roundNumber": 2, "startHole": 10, }, { "roundNumber": 3, "startHole": -1, "courseId": 950 }, { "roundNumber": 4, "startHole": -1, "courseId": 950 } ] }, { "place": 2, "player": { "playerId": 392990, "firstName": "Matt", "lastName": "Harmon", "country": { "countryId": 1, "name": "United States", "abbreviation": "USA" } }, "rounds": [ { "roundNumber": 1, "startHole": 1 }, { "roundNumber": 2, "startHole": 10, } ] } ] I need to filter and create a new json using Lodash in the below format. I tried _.filter but always I am getting undefined error { rounds: [ [ { player Name: "cheng", roundNumber: 1 starthole: 1 }, { player Name: "math", roundNumber: 1 starthole: 1 } ], [ { roundNumber: 2 player Name: "cheng", starthole: 2 }, { roundNumber: 2 player Name: "math", starthole: 2 } ] ] } Is there any way to restructure the Json to new format using lodash and Javascript
You dont need filter. You need mapping as you are reformatting. You can chain map and flatten. In Your case let modifiedArray=_(YOURARRAYHERE).map(s=>{ let x=_.map(s.rounds,a=>{ return { roundNumber:a.roundNumber, startHole:a.startHole, playerName:s.player.firstName } }); return x; }).flatten().value();