Remove the current node in cloud function in Firebase onWrite - javascript

I want to do a cloud function on the added node on firebase and delete it once the function is over. then((event)=>event.remove()) does not work in the following code.
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.makeUppercase = functions.database.ref('/Q/{pushId}')
.onWrite(event => {
const to = event.data.child("to").val();
const message = event.data.child("m").val();
const messageTime = Date.now()*-1;
const messageFromName = event.data.child('fromName').val();
var updateMessage = {};
for (var toCounter in to) {
updateMessage[`/${to[toCounter]}/c/${messageTime}`] = message;
updateMessage[`/${to[toCounter]}/i/fName`] = messageFromName;
}
admin.database().ref().update(updateMessage).then((event)=>event.remove());
});

You need to call remove() on the reference or parent's reference.
event.data.ref.parent.remove(); or event.data.ref.remove();
So if you have:
"-kwwe323r22g222322": {
"apples": "apples"
}
Your data would be:
{"apples":"apples"}
and event.data.ref would be:
-kwwe323r22g222322.

Related

Firebase function won't deleted child after certain time

I want to deleted a child after a certain time. I know that you need Firebase function to achief this. This is what I got so far:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.removeOldMessages = functions.https.onRequest((req, res) => {
const timeNow = Date.now();
const Ref = admin.database().ref('/Feed');
Ref.once('value', (snapshot) => {
snapshot.forEach((child) => {
if (1000*(Number(child.val()['timestamp']) + Number(child.val()['duration'])) >= timeNow) {
child.ref.set(null);
}
});
});
return res.status(200).end();
});
I want to deleted the child when the duration is over (the duration is in seconds). This is my structure:
Thanks!
You're sending a response to the caller at the end of the function, which will be executed before the data from the database is returned. And Cloud Functions will stop executing your code straight after that res.status(200).end(), so the database cleanup never happens.
To prevent this, only send a response to the caller after all data has been deleted from the database:
exports.removeOldMessages = functions.https.onRequest((req, res) => {
const timeNow = Date.now();
const Ref = admin.database().ref('/Feed');
return Ref.once('value', (snapshot) => {
let updates = [];
snapshot.forEach((child) => {
if (1000*(child.val().timestamp + child.val().duration) >= timeNow) {
updates[child.key] = null;
}
});
return Ref.update(updates).then(() => {
return res.status(200).end();
});
});
});
I highly recommend storing an additional property in your child nodes though, with the precalculated value of timestamp + duration. By having such a property, you can run a query on the nodes that have expired, instead of having to read all child nodes and then filtering in code.
For an example of this, see my answer to Delete firebase data older than 2 hours, and the Cloud Functions example that was based on that.

Code snippet executes after function is finished in Firebase Cloud Functions

I have a bit of a problem using firebase cloud functions. The code bellow is a function that writes to the Firestore DB an object that contains 2 arrays. After deploying the function, the idf_words array is populated, and the idf_weight is empty.
I tried placing a few log messages in the for loop and found the the query.get() executes after the function ends. Is there a way to make firestore wait until the query.get() finishes?
export const updateCakeAndPastriesIDF = functions.firestore.document("TF/tf/Cake_and_Pastries/{itemCategory}")
.onUpdate((change, context) => {
const itemBefore = change.before.data();
const itemAfter = change.after.data();
if (itemAfter['tf_tf_score'] === itemBefore['tf_tf_score']){
console.log('This TF score of the words in this item has not changed');
return null;
} else {
console.log('This TF score of the words in this item has changed');
const tfWords:string[] = itemAfter['tf_unique_words'];
const tfItemUid:string = itemAfter['tf_item_uid'];
const idfWords:string[] = [];
const idfWeight: number[] = [];
const db = admin.firestore().collection('TF').doc('tf').collection('Cake_and_Pastries');
tfWords.forEach(function (tfword) {
idfWords.push(tfword);
const query = db.where("tf_unique_words", "array-contains", tfword);
query.get().then(function (itemDoc) {
if (!itemDoc.empty){
const numberOfDocs = itemDoc.size;
console.log("For item: "+tfItemUid+", there are "+numberOfDocs+"Documents");
admin.firestore().collection('Number_of_Items')
.doc('Cake_and_Pastries')
.get()
.then(function (numberDoc){
const numberOfCakesAndPastries = numberDoc.data()['number_of_items_in_category'];
const idfOfWord = (Math.log(numberOfDocs/numberOfCakesAndPastries)+1);
idfWeight.push(idfOfWord);
console.log("Word IDF: "+idfOfWord);
console.log(idfWeight);
})
}else {
console.log("No such document!");
}
})
});
console.log("IDF weight array outside of loop: "+idfWeight);
admin.firestore()
.collection('IDF')
.doc('idf')
.collection('Cake_and_Pastries')
.doc(tfItemUid).set({
idf_item_uid: tfItemUid,
idf_words: idfWords,
idf_weight: idfWeight
});
}
});

Firebase Realtime Database and Cloud Functions -- increment counter based on data nodes

