I run a Cypher query and update labels of the nodes matching a certain criteria. I also want to update nodes that do not pass that criteria in the same query, before I update the matched ones. Is there a construct in Cypher that can help me achieve this?
Here is a concrete formulation. I have a pool of labels from which I choose and assign to nodes. When I run a certain query, I assign one of those labels, l, to the nodes returned under the conditions specified by WHERE clause in the query. However, l could have been assigned to other nodes previously, and I want to rid all those nodes of l which are not the result of this query.
The conditions in WHERE clause could be arbitrary; hence simple negation would probably not work. An example code is as follows:
MATCH (v)
WHERE <some set of conditions>
// here I want to remove 'l' from the nodes
// not satisfied by the above condition
SET v:l
I have solved this problem by using a temporary label through this process:
Assign x to v.
Remove l from all nodes.
Assign l to all nodes containing x.
Removing x from all nodes.
Is there a better way to achieve this in Cypher?
This seems like one reasonable solution:
MATCH (v)
WITH REDUCE(s = {a:[], d:[]}, x IN COLLECT(v) |
CASE
WHEN <some set of conditions> AND NOT('l' IN LABELS(x)) THEN {a: s.a+x, d: s.d}
WHEN 'l' IN LABELS(x) THEN {a: s.a, d: s.d+x}
END) AS actions
FOREACH (a IN actions.a | SET a:l)
FOREACH (d IN actions.d | REMOVE d:l)
The above query tests every node, and remembers in the actions.a list the nodes that need the l label but do not yet have it, and in the actions.d list the nodes that have the label but should not. Then it performs the appropriate action for each list, without updating any nodes that are already OK.
Related
I'm trying to use Neo4j Cypher to implement the following function: given a node, check if it has any outgoing edges with a specific relationship type. If so, return the nodes it can reach out by those edges, otherwise delete this node. And my code is like this
MATCH (m:Node{Properties})
WITH (size((m)-[:type]->(:Node))) AS c,m
WHERE c=0
DETACH DELETE m
However I don't know how to apply the if/else condition here, and this code only implements part of what I need. I'd really appreciate your help and suggestions!
For example the database is like this:
A-[type]->B
A-[type]->C
If the original node is A and it has two edges with that type to B and C, then I want the query to return B and C as result.
If the original node is B, it should be deleted because there's no such outgoing edge from B.
[UPDATED]
The following query uses a FOREACH hack to conditionally delete m, and returns either the found n nodes, or NULL if there were none.
OPTIONAL MATCH (m:Node {...Properties...})-[:type]->(n:Node)
FOREACH(x IN CASE WHEN n IS NULL THEN [1] END | DETACH DELETE m)
RETURN n
You could also use the APOC procedure apoc.do.when instead of the FOREACH hack:
OPTIONAL MATCH (m:Node {...Properties...})-[:type]->(n:Node)
CALL apoc.do.when(n IS NULL, 'DETACH DELETE m', '', {m: m}) YIELD value
RETURN n
I have a couple of nodes and I need to add a incremental value in all nodes of a particular label.
match wd= (w:MYNODE)
forEach(n IN nodes(wd)|
set n.incrementalId=??
);
currently I have tried for each to traverse on each node but unable to get index of the each loop
ps: I have also tried with but unable to increment ie ++ the current value.
This is a more succinct version of #InverseFalcon's first query (and resembles the form of the query in your question):
MATCH (w:MYNODE)
WITH COLLECT(w) as ws
FOREACH(i IN RANGE(0, SIZE(ws)-1) | SET (ws[i]).incrementalId = i);
If the nodes already exist, then you'll have to use a more awkward approach using collections and indexes:
MATCH (w:MYNODE)
WITH collect(w) as myNodes
UNWIND range(0, size(myNodes)-1) as index
WITH index, myNodes[index] as node
SET node.incrementalId = index
If the nodes don't already exist, and you want to create some number of them with incremental ids, then it's an easier task using the range() function to produce a list of indexes:
UNWIND range(1, 100) as id
CREATE (:MYNODE {id:id})
I have 3 labels, A, B, and Z. A & B both have a relationship to Z. I want to find all the A nodes that do not have share any of nodes Z in common with B
Currently, doing a normal query where the relationship DOES exist, works.
MATCH (a:A)-[:rel1]->(z:Z)<-[:rel2]-(b:B { uuid: {<SOME ID>} })
RETURN DISTINCT a
But when I do
MATCH (a:A)
WHERE NOT (a)-[:rel1]->(z:Z)<-[:rel2]-(b:B { uuid: {<SOME ID>} }))
RETURN DISTINCT a
It throws an error
Neo4j::Server::CypherResponse::ResponseError: z not defined
Not sure if the syntax for this is incorrect, I tried WHERE NOT EXIST() but no luck.
The query is part of a larger one called through a rails app using neo4jrb / (Neo4j::Session.query)
This is a problem to do with the scope of your query. When you describe a node in a MATCH clause like the below
MATCH (n:SomeLabel)
You're telling cypher to look for a node with the label SomeLabel, and assign it to the variable n in the rest of the query, and at the end of the query, you can return the values stored in this node using RETURN n (unless you drop n by not including it in a WITH clause).
Later on in you query, if you want to MATCH another node, you can do it in reference to n, so for example:
MATCH (m:SomeOtherLabel)-[:SOME_RELATIONSHIP]-(n)
Will match a variable connected (in any direction) to the node n, with a label SomeOtherLabel, and assign it to the variable m for the rest of the query.
You can only assign nodes to variables like this in MATCH, OPTIONAL MATCH, MERGE, CREATE and (sort of) in WITH and UNWIND clauses (someone correct me here if I've missed one, I suppose you also do this in list comprehensions and FOREACH clauses).
In your second query, you are trying to find a node with the label A, which is not connected to a node with the label Z. However, the way you have written the query means that you are actually saying find a node with label A which is not connected via a rel1 relationship to the node stored as z. This will fail (and as shown, neo complains that z is not defined), because you can't create a new variable like this in the WHERE clause.
To correct your error, you need to remove the reference to the variable z, and ensure you have also defined the variable b containing your node before the WHERE clause. Now, you keep the label in the query, like the below.
MATCH (a:A)
MATCH (b:B { uuid: {<SOME ID>} })
WHERE NOT (a)-[:rel1]->(:Z)<-[:rel2]-(b) // changed this line
RETURN DISTINCT a
And with a bit of luck, this will now work.
You get the error because z is the identifier of a node that you are using in a where clause that you have not yet identified.
Since you know b already I would match it first and then use it in your where clause. You don't need to assign :Z an identifier, simply using the node label will suffice.
MATCH (b:B { uuid: {<SOME ID>} })
WITH b
MATCH (a:A)
WHERE NOT (a)-[:rel1]->(:Z)<-[:rel2]-(b)
RETURN DISTINCT a
I have a tree-like structure in neo4j and I want to find the first nodes with a specific label which has a path to the ancestor and does not have a node of the same type within that path.
For example:
/ (:Y) <- (a:X)
A <- (:Y) <- (c:X) <- (:Y) <- (d:X) <- (e:X)
\ (b:X)<- (f:X)
\ (g:X)
In this case 'A' is the ancestor, and I want the nodes a,b,c,g because they have the label 'X' and are not separated from the ancestor by another node with the label 'X'
I started with the query:
match (c :X)-[:X*1]->(A) return c
but it only returns [b,g] and not [c,g] because there are other labelled nodes between the ancestor and 'X' labelled nodes
You're going to want APOC Procedures for this, there are configuration options in some of their path expander procedures that do exactly what you want.
You'll need to use the labelFilter using the termination operator /, which means path expansion will stop at the first node of the given label, and that only nodes of those labels will be returned.
Something like:
...
// assume you've already matched to ancester 'a'
CALL apoc.path.subgraphNodes(a, {labelFilter:'/X'}) YIELD node
RETURN node
That should return the first :X nodes along each path from the starting node.
First, get your ancestor node (I pretend it's going to have a :Ancestor label in this case, you might want to adjust the query to reflect reality).
MATCH (a:Ancestor), then get all connected nodes with :X label for up to 2 levels deep: MATCH (a)<-[*0..2]-(n:X) and assure that those n nodes are not connected to other X in between: WHERE NOT (n)-->(:X) and return: RETURN n.
So the whole cypher query would look like this:
MATCH (a:Ancestor) MATCH (a)<-[*0..2]-(n:X) WHERE NOT (n)-->(:X) RETURN n.
I have a scenario where I have more than 2 random nodes.
I need to get all possible paths connecting all three nodes. I do not know the direction of relation and the relationship type.
Example : I have in the graph database with three nodes person->Purchase->Product.
I need to get the path connecting these three nodes. But I do not know the order in which I need to query, for example if I give the query as person-Product-Purchase, it will return no rows as the order is incorrect.
So in this case how should I frame the query?
In a nutshell I need to find the path between more than two nodes where the match clause may be mentioned in what ever order the user knows.
You could list all of the nodes in multiple bound identifiers in the start, and then your match would find the ones that match, in any order. And you could do this for N items, if needed. For example, here is a query for 3 items:
start a=node:node_auto_index('name:(person product purchase)'),
b=node:node_auto_index('name:(person product purchase)'),
c=node:node_auto_index('name:(person product purchase)')
match p=a-->b-->c
return p;
http://console.neo4j.org/r/tbwu2d
I actually just made a blog post about how start works, which might help:
http://wes.skeweredrook.com/cypher-it-all-starts-with-the-start/
Wouldn't be acceptable to make several queries ? In your case you'd automatically generate 6 queries with all the possible combinations (factorial on the number of variables)
A possible solution would be to first get three sets of nodes (s,m,e). These sets may be the same as in the question (or contain partially or completely different nodes). The sets are important, because starting, middle and end node are not fixed.
Here is the code for the Matrix example with added nodes.
match (s) where s.name in ["Oracle", "Neo", "Cypher"]
match (m) where m.name in ["Oracle", "Neo", "Cypher"] and s <> m
match (e) where e.name in ["Oracle", "Neo", "Cypher"] and s <> e and m <> e
match rel=(s)-[r1*1..]-(m)-[r2*1..]-(e)
return s, r1, m, r2, e, rel;
The additional where clause makes sure the same node is not used twice in one result row.
The relations are matched with one or more edges (*1..) or hops between the nodes s and m or m and e respectively and disregarding the directions.
Note that cypher 3 syntax is used here.