Example #1
0
 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
Example #2
0
    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["_"]
Example #3
0
 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__)
Example #4
0
 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__)
Example #5
0
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
Example #6
0
 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)
Example #7
0
 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)
Example #8
0
 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)
Example #9
0
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)
Example #10
0
 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)
Example #11
0
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)
Example #12
0
 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)
Example #13
0
 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
Example #15
0
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
Example #16
0
 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)
Example #17
0
 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)