Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.
07-28-2020 07:23 PM
Hi all,
I want to know if it is possible when using UNWIND to only iterate a certain part of the query instead of iterating the whole thing.
This is a simplified example but gets my point across
UNWIND $user AS user
CREATE (u:User)
SET u += user
WITH u
// Attaching house to user
UNWIND $houses AS house
MATCH (h:House)
WHERE h.address = house.address
CREATE (u)-[:OWNS]->(h)
WITH u
// Attaching car to user
UNWIND $cars AS car
MATCH (c:Car)
WHERE c.brand = car.brand
CREATE (u)-[:OWNS]->(c)
RETURN DISTINCT c
In this example, I am creating a new user node and attaching the houses and cars the user owns. The parameter $user is only single but for parameter $houses and $cars can have multiple. Thus if I use the above query I will create duplicate relationship between user and car. I know this can be solved by using MERGE instead but MERGE is more costly than CREATE. Back to my original statement;
I want to know if it is possible when using UNWIND to only iterate a certain part of the query instead of iterating the whole thing? (i.e. when using UNWIND $houses just iterate the attaching house part of the query)
Thanks in advance
Solved! Go to Solution.
07-29-2020 02:03 AM
The short answer is that you can use MERGE
to find or create the relationship - this will give you one relationship rather than many.
The more important point though is that you should be careful about cardinality here. Say if you have one user, 2 cars and 2 houses, using UNWIND
will leave you with four rows, meaning that the (u)-[:OWNS]->(c)
operation will be run four times and you'll end up with four relationships. For each house, you will get two cars.
To illustrate the point, you can run this query:
with 1 as user
unwind range(1, 2) as house
unwind range(1, 2) as car
RETURN *
The user is present on every row, then for each house there are two rows which represent the individual cars.
So you have two options:
distinct
OR collect
step after each create to return the cardinality back down to 1// Distinct
UNWIND $houses AS house
MATCH (h:House)
WHERE h.address = house.address
CREATE (u)-[:OWNS]->(h)
WITH DISTINCT u // <-- back to 1 row per user
or
// Aggregate
UNWIND $houses AS house
MATCH (h:House)
WHERE h.address = house.address
CREATE (u)-[:OWNS]->(h)
WITH u, collect(h) AS houses // <-- user: 1, houses: [1,2]
FOREACH
- this is similar to UNWIND
except it only happens after the pipe (|
). Note, you'll have to use MERGE
rather than MATCH
inside the clause.UNWIND $user AS user
CREATE (u:User)
SET u += user
// Attaching house to user
FOREACH (house IN $houses |
MERGE (h:House {address: house.address})
MERGE (u)-[:OWNS]->(h)
)
// Attaching car to user
FOREACH (car in $cars |
MERGE (c:Car {brand: car.brand})
MERGE (u)-[:OWNS]->(c)
)
Which one you use depends on whether you need to return the cars/houses or not.
Hope that helps!
07-28-2020 08:56 PM
Post your data model as the answer depends what you have in your data model.
07-29-2020 02:03 AM
The short answer is that you can use MERGE
to find or create the relationship - this will give you one relationship rather than many.
The more important point though is that you should be careful about cardinality here. Say if you have one user, 2 cars and 2 houses, using UNWIND
will leave you with four rows, meaning that the (u)-[:OWNS]->(c)
operation will be run four times and you'll end up with four relationships. For each house, you will get two cars.
To illustrate the point, you can run this query:
with 1 as user
unwind range(1, 2) as house
unwind range(1, 2) as car
RETURN *
The user is present on every row, then for each house there are two rows which represent the individual cars.
So you have two options:
distinct
OR collect
step after each create to return the cardinality back down to 1// Distinct
UNWIND $houses AS house
MATCH (h:House)
WHERE h.address = house.address
CREATE (u)-[:OWNS]->(h)
WITH DISTINCT u // <-- back to 1 row per user
or
// Aggregate
UNWIND $houses AS house
MATCH (h:House)
WHERE h.address = house.address
CREATE (u)-[:OWNS]->(h)
WITH u, collect(h) AS houses // <-- user: 1, houses: [1,2]
FOREACH
- this is similar to UNWIND
except it only happens after the pipe (|
). Note, you'll have to use MERGE
rather than MATCH
inside the clause.UNWIND $user AS user
CREATE (u:User)
SET u += user
// Attaching house to user
FOREACH (house IN $houses |
MERGE (h:House {address: house.address})
MERGE (u)-[:OWNS]->(h)
)
// Attaching car to user
FOREACH (car in $cars |
MERGE (c:Car {brand: car.brand})
MERGE (u)-[:OWNS]->(c)
)
Which one you use depends on whether you need to return the cars/houses or not.
Hope that helps!
All the sessions of the conference are now available online