def get_message_id(manager, obj): unique_attrs = set() for cls, attr_name in manager.type_registry.get_unique_attrs(type(obj)): value = getattr(obj, attr_name) if value is not None: unique_attrs.add(( get_type_id(cls).lower(), # backwards compat; was index name attr_name, object_to_db_value(value), )) return json.dumps(sorted(unique_attrs))
def get_by_unique_attr(self, cls, attr_name, values): """Bulk load entities from a list of values for a unique attribute Returns: A generator (obj1, obj2, ...) corresponding to the `values` list If any values are missing in the index, the corresponding obj is None """ if not hasattr(cls, attr_name): raise ValueError("{} has no attribute {}".format(cls, attr_name)) registry = self.type_registry for declaring_cls, attr in registry.get_unique_attrs(cls): if attr == attr_name: break else: raise ValueError("{}.{} is not unique".format(cls, attr_name)) type_id = get_type_id(cls) query = "MATCH (n:%(label)s {%(attr)s: {id}}) RETURN n" % { 'label': type_id, 'attr': attr_name, } batch = neo4j.ReadBatch(self._conn) for value in values: db_value = object_to_db_value(value) batch.append_cypher(query, params={'id': db_value}) # When upgrading to py2neo 1.6, consider changing this to batch.stream batch_result = batch.submit() # `batch_result` is a list of either one element lists (for matches) # or empty lists. Unpack to flatten (and hydrate to Kaiso objects) result = (self._convert_value(row) for row in batch_result) return result
def get_match_clause(obj, name, type_registry): """Return node lookup by index for a match clause using unique attributes Args: obj: An object to create an index lookup. name: The name of the object in the query. Returns: A string with an index lookup for a cypher MATCH clause """ if isinstance(obj, PersistableType): value = get_type_id(obj) return '({name}:PersistableType {{id: {value}}})'.format( name=name, value=json.dumps(object_to_db_value(value)), ) if isinstance(obj, Relationship): if obj.start is None or obj.end is None: raise NoUniqueAttributeError( "{} is missing a start or end node".format(obj) ) neo4j_rel_name = get_neo4j_relationship_name(type(obj)) start_name = '{}__start'.format(name) end_name = '{}__end'.format(name) query = """ {start_clause}, {end_clause}, ({start_name})-[{name}:{neo4j_rel_name}]->({end_name}) """.format( name=name, start_clause=get_match_clause( obj.start, start_name, type_registry, ), end_clause=get_match_clause( obj.end, end_name, type_registry, ), start_name=start_name, end_name=end_name, neo4j_rel_name=neo4j_rel_name, ) return dedent(query) match_params = {} label_classes = set() for cls, attr_name in type_registry.get_unique_attrs(type(obj)): value = getattr(obj, attr_name) if value is not None: label_classes.add(cls) match_params[attr_name] = value if not match_params: raise NoUniqueAttributeError( "{} doesn't have any unique attributes".format(obj) ) match_params_string = inline_parameter_map( dict_to_db_values_dict(match_params) ) labels = ':'.join(get_type_id(cls) for cls in label_classes) return '({name}:{labels} {match_params_string})'.format( name=name, labels=labels, attr_name=attr_name, match_params_string=match_params_string, )