How can I target a deeply nested object in google realtime database? - javascript

I want to make a cron job that deletes deeply nested objects in my realtime database that are older than 24 hours.
I have looped through and reached the deeply nested object, but I can't grab/target the value of "addedTime" in the object. How do I grab that value so I can run .remove on the parent? So far, it comes back as undefined or it throws an error.
.schedule("every 1 hours")
.onRun(context => {
const rootDatabaseRef = admin.database().ref("ghostData/");
return rootDatabaseRef.ref.once("value").then(function(snapshot) {
console.log("snap", snapshot.val());
snapshot.forEach(function(userSnapshot) {
let buckets = userSnapshot.val().buckets;
console.log("buckets", buckets);
buckets.forEach(function(bucket) {
let currentTimeYesterday = new Date(
new Date().getTime() - 24 * 60 * 60 * 1000
).getTime();
let addedTime = bucket.val().addedTime;
console.log("curr time", currentTimeYesterday);
console.log("addedTime", addedTime);
});
});
Here is the data in my realtime database as well as the logs from the serverless cloud functions:

I think you're having problems with looping, because when you do this "buckets.forEach(function(bucket)" --> bucket in your case is the first element of the list ,
and every element has a nested dictionary , so first you have to iterate the dictionary and for each key in the dictionary , you'll get another dictionary , and you've to grab
only the added-time value.
I know it's difficult to understand but I think it's happening because you're not looping correctly.
Try the code below or something similar.
buckets.forEach(function(bucket){
let currentTimeYesterday = new ......
bucket.forEach(function(dict){
Object.keys(dict).forEach(k => {
console.log(k, ':', dict[k].addedTime);
let addedTime = dict[k].addedTime;
});
....
}
....
}

Related

How to convert mysql row of data into a javascript array? Node.js app

First, I'm a newbie. No doubt, I've made some simple errors.
Using Node.js with MySQL Database, I'm building a basic web app that allows users to login. Once they've logged in they will be brought to their profile page and are displayed results of a quiz they've done in the form of a bar chart.
I want covert a row of mysql data into an array.
const mysql = require('mysql');
const dbconfig = require('/config/database');
const connection = mysql.createConnection(dbconfig.connection);
connection.query('USE ' + dbconfig.database);
// Create an array of scores for each category depedning on the user who's
// loggedin.
var category1scoreQuery =
"SELECT c1q1, c1q2, c1q3, c1q4, c1q5, c1q6, c1q7, c1q8
FROM nodejs_login.assessment_score
AS a JOIN users as u ON a.respondent_id = u.user_respondent_id
WHERE a.respondent_id = user.user_respondent_id;";
connection.connect(function(err){
if (err) throw err;
connection.query(category1scoreQuery, function(err, result, fields) {
if (err) throw err;
Object.keys(result).forEach(function(key){
var cat1Array = result[key];
// want to return array e.g. ["45/60", "60/60", "40/40","30/40","15/20",
// "30/40", "30/60", "20/40"];
console.log(cat1Array);
})
})
});
// I want to convert it to an array to parse the array of strings into
// totalUserScore over maxCategoryScore
var i;
var userCategoryScore1 = 0;
var maxCategoryScore = 0;
for(i=0; i < cat1Array.length;i++){
var splitScore = cat1Array[i].split("/");
console.log(splitScore);
myQuestionScore = parseInt(splitScore[0], 10);
userCategoryScore1 += myQuestionScore;
console.log(userCategoryScore);
maxQuestionScore = parseInt(splitScore[1]);
maxCategoryScore = maxCategoryScore + maxQuestionScore;
console.log(maxCategoryScore);
}
This is what I am actually getting which doesn't allow me to loop through.
RowDataPacket {
c1q1: '15/60',
c1q2: '15/60',
c1q3: '10/40',
c1q4: '10/40',
c1q5: '5/20',
c1q6: '10/40',
c1q7: '15/60',
c1q8: '10/40' }
This should work for you:
const RowDataPacket= {
c1q1: '15/60',
c1q2: '15/60',
c1q3: '10/40',
c1q4: '10/40',
c1q5: '5/20',
c1q6: '10/40',
c1q7: '15/60',
c1q8: '10/40' }
const values=Object.values(RowDataPacket);
console.log(values)
Reference[1st part] : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values
Description:
The Object.values() method returns an array of a given object's own enumerable property values, in the same order as that provided by a for...in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).
For the second part, to calculate the total scores:
//using the values array from first part
const scores=values.reduce((accum,value)=>{
const splitValues=value.split('/')
return {
score:accum.score + parseInt(splitValues[0]),
maxScore:accum.maxScore + parseInt(splitValues[1]),
}
},{score:0,maxScore:0})
console.log(scores)
Reference[2nd part]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
Description:
The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.
This is fairly common problem when interacting with a database through javascript. To get what you want you can try using JSON library like this:
usersRows = JSON.parse(JSON.stringify(result));
Also this isn't exactly related to your question but it's something that made my life a lot easier when I was doing this: consider using the node Promisify module to transform your queries into promises (from here: https://www.npmjs.com/package/util-promisify). That way instead of having to use callbacks like you are doing your code would look something like this:
var results = await connection.query(category1scoreQuery);
//Process results here
Again this is only a suggestion but something that I found was very useful.
Cheers!

Paging objects within an object

I have a long list of chat rooms
let chatRooms = {
"general": ChatRoom,
"myRoomA": ChatRoom,
"bobsRoom": ChatRoom,
...
}
ChatRoom has a serialize method
ChatRoom.serialize = function(){
return {
name: this.name,
clients: this.clients,
...
}
}
In order to list all ChatRooms to a user, I must send this data to them
ChatRoomManager.serialize = function(){
let serializedObjects = [];
Util.each(this.chatRooms, function(i, e){
if(e.serialize){
serializedObjects.push(e.serialize());
}
});
return serializedObjects;
}
This becomes a performance issue as people regularly request to list all chat rooms and it gets serialized so often so I want to do paging. But if an object has no guaranteed order, how can I possibly say "here are the next 10 chat rooms"? Even if I could guarantee order, how could I start at index 11 without looping through all of the objects? Imagine if I was at index 1000, etc..
TLDR: is it possible to do paging with an object of objects efficiently and accurately.
You coulf just take the values of the objects which returns an array, so the order is guaranteed:
const ordered = Object.values(chatRooms);
You could now also apply a custom sort order, e.g.:
ordered.sort((roomA, roomB) => roomA.name.localeCompare(roomB.name));
To now serialize only one chunk it is as easy as:
let index = 0, chunk = 100;
const result = ordered.slice(index * chunk, (index + 1) * chunk).map(room => room.serialize());

Saving Dynamic Data in Objects?

I have a simple time entry form, see here: https://codepen.io/dikuw/pen/rmpozm
I want to store the time data dynamically as the user adds, modifies, and deletes rows and am thinking objects is the best approach.
My question is what is the best practice? Should I create a constructor, e.g.
function TimesheetRecord(id, TSDate, TSStaffId, Hours, Comments, TSTaskId, Billable) {
this.id = id;
this.TSDate = TSDate;
this.TSStaffId = TSStaffId;
this.Hours = Hours;
this.Comments = Comments;
this.TSTaskId = TSTaskId;
this.Billable = Billable;
}
and then dynamically create a new object every time the user adds a row? If I do that, how will I know how many objects there are?
Here you can create an Array to store your all TimesheetRecords and push a new dynamic object when you create a new one.
// When app starts (dataFromDB is data fetched from db if any)
const TimesheetRecords = dataFromDB || []
// create a new record(mainly in a function)
const TimesheetRecord = {
id: id,
TSDate: TSDate,
TSStaffId: TSStaffId,
Hours: Hours,
Comments: Comments,
TSTaskId: TSTaskId,
Billable: Billable
}
// store to db (ajax) and then push to array
TimesheetRecords.push(TimesheetRecord)
// Check how many records are there
const count = TimesheetRecords.length
This is common pattern for storing simple objects without any behaviors(methods) in JavaScript.

What is the best way to realise "join" with JS objects?

I'm working with MeteorJS (aned MongoDB).
I have two collections :
events, with idEvent
eventsType, with idEventType (finite list of
type of events)
The link between two collections must be realized with idEvent == idEventType.
The goal is to have an array of events, with eventstype object associed.
This following code is functionnal, but I find it horrible... What did you think about ?
events() {
// Type of event
const eventsType = EventsType.find();
const eventsTypeArray = [];
eventsType.forEach((ev) => {
eventsTypeArray[ev.idEventType] = ev;
});
// List of events
const eventsList = Events.find();
const eventsListArray = [];
// Merge both data
eventsList.forEach((ev) => {
const evObj = ev;
evObj.type = eventsTypeArray[ev.idEvent];
eventsListArray.push(evObj);
});
return eventsListArray;
}
Thanks ! :D
You could map your eventsList and use Object.assign to enrich the original item :
eventsListArray = eventsList.map(ev => Object.assign({type: eventsTypeArray[ev.idEvent]}, ev))
Test run :
originalArray = [{a:"1"}, {a:"2"}];
dataMap = { "1": 10, "2": 100 };
mappedArray = originalArray.map(i=>Object.assign({b:dataMap[i.a]}, i));
console.log(originalArray);
console.log(mappedArray);
Result :
[{a:"1"}, {a:"2"}] //original array left untouched
[{a:"1", b:10}, {a:"2", b:100}] // mappedArray contains the extra data
I actually had a similar problem recently where I wanted to join data from two collections.
My solution was to create a new local collection (this is a collection that lives on the client only).
client:
const LocalEvents = new Mongo.Collection(null);
From there, instead of pushing your joined objects in to an array, you can join them and push the new objects in to the LocalEvents collection. This gives you the benefit of being able to query the new objects from the local minimongo collection. You'll need to make sure you clear the local collection when the template/component is destroyed. Also run a tracker function to empty the LocalCollection if your cursor changes.
Tracker.autorun((eventsType) => {
LocalEvents.remove({});
});

Array incrementing Javascript / node js / mongodb

I'm trying to gather data from a MongoDB with Node JS to draw a graph later.
My goal is to collect all entries by hour of the day. In my collection there is a "created_at" field which stores a Date Object.
I'm trying to store the data in an array with 24 slots like this:
// padding the array for each hour of the day
var hours = new Array(23);
// defaulting the value for each hour to 0
for(var i=0; i<hours.length; i++){
hours[i] = 0;
}
db.collection.find({}, {"created_at": 1}, function(err, entry){
if (err){ doSomething(); }
else {
entry.forEach(function(item, index){
// get hour of the day from the Date object
h = item["created_at"].getHours();
h = parseInt(h);
// store hour of the day and count it up
hours[h]++;
console.log("#%s: %s", index, h);
});
}
});
console.log(hours);
Now when I log hours I get the array with the default values. ie [0, 0, 0, 0 ... 0]
I'm certain that the database has correct values as the console.log in the inner function gets correct data.
I suspect the problem is one of concurrency: the collection's .find method is asynchronous. So your console.log after the call to .find gets executed before the entry.forEach ever even executes. Try moving the console.log into the body of the else clause, after the forEach (which is synchronous), and you should see the result you're looking for.
Going forward, you'll have to employ promises or something to get the results you want.

Categories