def drop_uniqueness_constraint(self, label, property_key):
     """ Remove the node uniqueness constraint for a given label and
     property key.
     """
     cypher = "DROP CONSTRAINT ON (_:{}) ASSERT _.{} IS UNIQUE".format(
         cypher_escape(label), cypher_escape(property_key))
     self.graph.run(cypher).close()
 def __init__(self, subject, node, direction, relationship_type,
              related_class):
     assert isinstance(direction, int) and not isinstance(direction, bool)
     self.subject = subject
     self.node = node
     self.relationship_type = relationship_type
     self.related_class = related_class
     self.__related_objects = None
     if direction > 0:
         self.__match_args = {
             "nodes": (self.node, None),
             "r_type": relationship_type
         }
         self.__start_node = False
         self.__end_node = True
         self.__relationship_pattern = "(a)-[_:%s]->(b)" % cypher_escape(
             relationship_type)
     elif direction < 0:
         self.__match_args = {
             "nodes": (None, self.node),
             "r_type": relationship_type
         }
         self.__start_node = True
         self.__end_node = False
         self.__relationship_pattern = "(a)<-[_:%s]-(b)" % cypher_escape(
             relationship_type)
     else:
         self.__match_args = {
             "nodes": {self.node, None},
             "r_type": relationship_type
         }
         self.__start_node = True
         self.__end_node = True
         self.__relationship_pattern = "(a)-[_:%s]-(b)" % cypher_escape(
             relationship_type)
Exemple #3
0
def merge_nodes(tx, data, primary_label, primary_key, labels=None, keys=None):
    """ Merge nodes from an iterable sequence of raw node data.

    :param tx:
    :param data: list of properties
    :param primary_label:
    :param primary_key:
    :param labels:
    :param keys:
    :returns:
    """
    if keys:
        # data is list of lists
        primary_index = keys.index(primary_key)
        fields = [Literal("r[%d]" % i) for i in range(len(keys))]
        set_rhs = cypher_repr(dict(zip(keys, fields)))
    else:
        # data is list of dicts
        primary_index = primary_key
        set_rhs = "r"
    cypher = ("UNWIND $data AS r "
              "MERGE (_:%s {%s:r[$key]}) "
              "SET _%s "
              "SET _ = %s "
              "RETURN id(_)" %
              (cypher_escape(primary_label), cypher_escape(primary_key),
               _label_string(labels, primary_label), set_rhs))
    for record in tx.run(cypher, data=data, key=primary_index):
        yield record[0]
Exemple #4
0
def _unwind_merge_query(data,
                        merge_key,
                        labels=None,
                        keys=None,
                        return_expr=None):
    pl = merge_key[0]
    pk = merge_key[1:]
    if keys:
        # data is list of lists
        indexes = [keys.index(key) for key in pk]
        fields = [Literal("r[%d]" % i) for i in range(len(keys))]
        set_rhs = cypher_repr(dict(zip(keys, fields)))
    else:
        # data is list of dicts
        indexes = list(pk)
        set_rhs = "r"
    merge_key_string = ", ".join("{}:r[$ix[{}]]".format(cypher_escape(key), i)
                                 for i, key in enumerate(pk))
    merge_pattern = "(_:%s {%s})" % (cypher_escape(pl), merge_key_string)
    cypher = ("UNWIND $data AS r "
              "MERGE %s "
              "SET _%s "
              "SET _ = %s " %
              (merge_pattern, _label_string(pl, *(labels or ())), set_rhs))
    if return_expr:
        cypher += " RETURN %s" % return_expr
    return cypher, {"data": data, "ix": indexes}
Exemple #5
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["_"]
Exemple #6
0
 def __repr__(self):
     from py2neo.cypher import cypher_escape
     if self.__selected:
         return "".join(":%s" % cypher_escape(e, **self.__kwargs)
                        for e in self.__selected if e in self.__elements)
     else:
         return "".join(":%s" % cypher_escape(e, **self.__kwargs)
                        for e in sorted(self.__elements))
