@@ -40,7 +40,13 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
4040UNWIND $node_diff_dicts AS node_diff_map
4141CALL {
4242 WITH node_diff_map
43- WITH node_diff_map, CASE
43+ MATCH (n:Node {uuid: node_diff_map.uuid})
44+ RETURN n
45+ }
46+ WITH n, node_diff_map
47+ CALL {
48+ WITH n, node_diff_map
49+ WITH n, node_diff_map, CASE
4450 WHEN node_diff_map.action = "ADDED" THEN "active"
4551 WHEN node_diff_map.action = "REMOVED" THEN "deleted"
4652 ELSE NULL
@@ -49,33 +55,21 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
4955 // ------------------------------
5056 // only make IS_PART_OF updates if node is ADDED or REMOVED
5157 // ------------------------------
52- WITH node_diff_map, node_rel_status
53- WITH node_diff_map, node_rel_status
58+ WITH n, node_diff_map, node_rel_status
59+ WITH n, node_diff_map, node_rel_status
5460 WHERE node_rel_status IS NOT NULL
5561 MATCH (root:Root)
56- MATCH (n:Node {uuid: node_diff_map.uuid})
57- // ------------------------------
58- // check if IS_PART_OF relationship with node_rel_status already exists on the target branch
59- // ------------------------------
60- CALL {
61- WITH root, n, node_rel_status
62- OPTIONAL MATCH (root)<-[r_root:IS_PART_OF {branch: $target_branch}]-(n)
63- WHERE r_root.status = node_rel_status
64- AND r_root.from <= $at
65- AND (r_root.to >= $at OR r_root.to IS NULL)
66- RETURN r_root
67- }
6862 // ------------------------------
6963 // set IS_PART_OF.to on source branch and, optionally, target branch
7064 // ------------------------------
71- WITH root, r_root, n, node_rel_status
65+ WITH root, n, node_rel_status
7266 CALL {
7367 WITH root, n, node_rel_status
7468 OPTIONAL MATCH (root)<-[source_r_root:IS_PART_OF {branch: $source_branch, status: node_rel_status}]-(n)
7569 WHERE source_r_root.from <= $at AND source_r_root.to IS NULL
7670 SET source_r_root.to = $at
7771 }
78- WITH root, r_root, n, node_rel_status
72+ WITH root, n, node_rel_status
7973 CALL {
8074 WITH root, n, node_rel_status
8175 OPTIONAL MATCH (root)<-[target_r_root:IS_PART_OF {branch: $target_branch, status: "active"}]-(n)
@@ -86,10 +80,183 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
8680 // ------------------------------
8781 // create new IS_PART_OF relationship on target_branch
8882 // ------------------------------
89- WITH root, r_root, n, node_rel_status
90- WHERE r_root IS NULL
91- CREATE (root)<-[:IS_PART_OF { branch: $target_branch, branch_level: $branch_level, from: $at, status: node_rel_status }]-(n)
83+ WITH root, n, node_rel_status
84+ CALL {
85+ WITH root, n, node_rel_status
86+ OPTIONAL MATCH (root)<-[r_root:IS_PART_OF {branch: $target_branch}]-(n)
87+ WHERE r_root.status = node_rel_status
88+ AND r_root.from <= $at
89+ AND (r_root.to >= $at OR r_root.to IS NULL)
90+ WITH root, r_root, n, node_rel_status
91+ WHERE r_root IS NULL
92+ CREATE (root)
93+ <-[:IS_PART_OF { branch: $target_branch, branch_level: $branch_level, from: $at, status: node_rel_status }]
94+ -(n)
95+ }
96+ }
97+ WITH n, node_diff_map
98+ CALL {
99+ WITH n, node_diff_map
100+ UNWIND node_diff_map.attributes AS attribute_diff_map
101+ // ------------------------------
102+ // handle updates for attributes under this node
103+ // ------------------------------
104+ CALL {
105+ WITH n, attribute_diff_map
106+ WITH n, attribute_diff_map.name AS attr_name, CASE
107+ WHEN attribute_diff_map.action = "ADDED" THEN "active"
108+ WHEN attribute_diff_map.action = "REMOVED" THEN "deleted"
109+ ELSE NULL
110+ END AS attr_rel_status
111+ CALL {
112+ WITH n, attr_name
113+ MATCH (n)-[:HAS_ATTRIBUTE]->(a:Attribute {name: attr_name})
114+ RETURN a
115+ LIMIT 1
116+ }
117+ WITH n, attr_rel_status, a
118+ // ------------------------------
119+ // set HAS_ATTRIBUTE.to on source branch if necessary
120+ // ------------------------------
121+ CALL {
122+ WITH n, attr_rel_status, a
123+ OPTIONAL MATCH (n)
124+ -[source_r_attr:HAS_ATTRIBUTE {branch: $source_branch, status: attr_rel_status}]
125+ ->(a)
126+ WHERE source_r_attr.from <= $at AND source_r_attr.to IS NULL
127+ SET source_r_attr.to = $at
128+ }
129+ WITH n, attr_rel_status, a
130+ // ------------------------------
131+ // conditionally create new HAS_ATTRIBUTE relationship on target_branch, if necessary
132+ // ------------------------------
133+ CALL {
134+ WITH n, attr_rel_status, a
135+ OPTIONAL MATCH (n)-[r_attr:HAS_ATTRIBUTE {branch: $target_branch}]->(a)
136+ WHERE r_attr.status = attr_rel_status
137+ AND r_attr.from <= $at
138+ AND (r_attr.to >= $at OR r_attr.to IS NULL)
139+ WITH n, r_attr, attr_rel_status, a
140+ WHERE r_attr IS NULL
141+ CREATE (n)-[:HAS_ATTRIBUTE { branch: $target_branch, branch_level: $branch_level, from: $at, status: attr_rel_status }]->(a)
142+ }
143+ RETURN a
144+ }
145+ RETURN a
146+ }
147+ WITH n, node_diff_map
148+ CALL {
149+ WITH n,node_diff_map
150+ UNWIND node_diff_map.relationships AS relationship_diff_map
151+ // ------------------------------
152+ // handle updates for relationships under this node
153+ // ------------------------------
154+ CALL {
155+ WITH n, relationship_diff_map
156+ WITH n, relationship_diff_map.peer_id AS rel_peer_id, relationship_diff_map.name AS rel_name, CASE
157+ WHEN relationship_diff_map.action = "ADDED" THEN "active"
158+ WHEN relationship_diff_map.action = "REMOVED" THEN "deleted"
159+ ELSE NULL
160+ END AS related_rel_status
161+ // ------------------------------
162+ // set IS_RELATED.to on source branch and, optionally, target_branch
163+ // ------------------------------
164+ CALL {
165+ WITH n, rel_name, rel_peer_id, related_rel_status
166+ MATCH (n)
167+ -[source_r_rel_1:IS_RELATED {branch: $source_branch, status: related_rel_status}]
168+ -(r:Relationship {name: rel_name})
169+ -[source_r_rel_2:IS_RELATED {branch: $source_branch, status: related_rel_status}]
170+ -(:Node {uuid: rel_peer_id})
171+ WHERE source_r_rel_1.from <= $at AND source_r_rel_1.to IS NULL
172+ AND source_r_rel_2.from <= $at AND source_r_rel_2.to IS NULL
173+ SET source_r_rel_1.to = $at
174+ SET source_r_rel_2.to = $at
175+
176+ // ------------------------------
177+ // determine the directions of each IS_RELATED
178+ // ------------------------------
179+ RETURN r, CASE
180+ WHEN startNode(source_r_rel_1).uuid = n.uuid THEN "r"
181+ ELSE "l"
182+ END AS r1_dir,
183+ CASE
184+ WHEN startNode(source_r_rel_2).uuid = r.uuid THEN "r"
185+ ELSE "l"
186+ END AS r2_dir
187+ }
188+ WITH n, r, r1_dir, r2_dir, rel_name, rel_peer_id, related_rel_status
189+ CALL {
190+ WITH n, rel_name, rel_peer_id, related_rel_status
191+ OPTIONAL MATCH (n)
192+ -[target_r_rel_1:IS_RELATED {branch: $target_branch, status: "active"}]
193+ -(:Relationship {name: rel_name})
194+ -[target_r_rel_2:IS_RELATED {branch: $target_branch, status: "active"}]
195+ -(:Node {uuid: rel_peer_id})
196+ WHERE related_rel_status = "deleted"
197+ AND target_r_rel_1.from <= $at AND target_r_rel_1.to IS NULL
198+ AND target_r_rel_2.from <= $at AND target_r_rel_2.to IS NULL
199+ SET target_r_rel_1.to = $at
200+ SET target_r_rel_2.to = $at
201+ }
202+ WITH n, r, r1_dir, r2_dir, rel_name, rel_peer_id, related_rel_status
203+ // ------------------------------
204+ // conditionally create new IS_RELATED relationships on target_branch, if necessary
205+ // ------------------------------
206+ CALL {
207+ WITH n, r, r1_dir, r2_dir, rel_name, rel_peer_id, related_rel_status
208+ MATCH (p:Node {uuid: rel_peer_id})
209+ OPTIONAL MATCH (n)
210+ -[r_rel_1:IS_RELATED {branch: $target_branch, status: related_rel_status}]
211+ -(:Relationship {name: rel_name})
212+ -[r_rel_2:IS_RELATED {branch: $target_branch, status: related_rel_status}]
213+ -(p)
214+ WHERE r_rel_1.from <= $at
215+ AND (r_rel_1.to >= $at OR r_rel_1.to IS NULL)
216+ AND r_rel_2.from <= $at
217+ AND (r_rel_2.to >= $at OR r_rel_2.to IS NULL)
218+ WITH n, r, r1_dir, r2_dir, p, related_rel_status, r_rel_1, r_rel_2
219+ WHERE r_rel_1 IS NULL
220+ AND r_rel_2 IS NULL
221+ // ------------------------------
222+ // create IS_RELATED relationships with directions maintained from source
223+ // ------------------------------
224+ CALL {
225+ WITH n, r, r1_dir, related_rel_status
226+ WITH n, r, r1_dir, related_rel_status
227+ WHERE r1_dir = "r"
228+ CREATE (n)
229+ -[:IS_RELATED {branch: $target_branch, branch_level: $branch_level, from: $at, status: related_rel_status}]
230+ ->(r)
231+ }
232+ CALL {
233+ WITH n, r, r1_dir, related_rel_status
234+ WITH n, r, r1_dir, related_rel_status
235+ WHERE r1_dir = "l"
236+ CREATE (n)
237+ <-[:IS_RELATED {branch: $target_branch, branch_level: $branch_level, from: $at, status: related_rel_status}]
238+ -(r)
239+ }
240+ CALL {
241+ WITH r, p, r2_dir, related_rel_status
242+ WITH r, p, r2_dir, related_rel_status
243+ WHERE r2_dir = "r"
244+ CREATE (r)
245+ -[:IS_RELATED {branch: $target_branch, branch_level: $branch_level, from: $at, status: related_rel_status}]
246+ ->(p)
247+ }
248+ CALL {
249+ WITH r, p, r2_dir, related_rel_status
250+ WITH r, p, r2_dir, related_rel_status
251+ WHERE r2_dir = "l"
252+ CREATE (r)
253+ <-[:IS_RELATED {branch: $target_branch, branch_level: $branch_level, from: $at, status: related_rel_status}]
254+ -(p)
255+ }
256+ }
257+ }
92258 }
93259}
260+ RETURN NULL AS done
94261 """
95262 self .add_to_query (query = query )
0 commit comments