def _get_update_mask(self, allow_empty_mask=False): mask_paths = [] for field_path in self.top_level_paths: if field_path not in self.transform_paths: mask_paths.append(field_path.to_api_repr()) else: prefix = FieldPath(*field_path.parts[:-1]) if prefix.parts: mask_paths.append(prefix.to_api_repr()) return common_pb2.DocumentMask(field_paths=mask_paths)
def __init__(self, document_data): self.document_data = document_data self.field_paths = [] self.deleted_fields = [] self.server_timestamps = [] self.array_removes = {} self.array_unions = {} self.set_fields = {} self.empty_document = False prefix_path = FieldPath() iterator = self._get_document_iterator(prefix_path) for field_path, value in iterator: if field_path == prefix_path and value is _EmptyDict: self.empty_document = True elif value is transforms.DELETE_FIELD: self.deleted_fields.append(field_path) elif value is transforms.SERVER_TIMESTAMP: self.server_timestamps.append(field_path) elif isinstance(value, transforms.ArrayRemove): self.array_removes[field_path] = value.values elif isinstance(value, transforms.ArrayUnion): self.array_unions[field_path] = value.values else: self.field_paths.append(field_path) set_field_value(self.set_fields, field_path, value)
def extract_fields(document_data, prefix_path, expand_dots=False): """Do depth-first walk of tree, yielding field_path, value""" if not document_data: yield prefix_path, _EmptyDict else: for key, value in sorted(six.iteritems(document_data)): if expand_dots: sub_key = FieldPath.from_string(key) else: sub_key = FieldPath(key) field_path = FieldPath(*(prefix_path.parts + sub_key.parts)) if isinstance(value, dict): for s_path, s_value in extract_fields(value, field_path): yield s_path, s_value else: yield field_path, value
def has_updates(self): # for whatever reason, the conformance tests want to see the parent # of nested transform paths in the update mask # (see set-st-merge-nonleaf-alone.textproto) update_paths = set(self.data_merge) for transform_path in self.transform_paths: if len(transform_path.parts) > 1: parent_fp = FieldPath(*transform_path.parts[:-1]) update_paths.add(parent_fp) return bool(update_paths)
def __init__(self, document_data): super(DocumentExtractorForUpdate, self).__init__(document_data) self.top_level_paths = sorted( [FieldPath.from_string(key) for key in document_data]) tops = set(self.top_level_paths) for top_level_path in self.top_level_paths: for ancestor in top_level_path.lineage(): if ancestor in tops: raise ValueError("Conflicting field path: {}, {}".format( top_level_path, ancestor)) for field_path in self.deleted_fields: if field_path not in tops: raise ValueError( "Cannot update with nest delete: {}".format(field_path))
def __init__(self, document_data): super(DocumentExtractorForUpdate, self).__init__(document_data) self.top_level_paths = sorted( [FieldPath.from_string(key) for key in document_data] ) tops = set(self.top_level_paths) for top_level_path in self.top_level_paths: for ancestor in top_level_path.lineage(): if ancestor in tops: raise ValueError( "Conflicting field path: {}, {}".format( top_level_path, ancestor ) ) for field_path in self.deleted_fields: if field_path not in tops: raise ValueError( "Cannot update with nest delete: {}".format(field_path) )
def _construct_merge_paths(self, merge): for merge_field in merge: if isinstance(merge_field, FieldPath): yield merge_field else: yield FieldPath(*parse_field_path(merge_field))