How to get child Snapshots inside snapshots on Google Cloud functions (Firebase)? - javascript

I'm trying to get some for Loops running inside a google cloud functions everytime I delete my /users node.
This is the code I'm using
exports.deleteUserAssets = functions.database.ref('/users/{userId}').onWrite((change, context) => {
const beforeData = change.before.val();
const afterData = change.after.val();
const userBuildings = Object.keys(beforeData.isAdmin); // get the buildings of the user stored in the user/userId/isAdmin node .. so far so good
const userId = beforeData.userIDforCloudFunctions; // I'm getting this from a /users/userid/userIDforCloudFucntions node ...so far so good (i've been logging it to confirm)
// making sure it was a delete operation ... so far so good
if (afterData !== null) {
return 0;
}
else {
// on each building
for (var i = 0; i < userBuildings.length; i++) {
let eachBuilding = [userBuildings[i]]
// HERE IS WERE THE PROBLEM IS: Trying to delete all depts + rooms + doors
admin.database().ref('/buildings/' + eachBuilding)
.child("hasDepts")
.once("value")
.then(function(snapshot) { // This is where it goes south – snapshot is returning null
snapshot.forEach(function(childSnapshot) {
var deptKeyString = childSnapshot.key; // will try to get the keys of the departments stored under this space
var deptsOnNode = admin.database().ref('/depts/' + deptKeyString);
deptsOnNode.remove(); // and use the keys to delete each of the depts on depts
});
});
admin.database().ref('/buildings/' + eachBuilding).set({}); // this is working
admin.database().ref('/buildingsUserUid/' + userId + '/' + eachBuilding).remove(); // this is working
}
}
return 0;
});
The snapshot of admin.database().ref('/buildings/' + eachBuilding).child("hasDepts") is returning null.
How can I get to it? Besides admin.database().ref() I've tried to reach it with firebase.database().ref() which is the command/object i use to get this running on frontend functions. I've also tried functions.database() with no result.

Taking in consideration what Doug Stevenson mentioned in his second comment:
exports.deleteUserAssets = functions.database.ref('/users/{userId}').onDelete((change, context, event) => {
const beforeData = change.before.val(); // data before the write (data of all the doors child nodes)
const afterData = change.after.val(); // data before the write (data of all the doors child nodes)
const userBuildings = Object.keys(beforeData.isAdmin); // get the buildings of the user
const userId = beforeData.userIDforCloudFunctions;
// make sure user was deleted
if (afterData !== null) {
return 0;
}
else {
// on each building
for (var i = 0; i < userBuildings.length; i++) {
let eachBuilding = [userBuildings[i]]
// Need to RETURN the whole chain of promises
return admin.database().ref('/buildings/' + eachBuilding)
.child("hasDepts")
.once("value")
.then(function(snapshot) {
console.log(snapshot.val()) // this now works
snapshot.forEach(function(childSnapshot) {
console.log(childSnapshot.val()) // this works as well
var deptKeyString = childSnapshot.key; // get the keys of the departments stored under this space
var deptsOnNode = admin.database().ref('/depts/' + deptKeyString);
// and you can keep on going deeper if you return promises
return deptsOnNode
.child('hasRooms')
.once('value')
.then(function(grandchildSnapshot){
console.log(grandchildSnapshot.val())
grandchildSnapshot.forEach(function(grandGrandchildSnapshot){
var roomKeyString = grandGrandchildSnapshot.key;
var roomsOnDepts = admin.database().ref('/rooms/' + roomKeyString);
admin.database().ref('/roomOwners/' + userId + '/' + roomKeyString).remove();
// and return again here...
return roomsOnDepts
.child('hasDoors')
.once('value')
.then(function(grandgrandGrandchildSnapshot){
grandgrandGrandchildSnapshot.forEach(function(grandgrandGrandchildSnapshot){
var doorKeyString = grandgrandGrandchildSnapshot.key;
var doorsOnRooms = admin.database().ref('/doors/' + doorKeyString);
doorsOnRooms.remove();
let clipOwners = admin.database().ref('/clipOwners/' + doorKeyString);
clipOwners.remove();
})
roomsOnDepts.remove();
})
})
deptsOnNode.remove(); // use the keys to delete the depts on depts main Node
})
});
admin.database().ref('/buildings/' + eachBuilding).set({});
admin.database().ref('/buildingsUserUid/' + userId + '/' + eachBuilding).remove();
});
}
}
return 0;
});

