Attention comptage du nombre de documents pour les grandes collections avec une fonction cloud. C'est un peu complexe avec firestore base de données si vous voulez avoir un précalculées compteur pour chaque collection.
Ce Code ne fonctionne pas dans ce cas:
export const customerCounterListener =
functions.firestore.document('customers/{customerId}')
.onWrite((change, context) => {
// on create
if (!change.before.exists && change.after.exists) {
return firestore
.collection('metadatas')
.doc('customers')
.get()
.then(docSnap =>
docSnap.ref.set({
count: docSnap.data().count + 1
}))
// on delete
} else if (change.before.exists && !change.after.exists) {
return firestore
.collection('metadatas')
.doc('customers')
.get()
.then(docSnap =>
docSnap.ref.set({
count: docSnap.data().count - 1
}))
}
return null;
});
La raison en est que tous les nuages firestore détente doit être idempotent, comme firestore documentation dire: https://firebase.google.com/docs/functions/firestore-events#limitations_and_guarantees
Solution
Ainsi, afin d'éviter que plusieurs exécutions de votre code, vous avez besoin pour gérer les événements et les transactions. C'est ma façon particulière de traiter de gros de la collection de compteurs:
const executeOnce = (change, context, task) => {
const eventRef = firestore.collection('events').doc(context.eventId);
return firestore.runTransaction(t =>
t
.get(eventRef)
.then(docSnap => (docSnap.exists ? null : task(t)))
.then(() => t.set(eventRef, { processed: true }))
);
};
const documentCounter = collectionName => (change, context) =>
executeOnce(change, context, t => {
// on create
if (!change.before.exists && change.after.exists) {
return t
.get(firestore.collection('metadatas')
.doc(collectionName))
.then(docSnap =>
t.set(docSnap.ref, {
count: ((docSnap.data() && docSnap.data().count) || 0) + 1
}));
// on delete
} else if (change.before.exists && !change.after.exists) {
return t
.get(firestore.collection('metadatas')
.doc(collectionName))
.then(docSnap =>
t.set(docSnap.ref, {
count: docSnap.data().count - 1
}));
}
return null;
});
Les cas d'utilisation ici:
/**
* Count documents in articles collection.
*/
exports.articlesCounter = functions.firestore
.document('articles/{id}')
.onWrite(documentCounter('articles'));
/**
* Count documents in customers collection.
*/
exports.customersCounter = functions.firestore
.document('customers/{id}')
.onWrite(documentCounter('customers'));
Comme vous pouvez le voir, la clé pour empêcher l'exécution de plusieurs est la propriété appelée id de l'événement dans le contexte de l'objet. Si la fonction a été traitée de nombreuses fois pour le même événement, l'id d'événement sera le même dans tous les cas. Malheureusement, vous devez avoir des "événements" de la collection dans votre base de données.