pylpg 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pylpg/__init__.py +0 -0
- pylpg/backend/__init__.py +0 -0
- pylpg/backend/base.py +71 -0
- pylpg/backend/falkordb.py +151 -0
- pylpg/backend/falkordblite.py +22 -0
- pylpg/backend/neo4j.py +255 -0
- pylpg/cypher.py +204 -0
- pylpg/node.py +74 -0
- pylpg/relationship.py +215 -0
- pylpg/session.py +210 -0
- pylpg/types.py +69 -0
- pylpg-0.1.0.dist-info/METADATA +87 -0
- pylpg-0.1.0.dist-info/RECORD +14 -0
- pylpg-0.1.0.dist-info/WHEEL +4 -0
pylpg/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
pylpg/backend/base.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Abstract base class for database backends."""
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
if typing.TYPE_CHECKING:
|
|
7
|
+
import pylpg.node
|
|
8
|
+
import pylpg.relationship
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Backend(abc.ABC):
|
|
12
|
+
"""Abstract base class that all database backends must implement.
|
|
13
|
+
|
|
14
|
+
Backends handle all direct database operations: CRUD for nodes and
|
|
15
|
+
relationships, batch operations, traversal, and query execution.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
@abc.abstractmethod
|
|
19
|
+
def execute_query(
|
|
20
|
+
self, cypher: str, parameters: dict[str, typing.Any] | None = None
|
|
21
|
+
) -> list[dict[str, typing.Any]]: ...
|
|
22
|
+
|
|
23
|
+
@abc.abstractmethod
|
|
24
|
+
def is_node(self, value: typing.Any) -> bool: ...
|
|
25
|
+
|
|
26
|
+
@abc.abstractmethod
|
|
27
|
+
def deserialize_node(self, record: typing.Any) -> dict[str, typing.Any]: ...
|
|
28
|
+
|
|
29
|
+
@abc.abstractmethod
|
|
30
|
+
def create_node(self, node: "pylpg.node.Node") -> dict[str, typing.Any]: ...
|
|
31
|
+
|
|
32
|
+
@abc.abstractmethod
|
|
33
|
+
def update_node(self, node: "pylpg.node.Node") -> dict[str, typing.Any]: ...
|
|
34
|
+
|
|
35
|
+
@abc.abstractmethod
|
|
36
|
+
def delete_node(self, node: "pylpg.node.Node") -> None: ...
|
|
37
|
+
|
|
38
|
+
@abc.abstractmethod
|
|
39
|
+
def create_relationship(
|
|
40
|
+
self, relationship: "pylpg.relationship.Relationship"
|
|
41
|
+
) -> dict[str, typing.Any]: ...
|
|
42
|
+
|
|
43
|
+
@abc.abstractmethod
|
|
44
|
+
def update_relationship(
|
|
45
|
+
self, relationship: "pylpg.relationship.Relationship"
|
|
46
|
+
) -> dict[str, typing.Any]: ...
|
|
47
|
+
|
|
48
|
+
@abc.abstractmethod
|
|
49
|
+
def delete_relationship(
|
|
50
|
+
self, relationship: "pylpg.relationship.Relationship"
|
|
51
|
+
) -> None: ...
|
|
52
|
+
|
|
53
|
+
@abc.abstractmethod
|
|
54
|
+
def save_batch(
|
|
55
|
+
self,
|
|
56
|
+
items: "list[pylpg.node.Node | pylpg.relationship.Relationship]",
|
|
57
|
+
) -> None: ...
|
|
58
|
+
|
|
59
|
+
@abc.abstractmethod
|
|
60
|
+
def traverse(
|
|
61
|
+
self,
|
|
62
|
+
node: "pylpg.node.Node",
|
|
63
|
+
relationship_type: str,
|
|
64
|
+
direction: "pylpg.relationship.Direction",
|
|
65
|
+
) -> list[dict[str, typing.Any]]: ...
|
|
66
|
+
|
|
67
|
+
@abc.abstractmethod
|
|
68
|
+
def delete_all(self) -> None: ...
|
|
69
|
+
|
|
70
|
+
@abc.abstractmethod
|
|
71
|
+
def close(self) -> None: ...
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""FalkorDB backend using the FalkorDB Python driver."""
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
import falkordb
|
|
6
|
+
import falkordb.node
|
|
7
|
+
|
|
8
|
+
import pylpg.backend.base
|
|
9
|
+
import pylpg.cypher
|
|
10
|
+
import pylpg.node
|
|
11
|
+
import pylpg.relationship
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class FalkorDBBackend(pylpg.backend.base.Backend):
|
|
15
|
+
"""Backend for FalkorDB server instances.
|
|
16
|
+
|
|
17
|
+
Uses individual queries for batch operations, which is optimal
|
|
18
|
+
for FalkorDB's in-memory architecture.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
_database_id_func_name = "ID"
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
hostname: str = "localhost",
|
|
26
|
+
port: int = 6379,
|
|
27
|
+
database: str = "default",
|
|
28
|
+
username: str | None = None,
|
|
29
|
+
password: str | None = None,
|
|
30
|
+
) -> None:
|
|
31
|
+
self._db = falkordb.FalkorDB(
|
|
32
|
+
host=hostname, port=port, username=username, password=password
|
|
33
|
+
)
|
|
34
|
+
self._graph = self._db.select_graph(database)
|
|
35
|
+
|
|
36
|
+
def execute_query(
|
|
37
|
+
self, cypher: str, parameters: dict[str, typing.Any] | None = None
|
|
38
|
+
) -> list[dict[str, typing.Any]]:
|
|
39
|
+
result = self._graph.query(cypher, params=parameters or {})
|
|
40
|
+
if result.result_set:
|
|
41
|
+
header = [header_item[1] for header_item in result.header]
|
|
42
|
+
return [dict(zip(header, row)) for row in result.result_set]
|
|
43
|
+
return []
|
|
44
|
+
|
|
45
|
+
def is_node(self, value: typing.Any) -> bool:
|
|
46
|
+
return isinstance(value, falkordb.node.Node)
|
|
47
|
+
|
|
48
|
+
def deserialize_node(self, record: typing.Any) -> dict[str, typing.Any]:
|
|
49
|
+
properties = dict(record.properties)
|
|
50
|
+
properties["_labels"] = frozenset(record.labels)
|
|
51
|
+
properties["_database_id"] = record.id
|
|
52
|
+
return properties
|
|
53
|
+
|
|
54
|
+
def create_node(self, node: pylpg.node.Node) -> dict[str, typing.Any]:
|
|
55
|
+
cypher, params = pylpg.cypher.build_create_node_query(
|
|
56
|
+
node=node, database_id_func_name=self._database_id_func_name
|
|
57
|
+
)
|
|
58
|
+
result = self.execute_query(cypher, params)
|
|
59
|
+
return result[0] if result else {}
|
|
60
|
+
|
|
61
|
+
def update_node(self, node: pylpg.node.Node) -> dict[str, typing.Any]:
|
|
62
|
+
cypher, params = pylpg.cypher.build_update_node_query(
|
|
63
|
+
node=node, database_id_func_name=self._database_id_func_name
|
|
64
|
+
)
|
|
65
|
+
result = self.execute_query(cypher, params)
|
|
66
|
+
return result[0] if result else {}
|
|
67
|
+
|
|
68
|
+
def delete_node(self, node: pylpg.node.Node) -> None:
|
|
69
|
+
cypher, params = pylpg.cypher.build_delete_node_query(
|
|
70
|
+
node=node, database_id_func_name=self._database_id_func_name
|
|
71
|
+
)
|
|
72
|
+
self.execute_query(cypher, params)
|
|
73
|
+
|
|
74
|
+
def create_relationship(
|
|
75
|
+
self, relationship: pylpg.relationship.Relationship
|
|
76
|
+
) -> dict[str, typing.Any]:
|
|
77
|
+
cypher, params = pylpg.cypher.build_create_relationship_query(
|
|
78
|
+
relationship=relationship,
|
|
79
|
+
database_id_func_name=self._database_id_func_name,
|
|
80
|
+
)
|
|
81
|
+
result = self.execute_query(cypher, params)
|
|
82
|
+
return result[0] if result else {}
|
|
83
|
+
|
|
84
|
+
def update_relationship(
|
|
85
|
+
self, relationship: pylpg.relationship.Relationship
|
|
86
|
+
) -> dict[str, typing.Any]:
|
|
87
|
+
cypher, params = pylpg.cypher.build_update_relationship_query(
|
|
88
|
+
relationship=relationship,
|
|
89
|
+
database_id_func_name=self._database_id_func_name,
|
|
90
|
+
)
|
|
91
|
+
result = self.execute_query(cypher, params)
|
|
92
|
+
return result[0] if result else {}
|
|
93
|
+
|
|
94
|
+
def delete_relationship(
|
|
95
|
+
self, relationship: pylpg.relationship.Relationship
|
|
96
|
+
) -> None:
|
|
97
|
+
cypher, params = pylpg.cypher.build_delete_relationship_query(
|
|
98
|
+
relationship=relationship,
|
|
99
|
+
database_id_func_name=self._database_id_func_name,
|
|
100
|
+
)
|
|
101
|
+
self.execute_query(cypher, params)
|
|
102
|
+
|
|
103
|
+
def save_batch(
|
|
104
|
+
self,
|
|
105
|
+
items: list[pylpg.node.Node | pylpg.relationship.Relationship],
|
|
106
|
+
) -> None:
|
|
107
|
+
nodes: list[pylpg.node.Node] = []
|
|
108
|
+
relationships: list[pylpg.relationship.Relationship] = []
|
|
109
|
+
for item in items:
|
|
110
|
+
if isinstance(item, pylpg.node.Node):
|
|
111
|
+
nodes.append(item)
|
|
112
|
+
elif isinstance(item, pylpg.relationship.Relationship):
|
|
113
|
+
if not item.source.is_saved():
|
|
114
|
+
nodes.append(item.source)
|
|
115
|
+
if not item.target.is_saved():
|
|
116
|
+
nodes.append(item.target)
|
|
117
|
+
relationships.append(item)
|
|
118
|
+
for node in nodes:
|
|
119
|
+
if not node.is_saved():
|
|
120
|
+
row = self.create_node(node)
|
|
121
|
+
if row:
|
|
122
|
+
node._database_id = row["_database_id"]
|
|
123
|
+
else:
|
|
124
|
+
self.update_node(node)
|
|
125
|
+
for relationship in relationships:
|
|
126
|
+
if not relationship.is_saved():
|
|
127
|
+
row = self.create_relationship(relationship)
|
|
128
|
+
if row:
|
|
129
|
+
relationship._database_id = row["_database_id"]
|
|
130
|
+
else:
|
|
131
|
+
self.update_relationship(relationship)
|
|
132
|
+
|
|
133
|
+
def traverse(
|
|
134
|
+
self,
|
|
135
|
+
node: pylpg.node.Node,
|
|
136
|
+
relationship_type: str,
|
|
137
|
+
direction: pylpg.relationship.Direction,
|
|
138
|
+
) -> list[dict[str, typing.Any]]:
|
|
139
|
+
cypher, params = pylpg.cypher.build_traverse_query(
|
|
140
|
+
node=node,
|
|
141
|
+
relationship_type=relationship_type,
|
|
142
|
+
direction=direction,
|
|
143
|
+
database_id_func_name=self._database_id_func_name,
|
|
144
|
+
)
|
|
145
|
+
return self.execute_query(cypher, params)
|
|
146
|
+
|
|
147
|
+
def delete_all(self) -> None:
|
|
148
|
+
self.execute_query(pylpg.cypher.build_delete_all_query())
|
|
149
|
+
|
|
150
|
+
def close(self) -> None:
|
|
151
|
+
pass
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""FalkorDBLite embedded backend, no server required."""
|
|
2
|
+
|
|
3
|
+
import pylpg.backend.falkordb
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FalkorDBLiteBackend(pylpg.backend.falkordb.FalkorDBBackend):
|
|
7
|
+
"""Embedded FalkorDB backend using FalkorDBLite.
|
|
8
|
+
|
|
9
|
+
Runs FalkorDB as a local subprocess with file-based persistence.
|
|
10
|
+
No external server required. Requires the `falkordblite` extra.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self, path: str = "/tmp/falkordblite.db", database: str = "default"
|
|
15
|
+
) -> None:
|
|
16
|
+
import redislite
|
|
17
|
+
|
|
18
|
+
self._db = redislite.FalkorDB(path)
|
|
19
|
+
self._graph = self._db.select_graph(database)
|
|
20
|
+
|
|
21
|
+
def close(self) -> None:
|
|
22
|
+
self._db.close()
|
pylpg/backend/neo4j.py
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""Neo4j backend using the official Neo4j Python driver."""
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
import neo4j
|
|
6
|
+
import neo4j.graph
|
|
7
|
+
|
|
8
|
+
import pylpg.backend.base
|
|
9
|
+
import pylpg.cypher
|
|
10
|
+
import pylpg.node
|
|
11
|
+
import pylpg.relationship
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Neo4jBackend(pylpg.backend.base.Backend):
|
|
15
|
+
"""Backend for Neo4j databases.
|
|
16
|
+
|
|
17
|
+
Uses UNWIND queries for batch operations, providing significant
|
|
18
|
+
speedups over individual queries at scale.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
_database_id_func_name = "elementId"
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
hostname: str = "localhost",
|
|
26
|
+
port: int = 7687,
|
|
27
|
+
database: str = "neo4j",
|
|
28
|
+
username: str = "neo4j",
|
|
29
|
+
password: str = "neo4j",
|
|
30
|
+
protocol: str = "bolt",
|
|
31
|
+
) -> None:
|
|
32
|
+
uri = f"{protocol}://{hostname}:{port}"
|
|
33
|
+
self._driver = neo4j.GraphDatabase.driver(uri, auth=(username, password))
|
|
34
|
+
self._database = database
|
|
35
|
+
|
|
36
|
+
def execute_query(
|
|
37
|
+
self,
|
|
38
|
+
cypher: str,
|
|
39
|
+
parameters: dict[str, typing.Any] | None = None,
|
|
40
|
+
transaction: neo4j.Transaction | None = None,
|
|
41
|
+
) -> list[dict[str, typing.Any]]:
|
|
42
|
+
if transaction is not None:
|
|
43
|
+
result = transaction.run(cypher, parameters=parameters or {})
|
|
44
|
+
keys = result.keys()
|
|
45
|
+
records = list(result)
|
|
46
|
+
else:
|
|
47
|
+
records, summary, keys = self._driver.execute_query(
|
|
48
|
+
cypher, parameters_=parameters or {}, database_=self._database
|
|
49
|
+
)
|
|
50
|
+
return [dict(zip(keys, record.values())) for record in records]
|
|
51
|
+
|
|
52
|
+
def is_node(self, value: typing.Any) -> bool:
|
|
53
|
+
return isinstance(value, neo4j.graph.Node)
|
|
54
|
+
|
|
55
|
+
def deserialize_node(self, record: typing.Any) -> dict[str, typing.Any]:
|
|
56
|
+
properties = dict(record)
|
|
57
|
+
properties["_labels"] = frozenset(record.labels)
|
|
58
|
+
properties["_database_id"] = record.element_id
|
|
59
|
+
return properties
|
|
60
|
+
|
|
61
|
+
def create_node(self, node: pylpg.node.Node) -> dict[str, typing.Any]:
|
|
62
|
+
cypher, params = pylpg.cypher.build_create_node_query(
|
|
63
|
+
node=node, database_id_func_name=self._database_id_func_name
|
|
64
|
+
)
|
|
65
|
+
result = self.execute_query(cypher, params)
|
|
66
|
+
return result[0] if result else {}
|
|
67
|
+
|
|
68
|
+
def update_node(self, node: pylpg.node.Node) -> dict[str, typing.Any]:
|
|
69
|
+
cypher, params = pylpg.cypher.build_update_node_query(
|
|
70
|
+
node=node, database_id_func_name=self._database_id_func_name
|
|
71
|
+
)
|
|
72
|
+
result = self.execute_query(cypher, params)
|
|
73
|
+
return result[0] if result else {}
|
|
74
|
+
|
|
75
|
+
def delete_node(self, node: pylpg.node.Node) -> None:
|
|
76
|
+
cypher, params = pylpg.cypher.build_delete_node_query(
|
|
77
|
+
node=node, database_id_func_name=self._database_id_func_name
|
|
78
|
+
)
|
|
79
|
+
self.execute_query(cypher, params)
|
|
80
|
+
|
|
81
|
+
def create_relationship(
|
|
82
|
+
self, relationship: pylpg.relationship.Relationship
|
|
83
|
+
) -> dict[str, typing.Any]:
|
|
84
|
+
cypher, params = pylpg.cypher.build_create_relationship_query(
|
|
85
|
+
relationship=relationship,
|
|
86
|
+
database_id_func_name=self._database_id_func_name,
|
|
87
|
+
)
|
|
88
|
+
result = self.execute_query(cypher, params)
|
|
89
|
+
return result[0] if result else {}
|
|
90
|
+
|
|
91
|
+
def update_relationship(
|
|
92
|
+
self, relationship: pylpg.relationship.Relationship
|
|
93
|
+
) -> dict[str, typing.Any]:
|
|
94
|
+
cypher, params = pylpg.cypher.build_update_relationship_query(
|
|
95
|
+
relationship=relationship,
|
|
96
|
+
database_id_func_name=self._database_id_func_name,
|
|
97
|
+
)
|
|
98
|
+
result = self.execute_query(cypher, params)
|
|
99
|
+
return result[0] if result else {}
|
|
100
|
+
|
|
101
|
+
def delete_relationship(
|
|
102
|
+
self, relationship: pylpg.relationship.Relationship
|
|
103
|
+
) -> None:
|
|
104
|
+
cypher, params = pylpg.cypher.build_delete_relationship_query(
|
|
105
|
+
relationship=relationship,
|
|
106
|
+
database_id_func_name=self._database_id_func_name,
|
|
107
|
+
)
|
|
108
|
+
self.execute_query(cypher, params)
|
|
109
|
+
|
|
110
|
+
def save_batch(
|
|
111
|
+
self,
|
|
112
|
+
items: list[pylpg.node.Node | pylpg.relationship.Relationship],
|
|
113
|
+
) -> None:
|
|
114
|
+
new_node_groups: dict[frozenset[str], list[pylpg.node.Node]] = {}
|
|
115
|
+
existing_node_groups: dict[frozenset[str], list[pylpg.node.Node]] = {}
|
|
116
|
+
new_relationship_groups: dict[str, list[pylpg.relationship.Relationship]] = {}
|
|
117
|
+
existing_relationship_groups: dict[
|
|
118
|
+
str, list[pylpg.relationship.Relationship]
|
|
119
|
+
] = {}
|
|
120
|
+
seen_nodes: set[int] = set()
|
|
121
|
+
|
|
122
|
+
for item in items:
|
|
123
|
+
if isinstance(item, pylpg.node.Node):
|
|
124
|
+
self._triage_node(
|
|
125
|
+
node=item,
|
|
126
|
+
new_node_groups=new_node_groups,
|
|
127
|
+
existing_node_groups=existing_node_groups,
|
|
128
|
+
seen_nodes=seen_nodes,
|
|
129
|
+
)
|
|
130
|
+
elif isinstance(item, pylpg.relationship.Relationship):
|
|
131
|
+
self._triage_node(
|
|
132
|
+
node=item.source,
|
|
133
|
+
new_node_groups=new_node_groups,
|
|
134
|
+
existing_node_groups=existing_node_groups,
|
|
135
|
+
seen_nodes=seen_nodes,
|
|
136
|
+
)
|
|
137
|
+
self._triage_node(
|
|
138
|
+
node=item.target,
|
|
139
|
+
new_node_groups=new_node_groups,
|
|
140
|
+
existing_node_groups=existing_node_groups,
|
|
141
|
+
seen_nodes=seen_nodes,
|
|
142
|
+
)
|
|
143
|
+
if not item.is_saved():
|
|
144
|
+
new_relationship_groups.setdefault(item.__type__, []).append(item)
|
|
145
|
+
else:
|
|
146
|
+
existing_relationship_groups.setdefault(item.__type__, []).append(
|
|
147
|
+
item
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
with self._driver.session(database=self._database) as session:
|
|
151
|
+
with session.begin_transaction() as transaction:
|
|
152
|
+
for new_nodes in new_node_groups.values():
|
|
153
|
+
self._batch_create_nodes(nodes=new_nodes, transaction=transaction)
|
|
154
|
+
for existing_nodes in existing_node_groups.values():
|
|
155
|
+
self._batch_update_nodes(
|
|
156
|
+
nodes=existing_nodes, transaction=transaction
|
|
157
|
+
)
|
|
158
|
+
for new_relationships in new_relationship_groups.values():
|
|
159
|
+
self._batch_create_relationships(
|
|
160
|
+
relationships=new_relationships, transaction=transaction
|
|
161
|
+
)
|
|
162
|
+
for existing_relationships in existing_relationship_groups.values():
|
|
163
|
+
self._batch_update_relationships(
|
|
164
|
+
relationships=existing_relationships,
|
|
165
|
+
transaction=transaction,
|
|
166
|
+
)
|
|
167
|
+
transaction.commit()
|
|
168
|
+
|
|
169
|
+
def _triage_node(
|
|
170
|
+
self,
|
|
171
|
+
node: pylpg.node.Node,
|
|
172
|
+
new_node_groups: dict[frozenset[str], list[pylpg.node.Node]],
|
|
173
|
+
existing_node_groups: dict[frozenset[str], list[pylpg.node.Node]],
|
|
174
|
+
seen_nodes: set[int],
|
|
175
|
+
) -> None:
|
|
176
|
+
node_id = id(node)
|
|
177
|
+
if node_id in seen_nodes:
|
|
178
|
+
return
|
|
179
|
+
seen_nodes.add(node_id)
|
|
180
|
+
if not node.is_saved():
|
|
181
|
+
new_node_groups.setdefault(node.__labels__, []).append(node)
|
|
182
|
+
else:
|
|
183
|
+
existing_node_groups.setdefault(node.__labels__, []).append(node)
|
|
184
|
+
|
|
185
|
+
def _batch_create_nodes(
|
|
186
|
+
self,
|
|
187
|
+
nodes: list[pylpg.node.Node],
|
|
188
|
+
transaction: neo4j.Transaction,
|
|
189
|
+
) -> None:
|
|
190
|
+
cypher, params = pylpg.cypher.build_batch_create_nodes_query(
|
|
191
|
+
nodes=nodes, database_id_func_name=self._database_id_func_name
|
|
192
|
+
)
|
|
193
|
+
results = self.execute_query(cypher, params, transaction=transaction)
|
|
194
|
+
temp_id_to_node = {node._temp_id: node for node in nodes}
|
|
195
|
+
for row in results:
|
|
196
|
+
node = temp_id_to_node[row["temp_id"]]
|
|
197
|
+
node._database_id = row["_database_id"]
|
|
198
|
+
|
|
199
|
+
def _batch_update_nodes(
|
|
200
|
+
self,
|
|
201
|
+
nodes: list[pylpg.node.Node],
|
|
202
|
+
transaction: neo4j.Transaction,
|
|
203
|
+
) -> None:
|
|
204
|
+
cypher, params = pylpg.cypher.build_batch_update_nodes_query(
|
|
205
|
+
nodes=nodes, database_id_func_name=self._database_id_func_name
|
|
206
|
+
)
|
|
207
|
+
self.execute_query(cypher, params, transaction=transaction)
|
|
208
|
+
|
|
209
|
+
def _batch_create_relationships(
|
|
210
|
+
self,
|
|
211
|
+
relationships: list[pylpg.relationship.Relationship],
|
|
212
|
+
transaction: neo4j.Transaction,
|
|
213
|
+
) -> None:
|
|
214
|
+
cypher, params = pylpg.cypher.build_batch_create_relationships_query(
|
|
215
|
+
relationships=relationships,
|
|
216
|
+
database_id_func_name=self._database_id_func_name,
|
|
217
|
+
)
|
|
218
|
+
results = self.execute_query(cypher, params, transaction=transaction)
|
|
219
|
+
temp_id_to_relationship = {
|
|
220
|
+
relationship._temp_id: relationship for relationship in relationships
|
|
221
|
+
}
|
|
222
|
+
for row in results:
|
|
223
|
+
relationship = temp_id_to_relationship[row["temp_id"]]
|
|
224
|
+
relationship._database_id = row["_database_id"]
|
|
225
|
+
|
|
226
|
+
def _batch_update_relationships(
|
|
227
|
+
self,
|
|
228
|
+
relationships: list[pylpg.relationship.Relationship],
|
|
229
|
+
transaction: neo4j.Transaction,
|
|
230
|
+
) -> None:
|
|
231
|
+
cypher, params = pylpg.cypher.build_batch_update_relationships_query(
|
|
232
|
+
relationships=relationships,
|
|
233
|
+
database_id_func_name=self._database_id_func_name,
|
|
234
|
+
)
|
|
235
|
+
self.execute_query(cypher, params, transaction=transaction)
|
|
236
|
+
|
|
237
|
+
def traverse(
|
|
238
|
+
self,
|
|
239
|
+
node: pylpg.node.Node,
|
|
240
|
+
relationship_type: str,
|
|
241
|
+
direction: pylpg.relationship.Direction,
|
|
242
|
+
) -> list[dict[str, typing.Any]]:
|
|
243
|
+
cypher, params = pylpg.cypher.build_traverse_query(
|
|
244
|
+
node=node,
|
|
245
|
+
relationship_type=relationship_type,
|
|
246
|
+
direction=direction,
|
|
247
|
+
database_id_func_name=self._database_id_func_name,
|
|
248
|
+
)
|
|
249
|
+
return self.execute_query(cypher, params)
|
|
250
|
+
|
|
251
|
+
def delete_all(self) -> None:
|
|
252
|
+
self.execute_query(pylpg.cypher.build_delete_all_query())
|
|
253
|
+
|
|
254
|
+
def close(self) -> None:
|
|
255
|
+
self._driver.close()
|