Reputation: 49
I'm on a team that's working on a flutter mobile app for food waste management. We are also using Firestore and google cloud functions. Our problem is that we generate a lot of data. Every time someone scans their food items we collect data on that food item (expiry date, barcode etc.).
Every day we want to check the Firestore database (using a cloud function) and determine if the user has food items expiring on that day and if so, we send a push notification to their phone telling them
x items are expiring today
This works OK (see code below) but we only get notifications every couple of days, which indicates that there is an underlying problem. What is the best way to process "large" - we currently have 87 documents spanning 13 collections - amounts of data daily without going over the free limit. Is that possible? Even if not, surely we can make it work properly so that notifications work everyday?
Here's our code so far.
import * as admin from "firebase-admin";
import * as functions from "firebase-functions";
admin.initializeApp();
const db = admin.firestore();
const fcm = admin.messaging();
export const expiryDateChecker = functions.pubsub.schedule("0 06 * * *")
.timeZone("Europe/London").onRun(async (variable) => {
const collections = await db.listCollections();
const options = {
priority: "high",
timeToLive: 60 * 60 * 24,
};
await collections.forEach(async (collection) => {
const snapshot = await collection.get();
let amountToday = 0;
let amountTomorrow = 0;
let amountExpired = 0;
let counter = 0;
let check = 0;
await snapshot.forEach(async (doc) => {
const Date1 = new Date();
Date1.setHours(0, 0, 0, 0);
const Date2 = new Date(doc.data().ExpiryDate);
const diffInTime = Date2.getTime() - Date1.getTime();
const diffInDays = Math.floor(diffInTime / (1000 * 3600 * 24));
if (diffInDays == 0) {
amountToday++;
} else if (diffInDays == 1) {
amountTomorrow++;
} else if (diffInDays < 0) {
amountExpired++;
try {
counter = parseInt(doc.data().Quantity
.toString());
const doc2 = await db.collection("expiryGroups").doc("Users")
.collection(collection.id).doc(doc.id).get();
if (doc2.exists) {
db.collection("expiryGroups").doc("Users")
.collection(collection.id).doc(doc2.id)
.update({Expired: true});
check = 0;
check = parseInt(doc2?.data()?.expiryCount.toString()) || 0;
if (check == 0) {
db.collection("expiryGroups").doc("Users")
.collection(collection.id).doc(doc2.id)
.update({expiryCount: admin.firestore.FieldValue.increment(counter)});
}
}
} catch (e) {
console.log(e);
}
}
});
if (amountToday > 0) {
/*push notification code here*/
fcm.sendToTopic(collection.id, message, options);
}
if (amountTomorrow > 0) {
/*push notification code here*/
fcm.sendToTopic(collection.id, message, options);
}
if (amountExpired > 0) {
/*push notification code here*/
fcm.sendToTopic(collection.id, message, options);
}
});
});
Upvotes: 0
Views: 97
Reputation: 50830
The problem here seems to be improper handling of asynchronous functions.
await
a forEach
loopawait
in a forEach
loop (use Promise.all()
instead)if
statements at the bottom seems to be running even before the promises above resolve.collection.id
in FCM part doesn't seem to be defined (is out of the forEach
)Try refactoring your code to:
export const expiryDateChecker = functions.pubsub.schedule("0 06 * * *")
.timeZone("Europe/London").onRun(async (variable) => {
const collections = await db.listCollections();
const options = {
priority: "high",
timeToLive: 60 * 60 * 24,
};
const docsSnapshots = await Promise.all(collections.map(c => c.get()))
let amountToday = 0;
let amountTomorrow = 0;
let amountExpired = 0;
let counter = 0;
let check = 0;
const docsData = []
const docUpdates: any[] = []
const notificationsToSend: any[] = [];
docsSnapshots.forEach(qSnapshot => {
qSnapshot.forEach(doc => {
docsData.push(doc.data())
const Date1 = new Date();
Date1.setHours(0, 0, 0, 0);
const Date2 = new Date(doc.data().ExpiryDate);
const diffInTime = Date2.getTime() - Date1.getTime();
const diffInDays = Math.floor(diffInTime / (1000 * 3600 * 24));
if (diffInDays == 0) {
amountToday++;
} else if (diffInDays == 1) {
amountTomorrow++;
} else if (diffInDays < 0) {
amountExpired++;
counter = parseInt(doc.data().Quantity
.toString());
docUpdates.push(db.collection("expiryGroups").doc("Users")
.collection(doc.ref.parent.id).doc(doc.id).update({ expired: true }))
}
})
})
await Promise.all(docUpdates)
await Promise.all(notificationsToSend)
// terminate the function
})
You should try to push promises for sending notifications to the array in similar way. Also, I cannot see where message
that is being sent using FCM is defined.
Also checkout:
Using async/await with a forEach loop
Upvotes: 1