OReilly
OReilly

Reputation: 143

How to push items from orderBy method to the Firestore database in the same order?

I have a collection of user posts with documents that contain unique id's. In each document is a timestamp created by the server. To visualize the structure looks like this:

userPosts (collection) -> uniqueID (document) -> timeStamp (field)

There are around 100 or so uniqueID's with different timeStamp values. I have queried and ordered my data by the timeStamp field, using .orderBy() and have the ordered results and wish to write to a document in another collection called "mostRecent" with roughly this structure:

mostRecent (collection) -> recentTwenty(document) -> posts (array of maps)

await db.collection(userPosts)
.orderBy("timeStamp").limit(20).get()
    .then((querySnapshot) => {
            querySnapshot.docs.map((item,key) => {
               console.log(item.data())
               db.collection("mostRecent").doc("recentTwenty")
                 .update(
                    {
                      posts: FieldValue.arrayUnion(item.data())
                    },
                    { merge: true }
                  )
            }
     });

When console.log(item.data) the data is correctly ordered by the timestamp field. However, when I call .update() with the correctly ordered items in item.data() to the recentTwenty doc the order is completely out of whack. I would love to understand what is going on. Thanks in advance for whoever helps me out!

Upvotes: 0

Views: 207

Answers (2)

LeadDreamer
LeadDreamer

Reputation: 3499

You are NOT waiting for each .update() to complete - it IS an asynchronous operation. Whether it works at ALL also depends on what code you're running outside of what you show - I see no proof that the operations are guaranteed to be competed. The OUTER await doesn't guarantee the synchronicity of the INNER loop.

Firestore arrays ARE ordered by the order they were inserted (if that's how you create them) -but the order the DATABASE will see the operations, as you have shown it here, will depend on Network conditions, etc - you've set up a race condition. At a MINIMUM, you need to await the result of EACH .update() operation to get them in sequence.

A bigger question, though, is WHY use individual .update() at all? Just collect your information using the .map, and write the array in a single operation:

await db.collection(userPosts)
.orderBy("timeStamp").limit(20).get()
    .then((querySnapshot) => {
            //the following .map() is SYNCHRONOUS
            let result_array = querySnapshot.docs.map((item,key) => {
               console.log(item.data())
               return item.data();
            });
            //return the PROMISE from the .set(), for the OUTER await
            return db.collection("mostRecent").doc("recentTwenty")
                 .set(
                    {
                      posts: result_array;
                    },
                    { merge: true }
                  )
     });

a single write to the database (which is less expensive)

Upvotes: 1

Your problem is that the map runs over a callback function and in your case it is a function with an asynchronous .update() function call. Therefore, you will be never be able to be 100% sure that this will be in order. For that you need the execute them in a synchronous call in for loop.

  return new Promise((resolve, reject) => {
    db.collection("mostRecent").doc("recentTwenty")
      .update(
        {
          posts: FieldValue.arrayUnion(item.data())
        },
        { merge: true }
      )
      .then((result) => resolve(result))
      .catch((err) => reject(err))
    ;
  });
};

await db.collection(userPosts)
  .orderBy("timeStamp").limit(20).get()
  .then(async (querySnapshot) => {
    for(let i = 0; i < querySnapshot.docs.length; i++) {
      await updateItem(querySnapshot.docs[i]);
    }
  })
;

You could also find a way to bulk update all the querySnapshot.docs in a single operation, which would have a better performance. However, I don't know firebase very well so I can't help you on this.

Upvotes: 0

Related Questions