Related

Empty array getting logged in, in nodeJs

Flow:
The user submits a queryValue in index.html.
Three API calls are made (using a function called ytAxiosGetFunc) based on the queryValue.
The returned values are put in three arrays: ytQueryAppJs, ytCoverAppJs and ytLiveAppJs.
ytCoverAppJs and ytLiveAppJs contains redundant values. These are removed using a function called compareAndRemove.
Two new arrays are allocated which contain unique values from for each of these respectively. These are ytCoverUniqueAppJs and ytLiveUniqueAppJs.
Hence, a total of five arrays get logged in console, based on the query.
Expected Console Log:
All the arrays are filled.
Current Console Log:
All the arrays are filled, except ytCoverUniqueAppJs and ytLiveUniqueAppJs. These are empty.
Source Code from 'app.js':
// https://stackoverflow.com/a/14930567/14597561
function compareAndRemove(removeFromThis, compareToThis) {
return (removeFromThis = removeFromThis.filter(val => !compareToThis.includes(val)));
}
// Declaring variables for the function 'ytAxiosGetFunc'
let apiKey = "";
let urlOfYtAxiosGetFunc = "";
let ytResponse = "";
let ytExtractedResult = [];
// This function GETs data, parses it, allocates required values in an array.
async function ytAxiosGetFunc(queryOfYtAxiosGetFunc, maxResultsOfYtAxiosGetFunc) {
apiKey = "AI...5U"
urlOfYtAxiosGetFunc = "https://www.googleapis.com/youtube/v3/search?key=" + apiKey + "&part=snippet&order=relevance&type=video";
try {
ytResponse = await axios({
url: urlOfYtAxiosGetFunc,
method: "get",
params: {
q: queryOfYtAxiosGetFunc,
maxResults: maxResultsOfYtAxiosGetFunc
}
})
let ytResult = ytResponse.data;
for (i = 0; i < (ytResult.items).length; i++) {
ytExtractedResult[i] = ytResult.items[i].id.videoId;
// console.log(ytExtractedResult);
}
return (ytExtractedResult);
ytExtractedResult.length = 0;
ytResponse.length = 0;
} catch (e) {
console.log(e);
}
}
app.post("/", async function(req, res) {
// Accessing the queryValue user submitted in index.html.
query = req.body.queryValue;
// Fetcing top results related to user's query and putting them in the array.
ytQueryAppJs = await ytAxiosGetFunc(query, 4);
console.log("ytQueryAppJs");
console.log(ytQueryAppJs);
// Fetching 'cover' songs related to user's query and putting them in the array.
if (query.includes("cover") == true) {
ytCoverAppJs = await ytAxiosGetFunc(query, 8);
console.log("ytCoverAppJs");
console.log(ytCoverAppJs);
// Removing redundant values.
ytCoverUniqueAppJs = compareAndRemove(ytCoverAppJs, ytQueryAppJs);
console.log("ytCoverUniqueAppJs:");
console.log(ytCoverUniqueAppJs);
} else if (query.includes("live") == true) {
ytCoverAppJs = await ytAxiosGetFunc(query.replace("live", " cover "), 8);
console.log("ytCoverAppJs");
console.log(ytCoverAppJs);
// Removing redundant values.
ytCoverUniqueAppJs = compareAndRemove(ytCoverAppJs, ytQueryAppJs);
console.log("ytCoverUniqueAppJs:");
console.log(ytCoverUniqueAppJs);
} else {
ytCoverAppJs = await ytAxiosGetFunc(query + " cover ", 8);
console.log("ytCoverAppJs");
console.log(ytCoverAppJs);
// Removing redundant values.
ytCoverUniqueAppJs = compareAndRemove(ytCoverAppJs, ytQueryAppJs);
console.log("ytCoverUniqueAppJs:");
console.log(ytCoverUniqueAppJs);
}
// Fetching 'live performances' related to user's query and putting them in the array.
if (query.includes("live") == true) {
ytLiveAppJs = await ytAxiosGetFunc(query, 8);
console.log("ytLiveAppJs");
console.log(ytLiveAppJs);
// Removing redundant values.
ytLiveUniqueAppJs = compareAndRemove(ytLiveAppJs, ytQueryAppJs.concat(ytCoverUniqueAppJs));
console.log("ytLiveUniqueAppJs:");
console.log(ytLiveUniqueAppJs);
} else if (query.includes("cover") == true) {
ytLiveAppJs = await ytAxiosGetFunc(query.replace("cover", " live "), 8);
console.log("ytLiveAppJs");
console.log(ytLiveAppJs);
// Removing redundant values.
ytLiveUniqueAppJs = compareAndRemove(ytLiveAppJs, ytQueryAppJs.concat(ytCoverUniqueAppJs));
console.log("ytLiveUniqueAppJs:");
console.log(ytLiveUniqueAppJs);
} else {
ytLiveAppJs = await ytAxiosGetFunc(query + " live ", 8);
console.log("ytLiveAppJs");
console.log(ytLiveAppJs);
// Removing redundant values.
ytLiveUniqueAppJs = compareAndRemove(ytLiveAppJs, ytQueryAppJs.concat(ytCoverUniqueAppJs));
console.log("ytLiveUniqueAppJs:");
console.log(ytLiveUniqueAppJs);
}
// Emptying all the arrays.
ytQueryAppJs.length = 0;
ytCoverAppJs.length = 0;
ytCoverUniqueAppJs.length = 0;
ytLiveAppJs.length = 0;
ytLiveUniqueAppJs.length = 0;
});
(I am a beginner. Please guide and suggest a title to categorize this question for coming viewers.)
My friend suggested me to localise ytResponse and ytExtractedResult. So I declared them inside ytAxiosGetFunc.
Notice the commented code. Here's the required change:
// Declaring variables for the function 'ytAxiosGetFunc'
let apiKey = "";
let urlOfYtAxiosGetFunc = "";
// let ytResponse = "";
// let ytExtractedResult = [];
// This function GETs data, parses it, allocates required values in an array.
async function ytAxiosGetFunc(queryOfYtAxiosGetFunc, maxResultsOfYtAxiosGetFunc) {
let ytExtractedResult = [];
apiKey = "A...U"
urlOfYtAxiosGetFunc = "https://www.googleapis.com/youtube/v3/search?key=" + apiKey + "&part=snippet&order=relevance&type=video";
try {
let ytResponse = await axios({
url: urlOfYtAxiosGetFunc,
method: "get",
params: {
q: queryOfYtAxiosGetFunc,
maxResults: maxResultsOfYtAxiosGetFunc
}
})
let ytResult = ytResponse.data;
for (i = 0; i < (ytResult.items).length; i++) {
ytExtractedResult[i] = ytResult.items[i].id.videoId;
// console.log(ytExtractedResult);
}
return (ytExtractedResult);
// ytExtractedResult.length = 0; // These are unnecessary now.
// ytResponse.length = 0;
} catch (e) {
console.log(e);
}
}
Here's what I think was happening earlier:
ytQueryAppJs was getting 're-filled' for every ytAxiosGetFunc call. This was making ytQueryAppJs to have exact same values (on the same index) as the variable for which the call is actually made. For example, if the function call is made to assign values in ytCoverAppJs, it would set the same values for ytQueryAppJs as well. The same would happen at the time of calling function for ytLiveAppJs. This ultimately caused compareAndRemove function to clear out all the values.
Here's what I think is happening now:
The variables ytExtractedResult and ytResponse are being re-initialised for every call to the function ytAxiosGetFunc. This imply that they don't have the previous values. (And do not need to have their length set to zero.)
Thank you everyone for responding. :)

