NodeJS - Generate a dynamic JSON from data received via database - javascript

I'm trying to generate dynamic JSON based on the dynamic structure defined in DB. But the function doesn't wait till the response is received from database.
Expected JSON:
{
"ROOT_1": [
{
"ROOT_2.1": {
"column2.1": 1,
"column2.1.1": "XXX"
},
"ROOT_2.2": [
{
"column2.1": 1,
"column2.2.1": 11
},
{
"column2.1": 1,
"column2.2.1": 12
}
],
"ROOT_2.3": [
{
"column2.1": 1,
"column2.3.1": 21
},
{
"column2.1": 1,
"column2.3.1": 22
},
{
"column2.1": 1,
"column2.3.1": 23
}
]
},
{
"ROOT_2.1": {
"column2.1": 1,
"column2.1.1": "XXX"
},
"ROOT_2.2": [
{
"column2.1": 1,
"column2.2.1": 11
},
{
"column2.1": 1,
"column2.2.1": 12
}
],
"ROOT_2.3": [
{
"column2.1": 1,
"column2.3.1": 21
},
{
"column2.1": 1,
"column2.3.1": 22
},
{
"column2.1": 1,
"column2.3.1": 23
}
]
}
]
}
Actual JSON:
{
"ROOT_1": [
{},
{}
]
}
All the columns are generated from database and assigned as an object.
I tried promise for making the DB call and waiting for the response but the data doesn't bind. The await is calling before the response from DB.
async function getJSON(){
for (var m = 0; m < masterDataList.length; m++) {
var childObj = {};
var childList = [];
rootDataList.push(parseObjData(childObj, getChildList(null), masterDataList[m]));
}
await Promise.all(rootDataList).then(function () {
context.res = {
body: root
};
context.done();
}, function () {
return context.res = {
status: 400,
body: APP_CODE + "Error fetching data"
};
});
}
function parseObjData(dynamicObj, structureMapList, masterDataObj) {
for (var sml = 0; sml < dynamicObj.length; sml++) {
var rowDataPromise = getRowDataList(); //data that fetches from DB
rowDataPromise.then(function (rowData) {
parseObjData(rowData,structureMapList,masterDataObj);
}, function () { });
}
}
function getRowDataList() {
return new Promise((resolve, reject) => {
req.query(sqlQuery).then(function (result) {
return resolve(result.recordset);
}).catch(function (err) {
return reject(err);
});
});
}
Any suggestion would be great help...!!!

This line return rowData; is not working as you are expecting it to work. You could have written it like this:
var row;
rowDataPromise.then(function (rowData) {
row = rowData;
}, function () { });
return row;
And use async await with this function.

Related

MongoDB Find documents created in past 7 days and return 0 on days where no document was created

