How to create listener to custom variable inside document - firebase

This code updating when any change inside document but I want when change custom variable not any variable.
For example I want call this funcation when change Score variable.
exports.updateUser = functions.firestore.document('Test/uhfL5NE199eYTGyfSH1srtrtee').onUpdate((change, context) => {
const washingtonRef = admin.firestore().collection('Test').doc('uhfL5NE199eYTGyfSH1srtrtee');
return washingtonRef.update({Counts:admin.firestore.FieldValue.increment(1)});
});

This is not possible. With Cloud Function and Firestore an .onUpdate() is triggered when a document already exists and has any value changed (See https://firebase.google.com/docs/functions/firestore-events).
What you can do is to use the two snapshots that represent the data state before and after the triggering event and that are present in the change object, as follows:
exports.updateUser = functions.firestore.document('Test/uhfL5NE199eYTGyfSH1srtrtee').onUpdate((change, context) => {
const newValue = change.after.data();
const previousValue = change.before.data();
//Check if the Score field has changed
if (newValue.Score !== previousValue.Score) {
//Score field has changed! -> Do whatever you want
} else {
//End the Cloud Function
return false;
}
});

Related

how to make an action triggered only if a field is changed using onUpdate trigger Firestore cloud function?

There is a field in my event document called title which contains a boolean value. I have set onUpdate firestore trigger in my document. I want if my title is updated then I will do some action. but if other fields is updated then I will not perform any action at all. how to do that ?
I am okay if the function below is invoked every time there is an update on the document, but I want only do some further action only if the title is updated
exports.dbEventsOnUpdate = functions.firestore
.document('events/{eventId}').onUpdate(async (change,context) => {
try {
const eventID = context.params.eventId
if (titleIsUpdated) {
return db.doc(``).set(newData)
} else {
// do nothing here
return null
}
} catch(error) {
console.log(error)
return null
}
})
Currently, Firestore Cloud Functions cannot be triggered based on a field update. It's only triggered when a document is updated.
You can actually check if the title was updated using the following code:
const newValue = change.after.data();
const previousValue = change.before.data();
const titleIsUpdated = newValue.title !== previousValue.title;
But keep in mind that your function will always be triggered when a field is changed in that document. And this might incur more costs, since Cloud Functions charge based on functions invocations. (See pricing)

Firestore how to get last document of collection and add new with incremented id?

I have probably made a mistake with autogenerated id's for documents inside my events collection. I have added eventId for generated events and assigned eventId manually to each event.
Can I somehow get last document with its eventId and add new document with eventId of last document incremented by one.
Or should I delete autogenerated Id - based events and create new ones with non-autogenrated ids?
GitHub: https://github.com/Verthon/event-app
I have posted working React.js app on netlify: https://eventooo.netlify.com/
How it works:
I added unique eventId to each dummy events inside of events collection,
Based on that unique eventId I create link to specific single Event.js,
User can create event providing information in /create-event,
Once someone is creating an event I would like to add event to events collection with increment id, I have added 7 events created inside of console, so next should have id=7, something maybe like event1, event2 ... with autoincrement
Inside of users collection I store currentUser.uid from auth and host name provided by user
Event Creator
submitEvent = (e) => {
e.preventDefault();
const eventRef = this.props.firebase.db.collection("events").doc();
eventRef.set({
title: this.state.title,
host: this.state.host,
localization: this.state.localization,
description: this.state.description,
category: this.state.category,
day: this.state.day,
hour: this.state.hour,
featuredImage: this.state.imageUrl,
uid: this.props.firebase.auth.currentUser.uid
});
const citiesRef = this.props.firebase.db.collection("cities").where("city", "==", this.state.localization);
const cityRef = this.props.firebase.db.collection("cities").doc();
citiesRef.get()
.then(querySnapshot => {
if(querySnapshot.exists){
console.log("exist");
return
}else{
cityRef.set({
city: this.state.localization,
})
}
});
const userRef = this.props.firebase.db.collection("users").doc();
userRef.set({
user: this.state.host,
uid: this.props.firebase.auth.currentUser.uid
});
Thank you
I understand that you want your eventId value to come from a number sequence. The best approach for such need is to use a distributed counter, as explained in the doc: https://firebase.google.com/docs/firestore/solutions/counters
I don't know which language you are using, but I paste below the JavaScript code of the three functions from this documentation and I write the code that will generate the sequential number that you can use to create the documents.
var db = firebase.firestore();
//From the Firebase documentation
function createCounter(ref, num_shards) {
var batch = db.batch();
// Initialize the counter document
batch.set(ref, { num_shards: num_shards });
// Initialize each shard with count=0
for (let i = 0; i < num_shards; i++) {
let shardRef = ref.collection('shards').doc(i.toString());
batch.set(shardRef, { count: 0 });
}
// Commit the write batch
return batch.commit();
}
function incrementCounter(db, ref, num_shards) {
// Select a shard of the counter at random
const shard_id = Math.floor(Math.random() * num_shards).toString();
const shard_ref = ref.collection('shards').doc(shard_id);
// Update count
return shard_ref.update(
'count',
firebase.firestore.FieldValue.increment(1)
);
}
function getCount(ref) {
// Sum the count of each shard in the subcollection
return ref
.collection('shards')
.get()
.then(snapshot => {
let total_count = 0;
snapshot.forEach(doc => {
total_count += doc.data().count;
});
return total_count;
});
}
//Your code
var ref = firebase
.firestore()
.collection('counters')
.doc('1');
var num_shards = 2 //Adapt as required, read the doc
//Initialize the counter bay calling ONCE the createCounter() method
createCounter(ref, num_shards);
//Then, when you want to create a new number and a new doc you do
incrementCounter(db, ref, num_shards)
.then(() => {
return getCount(ref);
})
.then(count => {
console.log(count);
//Here you get the new number form the sequence
//And you use it to create a doc
db.collection("events").doc(count.toString()).set({
category: "education",
//....
})
});
Without much details on the Functional Requirements it is difficult to say if there is a difference between using the number form the sequence as the uid of the doc or as a field value in the document. It depends on the queries you may do on this collection.

how to set value to zero of key-value pair object which i got in firebase cloud function(snapshot)

i have created cloud function which will trigger onCreate() when new value is add to node Sample/Pen.whenever the new node is created in Sample/Pen, i wanted to create another node which is Final/Pen but the values of key-value pair should be zero.
The following will do the job:
exports.finalPen = functions.database
.ref('/Sample/Pen/{penId}')
.onCreate((snap, context) => {
const createdData = snap.val(); // data that was created
return admin
.database()
.ref('Final/Pen/' + snap.key)
.set(setAllToZero(createdData));
});
const setAllToZero = function(pen) {
Object.keys(pen).forEach(function(key) {
pen[key] = 0;
});
return pen;
};

Reuse wildcard value in Firebase Functions

I'm checking the onUpdate of a {postId}, and I want to run a firebase database call with that same {postId}.. if that makes sense. Here is my code:
exports.handleVoteKarma = functions.database
.ref('upvotes/{postId}')
.onUpdate(async change => {
const scoreBefore = change.before.val() || 0;
const scoreAfter = change.after.val();
//This {postId} should be the same as the one above for the upvotes/{postId}
adb.ref('{item}/{loc}/{postId}/score').once('value').then((usr) => {
});
return null;
});
Essentially I want the {postId} in upvotes/ to have the same value as the {postId} when I check the score.. will it work like this?
Realtime Database triggers accept a second argument, which you're not using in your function:
exports.handleVoteKarma = functions.database
.ref('upvotes/{postId}')
.onUpdate(async (change, context) => {
// note the "context" parameter here
});
This is an EventContext object and it contains a params property with the values of the wildcards in the path. You'd use it simply like this:
const postId = context.params.postId
You can then use the postId string later to build other refs.
There is more discussion in the documentation.

Firebase Cloud Functions return wrong value

I‘m running following function every time a certain write happens in my Firestore database. The counter get updated like it should and also if i check the value in my Realtime Database, it shows the right value. However if i query for the value in my then(), it always returns 1. If i change my transaction to always increase the counter by 3, then it returns 3. What am I doing wrong?
exports.someFunc = functions.firestore.document("/statistics/{userId}/postStats/{postId}/views/{user}/views/{view}").onCreate((event) => {
const userId = event.params.userId
const postId = event.params.postId
return admin.database().ref(`/statistics/${userId}/${postId}/views`).transaction(function(current) {
return current + 1;
}).then(admin.database().ref(`/statistics/${userId}/${postId}/views`).once('value').then(function(snapshot) {
console.log(snapshot.val())
);
});
I'm not really sure what's going on. But you shouldn't have to re-read the value anyway, since it's passed in to the promise as an argument. Get it with:
exports.someFunc = functions.firestore.document("/statistics/{userId}/postStats/{postId}/views/{user}/views/{view}").onCreate((event) => {
const userId = event.params.userId
const postId = event.params.postId
return admin.database().ref(`/statistics/${userId}/${postId}/views`).transaction(function(current) {
return current + 1;
}).then(function(committed, snapshot) {
console.log(snapshot.val())
);
});

Resources