Exemple #7
0
def _merge_clause(value, merge_key, keys, prefix, suffix):
    pl, pk = _unpack_merge_key(merge_key)
    if keys is None:
        ix = list(pk)
    else:
        ix = [keys.index(key) for key in pk]
    merge_key_string = ", ".join("%s:%s[%s]" % (cypher_escape(key), value, cypher_repr(ix[i]))
                                 for i, key in enumerate(pk))
    if merge_key_string:
        return "MERGE %s_:%s {%s}%s" % (prefix, cypher_escape(pl), merge_key_string, suffix)
    else:
        return "MERGE %s_:%s%s" % (prefix, cypher_escape(pl), suffix)
    def create_uniqueness_constraint(self, label, property_key):
        """ Create a node uniqueness constraint for a given label and property
        key.

        While indexes support the use of composite keys, unique constraints may
        only be tied to a single property key.
        """
        cypher = "CREATE CONSTRAINT ON (_:{}) ASSERT _.{} IS UNIQUE".format(
            cypher_escape(label), cypher_escape(property_key))
        self.graph.run(cypher).close()
        while property_key not in self.get_uniqueness_constraints(label):
            sleep(0.1)
Exemple #9
0
def _match_clause(value, name, node_key):
    if node_key is None:
        # ... add MATCH by id clause
        return "MATCH (%s) WHERE id(%s) = %s" % (name, name, value)
    else:
        # ... add MATCH by label/property clause
        pl, pk = _unpack_merge_key(node_key)
        n_pk = len(pk)
        if n_pk == 0:
            return "MATCH (%s:%s)" % (name, cypher_escape(pl))
        elif n_pk == 1:
            return "MATCH (%s:%s {%s:%s})" % (name, cypher_escape(pl), cypher_escape(pk[0]), value)
        else:
            match_key_string = ", ".join("%s:%s[%s]" % (cypher_escape(key), value, j)
                                         for j, key in enumerate(pk))
            return "MATCH (%s:%s {%s})" % (name, cypher_escape(pl), match_key_string)
Exemple #10
0
    def _query_and_parameters(self, count=False):
        """ A tuple of the Cypher query and parameters used to select
        the nodes that match the criteria for this selection.

        :return: Cypher query string
        """
        clauses = [
            "MATCH (_%s)" % "".join(":%s" % cypher_escape(label)
                                    for label in self._labels)
        ]
        parameters = {}
        if self._predicates:
            predicates = []
            for predicate in self._predicates:
                if isinstance(predicate, tuple):
                    predicate, param = predicate
                    parameters.update(param)
                predicates.append(predicate)
            clauses.append("WHERE %s" % " AND ".join(predicates))
        if count:
            clauses.append("RETURN count(_)")
        else:
            clauses.append("RETURN _")
            if self._order_by:
                clauses.append("ORDER BY %s" % (", ".join(self._order_by)))
            if self._skip:
                clauses.append("SKIP %d" % self._skip)
            if self._limit is not None:
                clauses.append("LIMIT %d" % self._limit)
        return " ".join(clauses), parameters
Exemple #11
0
 def query_entity(self, ntype, rel_conditions, attr_conditions, neg_attr):
     """
     query entity names for given relation and attribute conditions
     Negation conditions only support attributes for now.
     """
     statement = "MATCH (p:{})".format(ntype)
     for attr, value in attr_conditions:
         statement += "\nMATCH (p {{{}:'{}'}})".format(attr, cypher.cypher_escape(value))
     for rel, entity in rel_conditions:
         statement += "\nMATCH (p)-[:{}]->({{name:'{}'}})".format(rel, cypher.cypher_escape(entity))
     if neg_attr:
         statement += "\nWHERE p.{} <> '{}'".format(neg_attr[0][0], cypher.cypher_escape(neg_attr[0][1]))
         for attr, value in neg_attr[1:]:
             statement += "AND p.{} <> '{}'".format(attr, cypher.cypher_escape(value))
     statement += "\nRETURN p.name as name"
     return self.graph.run(statement).data()
Exemple #12
0
def _create_nodes(tx, labels, data):
    assert isinstance(labels, frozenset)
    label_string = "".join(":" + cypher_escape(label)
                           for label in sorted(labels))
    cypher = "UNWIND $x AS data CREATE (_%s) SET _ = data RETURN id(_)" % label_string
    for record in tx.run(cypher, x=data):
        yield record[0]
