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();

Categories