简体   繁体   English

在 Google Cloud Firestore 的子集合中更新依赖于多个文档的字段时,如何避免竞争条件?

[英]How can I avoid a race condition when updating a field that's dependent multiple documents in a subcollection in Google cloud firestore?

Here's a simplified representation of the data models for my firestore collections这是我的 Firestore collections 数据模型的简化表示

// /posts/{postId}
interface Post {
  id: string; 
  lastCommentedAt: Timestamp | null
}

// /posts/{postId}/comments/{commentId}
interface Comment {
  id: string;
  createdAt: Timestamp;
}

So there's a collection of posts, and within each post is a subcollection of comments.所以有一个帖子集合,每个帖子中都有评论的子集合。

If a post has no comments, the lastCommentedAt field should be null. Otherwise, lastCommmentedAt should match the createdAt value for the most recent comment on that post.如果帖子没有评论,则lastCommentedAt字段应为 null。否则, lastCommmentedAt应与该帖子的最新评论的createdAt值匹配。 The utility of this field is to enable me to query for posts and sort them by ones that have recent comments.该字段的用途是使我能够查询帖子并根据最近评论的帖子对它们进行排序。

I'm having trouble thinking through the scenario of deleting comments.我在考虑删除评论的场景时遇到了麻烦。

When a comment is deleted, the value of lastCommentedAt will become stale if the comment being deleted is the most recent comment on that post, and so will need to be updated.删除评论时,如果被删除的评论是该帖子的最新评论,则lastCommentedAt的值将变得陈旧,因此需要更新。 This is where I'm struggling to come up with a safe solution.这是我努力想出一个安全解决方案的地方。

My first thought was to query for the most recent comment on the post that doesn't match the comment to be deleted, and then do a batched write where I delete the comment and update lastCommentedAt on the post.我的第一个想法是查询与要删除的评论不匹配的帖子的最新评论,然后进行批量写入,删除评论并更新帖子的lastCommentedAt Example Javscript code using the Firebase web SDK here:此处使用 Firebase web SDK 的示例 Javscript 代码:

async function deleteComment(post, comment) {
  const batch = writeBatch(firestore);
  const postRef = doc("posts", post.id);
  const commentRef = doc("posts", post.id, "comments", comment.id);
  const commentsCollection = collection("posts", post.id, "comments");

  const recentCommentSnapshot = await getDocs(
    query(
      commentsCollection,
      where("id", "!=", comment.id),
      orderBy("createdAt", "desc"),
      limit(1)
    )
  );

  let lastCommentedAt = null;
  if (recentCommentSnapshot.docs.length > 0) {
    lastCommentedAt = recentCommentSnapshot.docs[0].data().createdAt;
  }

  batch.delete(commentRef);
  batch.update(postRef, { lastCommentedAt });
  await batch.commit();
}

However, I believe the code above would be vulnerable to a race condition if new comments are created after the query but before the writes, or if the recent comment was deleted after the query but before the writes.但是,我相信如果在查询之后但在写入之前创建了新评论,或者如果在查询之后但在写入之前删除了最近的评论,那么上面的代码将容易受到竞争条件的影响。

So I think I need a transaction, but you can't query for documents in a transaction, so I'm not really sure where to go from here.所以我想我需要一个交易,但你不能在交易中查询文件,所以我不太确定从这里到 go 的哪里。

You could use Firestore Getting real-time updates which listens to changes between snapshots.您可以使用Firestore Getting real-time updates来监听快照之间的变化。 Here's an example from the documentation:这是文档中的示例:

import { collection, query, where, onSnapshot } from "firebase/firestore";

const q = query(collection(db, "cities"), where("state", "==", "CA"));
const unsubscribe = onSnapshot(q, (snapshot) => {
  snapshot.docChanges().forEach((change) => {
    if (change.type === "added") {
        console.log("New city: ", change.doc.data());
    }
    if (change.type === "modified") {
        console.log("Modified city: ", change.doc.data());
    }
    if (change.type === "removed") {
        console.log("Removed city: ", change.doc.data());
    }
  });
});

In here you can listen to any changes to your documents.在这里,您可以收听对文档的任何更改。


Check the code that I created which is triggered when the document is deleted from firestore and automatically update the posts.lastCommentedAt :检查我创建的代码,该代码在从 firestore 中删除文档时触发并自动更新posts.lastCommentedAt

    var latestCreatedAt = null;
    const q = query(
        collection(db, "posts", "post.id", "comments")
    );
    const recentCommentQuery = query(
        collection(db, "posts", "post.id", "comments"),
        orderBy("createdAt", "desc"),
        limit(1)
    );
    const postsRef = query(collection(db, "posts"));
    const postRef = doc(db, "posts", "post.id"); 

    async function deletedComment (createdAt) {
        const querySnapshot = await getDocs(recentCommentQuery);

        querySnapshot.forEach((doc) => {
            latestCreatedAt = doc.data().createdAt.toDate();
        });

        await updateDoc(postRef, {
            lastCommentedAt: latestCreatedAt
        })
    }

    const unsubscribe = onSnapshot(q, (snapshot, querySnapshot) => {
        snapshot.docChanges().forEach((change) => {
            if (change.type === "removed") {
                deletedComment(change.doc.data().createdAt.toDate());
            }
        });
    });

You can even add a condition when adding or modifying documents that will also trigger to update the posts.lastCommentedAt .您甚至可以在添加或修改也会触发更新posts.lastCommentedAt的文档时添加条件

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Cloud Firestore 如何查找包含特定文档 ID 的子集合的所有文档? - Cloud Firestore how to find all documents with subcollection containing specific document ID? 如何避免 Firebase 云函数可能出现的竞争条件? - How do you avoid a possible race condition with firebase cloud functions? 如何避免 firestore 中的子集合拒绝权限? - how to avoid permission denied with a subcollection in firestore? 如何在 Cloud Firestore 查询中加入多个文档? - How to join multiple documents in a Cloud Firestore query? 如何从 Cloud Firestore 中删除多个文档? - How to delete multiple documents from Cloud Firestore? 我可以使用 Firestore 检索具有相同名称的不同集合中相同子集合的文档吗? - Can I retrieve documents of same subcollection in different collections with the same name, using Firestore? 如何使用帮助文档 ID 更新 Cloud firestore 文档字段? - How can i Update Cloud firestore document Field with the help DocumentID? 如何检索作为文档放置在 Firebase Cloud Firestore 中的 UID - How do I retrieve the UID's that I have placed as documents in my Firebase Cloud Firestore 如何在 firestore 中获取子集合路径 - how do I get subcollection path in firestore 如何根据 Cloud Firestore 中的主集合字段和子集合字段查询子集合中的文档? - How to query documents from a sub-collection, based on a main collection's field and a sub-collection's fields in cloud firestore?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM