Source code for architxt.database.export.cypher

import neo4j

from architxt.schema import Schema
from architxt.tree import Forest, NodeType, Tree, has_type

__all__ = ['export_cypher']


[docs] def export_cypher( forest: Forest, session: neo4j.Session, ) -> None: """ Export the graph instance as a dictionary using Neo4j. :param session: Neo4j session. :param forest: The forest to export. :return: A generator that yields dictionaries representing the graph. """ schema = Schema.from_forest(forest) collapsible_nodes = schema.find_collapsible_groups() for tree in forest: export_tree(tree, session, collapsible_nodes) delete_id_column(session)
[docs] def export_tree( tree: Tree, session: neo4j.Session, edge_data: set[str], ) -> None: """ Export the tree to the graph. :param tree: Tree to export. :param session: Neo4j session. :param edge_data: """ for group in tree.subtrees(lambda st: has_type(st, NodeType.GROUP) and st.label.name not in edge_data): export_group(group, session) export_edge_data = {} for relation in tree.subtrees(lambda subtree: has_type(subtree, NodeType.REL)): if relation[0].label.name in edge_data: if relation[0] not in export_edge_data: export_edge_data[relation[0]] = set() export_edge_data[relation[0]].add(relation[1]) elif relation[1].label.name in edge_data: if relation[1] not in export_edge_data: export_edge_data[relation[1]] = set() export_edge_data[relation[1]].add(relation[0]) else: export_relation(relation, session) export_relation_edge_with_data(export_edge_data, session)
[docs] def export_relation( tree: Tree, session: neo4j.Session, ) -> None: """ Export the relation to the graph. :param tree: Relation to export. :param session: Neo4j session. """ # Order is arbitrary, a better strategy could be used to determine source and target nodes src, dest = sorted(tree, key=lambda x: x.label) if tree.metadata.get('source') != src.label.name: src, dest = dest, src rel_name = tree.metadata.get('source_column', tree.label.name.replace('<->', '_')) session.run(f""" MATCH (src:`{src.label.name}` {{_architxt_oid: '{src.oid}'}}) MATCH (dest:`{dest.label.name}` {{_architxt_oid: '{dest.oid}'}}) MERGE (src)-[r:`{rel_name}`]->(dest) """)
[docs] def export_relation_edge_with_data( edge_data: dict[Tree, set[Tree]], session: neo4j.Session, ) -> None: """ Export the relation edge with data to the graph. :param edge_data: Dictionary of edges with data. :param session: Neo4j session. """ for edge, relations in edge_data.items(): src, dest = sorted(relations, key=lambda x: x.label) session.run(f""" MATCH (src:`{src.label.name}` {{_architxt_oid: '{src.oid}'}}) MATCH (dest:`{dest.label.name}` {{_architxt_oid: '{dest.oid}'}}) MERGE (src)-[r:{edge.label.name} {{ {', '.join(f'{k}: {v!r}' for k, v in get_properties(edge).items())} }}]->(dest) """)
[docs] def export_group( group: Tree, session: neo4j.Session, ) -> None: """ Export the group to the graph. :param group: Group to export. :param session: Neo4j session. """ session.run(f"CREATE INDEX _architxt_oid_index IF NOT EXISTS FOR (n:`{group.label.name}`) ON (n._architxt_oid)") session.run( f""" MERGE (n:`{group.label.name}` {{ _architxt_oid: $id }}) ON CREATE SET n += $data """, id=str(group.oid), data=get_properties(group), )
[docs] def get_properties(node: Tree) -> dict[str, str]: """ Get the properties of a node. :param node: Node to get properties from. :return: Dictionary of properties. """ return { child.label.name: child[0] if isinstance(child[0], str | int | float | bool) else str(child[0]) for child in node if has_type(child, NodeType.ENT) }
[docs] def delete_id_column( session: neo4j.Session, ) -> None: """ Delete the _architxt_oid property from all nodes in the graph. :param session: Neo4j session. """ session.run("MATCH (n) REMOVE n._architxt_oid") session.run("DROP INDEX _architxt_oid_index IF EXISTS")