WITH date(datetime({ epochMillis: apoc.date.parse($endDate, 'ms', 'dd-MMM-yyyy') })) AS endDate MATCH (nrsr:NrSoundRecording:NrOwnership { id: $srId }) CALL apoc.lock.nodes([nrsr]) // Lock origin-vendor monetize rels so concurrent property updates can't race the validate-then-mutate sequence. OPTIONAL MATCH (nrsr)-[originRel:HAS_OWNERSHIP_RULE_MONETIZE]->(:NrOwnershipService) WHERE originRel.vendorId = $vendorId WITH nrsr, endDate, collect(originRel) AS originRels CALL apoc.lock.all([], originRels) WITH nrsr, endDate // Collects any WORLD monetize rule OR monetize rule that starts after cutoff. // Outer CASE returns NULL on the OPTIONAL MATCH null-fallback row so collect drops it. OPTIONAL MATCH (nrsr)-[unsupportedRel:HAS_OWNERSHIP_RULE_MONETIZE]->(unsupportedService:NrOwnershipService) WHERE unsupportedRel.vendorId = $vendorId AND (unsupportedService.territory = 'WORLD' OR unsupportedRel.startDate > endDate) WITH nrsr, endDate, collect( CASE WHEN unsupportedRel IS NULL THEN NULL ELSE { ruleId: unsupportedRel.id, reason: CASE WHEN unsupportedService.territory = 'WORLD' THEN 'WORLD_MONETIZE' ELSE 'STARTS_AFTER_CUTOFF' END } END ) AS unsupportedRules // Eligible rules to end-date. size(unsupportedRules) = 0 guard makes the FOREACH a no-op on abort. OPTIONAL MATCH (nrsr)-[rel:HAS_OWNERSHIP_RULE_MONETIZE]->(:NrOwnershipService) WHERE size(unsupportedRules) = 0 AND rel.vendorId = $vendorId AND (rel.endDate IS NULL OR rel.endDate > endDate) WITH unsupportedRules, endDate, collect(rel) AS rulesToEnd FOREACH (rel IN rulesToEnd | SET rel.endDate = endDate, rel.lastModifiedAt = datetime(), rel.lastModifiedBy = "graphql-neighbouring-rights/endOwnershipNrRules/" + $identityId ) RETURN unsupportedRules, [rel IN rulesToEnd | rel.id] AS endedIds;