I have a firebase cloud function that should increment the value of a field when a new document is created. The function executes successfully as I can see this within the firebase logs, but it doesn't increment the value of the field.
exports.onFileAdded = functions.firestore.document("files/{id}").onCreate(async (change, context) => {
const file = change.data();
const hub = await getByCollectionAndId('hubs', file.hubId);
Firebase.firestore().collection('teams').doc(hub.teamId).set({tileCount: Firebase.database.ServerValue.increment(1)}, {merge: true});
});
As there are no errors, and the function executes successfully, what am I doing wrong?
The problem is:
Firebase.database.ServerValue.increment(1)
You're using the operator to increment a value on the Realtime Database, but you are using it on Cloud Firestore. While both databases are part of Firebase, they're completely separate, and the API for one doesn't apply to the other.
To fix the problem, use the increment operator for Firestore:
firebase.firestore.FieldValue.increment(1)
Also see my answer here: How to increment existing number field in Cloud Firestore
Related
I'm making a Firebase function, that is supposed to get the value of a field in the Realtime Database, write the value in a Firestore Document and increment the original field. The problem is when the function gets called very frequently e.g. 500 times a second, it gets and writes the same value in a lot of documents, because many executions will get the same value before it gets incremented. Is there any way to get the value of a Realtime DB field and increment it at the same time or somehow prevent this issue?
Thank you in advance.
My code:
const { getFirestore } = require('firebase-admin/firestore');
const { getDatabase, ServerValue } = require('firebase-admin/database');
const rb = getDatabase();
const db = getFirestore();
exports.increment = functions.https.onCall(async (data, context) => {
rb.ref('count').get().then((snapshot)=>{
let value = snapshot.val();
db.collection("documents").doc(value.toString()).set({count:value});
rb.ref("count").set(ServerValue.increment(1))
})
});
Since you're using an auto-scaling infrastructure with Cloud Functions, it will spin up new instances if there are a lot of requests coming in. If you don't want to do that, it might be worth setting a maximum number of instances on your Cloud Function.
How to retrieve a single value (restaurant_token)from firebase RealTime when there is a new child added
my structure is :
-RestaurantsOrders
-- random Push key
--- restaurant token
[See screenshot for node.js firebase cloud function][1]
[See screenshot for Firebase DB reference][2]
Have a look at the doc: You need to do along the following lines
exports.sendNotif = functions.database.ref('/RestaurantOrders/{pushId}')
.onWrite((change, context) => {
// Grab the current value of what was written to the Realtime Database.
const original = change.after.val().restaurant_token;
// ....
await admin.messaging(...)...
return null;
});
Note the return null; at the end: have a look at this doc for more details on this key point.
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.
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.
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.