Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.
01-20-2019 05:08 PM
I am attempting to use two separate lists to create nodes/relationships in cypher.
My current solution is:
MERGE (t:Test)
WITH t
UNWIND ["a","b"] AS name // first list
MERGE (t)-[:FOO_REL]->(a:Foo { name: name })
WITH DISTINCT t
UNWIND [100,200] AS id // second list
MERGE (t)-[:BAR_REL]->(b:Bar { id: id })
This works as long as both lists have entries in them. However, if the first list is empty then the second list is never unwound (and no :Bar
nodes are created).
How would I go about chaining UNWIND
s such that I can create nodes/relationships from two separate lists in a single query?
01-21-2019 08:36 AM
I ended up using FOREACH
instead of UNWIND
. Since this uses parentheses around the inner query, they are clearly delimited from eachother:
MERGE (t:Test)
FOREACH (name in ["a","b"] |
MERGE (t)-[:FOO_REL]->(a:Foo { name: name })
)
FOREACH (id in [100,200] |
MERGE (t)-[:BAR_REL]->(b:Bar { id: id })
)
01-21-2019 11:40 AM
Using FOREACH is the quicker solution for this.
If you needed to use UNWIND, we do have some documentation on this effect (as this is a cartesian product of each row with each element of the list on the row, an empty list means the row is wiped out, since it's a x 0), as well as a workaround (using CASE to unwind [null] instead, the single null element will ensure the row stays).
02-18-2020 01:21 PM
This helped me for the current scenario as i did not had any kind of match
statement, but if i have a match
statement, i think i need to use unwind
... in that case how to chain two or more unwinds together... i tried for a create case where my last unwind always repeated to the array length of the first unwind
So for the following query
with dt
unwind [{ name1:"value", name2: "value" }, { name1:"value", name2: "value" }] as propertyObj
create (dt)-[:HAS_DTP]->(p:DTP {
name1: propertyObj.name1,
name2: propertyObj.name2
})
with dt
unwind [{ name1:"value", name2: "value" }, { name1:"value", name2: "value" }] as variableObj
create (dt)-[:HAS_DTV]->(tv:DTV:RDTV {
name1: variableObj.name1,
name2: variableObj.name2,
})
with dt
return dt.uuid as id
the last is running 4 times but it should run 2 times as you see.
02-18-2020 02:04 PM
I honestly ain't sure what you're asking, but you certainly don't need to use UNWIND
just because you're using MATCH
. Here's an example for the movie recommendation dataset that comes with Neo4J:
MATCH (m:Movie {title:"Toy Story"})
FOREACH (usr in [{ name: "birjolaxew" }, { name: "mithun.das"}] |
CREATE (m)-[:TEST_REL]->(u:User {name: usr.name})
)
FOREACH (dir in [{ name: "foo" }, { name: "baz" }] |
CREATE (m)-[:TEST_REL_2]->(u:Director {name: dir.value1 })
)
RETURN m
05-15-2020 07:16 PM
How would you go about this with a FOREACH if you wanted to map relationships from a to 100, and b to 200 in the same query?
05-17-2020 09:27 AM
I think you'd need to add to your description, I'm not quite following your use case.
Though keep in mind that you cannot MATCH or otherwise introduce variables within a foreach and pass them back out, so if you're looking for a means to generate something from a FOREACH, and work with them outside of the FOREACH in the query (not just writing them to the graph), then FOREACH is not the right approach. You'd likely need to turn to UNWIND unless there's an APOC proc or function that could help.
02-18-2020 02:36 PM
First of all thank you for taking the time to answer my question.
Secondly I am sorry if I could not represent my question properly...
My question was bascially how to run two unwind queries simultaneously.
In my sample query... the last unwind should create 2 DTV instead it was creating 4.
I solved it using Foreach.
However... i was thinking of case when say i have to match, delete and create is that also possible via Foreach i will defintely give a try. Also somehow if i have to use uniwnd in any case, how to use it together or you advise to never do it?
02-18-2020 02:42 PM
Think of unwind like FOR loop in any language with all the code below unwind is in the scope.
When you add another unwind below an unwind it's like you are doing a nested FOR loop.
More unwinds you add more nested loops you are adding. So, the total number of iterations the lowest unwind segment is multiple of all unwind data.
FOREACH limits the scope, so it does not repeat the logic.
Hope this clears your confusion.
02-19-2020 01:15 AM
Yes it does.... thank you taking time to explain... my doubts are really clear on this
02-18-2020 02:47 PM
You just need to understand what it's doing, and whether that's the right thing for your query. You need to think about the cardinality, the number of rows being processed, as this is what's causing the duplications.
In your example we can clearly see the number of entries in each list, so that helps illustrate what's going on. Remember that Cypher operations execute per row (and this is what allows UNWIND to have an iterative effect even though it's not really an iterating structure like FOREACH):
Assuming at the staring WITH that we have 1 row.
At the first UNWIND we now have entry rows x the number of list elements, so 1 x 2 = 2 rows.
The CREATE occurs, creating the pattern once for each row. 2 :DTP nodes are created, with a relationship created to each.
The with dt
does NOT alter the cardinality, you still have 2 rows, with the same dt
node for each.
The next UNWIND executes: for each of the 2 input rows, they are multiplied by the number of list elements, so 2 x 2 = 4 rows.
The CREATE executes for each row, 4 new :DTV:RDTV nodes are created.
The with dt
again does not change the cardinality, you have 4 rows with the same dt
for each.
4 rows are returned with the same dt node's uuid property as id.
If you wanted to reset the cardinality at each WITH clause, use WITH DISTINCT dt
or WITH dt, count(dt)
or a similar aggregation to ensure dt is distinct.
02-19-2020 01:18 AM
And this is the answer I was exactly looking for ..... it is clear to me how to use UNWIND now... thank you @andrew.bowman
04-29-2021 12:50 AM
Hi @birjolaxew, you can use a apoc.do.when check up. If first list is empty your query will continue.
MERGE (t:Test)
WITH t, ['A', 'B'] as list
CALL apoc.do.when(
size(list)>0,
'UNWIND list AS name MERGE (t)-[:FOO_REL]->(a:Foo { name: name }) RETURN name',
'',
{list:list})
YIELD value // first list
WITH DISTINCT t
UNWIND [100,200] AS id // second list
MERGE (t)-[:BAR_REL]->(b:Bar { id: id })
All the sessions of the conference are now available online