Exemple #13
0
 def drop_uniqueness_constraint(self, label, *property_keys):
     """ Remove the uniqueness constraint for a given property key.
     """
     self.graph.run("DROP CONSTRAINT ON (a:%s) "
                    "ASSERT a.%s IS UNIQUE" %
                    (cypher_escape(label), ",".join(
                        map(cypher_escape, property_keys)))).close()
Exemple #14
0
def _merge_nodes(tx, p_label, p_key, labels, data):
    """

    :param tx:
    :param p_label:
    :param p_key:
    :param labels:
    :param data: list of (p_value, properties)
    :return:
    """
    assert isinstance(labels, frozenset)
    label_string = ":".join(cypher_escape(label) for label in sorted(labels))
    cypher = "UNWIND $x AS data MERGE (_:%s {%s:data[0]}) SET _:%s SET _ = data[1] RETURN id(_)" % (
        cypher_escape(p_label), cypher_escape(p_key), label_string)
    for record in tx.run(cypher, x=data):
        yield record[0]
Exemple #15
0
    def _query_and_parameters(self):
        """ A tuple of the Cypher query and parameters used to select
        the nodes that match the criteria for this selection.

        :return: Cypher query string
        """
        clauses = [
            "MATCH (_%s)" % "".join(":%s" % cypher_escape(label)
                                    for label in self._labels)
        ]
        parameters = {}
        if self._conditions:
            conditions = []
            for condition in self._conditions:
                if isinstance(condition, tuple):
                    condition, param = condition
                    parameters.update(param)
                conditions.append(condition)
            clauses.append("WHERE %s" % " AND ".join(conditions))
        clauses.append("RETURN _")
        if self._order_by:
            clauses.append("ORDER BY %s" % (", ".join(self._order_by)))
        if self._skip:
            clauses.append("SKIP %d" % self._skip)
        if self._limit is not None:
            clauses.append("LIMIT %d" % self._limit)
        return " ".join(clauses), parameters
Exemple #16
0
 def create_uniqueness_constraint(self, label, *property_keys):
     """ Create a uniqueness constraint for a label.
     """
     self.graph.run("CREATE CONSTRAINT ON (a:%s) "
                    "ASSERT a.%s IS UNIQUE" %
                    (cypher_escape(label), ",".join(map(cypher_escape, property_keys)))).close()
     while property_keys not in self.get_uniqueness_constraints(label):
         sleep(0.1)
Exemple #17
0
 def create_index(self, label, *property_keys):
     """ Create a schema index for a label and property
     key combination.
     """
     self.graph.run("CREATE INDEX ON :%s(%s)" %
                    (cypher_escape(label), ",".join(map(cypher_escape, property_keys)))).close()
     while property_keys not in self.get_indexes(label):
         sleep(0.1)
 def create_index(self, label, *property_keys):
     """ Create a schema index for a label and property
     key combination.
     """
     cypher = "CREATE INDEX ON :{}({})".format(
         cypher_escape(label), ", ".join(map(cypher_escape, property_keys)))
     self.graph.update(cypher)
     while property_keys not in self.get_indexes(label):
         sleep(0.1)
Exemple #19
0
    def query_attribute(self, ntype, name, attrs):
        """
        query attribute value for a given entity
        For all attributes to query, see http://123.57.246.134:7474/
        """
        statement = """MATCH (p:{} {{name:'{}'}})
RETURN p.{} as {}""".format(ntype, cypher.cypher_escape(name), attrs[0], attrs[0])
        for attr in attrs[1:]:
            statement += ", p.{} as {}".format(attr, attr)
        return self.graph.run(statement).data()
Exemple #20
0
 def __db_create__(self, tx):
     from py2neo.cypher import cypher_escape
     nodes = list(self.nodes())
     reads = []
     writes = []
     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:
             reads.append("MATCH (%s) WHERE id(%s)={%s}" %
                          (node_id, node_id, param_id))
             parameters[param_id] = remote_node._id
         else:
             label_string = "".join(":" + cypher_escape(label)
                                    for label in sorted(node.labels()))
             writes.append("CREATE (%s%s {%s})" %
                           (node_id, label_string, param_id))
             parameters[param_id] = dict(node)
             node._set_remote_pending(tx)
         returns[node_id] = node
     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
             writes.append("CREATE UNIQUE (%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(reads + writes +
                           ["RETURN %s LIMIT 1" % ", ".join(returns)])
     tx.entities.append(returns)
     list(tx.run(statement, parameters))
