0

Currently, I am querying for objects in the database to update. A snippet of the function that does so is

 return getPostsForDate.get().then(snapshot => {
        const updates = {}
        var counter = 0
        const batch = admin.firestore().batch()
        snapshot.forEach((doc) => {

            var key = doc.id
            return admin.database().ref('/convoID/' + key).once('value', (snapshot) => {
                if (snapshot.exists()) {
                    const convoIDCollection = snapshot.val()
                    for (var child in convoIDCollection) {

                        console.log(child)
                        updates["conversations/" + child] = null
                        updates["messages/"+ child] = null
                        updates["convoID/"+ child] = null
                    }
                }
                updates["/convoID/" + key] = null
                updates["/reveals/" + key] = null
                updates["/postDetails/" + key] = null
                const postFireStoreRef = admin.firestore().collection('posts').doc(key)
                const posterRef = admin.firestore().collection('posters').doc(key)
                batch.delete(postFireStoreRef)
                batch.delete(posterRef)
                counter++
                console.log(counter)
             })

        })
        if (counter > 0) {
            console.log("at the deletion point")
              return Promise.all[admin.database().ref().update(updates), batch.commit()] 
        }
        else {
            console.log("null")
            return null
        }
})

Essentially, after the firestore queries for the posts, additional details are received from the realtime database and added to an array. Finally, I commit all these updates through a promise. However, the function that returns the updates is never reached - in order to ensure that updates are needed to the database, I have a counter that counts the number of updates. If it is greater than 0, I return

 return Promise.all[admin.database().ref().update(updates), batch.commit()]

However, it seems like the return function is executed before the additional details are received, as it keeps on returning "null" to the console log which should only happen if the counter is less than 0.

Essentially, how do I wait for the data to be queried before executing the updates?

Update

    return getPostsForDate.get().then(snapshot => {
    const updates = {} 
    const promises = []
    var counter = 0
    const batch = admin.firestore().batch()
    snapshot.forEach((doc) => {
 promises.push (
        admin.database().ref('/convoID/' + key).once('value', (snapshot) => {
            if (snapshot.exists()) {
                const convoIDCollection = snapshot.val()
                for (var child in convoIDCollection) {
                    updates["conversations/" + child] = null
                    updates["messages/"+ child] = null
                    updates["convoID/"+ child] = null
                }
            }
            updates["/convoID/" + key] = null
            updates["/reveals/" + key] = null
            updates["/postDetails/" + key] = null
            const postFireStoreRef = admin.firestore().collection('posts').doc(key)
            const posterRef = admin.firestore().collection('posters').doc(key)
            batch.delete(postFireStoreRef)
            batch.delete(posterRef)
            counter++
         })
        )
    })
    Promise.all(promises).then(() => {
    if (counter > 0) {
        console.log("at the deletion")
          return Promise.all[admin.database().ref().update(updates), batch.commit()] 
    }
    else {
        console.log("null")
        return null
    }
})
})
})
Raim Khalil
  • 387
  • 3
  • 19

1 Answers1

1

admin.database().ref('/convoID/' + key).once(...) is asynchronous and returns a promise, but your code isn't using those promises to wait for each query to complete. This means your code is moving on to the if/else immediately after the snapshot iteration, but before counter is ever updated, or the batches are added.

You'll need to restructure your code to collect all those promises from once() into an array, use Promise.all() to wait for all those promises to resolve, then commit the batch.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • appreciate the fast response. How exactly would I collect the promises? Would I make it into a .then(snapshot) and return all the batch deletes? Something like `.once('value').then(snapshot => { ` and combine that with `return [updates]`? – Raim Khalil Jul 28 '18 at 03:27
  • 1
    https://stackoverflow.com/questions/38362231/how-to-use-promise-in-foreach-loop-of-array-to-populate-an-object/38362312 – Doug Stevenson Jul 28 '18 at 03:32
  • The link was extremely helpful; however, I tried implementing it as in my answer update and I am now receiving `Each promise should return a value` and `Expected catch or return` And `Avoid nesting promises` I created the array of promises; however, I need to return them. How can I return them while also returning from the `return getPostsForDate.get().then(snapshot => {` Sorry if the solution is obvious, appreciate your time. – Raim Khalil Jul 28 '18 at 03:40
  • sorry, that comment may have been too broad. Instead, would I need to do use promise.all for the conversations or for each document? As you can see, I do `snapshot.forEach(doc)` AND `for (var child in convoIDCollection) {` – Raim Khalil Jul 28 '18 at 04:06
  • I'm not sure how it would be helpful in the latter, as it's doing no async work. – Doug Stevenson Jul 28 '18 at 04:13
  • So something along the lines of `promises.push(admin.database().ref('/convoID/' + key).once('value', (snapshot) => {` ? In the end, would I return `promises.all(promises).then(() => if (counter > 0) { console.log("at the deletion") return Promise.all[admin.database().ref().update(updates), batch.commit()] } else { console.log("null") return null } );` ? – Raim Khalil Jul 28 '18 at 04:15
  • Something like that. – Doug Stevenson Jul 28 '18 at 04:16
  • one last thing, inside returning the promises it won't let me do my if statement to see if the counter is greater than zero as it says unexpected token if. Can I not do an if statement inside the .then(()? What should I return for the ` return getPostsForDate.get().then(snapshot => {`? I am pretty confused, do I return the promise return for the getPostsForDate? Because that needs something returned, too. – Raim Khalil Jul 28 '18 at 04:17
  • Sorry, it sounds like you now have a different problem than the one you started out with and I'm having a hard time picturing it from just your text. Please try asking another question describing your new problem. – Doug Stevenson Jul 28 '18 at 04:21