Mon application stocke les résultats de jeu qui peuvent avoir un score final compris entre -30 et +30 dans un champ appelé score
. Comment puis-je rechercher la moyenne globale de tous les résultats du jeu?Interrogez-vous sur les scores moyens de tous les résultats d'une collection?
Répondre
Solution simple
Si vous connaissez le nombre de résultats du jeu en cours d'écriture sera au plus une fois par seconde, vous pouvez utiliser les fonctions Cloud pour mettre à jour un document distinct average/score
. Pour chaque ajout de résultat de jeu, si le document n'existait pas, définissez un champ appelé count
sur 1 et un champ appelé score
sur le score du jeu. Si le document existe, ajoutez 1
au champ appelé count
et ajoutez le score au champ appelé score
.
Maintenant, pour interroger le score moyen, il suffit de lire average/score
et de diviser score
par count
.
Solution évolutive
Si vous pensez ou savez le nombre de résultats du jeu en cours d'écriture dépassera une fois par seconde, vous aurez besoin d'appliquer un style de compteur distribué de la solution simple.
Votre modèle de données pour le document moyen utilisera les sous-collections et ressembler à:
// average/score
{
"num_shards": NUM_SHARDS,
"shards": [subcollection]
}
// average/score/shards/${NUM}
{
"count": 115,
"score": 1472
}
Pour rendre votre code de mise à jour plus simple, vous pouvez initialiser d'abord ces tessons avec:
// ref points to db.collection('average').doc('score')
function createAverageAggregate(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, count: 0 });
}
// Commit the write batch
return batch.commit();
}
Mise à jour du l'agrégat moyen dans les fonctions Cloud est maintenant aussi simple que:
// ref points to db.collection('average').doc('score')
function updateAverage(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 in a transaction
return db.runTransaction(t => {
return t.get(shard_ref).then(doc => {
const new_count = doc.data().count + 1;
const new_score = doc.data().score + 1;
t.update(shard_ref, { count: new_count, score: new_score });
});
});
}
G ise la moyenne peut alors se faire avec:
// ref points to db.collection('average').doc('score')
function getAverage(ref) {
// Sum the count and sum the score of each shard in the subcollection
return ref.collection('shards').get().then(snapshot => {
let total_count = 0;
let total_score = 0;
snapshot.forEach(doc => {
total_count += doc.data().count;
total_score += doc.data().score;
});
return total_score/total_count;
});
}
Le taux d'écriture, vous pouvez obtenir dans ce système est NUM_SHARDS par seconde, afin de planifier en conséquence. Remarque: Vous pouvez commencer petit et augmenter le nombre de fragments facilement. Il suffit de créer une nouvelle version de createAverageAggregate
pour augmenter le nombre de partitions en initialisant les nouvelles, puis en mettant à jour le paramètre num_shards. Cela devrait être automatiquement pris en charge par vos fonctions updateAverage
et getAverage
.