0

I'm adding documents to a subcollection called workReports while I'm subscribed to it.

Method for query:

getWorkReportsByProjectId(projectId: string): Observable<ProjectWorkReport[]> {

  return this.db
    .collection("projects")
    .doc(projectId)
    .collection<ProjectWorkReport>(
      "workReports", 
      ref => ref.where("invalid", "==", false).orderBy("createdAt", "desc")
    )
    .valueChanges({ idField: "id" });
}

Method for creating (notice createdAt):

createProjectWorkReport(projectId: string, workReport: ProjectWorkReport): Promise<DocumentReference> {
  return this.db
    .collection("projects")
    .doc(projectId)
    .collection<ProjectWorkReport>("workReports")
    .add({ ...workReport, createdAt: firebase.firestore.FieldValue.serverTimestamp() });
}

My subscription (I'm only loging the result):

this.projectsService.getWorkReportsByProjectId(projectId)
  .pipe(takeUntil(this.destroyed$))
  .subscribe((reports) => {
    console.log(reports);
  });

This is what's being logged. The element at position 0 is the new one because of orderBy("createdAt", "desc"), but its createAt property is null, but if make changes on the DB or make the query again, the new results have that createdAt normally (and null in the new one if I make another creation).

enter image description here

My guess is that Firestore takes the timestamp sentinel into account independently from the creation of the document, and the subscription fires before the timestamp is set.

Am I doing something wrong? And if my guess is right, what could I do as a fix?

MarksASP
  • 494
  • 6
  • 15

1 Answers1

1

This behavior might be explained by this article. In summary, it is similar to what you suspected. serverTimestamps are not instantly defined in created documents, they are instead resolved by Firebase servers with a bit of delay after the document is created. This leads to very small differences between document creation and the value you see on the timestamp, in addition to seeing null values when there is a listener involved:

Then, when set() is called, the listener fires immediately, before the write hits the server. The SDK doesn’t deliver the token value to the listener. Instead, it simply delivers null (for now), because the final value isn’t known (yet).

After the timestamp is resolved, it’s when you start seeing it in your web console. There are two workarounds for this behavior, one of them is to use the snapshotOptions property to provide an instant estimate of the timestamp. The other one is to use the hasPendingWrites property, which lets you handle what to do with documents that are still pending to be resolved.

The same article goes in depth about how server timestamps work, and it has sample code for examples.

ErnestoC
  • 2,660
  • 1
  • 6
  • 19