Cloud Functions Listen to Firestore Map Field Changes (Python)

June 27, 2019

Refer to Cloud Functions Listen to Firestore Triggers for setup, deployment and observe normal fields.

This article focus on detecting changes to map fields named likes with the following structure.

  • Observe create/update/delete of likes
  • Observe addition and removal from likes
  • Assume likes[KEY] will not be modified upon creation, but might be deleted
likes:
    key01: value
    key02: value
import logging

log = logging.getLogger(__name__)

def observe_user_activity(data, context):
    if not data['oldValue'] and data['value']:
        log.info('create document')
        if 'likes' in data['value']['fields']:
            likes = data['value']['fields']['likes']['mapValue']['fields']
            for key, value in likes.items():
                log.info(f"{key}={value}")
    elif data['oldValue'] and not data['value']:
        log.info('delete document')
        if 'likes' in data['oldValue']['fields']:
            likes = data['oldValue']['fields']['likes']['mapValue']['fields']
            for key, value in likes.items():
                log.info(f"{key}={value}")
    elif data['updateMask']:
        for _field in data['updateMask']['fieldPaths']:
            if _field == 'likes':
                if _field not in data['oldValue']['fields'] and _field in data['value']['fields']:
                    log.info(f'new field: {_field}')
                    if _field == 'likes':
                        likes = data['value']['fields']['likes']['mapValue']['fields']
                        for key, value in likes.items():
                            log.info(f"{key}={value}")
                elif _field in data['oldValue']['fields'] and _field not in data['value']['fields']:
                    log.info(f'delete field: {_field}')
                    if _field == 'likes':
                        likes = data['oldValue']['fields']['likes']['mapValue']['fields']
                        for key, value in likes.items():
                            log.info(f"{key}={value}")
                else:
                    log.info(f'modified field: {_field}')
            elif _field.startswith('likes.'):
                key = _field[6:]

                exist_in_old_value = True
                try:
                    old_value = data['oldValue']['fields']['likes']['mapValue']['fields'][key]
                except KeyError:
                    exist_in_old_value = False


                exist_in_value = True
                try:
                    value = data['value']['fields']['likes']['mapValue']['fields'][key]
                except KeyError:
                    exist_in_value = False

                if not exist_in_old_value and exist_in_value:
                    log.info(f'new field: {_field}')
                     log.info(f"{key}={value}")
                elif exist_in_old_value and not exist_in_value:
                    log.info(f'delete field: {_field}')
                     log.info(f"{key}={old_value}")
                else:
                    log.info(f'modified field: {_field}')
            else:
                log.info(f'modified field: {_field}')
This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.