I'm working on a web application that will visualize data from my Firebase database. But first, I want to be able to "count" the total number of users with a given data so that I can then use that count number in my graphs.
For reference, my database looks like this:
Because I expect separate totals for the required keys, I'm guessing that I'll need separate counters for each one. I've started writing a cloud function to keep track of when a new user is created:
import * as functions from 'firebase-functions'
export const onMessageCreate = functions.database
.ref('/students/{studentID}')
.onCreate((snapshot, context) => {
const userData = snapshot.val()
const afterGrad = userData.afterGrad
const gender = userData.gender
const gradDate = userData.gradDate
const program = userData.program
const race = userData.race
const timeToComplete = userData.timeToComplete
})
But now, I'm extremely lost at how I should go about creating counters. Would something like this suffice, with an individual counter for each constant?
import * as functions from 'firebase-functions'
var counterAfterGrad;
export const onMessageCreate = functions.database
.ref('/students/{studentID}')
.onCreate((snapshot, context) => {
const userData = snapshot.val()
const afterGrad = userData.afterGrad
var counterAfterGrad++
})
Or should I be thinking about using a transaction in this case? I'm really not sure of the best way, and would really appreciate some help.
Yes, you should use a transaction. See the documentation here: https://firebase.google.com/docs/database/web/read-and-write#save_data_as_transactions and https://firebase.google.com/docs/reference/js/firebase.database.Reference#transaction
For counting the overall number of users you could do as follows:
export const onMessageCreate = functions.database
.ref('/students/{studentID}')
.onCreate((snapshot, context) => {
const userData = snapshot.val()
const afterGrad = userData.afterGrad
const allUsersCounterRef = admin
.database()
.ref('allUsersCounter');
return allUsersCounterRef
.transaction(counter_value => {
return (counter_value || 0) + 1;
})
})
Note that you may have to take into consideration the deletion of a user.
You could very well have several counters, for example by "gender" (male/female) and by "program". You would then use an object in the transaction as follows:
exports.onMessageCreate = functions.database
.ref('/students/{studentID}')
.onCreate((snapshot, context) => {
const userData = snapshot.val();
const countersRef = admin.database().ref('counters');
return countersRef.transaction(currentData => {
currentData[userData.gender] = (currentData[userData.gender] || 0) + 1;
currentData[userData.program] = (currentData[userData.program] || 0) + 1;
return currentData;
});
});

Google Cloud Function OnWrite how to get just the newly added child

I'm trying to get just the newly added child in my database, therefore I tried to subtract snapshot.after from snapshot.before. Unfortunately this one doesnt work. My code below:
const functions = require('firebase-functions');
// Create and Deploy Your First Cloud Functions
// https://firebase.google.com/docs/functions/write-firebase-functions
// exports.helloWorld = functions.https.onRequest((request, response) =>
//{
// response.send("Hello from Firebase!");
// })
exports.gameLoopBeing = functions.database.ref('/Chat/{pushId}')
.onWrite((snapshot, context) => {
//I want to retrieve the pushID
const original = snapshot.before.val();
const newValue = snapshot.after.val();
const difference = newValue-original
console.log('alool',context.params.pushId, difference);
// const uppercase = original.toUpperCase();
return snapshot.ref.set(original);
});
If you want to get the newly added data from a function you need to do this;
exports.gameLoopBeing = functions.database.ref('/Chat/{pushId}').onWrite((snapshot, context) => {
if (snapshot.after.exists() && !snapshot.before.exists()) {
const new_data = snapshot.after.val();
} else {
return null;
}
});

Querying firebase database at onwrite event

I am trying to write a firebase function which will check the the "user" and behave according to that on database write event. However when i query the database it returns null everytime and i didnt figure out what i am doing wrong. Any help is appreciated.
Thanks in advance.
My realtime database structure is like this:
ilk-projemiz-cd55baddclose
users
edRIPg8BcZU9YPbubp7HtQo7phl1
sayilar: 1532
status: "on"
hakan
sayilar: 5000
status: "waiting"
mehmet
sayilar: 7000
status: "on"
My firebase function file is this:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.sayi = functions.database.ref("/users/{uid}/status").onWrite(event => {
const status = event.data.val();
var user = event.data.ref.parent.key;
if (status =="on") {
console.log(status);
const events = event.data.adminRef.child('users');
const query =events.orderByChild('status').equalTo('on').limitToFirst(2);
query.on("value", sorunsuz,sorunlu);
}
});
function sorunlu(error) {
console.log("Something went wrong.");
console.log(error);
}
function sorunsuz(data) {
console.log("11111");
var fruits = data.val();
console.log(fruits); //it returns null here
var keys = Object.keys(fruits);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if(key==user){
//console.log(fruits[key].sayilar);
console.log("aaa");
}else{
console.log("bbbb");
}
}
}
This line: const events = event.data.adminRef.child('users'); tries to access a users node under the status node. And I think what you wanted to do is access the users node under the root reference.
Use the Admin SDK instead:
const events = admin.database().child('users');
Update: the user variable is out of scope, so I suggest you move the sorunsuz() function to be inside the on() function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sayi = functions.database.ref("/users/{uid}/status").onWrite(event => {
const status = event.data.val();
var user = event.data.ref.parent.key;
if (status =="on") {
console.log(status);
const events = admin.database().child('users');
const query =events.orderByChild('status').equalTo('on').limitToFirst(2);
query.on("value",
function(data) {
console.log("11111");
var fruits = data.val();
console.log(fruits); //it returns null here
var keys = Object.keys(fruits);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if(key==user){
//console.log(fruits[key].sayilar);
console.log("aaa");
}else{
console.log("bbbb");
}
}
}, sorunlu);
}
});
function sorunlu(error) {
console.log("Something went wrong.");
console.log(error);
}
you have to listen to users node
functions.database.ref("/users/{uid}/status"),
this path is not exist anywhere thats why you are getting null.
exports.sayi = functions.database.ref("/users").onWrite(event => {
const status = event.data.val(); //this data will be new
//Use above value to refer things
if (event.data.previous.exists()) {
//Update operation
}
});

Categories