def create_subgraph(tx, subgraph): """ Create new data in a remote :class:`.Graph` from a local :class:`.Subgraph`. :param tx: :param subgraph: :return: """ graph = tx.graph for labels, nodes in _node_create_dict(n for n in subgraph.nodes if n.graph is None).items(): pq = unwind_create_nodes_query(list(map(dict, nodes)), labels=labels) pq = cypher_join(pq, "RETURN id(_)") records = tx.run(*pq) for i, record in enumerate(records): node = nodes[i] node.graph = graph node.identity = record[0] node._remote_labels = labels for r_type, relationships in _rel_create_dict( r for r in subgraph.relationships if r.graph is None).items(): data = map( lambda r: [r.start_node.identity, dict(r), r.end_node.identity], relationships) pq = unwind_merge_relationships_query(data, r_type) pq = cypher_join(pq, "RETURN id(_)") for i, record in enumerate(tx.run(*pq)): relationship = relationships[i] relationship.graph = graph relationship.identity = record[0]
def __db_merge__(self, tx, primary_label=None, primary_key=None): """ Merge data into a remote :class:`.Graph` from this :class:`.Subgraph`. :param tx: :param primary_label: :param primary_key: """ graph = tx.graph # Convert nodes into a dictionary of # {(p_label, p_key, frozenset(labels)): [Node, Node, ...]} node_dict = {} for node in self.nodes: if node.graph is None: p_label = getattr(node, "__primarylabel__", None) or primary_label p_key = getattr(node, "__primarykey__", None) or primary_key key = (p_label, p_key, frozenset(node.labels)) node_dict.setdefault(key, []).append(node) # Convert relationships into a dictionary of # {rel_type: [Rel, Rel, ...]} rel_dict = {} for relationship in self.relationships: if relationship.graph is None: key = type(relationship).__name__ rel_dict.setdefault(key, []).append(relationship) for (pl, pk, labels), nodes in node_dict.items(): if pl is None or pk is None: raise ValueError( "Primary label and primary key are required for MERGE operation" ) pq = unwind_merge_nodes_query(map(dict, nodes), (pl, pk), labels) pq = cypher_join(pq, "RETURN id(_)") identities = [record[0] for record in tx.run(*pq)] if len(identities) > len(nodes): raise UniquenessError( "Found %d matching nodes for primary label %r and primary " "key %r with labels %r but merging requires no more than " "one" % (len(identities), pl, pk, set(labels))) for i, identity in enumerate(identities): node = nodes[i] node.graph = graph node.identity = identity node._remote_labels = labels for r_type, relationships in rel_dict.items(): data = map( lambda r: [r.start_node.identity, dict(r), r.end_node.identity], relationships) pq = unwind_merge_relationships_query(data, r_type) pq = cypher_join(pq, "RETURN id(_)") for i, record in enumerate(tx.run(*pq)): relationship = relationships[i] relationship.graph = graph relationship.identity = record[0]
def unwind_merge_relationships_query(data, merge_key, start_node_key=None, end_node_key=None, keys=None, preserve=None): """ Generate a parameterised ``UNWIND...MERGE`` query for bulk loading relationships into Neo4j. :param data: :param merge_key: tuple of (rel_type, key1, key2...) :param start_node_key: :param end_node_key: :param keys: :param preserve: Collection of key names for values that should be protected should the relationship already exist. :return: (query, parameters) tuple """ return cypher_join("UNWIND $data AS r", _match_clause("a", start_node_key, "r[0]"), _match_clause("b", end_node_key, "r[2]"), _merge_clause("_", merge_key, "r[1]", keys, "(a)-[", "]->(b)"), _on_create_set_properties_clause( "r[1]", keys, preserve), _set_properties_clause("r[1]", keys, exclude_keys=preserve), data=_relationship_data(data))
def __db_create__(self, tx): """ Create new data in a remote :class:`.Graph` from this :class:`.Subgraph`. :param tx: """ graph = tx.graph # Convert nodes into a dictionary of # {frozenset(labels): [Node, Node, ...]} node_dict = {} for node in self.nodes: if not self._is_bound(node, tx.graph): key = frozenset(node.labels) node_dict.setdefault(key, []).append(node) # Convert relationships into a dictionary of # {rel_type: [Rel, Rel, ...]} rel_dict = {} for relationship in self.relationships: if not self._is_bound(relationship, tx.graph): key = type(relationship).__name__ rel_dict.setdefault(key, []).append(relationship) for labels, nodes in node_dict.items(): pq = unwind_create_nodes_query(list(map(dict, nodes)), labels=labels) pq = cypher_join(pq, "RETURN id(_)") records = tx.run(*pq) for i, record in enumerate(records): node = nodes[i] node.graph = graph node.identity = record[0] node._remote_labels = labels for r_type, relationships in rel_dict.items(): data = map( lambda r: [r.start_node.identity, dict(r), r.end_node.identity], relationships) pq = unwind_merge_relationships_query(data, r_type) pq = cypher_join(pq, "RETURN id(_)") for i, record in enumerate(tx.run(*pq)): relationship = relationships[i] relationship.graph = graph relationship.identity = record[0]
def merge_subgraph(tx, subgraph, p_label, p_key): """ Merge data into a remote :class:`.Graph` from a local :class:`.Subgraph`. :param tx: :param subgraph: :param p_label: :param p_key: :return: """ graph = tx.graph for (pl, pk, labels), nodes in _node_merge_dict( p_label, p_key, (n for n in subgraph.nodes if n.graph is None)).items(): if pl is None or pk is None: raise ValueError( "Primary label and primary key are required for MERGE operation" ) pq = unwind_merge_nodes_query(map(dict, nodes), (pl, pk), labels) pq = cypher_join(pq, "RETURN id(_)") identities = [record[0] for record in tx.run(*pq)] if len(identities) > len(nodes): raise UniquenessError( "Found %d matching nodes for primary label %r and primary " "key %r with labels %r but merging requires no more than " "one" % (len(identities), pl, pk, set(labels))) for i, identity in enumerate(identities): node = nodes[i] node.graph = graph node.identity = identity node._remote_labels = labels for r_type, relationships in _rel_create_dict( r for r in subgraph.relationships if r.graph is None).items(): data = map( lambda r: [r.start_node.identity, dict(r), r.end_node.identity], relationships) pq = unwind_merge_relationships_query(data, r_type) pq = cypher_join(pq, "RETURN id(_)") for i, record in enumerate(tx.run(*pq)): relationship = relationships[i] relationship.graph = graph relationship.identity = record[0]
def unwind_create_nodes_query(data, labels=None, keys=None): """ Generate a parameterised ``UNWIND...CREATE`` query for bulk loading nodes into Neo4j. :param data: :param labels: :param keys: :return: (query, parameters) tuple """ return cypher_join("UNWIND $data AS r", _create_clause("_", (tuple(labels or ()), )), _set_properties_clause("r", keys), data=list(data))
def unwind_merge_nodes_query(data, merge_key, labels=None, keys=None): """ Generate a parameterised ``UNWIND...MERGE`` query for bulk loading nodes into Neo4j. :param data: :param merge_key: :param labels: :param keys: :return: (query, parameters) tuple """ return cypher_join("UNWIND $data AS r", _merge_clause("r", merge_key, keys, "(", ")"), _set_labels_clause(labels), _set_properties_clause("r", keys), data=list(data))
def unwind_merge_relationships_query(data, merge_key, keys=None, start_node_key=None, end_node_key=None): """ Generate a parameterised ``UNWIND...MERGE`` query for bulk loading relationships into Neo4j. :param data: :param merge_key: tuple of (rel_type, key1, key2...) :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), _merge_clause("r[1]", merge_key, keys, "(a)-[", "]->(b)"), _set_properties_clause("r[1]", keys), data=list(data))
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))
def unwind_merge_nodes_query(data, merge_key, labels=None, keys=None, preserve=None): """ Generate a parameterised ``UNWIND...MERGE`` query for bulk loading nodes into Neo4j. :param data: :param merge_key: :param labels: :param keys: :param preserve: Collection of key names for values that should be protected should the node already exist. :return: (query, parameters) tuple """ return cypher_join("UNWIND $data AS r", _merge_clause("_", merge_key, "r", keys), _on_create_set_properties_clause("r", keys, preserve), _set_properties_clause("r", keys, exclude_keys=preserve), _set_labels_clause(labels), data=list(data))