【问题标题】:How to create firestore transaction where target document needs to be found如何在需要找到目标文档的地方创建 Firestore 事务
【发布时间】:2017-12-06 16:07:04
【问题描述】:

我正在寻找一种创建 Firestore 事务的方法,在该事务中我从查询中找到一个文档,然后在事务中修改该文档。

类似的东西(kotlin):

firestore.runTransaction { transaction ->

  val snapshot = transaction.get(db.collection("document")
      .whereEqualTo("someField", null)
      .orderBy("creationDate", ASCENDING)
      .limit(1L))

  val myObject = snapshot.toObject(MyObject::class.java)
  myObject.someFiled = "123"
  transaction.set(snapshot.reference, myObject)
}

这里的问题是.limit(1)方法返回的查询不是DocumentReference,它是事务接受的唯一类型。因此我的问题是,如何在 java/kotlin 中实现这样的事务?

我在 this blog post 中使用 admin sdk 看到过类似的东西:

  return trs.get(db.collection('rooms')
    .where('full', '==', false)
    .where('size', '==', size)
    .limit(1));

【问题讨论】:

  • 嗨@Moritz,您找到任何解决方案了吗?

标签: android firebase google-cloud-firestore


【解决方案1】:

我不了解 java/kotlin,但这是我在 Cloud Function 中的 TypeScript/JavaScript 中的做法。

const beerTapIndex: number = parseInt(req.params.beerTapIndex);
const firestore: FirebaseFirestore.Firestore = admin.firestore();

firestore
    .runTransaction((transaction: FirebaseFirestore.Transaction) => {
        const query: FirebaseFirestore.Query = firestore
            .collection('beerOnTap')
            .where('tapIndexOrder', '==', beerTapIndex)
            .limit(1);

        return transaction
            .get(query)
            .then((snapshot: FirebaseFirestore.QuerySnapshot) => {
                const beerTapDoc: FirebaseFirestore.QueryDocumentSnapshot = snapshot.docs[0];
                const beerTapData: FirebaseFirestore.DocumentData = beerTapDoc.data();
                const beerTapRef: FirebaseFirestore.DocumentReference = firestore
                    .collection('beerOnTap')
                    .doc(beerTapDoc.id);

                transaction.update(beerTapRef, {enabled: !beerTapData.enabled});

                return beerTapData;
            })
    })
    .then((beerTapData: FirebaseFirestore.DocumentData) =>  {
        console.log('Transaction successfully committed!', beerTapData);
    })
    .catch((error: Error) => {
        console.log('Transaction failed:', error);
    });

计划 JavaScript 版本

const beerTapIndex = parseInt(req.params.beerTapIndex);
const firestore = admin.firestore();

firestore
    .runTransaction((transaction) => {
        const query = firestore
            .collection('beerOnTap')
            .where('tapIndexOrder', '==', beerTapIndex)
            .limit(1);

        return transaction
            .get(query)
            .then((snapshot) => {
                const beerTapDoc = snapshot.docs[0];
                const beerTapData = beerTapDoc.data();
                const beerTapRef = firestore
                    .collection('beerOnTap')
                    .doc(beerTapDoc.id);

                transaction.update(beerTapRef, {enabled: !beerTapData.enabled});

                return beerTapData;
            })
    })
    .then((beerTapData) =>  {
        console.log('Transaction successfully committed!', beerTapData);
    })
    .catch((error) => {
        console.log('Transaction failed:', error);
    });

在这里找到我的答案:https://medium.com/@feloy/building-a-multi-player-board-game-with-firebase-firestore-functions-part-1-17527c5716c5

异步/等待版本

private async _tapPouringStart(req: express.Request, res: express.Response): Promise<void> {
    const beerTapIndex: number = parseInt(req.params.beerTapIndex);
    const firestore: FirebaseFirestore.Firestore = admin.firestore();

    try {
        await firestore.runTransaction(async (transaction: FirebaseFirestore.Transaction) => {
            const query: FirebaseFirestore.Query = firestore
                .collection('beerOnTap')
                .where('tapIndexOrder', '==', beerTapIndex)
                .limit(1);

            const snapshot: FirebaseFirestore.QuerySnapshot = await transaction.get(query);

            const beerTapDoc: FirebaseFirestore.QueryDocumentSnapshot = snapshot.docs[0];
            const beerTapData: FirebaseFirestore.DocumentData = beerTapDoc.data();
            const beerTapRef: FirebaseFirestore.DocumentReference = firestore
                .collection('beerOnTap')
                .doc(beerTapDoc.id);

            transaction.update(beerTapRef, {enabled: !beerTapData.enabled});

            const beerTapModel = new BeerTapModel({
                ...beerTapData,
                tapId: beerTapDoc.id,
            });

            res.send(beerTapModel);
        });
    } catch (error) {
        res.send(error);
    }
}

【讨论】:

    【解决方案2】:

    经过调查,您似乎无法在 Kotlin/Java 中执行此操作,它不受支持。您将必须创建云功能并执行以下操作:

    exports.executeTransaction = functions.https.onRequest(async (req, res) => {
    
        const db = admin.firestore();
    
        const query = db
            .collection('collection')
            .where('name', '==', 'name')
            .limit(1);
    
        db.runTransaction(transaction => {
            return transaction
                .get(query)
                .then((querySnapshot) => {
                    const gameDocSnapshot = querySnapshot.docs[0];
                    const gameData = gameDocSnapshot.data();
                    transaction.update(gameDocSnapshot.ref, { name: 'change' });
                    return gameData;
                })
        })
            .then((gameData) => {
                res.send(gameData);
                console.log('Transaction successfully committed!', gameData);
            })
            .catch((error) => {
                res.send('Transaction failed:' + error);
                console.log('Transaction failed:', error);
            });
    });
    

    【讨论】:

    • 如果查询范围发生变化,而事务仍在运行且未完成,会发生什么情况。可能会更改查询范围的后续写入操作是否会被拒绝,或者如果查询范围发生更改,事务是否会自行重试?我在官方的 firebase 文档中没有找到任何关于这个边缘案例的内容。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多