I'm trying to find documents from last 7 days in my Mongo Database.
I'm able to receive data in desired format where specific date and numOfTickets created on that given date are returned:
{
"datesUsed": {
"startDate": "2022-04-02T14:42:14.223Z",
"endDate": "2022-04-09T14:42:14.223Z"
},
"data": [
{
"date": "02/04/2022",
"numOfTickets": 1
},
{
"date": "03/04/2022",
"numOfTickets": 1
},
{
"date": "04/04/2022",
"numOfTickets": 2
},
{
"date": "06/04/2022",
"numOfTickets": 1
},
{
"date": "07/04/2022",
"numOfTickets": 1
},
{
"date": "08/04/2022",
"numOfTickets": 2
},
{
"date": "09/04/2022",
"numOfTickets": 1
}
]
}
The problem is that Mongo only returns data on days where a document was created, but returns nothing on days where no document was created - in this case 05/04/2022.
What I'm trying to achieve is to also include another JSON object that would say something in these terms:
{
"date": "05/04/2022",
"numOfTickets": 0
}
This is what I have so far:
const companyId = req.query.companyId;
let dates = [];
const data = [];
// Last 7 days
const endDate = new Date();
const startDate = new Date(Date.now() - 604800000);
// Find tickets from past 7 days
const allCompanyTickets = await ticketModel
.find({
company_id: companyId,
createdAt: { $gte: new Date(startDate), $lte: new Date(endDate) },
})
.sort({ createdAt: 1 });
console.log(allCompanyTickets)
// Push them to an array
allCompanyTickets.forEach((ticket) => {
dates.push(ticket.createdAt.toLocaleDateString());
});
// Count occurences of days in the dates array
function countOccurrences(arr) {
return arr.reduce(function (a, b) {
a[b] = a[b] + 1 || 1;
return a;
}, []);
}
// Make an object from the data above
const datesOrdered = countOccurrences(dates);
// Give the data above keys and push it to a new array
for (let key in datesOrdered) {
const tempObj = { date: key, numOfTickets: datesOrdered[key] };
data.push(tempObj);
}
res.status(200).json({ datesUsed: { startDate, endDate }, data: data });
Something like this
db.ticketModel.aggregate([
{
$set: {
datesUnused: {
$range: [ 0, { $dateDiff: { startDate: "$startDate", endDate: "$endDate", unit: "day" } } ]
}
}
},
{
$set: {
datesUnused: {
$map: {
input: "$datesUnused",
in: {
date: {
$dateAdd: {
startDate: { $dateTrunc: { date: "$datesUsed.startDate", unit: "day" },
unit: "day",
amount: "$$this"
}
},
numOfTickets: 0
}
}
}
}
},
{
$set: {
data: {
$filter: {
input: "$datesUnused",
cond: { $not: { $in: [ "$$this.date", "$data.date" ] } }
}
}
}
}
])
Mongo Playground

Retrieving data from a complex inconsistent JSON

I'm trying to set variables from a JSON object that I retrieve with a POST query. But the results aren't returned in the same order every time, so it doesn't always work. I'm not sure how to correctly set my variables with the array positions not remaining constant:
var idle = Example1.results[0].data[1].stats.count;
var waiting = Example1.results[1].data[0].stats.count;
(i.e. This works on example 1, but not example 2)
Example1 = {"results":[{"group":{"queueId":"someID"},"data":[{"metric":"oOnQueueUsers","qualifier":"INTERACTING","stats":{"count":2}},{"metric":"oOnQueueUsers","qualifier":"IDLE","stats":{"count":5}}]},{"group":{"queueId":"someID","mediaType":"voice"},"data":[{"metric":"oWaiting","stats":{"count":0}}]}]}
Example2 = {"results":[{"group":{"queueId":"someID","mediaType":"voice"},"data":[{"metric":"oWaiting","stats":{"count":1}}]},{"group":{"queueId":"someID"},"data":[{"metric":"oOnQueueUsers","qualifier":"INTERACTING","stats":{"count":4}},{"metric":"oOnQueueUsers","qualifier":"IDLE","stats":{"count":6}}]}]}
You can use find() and some() to get the result you want.
const example1 = { results: [ { group: { queueId: "someID" }, data: [ { metric: "oOnQueueUsers", qualifier: "INTERACTING", stats: { count: 2 }, }, { metric: "oOnQueueUsers", qualifier: "IDLE", stats: { count: 5 } }, ], }, { group: { queueId: "someID", mediaType: "voice" }, data: [{ metric: "oWaiting", stats: { count: 0 } }], }, ], };
const example2 = { results: [ { group: { queueId: "someID", mediaType: "voice" }, data: [{ metric: "oWaiting", stats: { count: 1 } }], }, { group: { queueId: "someID" }, data: [ { metric: "oOnQueueUsers", qualifier: "INTERACTING", stats: { count: 4 }, }, { metric: "oOnQueueUsers", qualifier: "IDLE", stats: { count: 6 } }, ], }, ], };
const idle1 = example1.results
.find(a => a.data.some(d => d.qualifier === "IDLE"))
.data.find(b => b.qualifier === "IDLE").stats.count;
const waiting1 = example1.results
.find(a => a.data.some(d => d.metric === "oWaiting"))
.data.find(b => b.metric === "oWaiting").stats.count;
const idle2 = example2.results
.find(a => a.data.some(d => d.qualifier === "IDLE"))
.data.find(b => b.qualifier === "IDLE").stats.count;
const waiting2 = example2.results
.find(a => a.data.some(d => d.metric === "oWaiting"))
.data.find(b => b.metric === "oWaiting").stats.count;
console.log({ idle1 }, { waiting1 }, { idle2 }, { waiting2 });
If you cannot depend on the order being the same every time... or given your example data that each object doesn't even have the same properties... then you shouldn't search the resultant json for your data.
Rather then, you need to create your own data structure against which you will be able to search/index and then loop through your json, parsing each level within to decide how to map that particular element to your new data structure.
Here's an example of what you might could do...
var exampleResults1 = {
"results": [{
"group": {
"queueId": "someID"
},
"data": [{
"metric": "oOnQueueUsers",
"qualifier": "INTERACTING",
"stats": {
"count": 2
}
},
{
"metric": "oOnQueueUsers",
"qualifier": "IDLE",
"stats": {
"count": 5
}
}
]
},
{
"group": {
"queueId": "someID",
"mediaType": "voice"
},
"data": [{
"metric": "oWaiting",
"stats": {
"count": 0
}
}]
}
]
}
var newDataSource = {
parseResults: function(resultObj) {
resultObj.results.forEach(function(result) {
var queueID = result.group.queueId;
if (!newDataSource.hasOwnProperty(queueID)) {
newDataSource[queueID] = {
data: {}
};
}
var newDataSourceQueue = newDataSource[queueID];
result.data.forEach(function(dataObj) {
var metric = dataObj.metric;
if (!newDataSourceQueue.data.hasOwnProperty(metric)) {
newDataSourceQueue.data[metric] = {};
}
var queueMetric = newDataSourceQueue.data[metric];
var qualifier = "noQualifier";
if (dataObj.hasOwnProperty("qualifier")) {
qualifier = dataObj.qualifier;
}
queueMetric[qualifier] = {};
var metricQualifier = queueMetric[qualifier];
var statKeys = Object.keys(dataObj.stats);
statKeys.forEach(function(stat) {
if (!metricQualifier.hasOwnProperty(stat)) {
metricQualifier[stat] = dataObj.stats[stat];
}
});
});
});
}
};
newDataSource.parseResults(exampleResults1);
console.log(JSON.stringify(newDataSource));
console.log("IDLE Count = " + newDataSource["someID"]["data"]["oOnQueueUsers"]["IDLE"]["count"]);
Running this code, you should be able to see what the new data structure looks like after its been populated with values from your original json object. Notice how the keys of the objects are values from the original json.
My example code here doesn't take into account all data points in your example result sets... but should be enough to illustrate that you need to understand the data you are getting back and be able to come up with a consolidated data structure to encapsulate it using property keys that come from the actual returned results.

Update object array key with new value in javascript/React-native

I have the following Object array stored using AsyncStorage:
[
{
"groupId": 1,
"id": 1,
"name": "a",
},
{
"groupId": 2,
"id": 2,
"name": "ab",
},
{
"groupId": 3,
"id": 3,
"name": "abc",
},
{
"groupId": 2,
"id": 4,
"name": "abcd",
},
]
I'm retrieving the data to display it in a Flatlist but instead of displaying the groupId, I would like to display the groupName for each item.
I have another key with groups stored:
[
{
"groupName": "g1",
"id": 1,
},
{
"groupName": "g2",
"id": 2,
}
]
I made a function to retrieve the groupName based on groupId. This works fine:
const getGroupName = async (groupIdToFind) => {
const existingsGroups = (await AsyncStorage.getItem("groups")) || "[]";
const existingsGroupsParsed = JSON.parse(existingsGroups);
const groupDataFound = existingsGroupsParsed.find(
(results) => results.id.toString().indexOf(groupIdToFind) > -1
);
if (groupDataFound) {
return groupDataFound.groupName;
}
return false;
};
And I thought of modifying the data retrieved from the storage in my loadFlatlist function but this is where I'm struggling.
const loadFlatlist = async (storageKey) => {
try {
let itemsOnStorage = await AsyncStorage.getItem(storageKey);
itemsOnStorage = JSON.parse(itemsOnStorage);
if (itemsOnStorage !== null) {
let finalItemsOnStorage = "";
itemsOnStorage.map(async function (obj) {
console.log(obj.groupId); // Correctly returns 1, 2, 3 ...
let retrievedGroupName = await getGroupName(obj.groupId);
console.log(retrievedGroupName); // Correctly returns g1, g2 ...
finalItemsOnStorage = await update(itemsOnStorage, {
groupId: { $set: retrievedGroupName },
});
});
console.log(finalItemsOnStorage); // Returns same content as itemsOnStorage
setDataOnDevice(itemsOnStorage); // This will be loaded in my flatlist
}
} catch (err) {
alert(err);
}
};
The problem is in loadFlatlist, I did not manage to replace the groupId by the groupName. finalItemsOnStorage has the same content as itemsOnStorage.
Can you see what is wrong with my function? Is there a better way to do this?
Try like this
const loadFlatlist = async (storageKey) => {
try {
let itemsOnStorage = await AsyncStorage.getItem(storageKey);
itemsOnStorage = JSON.parse(itemsOnStorage);
const existingsGroups = (await AsyncStorage.getItem("groups")) || "[]";
const existingsGroupsParsed = JSON.parse(existingsGroups);
if (itemsOnStorage !== null) {
let finalItemsOnStorage = [];
itemsOnStorage.forEach(item => {
let index = existingsGroupsParsed.findIndex(obj => obj.id ===
item.groupId)
if(index != -1) {
finalItemsOnStorage.push({...item,
groupId:existingsGroupsParsed[index].groupName})
}
});
console.log(finalItemsOnStorage);
setDataOnDevice(finalItemsOnStorage);
}
} catch (err) {
alert(err);
}
};

How to get one json object format from another JSON format

Maybe this question has already been asked and answered somewhere but after searching for more than 3 hrs I'm asking this question.
Below is my JSON data
var my_data = [
{
"TempRture_qc": 4,
"VoltAGE": 44.09722,
"TempRture": 22.32,
"VoltAGE_qc": 55,
"_time": "2018-08-07T03:39:29.001Z"
},
{
"TempRture_qc": 2,
"VoltAGE": 42.09722,
"TempRture": 22.12,
"VoltAGE_qc": 0,
"_time": "2018-08-07T03:39:30.006Z"
},
{
"TempRture_qc": 1,
"VoltAGE": 43.09722,
"TempRture": 22.82,
"VoltAGE_qc": 0,
"_time": "2018-08-07T03:39:31.009Z"
}
];
desired output i need
[
{
"name": "TempRture_qc",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":4},
{"name":"2018-08-07T03:39:30.006Z","y":2},
{"name":"2018-08-07T03:39:33.017Z","y":1}
]
},
{
"name": "VoltAGE",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":44.09722},
{"name":"2018-08-07T03:39:30.006Z","y":42.09722},
{"name":"2018-08-07T03:39:33.017Z","y":43.09722}
]
},
{
"name": "TempRture",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":22.32},
{"name":"2018-08-07T03:39:30.006Z","y":22.12},
{"name":"2018-08-07T03:39:33.017Z","y":22.82}
]
},
{
"name": "VoltAGE_qc",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":55},
{"name":"2018-08-07T03:39:30.006Z","y":0},
{"name":"2018-08-07T03:39:33.017Z","y":0}
]
}
]
for getting this above output i have tried below code.
var accounting = [];
var fieldName = {};
for (var x in obj){
var mykey = Object.keys(obj[x]);
for (var mk in mykey){
if(mykey[mk]=='VoltAGE'){
fieldName.name = mykey[mk];
// accounting.push({
// "name":mykey[mk]
// })
}
if(mykey[mk]=='TempRture'){
fieldName.name = mykey[mk];
}
// console.log(mykey[mk]); //to get the key name
}
accounting.push({
"name" : obj[x]._time,
"y" : obj[x][employees.name],
})
fieldName.data = accounting;
}
console.log(fieldName );
by doing this what I'm getting is below JSON
{ name: 'TempRture',
data:
[ { name: '2018-08-07T03:39:29.001Z', y: 22.32 },
{ name: '2018-08-07T03:39:32.014Z', y: 22.12 },
{ name: '2018-08-07T03:39:33.017Z', y: 22.82 } ] }
I'm not able to understand how I will get the data in one JSON object.
For a solution with low time complexity, try .reduceing into an object indexed by keys of the inner object, creating a { name, data: [] } at that key in the accumulator if it doesn't exist there yet. Then, push to the data array, and get the values of the whole object:
var my_data=[{"TempRture_qc":4,"VoltAGE":44.09722,"TempRture":22.32,"VoltAGE_qc":55,"_time":"2018-08-07T03:39:29.001Z"},{"TempRture_qc":2,"VoltAGE":42.09722,"TempRture":22.12,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:30.006Z"},{"TempRture_qc":1,"VoltAGE":43.09722,"TempRture":22.82,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:31.009Z"}]
console.log(Object.values(
my_data.reduce((a, { _time, ...obj }) => {
Object.entries(obj).forEach(([name, val]) => {
if (!a[name]) a[name] = { name, data: [] };
a[name].data.push({ name: _time, y: val });
});
return a;
}, {})
));
var my_data=[{"TempRture_qc":4,"VoltAGE":44.09722,"TempRture":22.32,"VoltAGE_qc":55,"_time":"2018-08-07T03:39:29.001Z"},{"TempRture_qc":2,"VoltAGE":42.09722,"TempRture":22.12,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:30.006Z"},{"TempRture_qc":1,"VoltAGE":43.09722,"TempRture":22.82,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:31.009Z"}]
var keys = Object.keys(my_data[0])
var result= [];
for(i = 0; i<keys.length-1; i++) {
var obj = {name: keys[i],data: []}
obj.data = my_data.map(val=>({name: val["_time"], y: val[keys[i]]}));
result.push(obj);
}
console.log(result)
An understandable answer with map, findIndex and forEach functions will be
var my_data = [{ "TempRture_qc": 4, "VoltAGE": 44.09722, "TempRture": 22.32, "VoltAGE_qc": 55, "_time": "2018-08-07T03:39:29.001Z" }, { "TempRture_qc": 2, "VoltAGE": 42.09722, "TempRture": 22.12, "VoltAGE_qc": 0, "_time": "2018-08-07T03:39:30.006Z" }, { "TempRture_qc": 1, "VoltAGE": 43.09722, "TempRture": 22.82, "VoltAGE_qc": 0, "_time": "2018-08-07T03:39:31.009Z" } ],
result = [];
my_data.map(itm => {
let keys = Object.keys(itm);
keys.forEach(iitt => {
if (iitt != '_time') {
let index = result.findIndex(ii => {
return ii.name == iitt;
})
if (index == -1) {
result.push({
name: iitt,
data: []
});
result[result.length - 1].data.push({
name: itm["_time"],
y: itm[iitt]
})
} else {
result[index].data.push({
name: itm["_time"],
y: itm[iitt]
});
}
}
})
})
console.log(result)

