Cypher: Node not found in same query - path

I have this test database:
I want to remove that path with two +B nodes to the right. This case generally can be described as a path, that contains PS nodes (+B nodes are also PS nodes, that do not have an incoming :SOURCE edge. The sub path I want to delete is the one between (writer) (excluding) and and that node that has no incoming :SOURCE edge (including).
For that I have this query:
MATCH p1=(writer:A {type:'writer'})-[*]->(Q:PS)-[:TARGET*]->(T)
WITH (Q), (writer)
MATCH (Q)
WHERE NOT ()-[:SOURCE]->(Q)
WITH (Q), (writer)
MATCH p2=(writer)-[*]->(Q)
WHERE ANY (x IN NODES(p2)[1..] WHERE x:PS AND NOT ()-[:SOURCE]->(x))
WITH REDUCE(s = [], y IN NODES(p2)[1..] | CASE
WHEN y:PS THEN s + y
ELSE s END) AS todo
FOREACH (z IN todo | DETACH DELETE z);
It first identifies the said node(s) and then passes them on to make a new path selection, that ends in that node. This all works correctly. What does not work is the very last part beginning with WITH REDUCE. It says it does not find Q, but Q does not even occur in that part.
The error is Node <some ID> not found. Why is that? Why does it not find the node again and why does it even try to in the last part? Cutting the last part off and the query works as intended up to that point.

[UPDATED]
That error typically means that you are trying to delete a node that has already been deleted, or to delete a relationship that has an endpoint node that has already been deleted.
Try replacing your FOREACH clause with the following snippet, which eliminates duplicate nodes before attempting to delete them, and also deletes the relationships before the nodes:
UNWIND todo AS node
WITH DISTINCT node
MATCH (node)-[r]-()
WITH COLLECT(DISTINCT node) AS nodes, COLLECT(DISTINCT r) AS rels
FOREACH(r IN rels | DELETE r)
FOREACH(n IN nodes | DELETE n);
Also, your query seems to very inefficient. Here is a simpler version, which includes the above fix:
MATCH p=(:A {type:'writer'})-[*]->(:PS)-[:TARGET]->()
WHERE ANY (x IN NODES(p)[1..-1] WHERE x:PS AND NOT ()-[:SOURCE]->(x))
WITH REDUCE(s = [], y IN NODES(p)[1..] | CASE
WHEN y:PS THEN s + y
ELSE s END) AS todo
UNWIND todo AS node
WITH DISTINCT node
MATCH (node)-[r]-()
WITH COLLECT(DISTINCT node) AS nodes, COLLECT(DISTINCT r) AS rels
FOREACH(r IN rels | DELETE r)
FOREACH(n IN nodes | DELETE n);

Related

Neo4j - creating multiple relationships with FOREACH/CASE WHEN trick, and then return number of relationships

I am creating a node, and creating relationships between it and a bunch of different existing nodes at the same time. Some of the other nodes may not exist, so I am using the FOREACH/CASE WHEN trick described here by neo4j staff and also here on StackOverflow.
I'd like to return the count of each relationship that were created, but I can't pull that out of the FOREACH. My current query is something like:
CREATE (p:Paper {title: $title})
WITH p
OPTIONAL MATCH (a:Author) WHERE a.name IN $a_names
OPTIONAL MATCH (p2:Paper) WHERE p2.title IN $citee_titles
OPTIONAL MATCH (p3:Paper) WHERE p3.title IN $citer_titles
FOREACH (_ IN CASE WHEN a IS NOT NULL THEN [1] END | MERGE (a)-[:AUTHORED]->(p))
FOREACH (_ IN CASE WHEN p2 IS NOT NULL THEN [1] END | MERGE (p)-[:CITES]->(p2))
FOREACH (_ IN CASE WHEN p3 IS NOT NULL THEN [1] END | MERGE (p3)-[:CITES]->(p))
Any of $a_names, $citee_titles or $citer_titles can be an empty set or have no elements matching any existing nodes.
I have tried adding a WITH and additional MATCHes on the end, something like:
WITH p, a, p2, p3
OPTIONAL MATCH (a)-[r1:AUTHORED]->(p)
OPTIONAL MATCH (p)-[r2:CITES]->(p2)
OPTIONAL MATCH (p3)-[r3:CITES]->(p)
RETURN COUNT(DISTINCT r1), COUNT(DISTINCT r2), COUNT(DISTINCT r3)
It works, but runs into the original problem that the FOREACH solution was trying to solve - if there are no matches for r1 then I don't get results for r2 and r3.
Any thoughts on how to deal with this? My goal is to test that the number of relationships created matches what I expect based on the parameters passed in.
In your query, you are trying to create the relationships with the newly created paper node, which is p. Since, there is only one p node, to get the count of each type of node created by the FOREACH statement, you can simply return the count of distinct a, p2, p3 nodes matched by the filters, like this
CREATE (p:Paper {title: $title})
WITH p
OPTIONAL MATCH (a:Author) WHERE a.name IN $a_names
OPTIONAL MATCH (p2:Paper) WHERE p2.title IN $citee_titles
OPTIONAL MATCH (p3:Paper) WHERE p3.title IN $citer_titles
FOREACH (_ IN CASE WHEN a IS NOT NULL THEN [1] END | MERGE (a)-[:AUTHORED]->(p))
FOREACH (_ IN CASE WHEN p2 IS NOT NULL THEN [1] END | MERGE (p)-[:CITES]->(p2))
FOREACH (_ IN CASE WHEN p3 IS NOT NULL THEN [1] END | MERGE (p3)-[:CITES]->(p))
RETURN COUNT(DISTINCT a), COUNT(DISTINCT p2), COUNT(DISTINCT p3)

is it possible to iterate though property of relationship cypher

This is related to this question: How to store properties of a neo4j node as an array?
I would like to iterate through a property of a relationship and check max of that value and assign a new relationship of node1 and node2 and delete node1 from the pool and move to the second one. In other words as in the context of my previous question, How to assign a given employee to a given position based max(r.score) and move to the other employee who has a maximum r.score for another position? Thanks
Have this basic query to assign a position for the employee who has a maximum r.score w.r.t position and remove him from pool of candidates. However, I have to run this manually for the second position. Ideally I want something that checks length if available positions and then fills positions with max(r.score) and then stops when all positions are filled. may be returns a report of hired employees...
MATCH (e:Employee)-[r:FUTURE_POSITION]->(p:Position)
WITH MAX(r.score) as s
MATCH (e)-[r]->(p) WHERE r.score = s
CREATE (e)-[r2:YOUAREHIRED]->(p)
DELETE r
RETURN e.name, s
This query may work for you:
MATCH (:Employee)-[r:FUTURE_POSITION]->(p:Position)
WITH p, COLLECT(r) AS rs
WITH p, REDUCE(t = rs[0], x IN rs[1..] |
CASE WHEN x.score > t.score THEN x ELSE t END) AS maxR
WITH p, maxR, maxR.score AS maxScore, STARTNODE(maxR) AS e
CREATE (e)-[:YOUAREHIRED]->(p)
DELETE maxR
RETURN p, e.name AS name, maxScore;
The first WITH clause collects all the FUTURE_POSITION relationships for each p.
The second WITH clause obtains, for each p, the relationship with the maximum score.
The third WITH clause extracts the variables needed by subsequent clauses.
The CREATE clause creates the YOUAREHIRED relationship between e (the employee with the highest score for a given p) and p.
The DELETE clause deletes the FUTURE_POSITION relationship between e and p.
The RETURN clause returns each p, along with and the name of the employee who was just hired for p, and his score, maxScore.
[UPDATE]
If you want to delete all FUTURE_POSITION relationships of each p node that gets a YOUAREHIRED relationship, you can use this slightly different query:
MATCH (:Employee)-[r:FUTURE_POSITION]->(p:Position)
WITH p, COLLECT(r) AS rs
WITH p, rs, REDUCE(t = rs[0], x IN rs[1..] |
CASE WHEN x.score > t.score THEN x ELSE t END) AS maxR
WITH p, rs, maxR.score AS maxScore, STARTNODE(maxR) AS e
CREATE (e)-[:YOUAREHIRED]->(p)
FOREACH(x IN rs | DELETE x)
RETURN p, e.name AS name, maxScore;

cypher query to return or keep only the final sequence when variable length relationship identifiers are used

Is there a way to keep or return only the final full sequences of nodes instead of all subpaths when variable length identifiers are used in order to do further operations on each of the final full sequence path.
MATCH path = (S:Person)-[rels:NEXT*]->(E:Person)................
eg: find all sequences of nodes with their names in the given list , say ['graph','server','db'] with same 'seqid' property exists in the relationship in between.
i.e.
(graph)->(server)-(db) with same seqid :1
(graph)->(db)->(server) with same seqid :1 //there can be another matching
sequence with same seqid
(graph)->(db)->(server) with same seqid :2
Is there a way to keep only the final sequence of nodes say ' (graph)->(server)->(db)' for each sequences instead of each of the subpath of a large sequence like (graph)->(server) or (server)->(db)
pls help me to solve this.........
(I am using neo4j 2.3.6 community edition via java api in embedded mode..)
What we could really use here is a longestSequences() function that would do exactly what you want it to do, expand the pattern such that a and b would always be matched to start and end points in the sequence such that the pattern is not a subset of any other matched pattern.
I created a feature request on neo4j for exactly this: https://github.com/neo4j/neo4j/issues/7760
And until that gets implemented, we'll have to make do with some alternate approach. I think what we'll have to do is add additional matching to restrict a and b to start and end nodes of full sequences.
Here's my proposed query:
WITH ['graph', 'server' ,'db'] as names
MATCH p=(a)-[rels:NEXT*]->(b)
WHERE ALL(n in nodes(p) WHERE n.name in names)
AND ALL( r in rels WHERE rels[0]['seqid'] = r.seqid )
WITH names, p, a, rels, b
// check if b is a subsequence node instead of an end node
OPTIONAL MATCH (b)-[rel:NEXT]->(c)
WHERE c.name in names
AND rel.seqid = rels[0]['seqid']
// remove any existing matches where b is a subsequence node
WITH names, p, a, rels, b, c
WHERE c IS NULL
WITH names, p, a, rels, b
// check if a is a subsequence node instead of a start node
OPTIONAL MATCH (d)-[rel:NEXT]->(a)
WHERE d.name in names
AND rel.seqid = rels[0]['seqid']
// remove any existing matches where a is a subsequence node
WITH p, a, b, d
WHERE d IS NULL
RETURN p, a as startNode, b as endNode
MATCH (S:Person)-[r:NEXT]->(:Person)
// Possible starting node
WHERE NOT ( (:Person)-[:NEXT {seqid: r.seqid}]->(S) )
WITH S,
// Collect all possible values of `seqid`
collect (distinct r.seqid) as seqids
UNWIND seqids as seqid
// Possible terminal node
MATCH (:Person)-[r:NEXT {seqid: seqid}]->(E:Person)
WHERE NOT ( (E)-[:NEXT {seqid: seqid}]->(:Person) )
WITH S,
seqid,
collect(distinct E) as ES
UNWIND ES as E
MATCH path = (S)-[rels:NEXT* {seqid: seqid}]->(E)
RETURN S,
seqid,
path
[EDITED]
This query might do what you want:
MATCH (p1:Person)-[rel:NEXT]->(:Person)
WHERE NOT (:Person)-[:NEXT {seqid: rel.seqid}]->(p1)
WITH DISTINCT p1, rel.seqid AS seqid
MATCH path = (p1)-[:NEXT* {seqid: seqid}]->(p2:Person)
WHERE NOT (p2)-[:NEXT {seqid: seqid}]->(:Person)
RETURN path;
It first identifies all Person nodes (p1) with at least one outgoing NEXT relationship that have no incoming NEXT relationships (with the same seqid), and their distinct outgoing seqid values. Then it finds all "complete" paths (i.e., paths whose start and end nodes have no incoming or outgoing NEXT relationships with the desired seqid, respectively) starting at each p1 node and having relationships all sharing the same seqid. Finally, it returns each complete path.
If you just want to get the name property of all the Person nodes in each path, try this query (with a different RETURN clause):
MATCH (p1:Person)-[rel:NEXT]->(:Person)
WHERE NOT (:Person)-[:NEXT {seqid: rel.seqid}]->(p1)
WITH DISTINCT p1, rel.seqid AS seqid
MATCH path = (p1)-[:NEXT* {seqid: seqid}]->(p2:Person)
WHERE NOT (p2)-[:NEXT {seqid: seqid}]->(:Person)
RETURN EXTRACT(n IN NODES(path) | n.name);

how to remove Neo4j nodes with duplicate properties?

In Neo4j 2.1.6, I have nodes that are non-unique in respect of a certain property, inputID.
Using Cypher, how do I remove all nodes that are duplicates in terms of a given property, leaving only uniques?
I have tried the following...
MATCH (n:Input)
WITH n.inputID, collect(n) AS nodes
WHERE size(nodes) > 1
FOREACH (n in tail(nodes) | DELETE n)
...but it results in...
Expression in WITH must be aliased (use AS) (line 2, column 6)
"WITH n.inputID, collect(n) AS nodes"
^
Thanks,
G
You're not aliasing that WITH variable. Change this:
WITH n.inputID, collect(n) AS nodes
To this:
WITH n.inputID AS inputID, collect(n) AS nodes
As you correctly found out, using tail on a collection will let you remove the duplicates, don't forget to remove relationships before the node (DETACH) and alias the field as FrobberOfBits mentioned:
MATCH (n:Input)
WITH n.inputID AS inputID, collect(n) AS nodes
WHERE size(nodes) > 1
FOREACH (n in tail(nodes) | DETACH DELETE n)

Deleting a tree of nodes, using cypher?

I've got an arbitrary depth tree of nodes which I'd like to delete.
I'd expected this to work:
MATCH (a)<-[rels*]-(t)
WHERE ID(a)=135
FOREACH(r in rels | DELETE r)
DELETE t,a
but it fails:
javax.transaction.HeuristicRollbackException: Failed to commit transaction
Transaction(32, owner:"qtp1200654940-73")
[STATUS_NO_TRANSACTION,Resources=1], transaction rolled back --->
Node record Node[137,used=false,rel=347,prop=-1,labels=Inline(0x0:
[]),light] still has relationships
Why is this? Shouldn't the relationships be removed first? I imagine that it's iterating over all the found paths and processing the shortest ones first.
Has anyone got an idea of how to work around this?
The a node may have outgoing relationships, and any of the t nodes may also have incoming/outgoing relationships not in the path leading to a. Your query does not delete such relationships.
This query should delete the same nodes that your query attempted to delete, but it should also delete all the relationships that need to be deleted to allow that:
MATCH ()<-[r1*0..1]-(a)<-[rels*]-(t)-[r2*0..1]-()
WHERE ID(a)=135
FOREACH (x IN r1 | DELETE x)
FOREACH (x IN r2 | DELETE x)
FOREACH (x IN rels | DELETE x)
DELETE a, t

Resources