'Apply an update operation to a document.'

def apply_update(self, doc, update_spec):

    def _convert_or_raise(container, key):
        if isinstance(container, dict):
            return key
        elif isinstance(container, list):
            return int(key)
        else:
            raise ValueError
    def _retrieve_path(container, path, create=False):
        looking_at = container
        for part in path:
            if isinstance(looking_at, dict):
                if (create and (not (part in looking_at))):
                    looking_at[part] = {}
                looking_at = looking_at[part]
            elif isinstance(looking_at, list):
                index = int(part)
                if (create and (len(looking_at) <= index)):
                    looking_at.extend(([None] * (index - len(looking_at))))
                    looking_at.append({})
                looking_at = looking_at[index]
            else:
                raise ValueError
        return looking_at
    def _set_field(doc, to_set, value):
        if ('.' in to_set):
            path = to_set.split('.')
            where = _retrieve_path(doc, path[:(-1)], create=True)
            index = _convert_or_raise(where, path[(-1)])
            wl = len(where)
            if (isinstance(where, list) and (index >= wl)):
                where.extend(([None] * ((index + 1) - wl)))
            where[index] = value
        else:
            doc[to_set] = value
    def _unset_field(doc, to_unset):
        try:
            if ('.' in to_unset):
                path = to_unset.split('.')
                where = _retrieve_path(doc, path[:(-1)])
                index_or_key = _convert_or_raise(where, path[(-1)])
                if isinstance(where, list):
                    where[index_or_key] = None
                else:
                    del where[index_or_key]
            else:
                del doc[to_unset]
        except (KeyError, IndexError, ValueError):
            source_version = get_mininum_mongodb_version()
            if ((source_version is None) or source_version.at_least(2, 6)):
                raise
            LOG.warning(('Could    not    unset    field    %r    from    document    %r.    This    may    be    normal    when    replicating    from    MongoDB    2.4    or    the    destination    could    be    out    of    sync.' % (to_unset, doc)))
    if ((not ('$set' in update_spec)) and (not ('$unset' in update_spec))):
        return update_spec
    else:
        try:
            for to_set in update_spec.get('$set', []):
                value = update_spec['$set'][to_set]
                _set_field(doc, to_set, value)
            for to_unset in update_spec.get('$unset', []):
                _unset_field(doc, to_unset)
        except (KeyError, ValueError, AttributeError, IndexError):
            (exc_t, exc_v, exc_tb) = sys.exc_info()
            reraise(UpdateDoesNotApply, ('Cannot    apply    update    %r    to    %r' % (update_spec, doc)), exc_tb)
        return doc
