I am trying to execute the following cypher query to create a relationship between all the nodes which are present in the collection nodelist.
START n=node(*)
MATCH(n)
WHERE has(n.Gender)
WITH n.Gender as Gender
WITH collect(n) as nodelist
FOREACH (i in RANGE(0,length(nodelist-2))|
FOREACH(si in [nodelist[i]|
FOREACH(si2 in[nodelist[i+1] |
CREATE UNIQUE (si-[:KNOWS]->si2))))
It gives me an error in the second FOREACH loop at nodelist[i] .
I tried putting nodelist(i) and it gives me an error saying
`-' expected but '|' found
Any idea where I am going wrong?
If you want to create a knows relation between all nodes in a collection:
MATCH(n)
WHERE has(n.Gender)
WITH collect(n) as nodelist
FOREACH (x IN nodelist |
FOREACH (y IN filter(z IN nodelist WHERE NOT (z=x)) |
MERGE (x)-[:knows]-(y)
)
)
Note the direction of your relations. Each node will be connected to each other node in nodelist once in a particular direction. If you want double relations i.e. a-[knows]->b as well as b-[:knows]->a you'll have to modify the query accordingly.
Your first FOREACH should be like below. Also I think line 4 is redundant in your query. In case you want to collect gender you can directly do collect(n.Gender) as Gender. Incase you do need to use that line you need to pass n in the first WITH too, otherwise it wont be reconginized at the second WITH clause. Also you dont need to start with START n=node(*)
Try below
MATCH(n)
WHERE has(n.Gender)
WITH collect(n) as nodelist,collect(n.Gender) as Gender
FOREACH (i in RANGE(0,length(nodelist)-2)|
FOREACH(si in [nodelist[i]]|
FOREACH(si2 in [nodelist[i+1]] |
CREATE UNIQUE (si)-[:KNOWS]->(si2) )))
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´m struggling to do a linked list with a cypher statement. I´m trying to do the following for each k. The linked list and :NEXT relationship should be isolated by k.
match (elem:Event)<-[r:HAS_EVENT]-(k)
WITH elem ORDER BY elem.id ASC
WITH COLLECT(elem) AS elems
FOREACH (n IN RANGE(0, LENGTH(elems)-2) |
FOREACH (prec IN [elems[n]] |
FOREACH (next IN [elems[n+1]] |
MERGE (prec)-[:NEXT]->(next))))
Since it´s not possible to do a MATCH statement inside a FOREACH statement I tried MATCH (a:Some_Label) with a as x UNWIND x as k and other possible ways to run this code by for each k (id lockup etc). I always end up with a long chain of :NEXT throu all :Event nodes, suggesting that the match statement considers all k nodes. I need help on how to match :Some_Label nodes and pass each one of them to the above code.
Sounds like there are two parts to this. First, matching on just the k nodes that you want included in a linked list (filtering, sorting, and so forth).
Once you have this, all you need to do is COLLECT them into a list, and use apoc.coll.pairs([list]) YIELD value . This changes the list into a list of pairs of each sequential element ([[first, second], [second, third]...]). You just have to watch out for the last pair, which is [last, null].
At that point all you need to do is do a FOREACH on each element (pair) and merge your :NEXT relationship.
EDIT
Looks like you're using the periodic function to iterate over the k elements you're interested in. You can use the COLLECT() and pairs() function to replace your nested FOREACH loops.
UPDATE
While there is an apoc.coll.pairsMin() function that ensures we don't have a null within the last pairing, it's highly recommended nowadays to use apoc.nodes.link() instead, passing the ordered list of nodes to link together and the type of the relationship to use:
Match (elem:Event)<-[r:HAS_EVENT]-(k)
WITH elem ORDER BY elem.id ASC
WITH COLLECT(elem) AS elems
CALL apoc.nodes.link(elems, 'NEXT')
RETURN elems
I assume you want to create the NEXT chain within a list of Events. And each Event list comes from a MATCH to a specific k node.
I think you might be able to achieve this with just a slightly different WITH clause followed by the ordering step:
MATCH (k:Ticket) // however you match here...
WITH k
// MATCH your k to Events
MATCH (elem:Event)<-[r:HAS_EVENT]-(k)
// include k in the WITH clause
// now you have one result 'row' per k with the matching elem nodes
WITH k, COLLECT(elem) AS elems
// UNWIND, ORDER BY and collect() again to sort the list in each row
UNWIND elems AS x
WITH x ORDER BY x.id ASC
WITH collect(x) AS ordered_elems_per_k
// now the foreach should be applied for each k/list of elem pair
FOREACH (n IN RANGE(0, LENGTH(ordered_elems_per_k)-2) |
FOREACH (prec IN [ordered_elems_per_k[n]] |
FOREACH (next IN [ordered_elems_per_k[n+1]] |
MERGE (prec)-[:NEXT]->(next))))
I think it's not possible to combine ORDER BY and collect(). This would solve your problem in a simpler way, but something along this line should work.
I managed to solve this via;
CALL apoc.periodic.iterate('Match (a:Ticket) return id(a) as id_p',
'match (elem:Event)<-[r:HAS_EVENT]-(k) where id(k)={id_p}
WITH elem ORDER BY elem.id ASC WITH COLLECT(elem) AS elems
FOREACH (n IN RANGE(0, LENGTH(elems)-2) |
FOREACH (prec IN [elems[n]] |
FOREACH (next IN [elems[n+1]] | MERGE (prec)-[:NEXT]->(next))))',
{batchSize:1000,parallel:true}) YIELD batches, total
I have a problem to connect the list of movies in the database in form of a sorted linked list (in a single cypher query).
Number of movie nodes: 25L
MATCH (movie:Movie)
WITH movie
ORDER BY movie.rating DESC
WITH collect(movie) as p
FOREACH (n IN nodes(p)| CREATE PREV_MOVIE-[:NextMovie]->(n) )
RETURN p
This will need reference to the previous node PREV_MOVIE and the current node n in the FOREACH to create a relationship between the two. How to find the reference to the previous node PREV_MOVIE here, or is there any other way to do the same?
you need to apply some FOREACH magic:
MATCH (movie:Movie)
WITH movie
ORDER BY movie.rating DESC
WITH collect(movie) as p
FOREACH(i in RANGE(0, length(p)-2) |
FOREACH(p1 in [p[i]] |
FOREACH(p2 in [p[i+1]] |
CREATE UNIQUE (p1)-[:PREV_MOVIE]->(p2))))
In the tutorial part of the Neo4j Documentation there is a Linked-list chapter, maybe that will help you:
http://docs.neo4j.org/chunked/stable/cypherdoc-linked-lists.html
I have a neo4j database and I would like to use the result of a part of the cypher code (a set of node ids) to use in the second part:
Something like:
MATCH ()-[:KNOWS]->(b)
FOREACH (n IN distinct(id(b))| SET n :label)
In a pure cypher code, is there a way to loop over the result "distinct(id(b))" and apply to each element another query?
Two problems with the original query:
You have to have a collection to use FOREACH.
You bind n to a node id, and you can't set labels on node ids, only on nodes.
You can use FOREACH to set labels by doing
MATCH ()-[:KNOWS]->(b)
WITH collect (distinct b) as bb
FOREACH (b IN bb | SET b:MyLabel)
In this case you don't need to do it as a collection, you can just do
MATCH ()-[:KNOWS]->(b)
WITH distinct b
SET b:MyLabel
And in general you can pipe results to an additional query part with WITH
I obtained the needed result with:
MATCH ()-[:KNOWS]->(b)
WITH DISTINCT (b)
RETURN id(b)
So I'm trying to send a query that will Merge a number of nodes and I want the query to return the nodes that it created but I can't put the return statement inside the FOREACH so is there a way to collect the nodes and then return that collection at the end?
FOREACH (tagName in {tags} |
MERGE (n:items {classid:tagName.pClassid})
ON CREATE
COLLECT(n) as allCreatedNodes)
RETURN allCreatedNodes;
"params" : {
"tags": [{"pClassid" : 1}, {"pClassid" : 2}, {"pClassid" : 3}]
}
Right now that's unfortunately not possible.
The only thing that you can do (if you really need it is to look the nodes up after the fact. And unfortunately using IN with a MATCH is not optimized yet.
FOREACH (tagName in {tags} | MERGE (n:items {classid:tagName.pClassid}))
WITH [t IN {tags} | t.pClassid ] as classIds
MATCH (allCreatedNodes:items)
WHERE allCreatedNodes.classid IN classIds
RETURN allCreatedNodes;
One thing you could try is creating a temporary graph structure to quickly look up the nodes after foreach. It probably won't be worth it if the tag collection is small, but it's sometimes a useful strategy and until label indices handle IN collection style lookups it may be worth a shot. Basically it means maintaining some small graph structure as a transient (query-local or query-type-local) index. In this case,
begin the query by creating (query-local) or merging (query-type-local) an index node
relate all the merged nodes to it
and when its time to return match from index node to the created nodes
delete the relationships (and index node if query-local) and return.
I can't test this at the moment, but you could try something like this
CREATE (index)
FOREACH (tagName in {tags} |
MERGE (n:items {classid:tagName.pClassid} )
CREATE index-[:TRANSIENT]->n
)
WITH index
MATCH index-[t:TRANSIENT]->n
DELETE t, index
RETURN n
(This may be overkill for your type of query. If you try it, do try profiling the query in comparison with a re-fetch from label index and post back.)