Exemple #21
0
def _property_equality_conditions(properties, offset=1):
    for i, (key, value) in enumerate(properties.items(), start=offset):
        if key == "__id__":
            condition = "id(_)"
        else:
            condition = "_.%s" % cypher_escape(key)
        if isinstance(value, (tuple, set, frozenset)):
            condition += " IN {%d}" % i
            parameters = {"%d" % i: list(value)}
        else:
            condition += " = {%d}" % i
            parameters = {"%d" % i: value}
        yield condition, parameters
Exemple #22
0
def _merge_relationships(tx, r_type, data):
    """

    :param tx:
    :param r_type:
    :param data: list of (a_id, b_id, properties)
    :return:
    """
    cypher = ("UNWIND $x AS data "
              "MATCH (a) WHERE id(a) = data[0] "
              "MATCH (b) WHERE id(b) = data[1] "
              "MERGE (a)-[_:%s]->(b) SET _ = data[2] RETURN id(_)" % cypher_escape(r_type))
    for record in tx.run(cypher, x=data):
        yield record[0]
Exemple #23
0
def _match_clause(name, node_key, value, prefix="(", suffix=")"):
    if node_key is None:
        # ... add MATCH by id clause
        return "MATCH %s%s%s WHERE id(%s) = %s" % (prefix, name, suffix, name, value)
    else:
        # ... add MATCH by label/property clause
        nk = NodeKey(node_key)
        n_pk = len(nk.keys())
        if n_pk == 0:
            return "MATCH %s%s%s%s" % (
                prefix, name, nk.label_string(), suffix)
        elif n_pk == 1:
            return "MATCH %s%s%s {%s:%s}%s" % (
                prefix, name, nk.label_string(), cypher_escape(nk.keys()[0]), value, suffix)
        else:
            return "MATCH %s%s%s {%s}%s" % (
                prefix, name, nk.label_string(), nk.key_value_string(value, list(range(n_pk))),
                suffix)
Exemple #24
0
def unwind_create_relationships_query(data, rel_type, keys=None,
                                      start_node_key=None, end_node_key=None):
    """ Generate a parameterised ``UNWIND...CREATE`` query for bulk
    loading relationships into Neo4j.

    :param data:
    :param rel_type:
    :param keys:
    :param start_node_key:
    :param end_node_key:
    :return: (query, parameters) tuple
    """
    return cypher_join("UNWIND $data AS r",
                       _match_clause("r[0]", "a", start_node_key),
                       _match_clause("r[2]", "b", end_node_key),
                       "CREATE (a)-[_:%s]->(b)" % cypher_escape(rel_type),
                       _set_properties_clause("r[1]", keys),
                       data=list(data))
Exemple #25
0
    def __call__(self, *args, **kwargs):
        """ Call a procedure by name.

        For example:
            >>> from py2neo import Graph
            >>> g = Graph()
            >>> g.call("dbms.components")
             name         | versions  | edition
            --------------|-----------|-----------
             Neo4j Kernel | ['4.0.2'] | community

        :param procedure: fully qualified procedure name
        :param args: positional arguments to pass to the procedure
        :returns: :class:`.Cursor` object wrapping the result
        """
        procedure_name = ".".join(cypher_escape(part) for part in self.name.split("."))
        arg_list = [(str(i), arg) for i, arg in enumerate(args)]
        cypher = "CALL %s(%s)" % (procedure_name, ", ".join("$" + a[0] for a in arg_list))
        keys = kwargs.get("keys")
        if keys:
            cypher += " YIELD %s" % ", ".join(keys)
        return self.graph.run(cypher, dict(arg_list))
def _property_conditions(properties, offset=1):
    for i, (key, value) in enumerate(properties.items(), start=offset):
        if key == "__id__":
            condition = "id(_)"
        else:
            condition = "_.%s" % cypher_escape(key)
        if value is None:
            condition += " IS NULL"
            parameters = {}
        elif isinstance(value, (tuple, set, frozenset)):
            condition += " IN {%d}" % i
            parameters = {"%d" % i: list(value)}
        elif re.match(_operators_search, key):
            parts = re.search(_operators_search, key)
            prop = parts.group(1)
            operator = parts.group(2)
            condition = "_.%s %s {%d}" % (prop, _operators[operator], i)
            parameters = {"%d" % i: value}
        else:
            condition += " = {%d}" % i
            parameters = {"%d" % i: value}
        yield condition, parameters
