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;
}
});
Related
I am trying to make barbershop web app where costumer can see list of free appointments and when they reserve free appointment I want to delete that field from firebase.
I have a collection which represents one barber.
This is how it looks in firebase.
As you see radno_vrijeme is object or map in firebase which contains 6 arrays, and in each array there is list of free working hours.
In my function I am able to do everthing except last line where I need to update firebase collection.
const finishReservation = async () => {
try {
const freeTimeRef = collection(db, `${barber}`);
const q = query(freeTimeRef);
const querySnap = await getDoc(q);
querySnap.forEach(async (doc) => {
const radnoVrijeme = doc.data().radno_vrijeme;
// Find the index of the hour you want to delete
const index = radnoVrijeme["Mon"].indexOf(hour);
// Remove the hour from the array
radnoVrijeme["Mon"].splice(index, 1);
// Update the document in the collection
console.log(radnoVrijeme);
const radnoVrijemeMap = new Map(Object.entries(radnoVrijeme));
await freeTimeRef.update({ radno_vrijeme: radnoVrijemeMap });
});
} catch (error) {
console.log(error);
}
};
I tried to pass it as JSON stringified object, but it didn't work. I always get this error :
"FirebaseError: Expected type 'ya', but it was: a custom Ia object"
When you are trying to fetch multiple documents using a collection reference or query, then you must use getDocs():
const finishReservation = async () => {
try {
const freeTimeRef = collection(db, `${barber}`);
const q = query(freeTimeRef);
const querySnap = await getDocs(q);
const updates = [];
querySnap.forEach((d) => {
const radnoVrijeme = d.data().radno_vrijeme;
const index = radnoVrijeme["Mon"].indexOf(hour);
radnoVrijeme["Mon"].splice(index, 1);
const radnoVrijemeMap = new Map(Object.entries(radnoVrijeme));
updates.push(updateDoc(d.ref, { radno_vrijeme: radnoVrijemeMap }))
});
await Promise.all(updates);
console.log("Documents updated")
} catch (error) {
console.log(error);
}
};
getDoc() is used to fetch a single document using a document reference.
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.
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
});
}
});
I'm trying to improve a firestore get function, I have something like:
return admin.firestore().collection("submissions").get().then(
async (x) => {
var toRet: any = [];
for (var i = 0; i < 10; i++) {
try {
var hasMedia = x.docs[i].data()['mediaRef'];
if (hasMedia != null) {
var docData = (await x.docs[i].data()) as MediaSubmission;
let submission: MediaSubmission = new MediaSubmission();
submission.author = x.docs[i].data()['author'];
submission.description = x.docs[i].data()['description'];
var mediaRef = await admin.firestore().doc(docData.mediaRef).get();
submission.media = mediaRef.data() as MediaData;
toRet.push(submission);
}
}
catch (e) {
console.log("ERROR GETTIGN MEDIA: " + e);
}
}
return res.status(200).send(toRet);
});
The first get is fine but the performance is worst on the line:
var mediaRef = await admin.firestore().doc(docData.mediaRef).get();
I think this is because the call is not batched.
Would it be possible to do a batch get on an array of mediaRefs to improve performance?
Essentially I have a collection of documents which have foreign references stored by a string pointing to the path in a separate collection and getting those references has been proven to be slow.
What about this? I did some refactoring to use more await/async code, hopefully my comments are helpful.
The main idea is to use Promise.all and await all the mediaRefs retrieval
async function test(req, res) {
// get all docs
const { docs } = await admin
.firestore()
.collection('submissions')
.get();
// get data property only of docs with mediaRef
const datas = await Promise.all(
docs.map(doc => doc.data()).filter(data => data.mediaRef),
);
// get all media in one batch - this is the important change
const mediaRefs = await Promise.all(
datas.map(({ mediaRef }) =>
admin
.firestore()
.doc(mediaRef)
.get(),
),
);
// create return object
const toRet = datas.map((data: MediaSubmission, i) => {
const submission = new MediaSubmission();
submission.author = data.author;
submission.description = data.description;
submission.media = mediaRefs[i].data() as MediaData;
return submission;
});
return res.status(200).send(toRet);
}
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.