How can I receive data from firebase under a certain variable in jquery or javascript

I am trying to receive data from realtime database every time when I load the page. I don't know why it's not working. I am trying to receive data under the word "email". I don't know how to do this with a variable because firebase doesn't let me do this. But can you help me with receiving the data?
Thank you!
$('.save').on('click', function() {
var Email = localStorage.getItem("email");
window.alert(Email)
todoArray = [];
for (i = 1; i < 25; i++) {
let todoValue = $('#' + i).val();
let todoObject = {
todoHour: i,
todoItem: todoValue
}
firebase.database().ref('email').set({
todoArray
});
todoArray.push(todoObject);
}
})
// save data
const issuesRef = firebase.database().ref('email');
function loadTodos() {
issuesRef.on("value", function(snapshot) {
snapshot.forEach(snap => {
const issue = snap.val();
todoObject = issue
})
})
}
//get data

Online Matchmaking Issues in Cloud Function and Realtime Database

A Cloud Function is set up to trigger when a new entry is created, and it checks whether we have enough players to start a game. If so, it creates the game at ‍‍/matches , and writes the gameId to each player's entry, like so: /users/{playerId}/profile/cm
Players listen to gameId on their own database entries, so they know when to transition to the GamePlay screen.
Since there are a lot of players request for find opponent simultaneously, and the Cloud Function is asynchronous concurrency , the concurrency issues come out and as you can see in the picture one user added in two diffect matches.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.onNewRequest = functions.database.ref("/requests/{boardId}/{userId}").onCreate((change, context) => {
var boardId = context.params.boardId;
var createdUserId = context.params.boardId;
// new request created
var requests = admin.database().ref("/requests/"+boardId);
requests.once("value").then((requestsSnapshot)=>{
var waitersCount = requestsSnapshot.numChildren();
if(waitersCount > 1){
// more than one user waiting to join the game
// lets link users
var creatorSnapshot;
var guestSnapshot;
// more than two players waiting to play a game on the board{boardId}
var index = 0;
requestsSnapshot.forEach((childSnapshot) => {
if (index === 0) creatorSnapshot = childSnapshot;
if (index === 1) guestSnapshot = childSnapshot;
index++;
});
// we have the creator and the guest
// creatorSnapshot.key is the id of the user /requests/boardId/userId <<<<--
var match = admin.database().ref("/matches").push({
s: 1,
peers:{
[creatorSnapshot.key]:true,
[guestSnapshot.key]:true
}
});
match.then(()=>{
// delete waiters
admin.database().ref("/users/"+creatorSnapshot.key+"/profile/cm").set(match.key);
admin.database().ref("/users/"+guestSnapshot.key+"/profile/cm").set(match.key);
creatorSnapshot.ref.remove();
guestSnapshot.ref.remove();
return null;
}).catch((error) => {
console.log(error);
});
// var matchId = boardId + "-" + creatorSnapshot.key + "-" + guestSnapshot.key;
// return admin.database().ref("/matches/"+matchId).set({
// s: 1,
// peers:{
// [creatorSnapshot.key]:true,
// [guestSnapshot.key]:true
// }
// }).then(()=>{
// // delete waiters
// // creatorSnapshot.ref.remove();
// // guestSnapshot.ref.remove();
// return null;
// }).catch((error) => {
// console.log(error);
// });
}
return null;
}).catch((error) => {
console.log(error);
});
});

