2

Lets say I have two top level collection with users and stories. Now everytime a document from user gets update (only the values username or photoUrl), I want to update these properties on a document from the story collection.

One users doc could look like this (shortened):

{
    username: 'blubber',
    photoUrl: 'my/photo/path',
    uid: 'usersUniqueId'
}

The story doc could look like this (shortened):

{
    username: 'blubber',
    photoUrl: 'my/photo/path',
    uid: 'usersUniqueId',
    sid: 'storiesUniqueId,
    content: '...'
}

Now on the cloud functions part. I dont now how to query all documents from the stories collection that contains the users id. The code from now looks like this:

export const onUpdateUser = functions.firestore.document('/users/{userId}')
    .onUpdate((change, context) => {
    const before = change.before.data();
    const after = change.after.data();

    if(before.username !== after.username || before.photoURL !== after.photoURL) {
        return admin.firestore().collection('/stories/')
               .where(before.uid, '==', ***fetch the comparison***)
        // do something
    } else {
        return null;
    }
});

Can anyone help me out on this?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807

2 Answers2

1

Updated the code below. Pretty much had it. I added async await as it will make the code cleaner to follow, you would want to add in error handling etc.

export const onUpdateUser = functions.firestore.document('/users/{userId}')
    .onUpdate(async (change, context) => {
    const before = change.before.data();
    const after = change.after.data();

    if (before.username !== after.username || before.photoURL !== after.photoURL) {
        return admin.firestore().collection('/stories/')
           .where('uid', '==', before.uid).get().then(  
               result => {
                   if (result.size > 0) {
                       result.forEach(async doc => {
                           await doc.ref.update({
                               username: after.username,
                               photoUrl: after.photoUrl
                           })
                        })
                   }
                   return;
               }
           )
    } else {
        return null;
    }
});
Jack Woodward
  • 991
  • 5
  • 9
0

I believe, this is what you are trying to achieve.

I just separated out the function call from its reference in the .onUpdate call. Since you haven't explicitly stated, I have also assumed that you are going to update multiple docs in the stories collection and for that, we are using batch.

const onUpdateUser = functions
  .firestore
  .document('/users/{userID}')
  .onUpdate(myFunc);

const myFunc = (change, context) => {
  const before = change.before.data();
  const after = change.after.data();

  // Nothing changed, so exiting the cloud function early.
  if (before.username === after.username
    && before.photoURL === after.photoURL) {
    return Promise.resolve();
  }

  return admin
    .firestore()
    .collection('stories')
    .where('uid', '==', before.uid)
    .get()
    .then((docs) => {
      const batch = admin.firestore().batch();

      docs.forEach((doc) => {
        batch.set(doc.ref, {
          username: after.username,
          photoURL: after.photoURL,
        }, {
            // Merges the fields rather than replacing the 
            // whole document data
            merge: true,
        });
      });

      return batch.commit();
    })
    .catch(console.error);
};
Utkarsh Bhatt
  • 1,536
  • 2
  • 14
  • 23
  • Seems to work perfectly fine, like jacks approach! Thanks! One more question: returning Promise.resolve() or returning null exits the function the same, right? –  Nov 14 '18 at 11:15
  • @Fantasia Cloud Functions running in the background expect a Promise in return. If you return null, you'll probably get the error `Function returned undefined, expected Promise or value` on each invocation. – Utkarsh Bhatt Nov 14 '18 at 11:55
  • Strange because doug stevenson, google advocat also recommended to return null if a function should finish. And I guess null is assignable on any type. src: https://www.youtube.com/watch?v=Bdm7QNwSHOg&t=120s –  Nov 14 '18 at 12:00
  • 1
    @Fantasia. Looks like I missed that. With Doug's advice, I think that returning a `Promise` or `null` should work, then. – Utkarsh Bhatt Nov 15 '18 at 06:31