MongoDB Reduce Key with single value

I want to count the number of orders by clients, and thus, on the last year, last month and last week.
I wrote a MapReduce program:
var mapOrders = function() {
var v_order = {
order_date : this.dt_order
...
};
emit(this.clientid, v_order);
};
var reduceOrders = function(p_clientid, p_orders) {
// Initialization of the output format of the couters
var r_result = { orders_count : {
total: {
1year: 0,
1month: 0,
7day: 0
}
...
}}
for (var c_order = 0; c_order < p_orders.length; c_order++) {
// Increment counters
}
return (r_result);
};
db.orders.mapReduce(
mapOrders,
reduceOrders,
{
out: { merge: "tmp_orders_indicators" }
}
)
In my output collection, I have 2 types of records
{
"_id" : 80320,
"value" : {
"order_date" : ISODate("2015-10-30T11:09:51.000Z")
...
}
}
{
"_id" : 80306,
"value" : {
"orders_count" : {
"total" : {
"count_1year" : 18,
"count_1month" : 6,
"count_7day" : 1
}
...
}
}
The clients with only 1 order don't go through the reduce function.
I found this in the MongoDB doucmentation that explain that behaviour:
MongoDB will not call the reduce function for a key that has only a
single value.
How can i do to have only 1 type of record in my output collection looking like this? Force all the record to go throught the reduce function?
{
"_id" : 80306,
"value" : {
"orders_count" : {
"total" : {
"count_1year" : 18,
"count_1month" : 6,
"count_7day" : 1
}
...
}
}
You can achieve this seamlessly with aggregation. Consider the following pipeline:
var dateSevenDaysAgo = new Date();
dateSevenDaysAgo.setDate(dateSevenDaysAgo.getDate()-7);
var dateMonthAgo = new Date();
dateMonthAgo.setMonth(dateMonthAgo.getMonth()-1);
var dateYearAgo = new Date();
dateYearAgo.setFullYear(dateYearAgo.getFullYear()-1);
var pipeline = [
{ "$match": { "$dt_order": { "$gte": dateYearAgo } } },
{
"$group": {
"_id": "$id_client",
"count_1year": {
"$sum": {
"$cond": [
{ "$gte": [ "$dt_order", dateYearAgo ] },
1, 0
]
}
},
"count_1month": {
"$sum": {
"$cond": [
{ "$gte": [ "$dt_order", dateMonthAgo ] },
1, 0
]
}
},
"count_7day": {
"$sum": {
"$cond": [
{ "$gte": [ "$dt_order", dateSevenDaysAgo ] },
1, 0
]
}
}
}
},
{ "$out": "tmp_indicators" }
];
db.orders.aggregate(pipeline);
db.tmp_indicators.find();
Found a solution using the finalize utility.
var mapOrders = function() {
var v_order = {
order_date : this.dt_order
...
};
emit(this.clientid, v_order);
};
var reduceOrders = function(p_clientid, p_orders) {
// Initialization of the output format of the couters
var r_result = { orders_count : {
total: {
1year: 0,
1month: 0,
7day: 0
}
...
}}
for (var c_order = 0; c_order < p_orders.length; c_order++) {
// Increment counters
}
return (r_result);
};
var finalizeOrders = function(p_clientid, p_ReducedDrders) {
if (typeof p_ReducedDrders.orders_count === 'undefined' )
// Initialization of the output format of the couters
var r_result = { orders_count : {
total: {
1year: 0,
1month: 0,
7day: 0
}
...
}}
// do the same stuff as the for loop in the reducer
}
else {
r_result = p_ReducedDrders
}
return (r_result);
};
db.orders.mapReduce(
mapOrders,
reduceOrders,
{
out: { merge: "tmp_orders_indicators" },
finalize : finalizeOrders
}
)

Categories

Resources