Accessing Value from Key via Change (onWrite) - node.js

I am trying to access the value of a key(node) from the firebase realtime database. I am using the onWrite trigger which passes the change paramters, which holds snapshot of the database before the trigger and after the trigger. Therefore with the after trigger's snapshot I wish to place the key of a node into a constant variable although when I try I recieve a TypeError.
Type Error Bug:
TypeError: change.after.ref.child(...).val is not a function
at exports.sendNotification.functions.database.ref.onWrite (/user_code/index.js:13:64)`
Thus meaning that .val() cannot be used in the way I have tried to use it, although I cannot see any other information to see how to handle this.
Relevant Segment of Code:
'user-strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase); // load the firebase-functions and firebase-admin modules, and initialize an admin app instance from which Realtime Database changes can be made.
exports.sendNotification = functions.database.ref("/Notifications/{user_id}/{notification_type}/{sender_id}").onWrite((change, context) => {
const user_id = context.params.user_id;
const notification_type = context.params.notification_type;
const sender_id = context.params.sender_id;
//Check if the notification is still to be sent (true not false)
const notficationValid = change.after.ref.child(sender_id).val();
console.log(`Notification Valid: `, notficationValid);
I expected the notificationValid variable to hold the value of 'true'.
Firebase Database

The .ref in your code is not needed:
change.after.child(sender_id).val()
Calling .ref on a DataSnapshot gives you back a DatabaseReference, which doesn't have a val() method.

Related

Realtime Database ref change can' t detect

I created a Db. Name Tomris
Below that is a 'Cumle': "Sentence". Whatever I do here does not happen.
https://i.stack.imgur.com/EEKAk.png
What I want is Trigger work when the sentence changes. But I couldn't.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.DBTrigger = functions.database.ref('tomris-d96a7/Cumle')
.onWrite( (change, context) => {
const afterData = change.after.data();
console.log(afterData);
console.log ("onur");
});
You included the name of your database in the Cloud Functions trigger, which is not needed and in fact means your Function is now looking at a non-existing path.
To trigger on the correct path, only include that path in the Function definition:
exports.DBTrigger = functions.database.ref('Cumle')
...

Is it safe to initialize db references outside of a cloud function definition?

I've written the following as a hello-world sort of "my first cloud function":
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
const db = admin.database();
const messagesRef = db.ref('/messages/general');
export const sendMessage = functions.https.onCall(async (data, context) => {
const writeResult = await messagesRef.push(data.text)
return {
result: writeResult
};
});
Note that admin, db, and messagesRef are all being declared outside of the function definition, in the interests of not having to do it every time.
Does this approach work? Are there any problems with doing it this way? For instance, if the db connection is lost somehow (perhaps by an outage), will this get restarted and safely reconnected?
All a DatabaseReference does is point to a location in the database, so no interaction with the database happens at that point. Since the messageRef has no dependency on any data that is passed into sendMessage, it can safely be initialized in the global scope.
Yes it works perfectly. Your db object is imported from the SDK without any dependency on variables from within the function, so it is fine.

How to update DocumentReference?

I tried to update DocumentReference but could not do it.
update() method fails. How to use it? (How to pass argument?)
firebase-admin version is 6.3.0.
#google-cloud/firestore version is 0.19.0.
❯ firebase functions:shell
i functions: Preparing to emulate functions.
Warning: You're using Node.js v8.14.0 but Google Cloud Functions only supports v6.11.5.
✔ functions: sampleFunc
firebase > const admin = require('firebase-admin');
firebase > admin.initializeApp();
firebase > let ref = admin.firestore().collection("users").doc('edqupYQhzqV1ODjEpoJn');
firebase > let updates = { email: 'xxx#yyy.zzz' };
firebase > ref.update(updates).then(value => console.log(value) );
Error: Update() requires either a single JavaScript object or an alternating list of field/value pairs that can be followed by an optional precondition. Argument "dataOrField" is not a valid Document. Input is not a plain JavaScript object.
at WriteBatch.update (/Users/xxx/Desktop/sample-functions/functions/node_modules/#google-cloud/firestore/build/src/write-batch.js:359:23)
at DocumentReference.update (/Users/xxx/Desktop/sample-functions/functions/node_modules/#google-cloud/firestore/build/src/reference.js:387:14)
Update
Document has already been created, so get() works.
firebase > ref.get().then(snapshot => console.log(snapshot.data()));
Errors also occur in set() as well.
firebase > ref.set({email: 'aaa#bbb.ccc'}, {merge: true}).then(value => console.log(value));
Error: Argument "data" is not a valid Document. Input is not a plain JavaScript object.
at Validator.(anonymous function).values [as isDocument] (/Users/xxx/Desktop/sample-functions/functions/node_modules/#google-cloud/firestore/build/src/validate.js:99:27)
at WriteBatch.set (/Users/xxx/Desktop/sample-functions/functions/node_modules/#google-cloud/firestore/build/src/write-batch.js:232:25)
at DocumentReference.set (/Users/xxx/Desktop/sample-functions/functions/node_modules/#google-cloud/firestore/build/src/reference.js:349:27)
Following the example you posted, you are trying to update a document that does not exist. In this case you should create it first.
If you're not sure whether the document exists, pass the option to merge the new data with any existing document to avoid overwriting entire documents.
From Google's Documentation:
var cityRef = db.collection('cities').doc('BJ');
var setWithOptions = cityRef.set({
capital: true
}, {merge: true});
Reference: Firestore Add Data
I've never tried using the Admin SDK in the functions:shell like you do in your example. However I can easily reproduce your error. I guess that the functions:shell somehow tampers with your variable updates. When i log updates undefined is concatenated in output
firebase > console.log(updates)
{ email2: 'xxx#yyy.zzz' }
undefined
If you put your code in a JS file, initialize your app with a service account and run in with node it will most likely work just fine!
Read more about how to use Service Account
create an object first then assign your values to a field/property on that object like:
var myObj = {};
myObj["myfield"] = myvalue;
now pass as the 2nd argument of .set or .update.

How to query firebase realtime database in cloud code

I am using Firebase cloud code and firebase realtime database.
My database structure is:
-users
-userid32
-userid4734
-flag=true
-userid722
-flag=false
-userid324
I want to query only the users who's field 'flag' is 'true' .
What I am doing currently is going over all the users and checking one by one. But this is not efficient, because we have a lot of users in the database and it takes more than 10 seconds for the function to run:
const functions = require('firebase-functions');
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
exports.test1 = functions.https.onRequest((request, response) => {
// Read Users from database
//
admin.database().ref('/users').once('value').then((snapshot) => {
var values = snapshot.val(),
current,
numOfRelevantUsers,
res = {}; // Result string
numOfRelevantUsers = 0;
// Traverse through all users to check whether the user is eligible to get discount.
for (val in values)
{
current = values[val]; // Assign current user to avoid values[val] calls.
// Do something with the user
}
...
});
Is there a more efficient way to make this query and get only the relevant records? (and not getting all of them and checking one by one?)
You'd use a Firebase Database query for that:
admin.database().ref('/users')
.orderByChild('flag').equalTo(true)
.once('value').then((snapshot) => {
const numOfRelevantUsers = snapshot.numChildren();
When you need to loop over child nodes, don't treat the resulting snapshot as an ordinary JSON object please. While that may work here, it will give unexpected results when you order on a value with an actual range. Instead use the built-in Snapshot.forEach() method:
snapshot.forEach(function(userSnapshot) {
console.log(userSnapshot.key, userSnapshot.val());
}
Note that all of this is fairly standard Firebase Database usage, so I recommend spending some extra time in the documentation for both the Web SDK and the Admin SDK for that.

How to get inner child in cloud function for Firebase?

Here is my database and I want to trigger onWrite event on children of PUBLISHED_CONTENT_LIKES. When I add another userId under publishedContentId1, I can identify contentId as publishedContentId1 in my cloud function using event.params.pushId.
exports.handleLikeEvent = functions.database.ref('/USER_MANAGEMENT/PUBLISHED_CONTENT_LIKES/{pushId}')
.onWrite(event => {
// Grab the current value of what was written to the Realtime Database.
//const userId = event.data.child(publishedContentId);
//const test = event.params.val();
const publishedContentId = event.params.pushId;
var result = {"publishedContentId" : "saw"}
// You must return a Promise when performing asynchronous tasks inside a Functions such as
// writing to the Firebase Realtime Database.
// Setting an "uppercase" sibling in the Realtime Database returns a Promise.
return event.data.ref.parent.parent.child('PUBLISHED_CONTENTS/'+publishedContentId).set(result);
});
However I want to get newly added userId as well. How to get that userId using above event?
You can get the data that is being written under event.data. To determine the new user ID:
event.data.val().userID
I recommend watching the latest Firecast on writing Database functions as it covers precisely this topic.

Resources