Firebase Web - Is it possible to get the value's name?

is there a way for me to retrieve the name of a value?
For example:
I want to get the value names highlighted in yellow.
However, right now I can only get:
From my understanding the below code only return the value which is the player's scores.
var childData = childSnapshot.val();
Can I do something like this to get the value name?
var childValueName = childSnapshot.val().name;
This is my java code:
function PrintData()
{
var ref = firebase.database().ref('Score');
var PrintData = document.getElementById('PrintOutData');
ref.on("value", function(snapshot)
{
PrintData.innerText = "";
snapshot.forEach(function(childSnapshot)
{
console.log("childSnapshot.key: " + childSnapshot.key);
var childData = childSnapshot.val();
let keys = Object.keys(childData)
keys.forEach(key => {
let value = childData[key.toString()];
console.log("value: " + value);
PrintData.innerText += childSnapshot.key + ": " + value +"\r";
})
});
});
}
My html code:
<button type="button" onclick="PrintData()">Print!</button>
Please correct me if I am wrong! Thankyou.
See firebase.database.DataSnapshot
key
(string or null)
The key (last part of the path) of the location of this DataSnapshot.
The last token in a Database location is considered its key. For example, "ada" is the key for the /users/ada/ node. Accessing the key on any DataSnapshot will return the key for the location that generated it. However, accessing the key on the root URL of a Database will return null.
// Assume we have the following data in the Database:
{
"name": {
"first": "Ada",
"last": "Lovelace"
}
}
var ref = firebase.database().ref("users/ada");
ref.once("value")
.then(function(snapshot) {
var key = snapshot.key; // "ada"
var childKey = snapshot.child("name/last").key; // "last"
});
Firebase has a well versed and beautiful documentation.
According to firebase documentation, datasnapshot is returned when you pass a relative path to the child() method
// Assume we have the following data in the Database:
{
"name": {
"first": "Ada",
"last": "Lovelace"
}
}
// Test for the existence of certain keys within a DataSnapshot
var ref = firebase.database().ref("users/ada");
ref.once("value")
.then(function(snapshot) {
var name = snapshot.child("name").val(); // {first:"Ada",last:"Lovelace"}
var firstName = snapshot.child("name/first").val(); // "Ada"
var lastName = snapshot.child("name").child("last").val(); // "Lovelace"
var age = snapshot.child("age").val(); // null
});
The following may work for your purpose:
firebase.database().ref('score').once('value', snap => {
var data = snap.val() // should return => {User1: {GamePlay00: 3}, User2:
{GamePlay00: 1}, ...}
var users = Object.keys('data') should return // => [User1, User2, ...]
var usersDatas = users.map(user_id => data[user_id]) // should return something like => [{gamePlay00: 3}, {gamePlay00:1}, ...]
var value = usersDatas.map(game_play_id => game_play_id) // should return => [gamePlay00, gamePlay00...]
})
Please refer to these link for further documentation: Object.keys Firebase Docs
I'd stick to using Snapshot.forEach() for the lower level too:
var ref = firebase.database().ref('Score');
var PrintData = document.getElementById('PrintOutData');
ref.on("value", function(snapshot) {
PrintData.innerText = "";
snapshot.forEach(function(userSnapshot) {
console.log("childSnapshot.key: " + userSnapshot.key);
userSnapshot.forEach(function(gameSnapshot) {
PrintData.innerText += gameSnapshot.key + ": " + gameSnapshot.val() +"\r";
})
});
});

