graphiti-core 0.18.9__py3-none-any.whl → 0.19.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.
Potentially problematic release.
This version of graphiti-core might be problematic. Click here for more details.
- graphiti_core/driver/driver.py +4 -0
- graphiti_core/driver/falkordb_driver.py +3 -14
- graphiti_core/driver/kuzu_driver.py +175 -0
- graphiti_core/driver/neptune_driver.py +301 -0
- graphiti_core/edges.py +155 -62
- graphiti_core/graph_queries.py +31 -2
- graphiti_core/graphiti.py +6 -1
- graphiti_core/helpers.py +8 -8
- graphiti_core/llm_client/config.py +1 -1
- graphiti_core/llm_client/openai_base_client.py +12 -2
- graphiti_core/llm_client/openai_client.py +10 -2
- graphiti_core/migrations/__init__.py +0 -0
- graphiti_core/migrations/neo4j_node_group_labels.py +114 -0
- graphiti_core/models/edges/edge_db_queries.py +205 -76
- graphiti_core/models/nodes/node_db_queries.py +253 -74
- graphiti_core/nodes.py +271 -98
- graphiti_core/search/search.py +42 -12
- graphiti_core/search/search_config.py +4 -0
- graphiti_core/search/search_filters.py +35 -22
- graphiti_core/search/search_utils.py +1329 -392
- graphiti_core/utils/bulk_utils.py +50 -15
- graphiti_core/utils/datetime_utils.py +13 -0
- graphiti_core/utils/maintenance/community_operations.py +39 -32
- graphiti_core/utils/maintenance/edge_operations.py +47 -13
- graphiti_core/utils/maintenance/graph_data_operations.py +100 -15
- {graphiti_core-0.18.9.dist-info → graphiti_core-0.19.0.dist-info}/METADATA +87 -13
- {graphiti_core-0.18.9.dist-info → graphiti_core-0.19.0.dist-info}/RECORD +29 -25
- {graphiti_core-0.18.9.dist-info → graphiti_core-0.19.0.dist-info}/WHEEL +0 -0
- {graphiti_core-0.18.9.dist-info → graphiti_core-0.19.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import csv
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from graphiti_core.driver.driver import GraphDriver
|
|
6
|
+
from graphiti_core.driver.neo4j_driver import Neo4jDriver
|
|
7
|
+
from graphiti_core.helpers import validate_group_id
|
|
8
|
+
from graphiti_core.utils.maintenance.graph_data_operations import build_dynamic_indexes
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def neo4j_node_group_labels(driver: GraphDriver, group_id: str, batch_size: int = 100):
|
|
12
|
+
validate_group_id(group_id)
|
|
13
|
+
await build_dynamic_indexes(driver, group_id)
|
|
14
|
+
|
|
15
|
+
episode_query = """
|
|
16
|
+
MATCH (n:Episodic {group_id: $group_id})
|
|
17
|
+
CALL {
|
|
18
|
+
WITH n
|
|
19
|
+
SET n:$($group_label)
|
|
20
|
+
} IN TRANSACTIONS OF $batch_size ROWS"""
|
|
21
|
+
|
|
22
|
+
entity_query = """
|
|
23
|
+
MATCH (n:Entity {group_id: $group_id})
|
|
24
|
+
CALL {
|
|
25
|
+
WITH n
|
|
26
|
+
SET n:$($group_label)
|
|
27
|
+
} IN TRANSACTIONS OF $batch_size ROWS"""
|
|
28
|
+
|
|
29
|
+
community_query = """
|
|
30
|
+
MATCH (n:Community {group_id: $group_id})
|
|
31
|
+
CALL {
|
|
32
|
+
WITH n
|
|
33
|
+
SET n:$($group_label)
|
|
34
|
+
} IN TRANSACTIONS OF $batch_size ROWS"""
|
|
35
|
+
|
|
36
|
+
async with driver.session() as session:
|
|
37
|
+
await session.run(
|
|
38
|
+
episode_query,
|
|
39
|
+
group_id=group_id,
|
|
40
|
+
group_label='Episodic_' + group_id.replace('-', ''),
|
|
41
|
+
batch_size=batch_size,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
async with driver.session() as session:
|
|
45
|
+
await session.run(
|
|
46
|
+
entity_query,
|
|
47
|
+
group_id=group_id,
|
|
48
|
+
group_label='Entity_' + group_id.replace('-', ''),
|
|
49
|
+
batch_size=batch_size,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
async with driver.session() as session:
|
|
53
|
+
await session.run(
|
|
54
|
+
community_query,
|
|
55
|
+
group_id=group_id,
|
|
56
|
+
group_label='Community_' + group_id.replace('-', ''),
|
|
57
|
+
batch_size=batch_size,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def pop_last_n_group_ids(csv_file: str = 'group_ids.csv', count: int = 10):
|
|
62
|
+
with open(csv_file) as file:
|
|
63
|
+
reader = csv.reader(file)
|
|
64
|
+
group_ids = [row[0] for row in reader]
|
|
65
|
+
|
|
66
|
+
total_count = len(group_ids)
|
|
67
|
+
popped = group_ids[-count:]
|
|
68
|
+
remaining = group_ids[:-count]
|
|
69
|
+
|
|
70
|
+
with open(csv_file, 'w', newline='') as file:
|
|
71
|
+
writer = csv.writer(file)
|
|
72
|
+
for gid in remaining:
|
|
73
|
+
writer.writerow([gid])
|
|
74
|
+
|
|
75
|
+
return popped, total_count
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
async def get_group_ids(driver: GraphDriver):
|
|
79
|
+
query = """MATCH (n:Episodic)
|
|
80
|
+
RETURN DISTINCT n.group_id AS group_id"""
|
|
81
|
+
|
|
82
|
+
results, _, _ = await driver.execute_query(query)
|
|
83
|
+
group_ids = [result['group_id'] for result in results]
|
|
84
|
+
|
|
85
|
+
with open('group_ids.csv', 'w', newline='') as file:
|
|
86
|
+
writer = csv.writer(file)
|
|
87
|
+
for gid in group_ids:
|
|
88
|
+
writer.writerow([gid])
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
async def neo4j_node_label_migration(driver: GraphDriver, batch_size: int = 10):
|
|
92
|
+
group_ids, total = pop_last_n_group_ids(csv_file='group_ids.csv', count=batch_size)
|
|
93
|
+
while len(group_ids) > 0:
|
|
94
|
+
await asyncio.gather(*[neo4j_node_group_labels(driver, group_id) for group_id in group_ids])
|
|
95
|
+
group_ids, _ = pop_last_n_group_ids(csv_file='group_ids.csv', count=batch_size)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
async def main():
|
|
99
|
+
neo4j_uri = os.environ.get('NEO4J_URI') or 'bolt://localhost:7687'
|
|
100
|
+
neo4j_user = os.environ.get('NEO4J_USER') or 'neo4j'
|
|
101
|
+
neo4j_password = os.environ.get('NEO4J_PASSWORD') or 'password'
|
|
102
|
+
|
|
103
|
+
driver = Neo4jDriver(
|
|
104
|
+
uri=neo4j_uri,
|
|
105
|
+
user=neo4j_user,
|
|
106
|
+
password=neo4j_password,
|
|
107
|
+
)
|
|
108
|
+
await get_group_ids(driver)
|
|
109
|
+
await neo4j_node_label_migration(driver)
|
|
110
|
+
await driver.close()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
if __name__ == '__main__':
|
|
114
|
+
asyncio.run(main())
|
|
@@ -20,18 +20,36 @@ EPISODIC_EDGE_SAVE = """
|
|
|
20
20
|
MATCH (episode:Episodic {uuid: $episode_uuid})
|
|
21
21
|
MATCH (node:Entity {uuid: $entity_uuid})
|
|
22
22
|
MERGE (episode)-[e:MENTIONS {uuid: $uuid}]->(node)
|
|
23
|
-
SET
|
|
23
|
+
SET
|
|
24
|
+
e.group_id = $group_id,
|
|
25
|
+
e.created_at = $created_at
|
|
24
26
|
RETURN e.uuid AS uuid
|
|
25
27
|
"""
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
|
|
30
|
+
def get_episodic_edge_save_bulk_query(provider: GraphProvider) -> str:
|
|
31
|
+
if provider == GraphProvider.KUZU:
|
|
32
|
+
return """
|
|
33
|
+
MATCH (episode:Episodic {uuid: $source_node_uuid})
|
|
34
|
+
MATCH (node:Entity {uuid: $target_node_uuid})
|
|
35
|
+
MERGE (episode)-[e:MENTIONS {uuid: $uuid}]->(node)
|
|
36
|
+
SET
|
|
37
|
+
e.group_id = $group_id,
|
|
38
|
+
e.created_at = $created_at
|
|
39
|
+
RETURN e.uuid AS uuid
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
return """
|
|
43
|
+
UNWIND $episodic_edges AS edge
|
|
44
|
+
MATCH (episode:Episodic {uuid: edge.source_node_uuid})
|
|
45
|
+
MATCH (node:Entity {uuid: edge.target_node_uuid})
|
|
46
|
+
MERGE (episode)-[e:MENTIONS {uuid: edge.uuid}]->(node)
|
|
47
|
+
SET
|
|
48
|
+
e.group_id = edge.group_id,
|
|
49
|
+
e.created_at = edge.created_at
|
|
50
|
+
RETURN e.uuid AS uuid
|
|
51
|
+
"""
|
|
52
|
+
|
|
35
53
|
|
|
36
54
|
EPISODIC_EDGE_RETURN = """
|
|
37
55
|
e.uuid AS uuid,
|
|
@@ -43,82 +61,193 @@ EPISODIC_EDGE_RETURN = """
|
|
|
43
61
|
|
|
44
62
|
|
|
45
63
|
def get_entity_edge_save_query(provider: GraphProvider) -> str:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
match provider:
|
|
65
|
+
case GraphProvider.FALKORDB:
|
|
66
|
+
return """
|
|
67
|
+
MATCH (source:Entity {uuid: $edge_data.source_uuid})
|
|
68
|
+
MATCH (target:Entity {uuid: $edge_data.target_uuid})
|
|
69
|
+
MERGE (source)-[e:RELATES_TO {uuid: $edge_data.uuid}]->(target)
|
|
70
|
+
SET e = $edge_data
|
|
71
|
+
RETURN e.uuid AS uuid
|
|
72
|
+
"""
|
|
73
|
+
case GraphProvider.NEPTUNE:
|
|
74
|
+
return """
|
|
75
|
+
MATCH (source:Entity {uuid: $edge_data.source_uuid})
|
|
76
|
+
MATCH (target:Entity {uuid: $edge_data.target_uuid})
|
|
77
|
+
MERGE (source)-[e:RELATES_TO {uuid: $edge_data.uuid}]->(target)
|
|
78
|
+
SET e = removeKeyFromMap(removeKeyFromMap($edge_data, "fact_embedding"), "episodes")
|
|
79
|
+
SET e.fact_embedding = join([x IN coalesce($edge_data.fact_embedding, []) | toString(x) ], ",")
|
|
80
|
+
SET e.episodes = join($edge_data.episodes, ",")
|
|
81
|
+
RETURN $edge_data.uuid AS uuid
|
|
82
|
+
"""
|
|
83
|
+
case GraphProvider.KUZU:
|
|
84
|
+
return """
|
|
85
|
+
MATCH (source:Entity {uuid: $source_uuid})
|
|
86
|
+
MATCH (target:Entity {uuid: $target_uuid})
|
|
87
|
+
MERGE (source)-[:RELATES_TO]->(e:RelatesToNode_ {uuid: $uuid})-[:RELATES_TO]->(target)
|
|
88
|
+
SET
|
|
89
|
+
e.group_id = $group_id,
|
|
90
|
+
e.created_at = $created_at,
|
|
91
|
+
e.name = $name,
|
|
92
|
+
e.fact = $fact,
|
|
93
|
+
e.fact_embedding = $fact_embedding,
|
|
94
|
+
e.episodes = $episodes,
|
|
95
|
+
e.expired_at = $expired_at,
|
|
96
|
+
e.valid_at = $valid_at,
|
|
97
|
+
e.invalid_at = $invalid_at,
|
|
98
|
+
e.attributes = $attributes
|
|
99
|
+
RETURN e.uuid AS uuid
|
|
100
|
+
"""
|
|
101
|
+
case _: # Neo4j
|
|
102
|
+
return """
|
|
103
|
+
MATCH (source:Entity {uuid: $edge_data.source_uuid})
|
|
104
|
+
MATCH (target:Entity {uuid: $edge_data.target_uuid})
|
|
105
|
+
MERGE (source)-[e:RELATES_TO {uuid: $edge_data.uuid}]->(target)
|
|
106
|
+
SET e = $edge_data
|
|
107
|
+
WITH e CALL db.create.setRelationshipVectorProperty(e, "fact_embedding", $edge_data.fact_embedding)
|
|
108
|
+
RETURN e.uuid AS uuid
|
|
109
|
+
"""
|
|
63
110
|
|
|
64
111
|
|
|
65
112
|
def get_entity_edge_save_bulk_query(provider: GraphProvider) -> str:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
113
|
+
match provider:
|
|
114
|
+
case GraphProvider.FALKORDB:
|
|
115
|
+
return """
|
|
116
|
+
UNWIND $entity_edges AS edge
|
|
117
|
+
MATCH (source:Entity {uuid: edge.source_node_uuid})
|
|
118
|
+
MATCH (target:Entity {uuid: edge.target_node_uuid})
|
|
119
|
+
MERGE (source)-[r:RELATES_TO {uuid: edge.uuid}]->(target)
|
|
120
|
+
SET r = {uuid: edge.uuid, name: edge.name, group_id: edge.group_id, fact: edge.fact, episodes: edge.episodes,
|
|
121
|
+
created_at: edge.created_at, expired_at: edge.expired_at, valid_at: edge.valid_at, invalid_at: edge.invalid_at, fact_embedding: vecf32(edge.fact_embedding)}
|
|
122
|
+
WITH r, edge
|
|
123
|
+
RETURN edge.uuid AS uuid
|
|
124
|
+
"""
|
|
125
|
+
case GraphProvider.NEPTUNE:
|
|
126
|
+
return """
|
|
127
|
+
UNWIND $entity_edges AS edge
|
|
128
|
+
MATCH (source:Entity {uuid: edge.source_node_uuid})
|
|
129
|
+
MATCH (target:Entity {uuid: edge.target_node_uuid})
|
|
130
|
+
MERGE (source)-[r:RELATES_TO {uuid: edge.uuid}]->(target)
|
|
131
|
+
SET r = removeKeyFromMap(removeKeyFromMap(edge, "fact_embedding"), "episodes")
|
|
132
|
+
SET r.fact_embedding = join([x IN coalesce(edge.fact_embedding, []) | toString(x) ], ",")
|
|
133
|
+
SET r.episodes = join(edge.episodes, ",")
|
|
134
|
+
RETURN edge.uuid AS uuid
|
|
135
|
+
"""
|
|
136
|
+
case GraphProvider.KUZU:
|
|
137
|
+
return """
|
|
138
|
+
MATCH (source:Entity {uuid: $source_node_uuid})
|
|
139
|
+
MATCH (target:Entity {uuid: $target_node_uuid})
|
|
140
|
+
MERGE (source)-[:RELATES_TO]->(e:RelatesToNode_ {uuid: $uuid})-[:RELATES_TO]->(target)
|
|
141
|
+
SET
|
|
142
|
+
e.group_id = $group_id,
|
|
143
|
+
e.created_at = $created_at,
|
|
144
|
+
e.name = $name,
|
|
145
|
+
e.fact = $fact,
|
|
146
|
+
e.fact_embedding = $fact_embedding,
|
|
147
|
+
e.episodes = $episodes,
|
|
148
|
+
e.expired_at = $expired_at,
|
|
149
|
+
e.valid_at = $valid_at,
|
|
150
|
+
e.invalid_at = $invalid_at,
|
|
151
|
+
e.attributes = $attributes
|
|
152
|
+
RETURN e.uuid AS uuid
|
|
153
|
+
"""
|
|
154
|
+
case _:
|
|
155
|
+
return """
|
|
156
|
+
UNWIND $entity_edges AS edge
|
|
157
|
+
MATCH (source:Entity {uuid: edge.source_node_uuid})
|
|
158
|
+
MATCH (target:Entity {uuid: edge.target_node_uuid})
|
|
159
|
+
MERGE (source)-[e:RELATES_TO {uuid: edge.uuid}]->(target)
|
|
160
|
+
SET e = edge
|
|
161
|
+
WITH e, edge CALL db.create.setRelationshipVectorProperty(e, "fact_embedding", edge.fact_embedding)
|
|
162
|
+
RETURN edge.uuid AS uuid
|
|
163
|
+
"""
|
|
88
164
|
|
|
89
|
-
ENTITY_EDGE_RETURN = """
|
|
90
|
-
e.uuid AS uuid,
|
|
91
|
-
n.uuid AS source_node_uuid,
|
|
92
|
-
m.uuid AS target_node_uuid,
|
|
93
|
-
e.group_id AS group_id,
|
|
94
|
-
e.name AS name,
|
|
95
|
-
e.fact AS fact,
|
|
96
|
-
e.episodes AS episodes,
|
|
97
|
-
e.created_at AS created_at,
|
|
98
|
-
e.expired_at AS expired_at,
|
|
99
|
-
e.valid_at AS valid_at,
|
|
100
|
-
e.invalid_at AS invalid_at,
|
|
101
|
-
properties(e) AS attributes
|
|
102
|
-
"""
|
|
103
165
|
|
|
166
|
+
def get_entity_edge_return_query(provider: GraphProvider) -> str:
|
|
167
|
+
# `fact_embedding` is not returned by default and must be manually loaded using `load_fact_embedding()`.
|
|
104
168
|
|
|
105
|
-
|
|
106
|
-
if provider == GraphProvider.FALKORDB:
|
|
169
|
+
if provider == GraphProvider.NEPTUNE:
|
|
107
170
|
return """
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
171
|
+
e.uuid AS uuid,
|
|
172
|
+
n.uuid AS source_node_uuid,
|
|
173
|
+
m.uuid AS target_node_uuid,
|
|
174
|
+
e.group_id AS group_id,
|
|
175
|
+
e.name AS name,
|
|
176
|
+
e.fact AS fact,
|
|
177
|
+
split(e.episodes, ',') AS episodes,
|
|
178
|
+
e.created_at AS created_at,
|
|
179
|
+
e.expired_at AS expired_at,
|
|
180
|
+
e.valid_at AS valid_at,
|
|
181
|
+
e.invalid_at AS invalid_at,
|
|
182
|
+
properties(e) AS attributes
|
|
183
|
+
"""
|
|
114
184
|
|
|
115
185
|
return """
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
186
|
+
e.uuid AS uuid,
|
|
187
|
+
n.uuid AS source_node_uuid,
|
|
188
|
+
m.uuid AS target_node_uuid,
|
|
189
|
+
e.group_id AS group_id,
|
|
190
|
+
e.created_at AS created_at,
|
|
191
|
+
e.name AS name,
|
|
192
|
+
e.fact AS fact,
|
|
193
|
+
e.episodes AS episodes,
|
|
194
|
+
e.expired_at AS expired_at,
|
|
195
|
+
e.valid_at AS valid_at,
|
|
196
|
+
e.invalid_at AS invalid_at,
|
|
197
|
+
""" + (
|
|
198
|
+
'e.attributes AS attributes'
|
|
199
|
+
if provider == GraphProvider.KUZU
|
|
200
|
+
else 'properties(e) AS attributes'
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def get_community_edge_save_query(provider: GraphProvider) -> str:
|
|
205
|
+
match provider:
|
|
206
|
+
case GraphProvider.FALKORDB:
|
|
207
|
+
return """
|
|
208
|
+
MATCH (community:Community {uuid: $community_uuid})
|
|
209
|
+
MATCH (node {uuid: $entity_uuid})
|
|
210
|
+
MERGE (community)-[e:HAS_MEMBER {uuid: $uuid}]->(node)
|
|
211
|
+
SET e = {uuid: $uuid, group_id: $group_id, created_at: $created_at}
|
|
212
|
+
RETURN e.uuid AS uuid
|
|
213
|
+
"""
|
|
214
|
+
case GraphProvider.NEPTUNE:
|
|
215
|
+
return """
|
|
216
|
+
MATCH (community:Community {uuid: $community_uuid})
|
|
217
|
+
MATCH (node {uuid: $entity_uuid})
|
|
218
|
+
WHERE node:Entity OR node:Community
|
|
219
|
+
MERGE (community)-[r:HAS_MEMBER {uuid: $uuid}]->(node)
|
|
220
|
+
SET r.uuid= $uuid
|
|
221
|
+
SET r.group_id= $group_id
|
|
222
|
+
SET r.created_at= $created_at
|
|
223
|
+
RETURN r.uuid AS uuid
|
|
224
|
+
"""
|
|
225
|
+
case GraphProvider.KUZU:
|
|
226
|
+
return """
|
|
227
|
+
MATCH (community:Community {uuid: $community_uuid})
|
|
228
|
+
MATCH (node:Entity {uuid: $entity_uuid})
|
|
229
|
+
MERGE (community)-[e:HAS_MEMBER {uuid: $uuid}]->(node)
|
|
230
|
+
SET
|
|
231
|
+
e.group_id = $group_id,
|
|
232
|
+
e.created_at = $created_at
|
|
233
|
+
RETURN e.uuid AS uuid
|
|
234
|
+
UNION
|
|
235
|
+
MATCH (community:Community {uuid: $community_uuid})
|
|
236
|
+
MATCH (node:Community {uuid: $entity_uuid})
|
|
237
|
+
MERGE (community)-[e:HAS_MEMBER {uuid: $uuid}]->(node)
|
|
238
|
+
SET
|
|
239
|
+
e.group_id = $group_id,
|
|
240
|
+
e.created_at = $created_at
|
|
241
|
+
RETURN e.uuid AS uuid
|
|
242
|
+
"""
|
|
243
|
+
case _: # Neo4j
|
|
244
|
+
return """
|
|
245
|
+
MATCH (community:Community {uuid: $community_uuid})
|
|
246
|
+
MATCH (node:Entity | Community {uuid: $entity_uuid})
|
|
247
|
+
MERGE (community)-[e:HAS_MEMBER {uuid: $uuid}]->(node)
|
|
248
|
+
SET e = {uuid: $uuid, group_id: $group_id, created_at: $created_at}
|
|
249
|
+
RETURN e.uuid AS uuid
|
|
250
|
+
"""
|
|
122
251
|
|
|
123
252
|
|
|
124
253
|
COMMUNITY_EDGE_RETURN = """
|