Firebase - 如何在一个事务中对文档运行查询和更新

时间:2018-03-31 12:45:45

标签: google-cloud-firestore

我的问题是,交易只允许deletegetsetupdate文件。

对于2人游戏,我需要创建游戏并加入游戏。这个想法如下:

  • 检查没有对手的游戏是否存在=>如果我找到一个
  • ,请加入
  • 如果没有对手存在游戏,则创建新游戏

这意味着,在没有对手的情况下搜索游戏并在事务中运行更新的查询。我怎么能这样做?

问题:

如果游戏在此期间(查询和更新之间)发生了变化并且已经更新了怎么办?我目前可能会覆盖其他一些在此期间加入游戏的玩家...我该如何解决这个问题呢?

代码

这是我到目前为止所做的:

// get a game without opponent
var gameQueryRef = db.collection('games')
    .where('state', '==', '1')
    .limit(1);

return gameQueryRef.get()
    .then(querySnapshot => {
        var gameRef = null;
        if (querySnapshot.docs.length === 0) {
            return null;
        } else {
            return querySnapshot.docs[0].ref;   
        }
    })
    .then(gameRef => {
        log("GameRequest", gameRequestId, "gameRef = " + gameRef);
        if (gameRef === null)
        {
            var newGameRef = db.collection("games").doc();
            var game = {
                state: 1,
                creationDate: Date.now(),
                finishDate: 0,
                docPlayer1: docPlayer,
                docPlayer2: null
            };
            return newGameRef.set(game);
        }
        else
        {
            // update game => THIS IS NOT SAVE!
            // what if the game did change in the meantime and was updated already????
            // I possibly override some other player who has joined this game in the meantime...
            return gameRef.update(state => 2, docPlayer2 => docPlayer);
        }
    })
    // continue with game

编辑 - 解决方案

我发布了一个可能的解决方案,我将测试,如果有人对此有评论,我也对此感兴趣。

1 个答案:

答案 0 :(得分:0)

可能的解决方案

文档ID可以手动定义,并且必须在一个集合中是唯一的,因此我们可以在另一个集合中创建相同的文档。

所以我将创建一个我称之为GameLocks的集合。

有了这个想法,我认为以下确实有效:

  • 我查询游戏并获取Game的文档=>遗憾的是,在交易之外,所以我需要在交易中重新检查游戏的状态,以确保查询和交易之间没有任何变化
  • 然后我按照以下步骤开始transaction
    • 检查GameLocks文档是否存在(通过transaction.get)与Game文档=>具有相同ID如果是真的,我知道其他人已经尝试加入这个游戏,我可以处理这个,所以我在这里停止这个交易(要么我搜索另一个游戏,要么我只是开始一个没有对手的新游戏,等待其他人加入这个游戏)
    • 没有GameLock文档,所以我创建了一个=>我只是将Game的ID用于锁定
    • 检查游戏是否仍然是有效目标=>它还没有对手吗? (通过transaction.get)=>重要的是,因为我们在执行查询时没有处于事务中! =>结束交易,如果其他人已加入此游戏
    • 更新游戏(通过transaction.update
    • 再次删除GameLock文档(通过transaction.delete
    • 完成交易

通过在事务中运行锁定文档的createdelete操作并检查Game以及它们之间的所有操作,我可以肯定没有其他人可以在两者之间操纵相同的Game文档,这样就可以解决问题。