Node / MySQL - code: 'ER_PARSE_ERROR', when trying to insert ~800 records

I am working on small idea to collect errors from pages and to store them in DB and then use graph API to display information visually.
There is 8 sites and on each of them there is 100 entries - so 800 transactions per time.
I loop through each site and then sub-loop through table of errors and collect them.
I got it working if I make insert query on each of those sub-loops for all 800 entries but I am getting some sort of memory leak from so many transactions and after few minutes - Node breaks due to memory exceeding.
So I tried queuing all 800 entries into Array of Arrays and then performing multi-insert at the end of every iteration but I am getting ER_PARSE_ERROR.
var tabletojson = require('tabletojson');
var mysql = require("mysql");
var striptag = require("striptags");
var fs = require("fs");
var path = require('path');
var startCollector;
var iterations = 0;
var insertions = 0;
var duplicated = 0;
var datas = [];
var clients = ["ClientA", "ClientB", "ClientC", "ClientD", "ClientE", "ClientF", "ClientG", "ClientH"];
var appDir = path.dirname(require.main.filename);
var errorList = ["err1", "err2", "err3", "err4", "err5", "err6"];
var con = mysql.createPool({
host: "localhost",
user: "User",
password: "Password",
database: "errors"
});
function CollectErrors() {
startCollector = new Date();
for(var a = 0; a < clients.length; a++) {
(function(a) {
tabletojson.convertUrl("http://example.com" + clients[a] + "/page.php?limit=100", { stripHtmlFromCells: false }, function(response) {
var rs = response[0];
for(var l = rs.length-1; l > -1; l--) {
var newDate = formatDate(striptag(rs[l]["Date"]), striptag(rs[l]["Time"]));
var user = getUser(striptag(rs[l]["User"]));
var msg = striptag(rs[l]["Error"]);
var splitError = rs[l]["Error"].split("<a href=\"");
var link = getUrl(splitError[1]);
var id = getId(link);
var type = getType(striptag(splitError[0]));
var temp = [newDate, link, type, user, clients[a], id, msg];
datas.push(temp);
}
});
})(a);
}
con.getConnection(function(err, connection) {
connection.query("INSERT IGNORE INTO entries (time, url, type, author, client, uid, message) VALUES ?", [datas], function(err, rows) {
console.log(err);
});
connection.release();
datas = [];
});
setTimeout(CollectErrors, 10000);
}
function formatDate(date, time) {
var newdate = date.split("/").reverse().join("-");
var newtime = time+":00";
return newdate + " " + newtime;
}
function getUrl(uri) {
return "http://example.com/"+uri.split("\">Details")[0];
}
function getId(url) {
return decodeURIComponent((new RegExp('[?|&]' + "id" + '=' + '([^&;]+?)(&|#|;|$)').exec(url) || [null, ''])[1].replace(/\+/g, '%20')) || null;
}
function getType(error) {
for(var a = 0; a < errorList.length; a++) {
if(error.indexOf(errorList[a]) !== -1) {
return errorList[a];
}
}
return "Other";
}
function getUser(user) {
if(user == "" || user == " " || user == null) {
return "System";
}
return user;
}
CollectErrors();
I've tried mysql.createConnection too but that also gave me same issue.
I've been stuck for past 12 hours and I can't see what's wrong, I've even tried populating Datas table with just strings but got same error.
I've changed your code to use ES6 and correct modules features.
Useful links: correct pooling with mysql, correct insert query, async/await, IIFE, enhanced object
const tabletojson = require('tabletojson'),
mysql = require("mysql"),
striptag = require("striptags"),
fs = require("fs"),
path = require('path');
const startCollector,
iterations = 0,
insertions = 0,
duplicated = 0;
let datas = [];
const clients = ["ClientA", "ClientB", "ClientC", "ClientD", "ClientE", "ClientF", "ClientG", "ClientH"];
const appDir = path.dirname(require.main.filename);
const errorList = ["err1", "err2", "err3", "err4", "err5", "err6"];
const con = mysql.createPool({
host: "localhost",
user: "User",
password: "Password",
database: "errors"
});
// We'll use async/await from ES6
const collectErrors = async() => {
// Up to here I've only changed syntax to ES6
let startCollector = new Date();
// We'll try to iterate through each client. And we use here for..of syntax to allow us using await
for (let client of clients) {
// Please, check that client value return correct data. If not, change for..of to your for..each and client variable to clients[a]
const tbj = await tabletojson.convertUrl("http://example.com" + client + "/page.php?limit=100", {
stripHtmlFromCells: false
});
const result = tgj[0];
for (rs of result) {
// I can't check this part, but I hope your example was with correct values.
let newDate = formatDate(striptag(rs[l]["Date"]), striptag(rs[l]["Time"]));
let user = getUser(striptag(rs[l]["User"]));
let link = getUrl(splitError[1]);
let msg = striptag(rs[l]["Error"]);
let id = getId(link);
let splitError = rs[l]["Error"].split("<a href=\"");
let getType = getType(striptag(splitError[0]));
// ES6 enhanced object syntax
datas.push({
newDate,
user,
msg,
id,
splitError,
link,
getType,
temp: [newDate, link, type, user, client, id, msg]
});
}
}
// OK, here we have fulfilled datas array. And we want to save it.
con.getConnection((err, connection) => {
// Please, notice, here I've changed your insert query to prepared statement.
connection.query("INSERT IGNORE INTO entries SET ?", datas, (err, rows) => {
console.log(err);
connection.release();
datas = [];
});
});
// I don't see why do you need timeout here, so I've left it commented.
// setTimeout(CollectErrors, 10000);
};
// Here your other methods go....
// And to call your async function we'll use IIFE
(async() => {
await collectErrors();
})();
Probably there may be errors with mysql insert, but that's not for sure. If occurred, please write in comments and I'll help you with that.

Categories