title: Best practice way to update a parent document field that is computed from sub collection documents in google cloud firestore
body: <p>Here's an abbreviated version of the data model for my firestore collections:</p>
  <pre class="lang-js prettyprint-override"><code>// /posts/{postId}
  interface Post {
    id: string;
    lastCommentedAt: Timestamp | null
  }
  
  // /posts/{postId}/comments/{commentId}
  interface Comment {
    id: string;
    createdAt: Timestamp;
  }
  </code></pre>
  <p>So I have a collection of posts, and within each post is a subcollection of comments.</p>
  <p>I want to do the following:</p>
  <ul>
  <li>When a comment is created, update the <code>lastCommentedAt</code> field of the parent Post document with the comment's <code>createdAt</code> value</li>
  <li>When a comment is deleted, the parent Post's <code>lastCommentedAt</code> field may no longer be valid so we need to get all of the Post's comments and get the most recent comment to update <code>lastCommentedAt</code>.</li>
  </ul>
  <p>I see a couple ways to do this:</p>
  <ol>
  <li>In my client code, I can do the above logic inside of functions like <code>createComment(post, comment)</code>, and <code>deleteComment(post, comment)</code>
  <ul>
  <li>Especially in the case of deleting it seems like it is not ideal to require the client to fetch all comments for the post and iterate through them just to delete one</li>
  <li>Would I need to use transactions for this since someone could be deleting a comment at the same time someone was creating a new one?</li>
  </ul>
  </li>
  <li>In my cloud functions I could create triggers on <code>/posts/{postId}/comments/{commentId}</code> for create and delete and do this logic on the backend
  <ul>
  <li>Is there risk of race conditions here as well? Again maybe I should use a transaction?</li>
  </ul>
  </li>
  </ol>
  <p>The use case for this <code>lastCommentedAt</code> field is that I want to be able to query for posts and sort them by the ones that have recent comments.</p>
  <hr />
  <p>Edit: possible implementation for <code>deleteComment</code> using a batched write. Is it actually safe to do a query for documents before the writes though?</p>
  <pre class="lang-js prettyprint-override"><code>async function deleteComment(post, comment) {
    const batch = writeBatch(firestore);
    const postRef = doc(&quot;posts&quot;, post.id);
    const commentRef = doc(&quot;posts&quot;, post.id, &quot;comments&quot;, comment.id);
    const commentsCollection = collection(&quot;posts&quot;, post.id, &quot;comments&quot;);
  
    const recentCommentSnapshot = await getDocs(
      query(
        commentsCollection,
        where(&quot;id&quot;, &quot;!=&quot;, comment.id),
        orderBy(&quot;createdAt&quot;, &quot;desc&quot;),
        limit(1)
      )
    );
  
    let lastCommentedAt = null;
    if (recentCommentSnapshot.docs.length &gt; 0) {
      lastCommentedAt = recentCommentSnapshot.docs[0].data().createdAt;
    }
  
    batch.delete(commentRef);
    batch.update(postRef, { lastCommentedAt });
    await batch.commit();
  }
  </code></pre>