Exemple #27
0
    def check_credentials(cls, username, password):
        """
        Trying to find user with provided username and password. Returns None if failing. Otherwise cls instance.

        :param username: str
        :param password: str
        :return: cls | None
        """

        res = g.cypher.execute("MATCH (u:{class_name}) WHERE u.username={{username}} "
                               "AND u.password={{password}} RETURN u".format(class_name=cypher_escape(cls.__name__)),
                               username=username,
                               password=password)
        if res.one:
            return res.one
    def _query_and_parameters(self, count=False):
        """ A tuple of the Cypher query and parameters used to select
        the relationships that match the criteria for this selection.

        :return: Cypher query string
        """
        def verify_node(n):
            if n.graph != self.graph:
                raise ValueError("Node %r does not belong to this graph" % n)
            if n.identity is None:
                raise ValueError("Node %r is not bound to a graph" % n)

        def r_type_name(r):
            try:
                return r.__name__
            except AttributeError:
                return r

        clauses = []
        parameters = {}
        if self._r_type is None:
            relationship_detail = ""
        elif is_collection(self._r_type):
            relationship_detail = ":" + "|:".join(
                cypher_escape(r_type_name(t)) for t in self._r_type)
        else:
            relationship_detail = ":%s" % cypher_escape(
                r_type_name(self._r_type))
        if not self._nodes:
            clauses.append("MATCH (a)-[_" + relationship_detail + "]->(b)")
        elif isinstance(self._nodes, Sequence):
            if len(self._nodes) >= 1 and self._nodes[0] is not None:
                start_node = Node.cast(self._nodes[0])
                verify_node(start_node)
                clauses.append("MATCH (a) WHERE id(a) = {x}")
                parameters["x"] = start_node.identity
            if len(self._nodes) >= 2 and self._nodes[1] is not None:
                end_node = Node.cast(self._nodes[1])
                verify_node(end_node)
                clauses.append("MATCH (b) WHERE id(b) = {y}")
                parameters["y"] = end_node.identity
            if len(self._nodes) >= 3:
                raise ValueError("Node sequence cannot be longer than two")
            clauses.append("MATCH (a)-[_" + relationship_detail + "]->(b)")
        elif isinstance(self._nodes, Set):
            nodes = {node for node in self._nodes if node is not None}
            if len(nodes) >= 1:
                start_node = Node.cast(nodes.pop())
                verify_node(start_node)
                clauses.append("MATCH (a) WHERE id(a) = {x}")
                parameters["x"] = start_node.identity
            if len(nodes) >= 1:
                end_node = Node.cast(nodes.pop())
                verify_node(end_node)
                clauses.append("MATCH (b) WHERE id(b) = {y}")
                parameters["y"] = end_node.identity
            if len(nodes) >= 1:
                raise ValueError("Node set cannot be larger than two")
            clauses.append("MATCH (a)-[_" + relationship_detail + "]-(b)")
        else:
            raise ValueError("Nodes must be passed as a Sequence or a Set")
        if self._conditions:
            conditions = []
            for condition in self._conditions:
                if isinstance(condition, tuple):
                    condition, param = condition
                    parameters.update(param)
                conditions.append(condition)
            clauses.append("WHERE %s" % " AND ".join(conditions))
        if count:
            clauses.append("RETURN count(_)")
        else:
            clauses.append("RETURN _")
            if self._order_by:
                clauses.append("ORDER BY %s" % (", ".join(self._order_by)))
            if self._skip:
                clauses.append("SKIP %d" % self._skip)
            if self._limit is not None:
                clauses.append("LIMIT %d" % self._limit)
        return " ".join(clauses), parameters
Exemple #29
0
 def compile(self, key, i):
     return "_.%s < $%s" % (cypher_escape(key), i), {"%s" % i: self.value}
Exemple #30
0
 def compile(self, key, _):
     return "_.%s IS NOT NULL" % cypher_escape(key), {}
Exemple #31
0
 def compile(self, key, i):
     return "_.%s IN $%s" % (cypher_escape(key), i), {
         "%s" % i: list(self.value)
     }