查询集合中所有游戏结果的平均分数?

时间:2017-10-03 16:18:51

标签: javascript database firebase google-cloud-firestore

我的应用会在名为score的字段中存储最终得分在-30到+30范围内的游戏结果。如何查询所有游戏结果的总体平均值?

1 个答案:

答案 0 :(得分:3)

简单解决方案

如果您知道正在编写的游戏结果数量最多每秒一次,则可以使用云功能更新单独的文档average/score。对于每个游戏结果添加,如果文档不存在,则将名为count的字段设置为1,将名为score的字段设置为游戏分数。如果文档确实存在,请将1添加到名为count的字段中,并将分数添加到名为score的字段中。

现在,要查询平均分数,只需阅读average/score并将score除以count

可扩展解决方案

如果您怀疑或知道正在编写的游戏结果数量将超过每秒一次,您将需要应用简单解决方案的分布式计数器样式。

普通文档的数据模型将使用子集合,如下所示:

// average/score
{
  "num_shards": NUM_SHARDS,
  "shards": [subcollection]
}

// average/score/shards/${NUM}
{
  "count": 115,
  "score": 1472
}

为了使您的更新代码更加简化,您可以先使用以下命令初始化这些分片:

// 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();
}

现在,在Cloud Functions中更新平均聚合非常简单:

// 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 });
        });
    });
}

然后可以通过以下方式获得平均值:

// 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;
    });
}

您在此系统中可以达到的写入速率为每秒NUM_SHARDS,因此请进行相应的计划。注意:您可以从小处开始,轻松增加分片数量。只需创建一个新版本的createAverageAggregate,通过首先初始化新的分片来增加分片数,然后更新num_shards设置以匹配。这应该由updateAveragegetAverage函数自动选取。