def hydrate_(data, inst=None): if isinstance(data, dict): if "self" in data: if "type" in data: return Relationship.hydrate(data["self"], inst=inst, **data) else: return Node.hydrate(data["self"], inst=inst, **data) elif "nodes" in data and "relationships" in data: if "directions" not in data: directions = [] relationships = graph.evaluate( "MATCH ()-[r]->() WHERE id(r) IN {x} RETURN collect(r)", x=[int(uri.rpartition("/")[-1]) for uri in data["relationships"]]) node_uris = data["nodes"] for i, relationship in enumerate(relationships): if remote(relationship.start_node()).uri == node_uris[i]: directions.append("->") else: directions.append("<-") data["directions"] = directions return Path.hydrate(data) else: # from warnings import warn # warn("Map literals returned over the Neo4j REST interface are ambiguous " # "and may be hydrated as graph objects") return data elif is_collection(data): return type(data)(map(hydrate_, data)) else: return data
def match(self, start_node=None, rel_type=None, end_node=None, bidirectional=False, limit=None): """ Match and return all relationships with specific criteria. For example, to find all of Alice's friends:: for rel in graph.match(start_node=alice, rel_type="FRIEND"): print(rel.end_node()["name"]) :param start_node: start node of relationships to match (:const:`None` means any node) :param rel_type: type of relationships to match (:const:`None` means any type) :param end_node: end node of relationships to match (:const:`None` means any node) :param bidirectional: :const:`True` if reversed relationships should also be included :param limit: maximum number of relationships to match (:const:`None` means unlimited) """ clauses = [] returns = [] if start_node is None and end_node is None: clauses.append("MATCH (a)") parameters = {} returns.append("a") returns.append("b") elif end_node is None: clauses.append("MATCH (a) WHERE id(a) = {1}") start_node = cast_node(start_node) if not remote(start_node): raise TypeError("Nodes for relationship match end points must be bound") parameters = {"1": remote(start_node)._id} returns.append("b") elif start_node is None: clauses.append("MATCH (b) WHERE id(b) = {2}") end_node = cast_node(end_node) if not remote(end_node): raise TypeError("Nodes for relationship match end points must be bound") parameters = {"2": remote(end_node)._id} returns.append("a") else: clauses.append("MATCH (a) WHERE id(a) = {1} MATCH (b) WHERE id(b) = {2}") start_node = cast_node(start_node) end_node = cast_node(end_node) if not remote(start_node) or not remote(end_node): raise TypeError("Nodes for relationship match end points must be bound") parameters = {"1": remote(start_node)._id, "2": remote(end_node)._id} if rel_type is None: relationship_detail = "" elif is_collection(rel_type): relationship_detail = ":" + "|:".join(cypher_escape(t) for t in rel_type) else: relationship_detail = ":%s" % cypher_escape(rel_type) if bidirectional: clauses.append("MATCH (a)-[_" + relationship_detail + "]-(b)") else: clauses.append("MATCH (a)-[_" + relationship_detail + "]->(b)") returns.append("_") clauses.append("RETURN %s" % ", ".join(returns)) if limit is not None: clauses.append("LIMIT %d" % limit) cursor = self.run(" ".join(clauses), parameters) while cursor.forward(): record = cursor.current() yield record["_"]
def apply(x): if isinstance(x, dict): inst.update(x) elif is_collection(x): for item in x: apply(item) elif isinstance(x, string): inst.add_label(ustr(x)) else: raise TypeError("Cannot cast %s to Node" % obj.__class__.__name__)
def _hydrated(data): if isinstance(data, dict): if 'self' in data: obj_type = URI(data['self']).path.segments[-2] if obj_type == 'node': return Node(data) elif obj_type == 'relationship': return Rel(data) raise NotImplemented("Don't know how to inflate: " + repr(data)) elif is_collection(data): return type(data)([_hydrated(datum) for datum in data]) else: return data
def write(self, obj): if obj is None: pass elif isinstance(obj, Node): self.write_node(obj) elif isinstance(obj, Rel): self.write_rel(obj) elif isinstance(obj, Relationship): self.write_relationship(obj) elif isinstance(obj, Path): self.write_path(obj) elif isinstance(obj, dict): self.write_mapping(obj) elif is_collection(obj): self.write_collection(obj) else: self.write_value(obj)
def write(self, obj): """ Write any entity, value or collection. """ if obj is None: pass elif isinstance(obj, Node): self.write_node(id(obj), obj.labels, obj.properties) elif isinstance(obj, Rev): self.write_rev(obj.type, obj.properties) elif isinstance(obj, Rel): self.write_rel(obj.type, obj.properties) elif isinstance(obj, Path): self.write_path(obj) elif isinstance(obj, dict): self.write_map(obj) elif is_collection(obj): self.write_list(obj) else: self.write_value(obj)
def dumps(obj, separators=(", ", ": "), ensure_ascii=True): """ Dumps an object as a Cypher expression string. :param obj: :param separators: :return: """ def dump_mapping(obj): buffer = ["{"] link = "" for key, value in obj.items(): buffer.append(link) if " " in key: buffer.append("`") buffer.append(key.replace("`", "``")) buffer.append("`") else: buffer.append(key) buffer.append(separators[1]) buffer.append(dumps(value, separators=separators, ensure_ascii=ensure_ascii)) link = separators[0] buffer.append("}") return "".join(buffer) def dump_collection(obj): buffer = ["["] link = "" for value in obj: buffer.append(link) buffer.append(dumps(value, separators=separators, ensure_ascii=ensure_ascii)) link = separators[0] buffer.append("]") return "".join(buffer) if isinstance(obj, dict): return dump_mapping(obj) elif is_collection(obj): return dump_collection(obj) else: return json.dumps(obj, ensure_ascii=ensure_ascii)
def write(self, obj): """ Write any entity, value or collection. """ if obj is None: pass elif isinstance(obj, Node): self.write_node(obj) elif isinstance(obj, Rel): self.write_rel(obj) elif isinstance(obj, Relationship): self.write_relationship(obj) elif isinstance(obj, Path): self.write_path(obj) elif isinstance(obj, dict): self.write_map(obj) elif is_collection(obj): self.write_list(obj) else: self.write_value(obj)
def presubstitute(statement, parameters): more = True while more: before, opener, key = statement.partition(u"«") if opener: key, closer, after = key.partition(u"»") try: value = parameters.pop(key) except KeyError: raise KeyError("Expected a presubstitution parameter named %r" % key) if is_integer(value): value = ustr(value) elif isinstance(value, tuple) and all(map(is_integer, value)): value = u"%d..%d" % (value[0], value[-1]) elif is_collection(value): value = ":".join(map(cypher_escape, value)) else: value = cypher_escape(value) statement = before + value + after else: more = False return statement, parameters
def presubstitute(statement, parameters): more = True while more: before, opener, key = statement.partition(u"«") if opener: key, closer, after = key.partition(u"»") try: value = parameters.pop(key) except KeyError: raise KeyError( "Expected a presubstitution parameter named %r" % key) if is_integer(value): value = ustr(value) elif isinstance(value, tuple) and all(map(is_integer, value)): value = u"%d..%d" % (value[0], value[-1]) elif is_collection(value): value = ":".join(map(cypher_escape, value)) else: value = cypher_escape(value) statement = before + value + after else: more = False return statement, parameters
def __db_merge__(self, tx, primary_label=None, primary_key=None): from py2neo.database.cypher import cypher_escape, cypher_repr nodes = list(self.nodes()) match_clauses = [] merge_clauses = [] parameters = {} returns = {} for i, node in enumerate(nodes): node_id = "a%d" % i param_id = "x%d" % i remote_node = remote(node) if remote_node: match_clauses.append("MATCH (%s) WHERE id(%s)={%s}" % (node_id, node_id, param_id)) parameters[param_id] = remote_node._id else: merge_label = getattr(node, "__primarylabel__", None) or primary_label if merge_label is None: label_string = "".join(":" + cypher_escape(label) for label in sorted(node.labels())) elif node.labels(): label_string = ":" + cypher_escape(merge_label) else: label_string = "" merge_keys = getattr(node, "__primarykey__", None) or primary_key if merge_keys is None: merge_keys = () elif is_collection(merge_keys): merge_keys = tuple(merge_keys) else: merge_keys = (merge_keys, ) if merge_keys: property_map_string = cypher_repr({ k: v for k, v in dict(node).items() if k in merge_keys }) else: property_map_string = cypher_repr(dict(node)) merge_clauses.append( "MERGE (%s%s %s)" % (node_id, label_string, property_map_string)) if node.labels(): merge_clauses.append( "SET %s%s" % (node_id, "".join(":" + cypher_escape(label) for label in sorted(node.labels())))) if merge_keys: merge_clauses.append("SET %s={%s}" % (node_id, param_id)) parameters[param_id] = dict(node) node._set_remote_pending(tx) returns[node_id] = node clauses = match_clauses + merge_clauses for i, relationship in enumerate(self.relationships()): if not remote(relationship): rel_id = "r%d" % i start_node_id = "a%d" % nodes.index(relationship.start_node()) end_node_id = "a%d" % nodes.index(relationship.end_node()) type_string = cypher_escape(relationship.type()) param_id = "y%d" % i clauses.append("MERGE (%s)-[%s:%s]->(%s) SET %s={%s}" % (start_node_id, rel_id, type_string, end_node_id, rel_id, param_id)) parameters[param_id] = dict(relationship) returns[rel_id] = relationship relationship._set_remote_pending(tx) statement = "\n".join(clauses + ["RETURN %s LIMIT 1" % ", ".join(returns)]) tx.entities.append(returns) tx.run(statement, parameters)
def __db_merge__(self, tx, primary_label=None, primary_key=None): from py2neo.database.cypher import cypher_escape, cypher_repr nodes = list(self.nodes()) match_clauses = [] merge_clauses = [] parameters = {} returns = {} for i, node in enumerate(nodes): node_id = "a%d" % i param_id = "x%d" % i remote_node = remote(node) if remote_node: match_clauses.append("MATCH (%s) WHERE id(%s)={%s}" % (node_id, node_id, param_id)) parameters[param_id] = remote_node._id else: merge_label = getattr(node, "__primarylabel__", None) or primary_label if merge_label is None: label_string = "".join(":" + cypher_escape(label) for label in sorted(node.labels())) elif node.labels(): label_string = ":" + cypher_escape(merge_label) else: label_string = "" merge_keys = getattr(node, "__primarykey__", None) or primary_key if merge_keys is None: merge_keys = () elif is_collection(merge_keys): merge_keys = tuple(merge_keys) else: merge_keys = (merge_keys,) if merge_keys: property_map_string = cypher_repr({k: v for k, v in dict(node).items() if k in merge_keys}) else: property_map_string = cypher_repr(dict(node)) merge_clauses.append("MERGE (%s%s %s)" % (node_id, label_string, property_map_string)) if node.labels(): merge_clauses.append("SET %s%s" % ( node_id, "".join(":" + cypher_escape(label) for label in sorted(node.labels())))) if merge_keys: merge_clauses.append("SET %s={%s}" % (node_id, param_id)) parameters[param_id] = dict(node) node._set_remote_pending(tx) returns[node_id] = node clauses = match_clauses + merge_clauses for i, relationship in enumerate(self.relationships()): if not remote(relationship): rel_id = "r%d" % i start_node_id = "a%d" % nodes.index(relationship.start_node()) end_node_id = "a%d" % nodes.index(relationship.end_node()) type_string = cypher_escape(relationship.type()) param_id = "y%d" % i clauses.append("MERGE (%s)-[%s:%s]->(%s) SET %s={%s}" % (start_node_id, rel_id, type_string, end_node_id, rel_id, param_id)) parameters[param_id] = dict(relationship) returns[rel_id] = relationship relationship._set_remote_pending(tx) statement = "\n".join(clauses + ["RETURN %s LIMIT 1" % ", ".join(returns)]) tx.entities.append(returns) tx.run(statement, parameters)