0

How to create all edges that using one statement in Cypher?

For example: lets say I have one object like this

Employees {name: "abc, country: "NZ", zipcode: "123456"}

Employees {name: "def", country: "AUS", zipcode: "964573"}

and lets say I have the following Manager objects

Manager { name: "abc", depatment: "product"}

Manager {name: "abc", depatment: "sales"}

Manager {name: "abc", depatment: "marketing"}

and Finally the Address Objects

Address {zipcode: "964573", street: "Auckland St"}

Now I want to create all the edges where Employees.name = Manager.name and Employees.zipcode = Address.zipcode however if Employees.name != Manager.name but Employees.zipcode = Address.zipcode then I want all the edges to be created between Employees and Address similarly if Employees.zipcode != Address.zipcode but Employees.name = Manager.namethen I want all the edges to be created between Employees and Manager. And I want to achieve all of this in one single statement/query

Simply put if there are matching vertices between Employees, Manager and Address I want all the edges to be created between them but if there only a match between any two I want the edge to be created between those two vertices as well. And I am trying to all of this in a single query/statement?

Is this possible to write a query in one statement that can satisfy all the conditions above?

What I tried so far is this

Find the pairs first with MATCH clause and then CREATE a relationship between them.

MATCH (e:Employees),(m:Manager), (a:Address)
WHERE e.name=m.name or e.zipcode = a.zipcode
WITH e,m,a
CREATE (m)-[:REL_NAME]->(e), (e)-[:ADDR_REL]->(a)

This clearly won't work because of the Where clause because if e.name=m.name then e.zipcode = a.zipcode won't be checked and therefore no edge will be created between employees and address.

user1870400
  • 6,028
  • 13
  • 54
  • 115
  • have you tried `MATCH (e:Employees),(m:Manager), (a:Address) WHERE e.name=m.name or e.zipcode = a.zipcode SET (m)-[:REL_NAME]->(e), (e)-[:ADDR_REL]->(a)` ? – Archemar Apr 01 '19 at 09:50
  • No. can you please explain how this would work? so I can ask limit my questions. – user1870400 Apr 01 '19 at 09:51
  • Now that I see it, you want a `match .. where name... set ... UNION match ... where zip ... set ...` It looks you can't mix `match .. set ..` with `union`. – Archemar Apr 01 '19 at 10:01
  • I was thinking more like `Optional match` although not sure on complete syntax. – user1870400 Apr 01 '19 at 10:04
  • Can you verify your last statement? I think this query should create both the edges REL_NAME and ADDR_REL if any of the condition is true in WHERE clause. – Rajendra Kadam Apr 01 '19 at 13:22
  • Strange use case. Maybe this is just a simplified example, but everybody with the same zip code is supposed to have the same address? – cybersam Apr 01 '19 at 19:22
  • @cybersam No. You can assume every zip code has a unique address – user1870400 Apr 01 '19 at 19:24
  • But is there only one `Address` per zip code? Or is every person in a zip code supposed to have a relationship to every address with the same zip code? – cybersam Apr 01 '19 at 19:25
  • you can assume multiple Addresses per Zipcode. – user1870400 Apr 01 '19 at 19:25
  • OK, so how do you know which `Address` node belongs to each `Employee`? Just matching by zip code is not enough -- that would just create relationships to all the addresses with the same zip code. – cybersam Apr 01 '19 at 19:27
  • For now, you can assume matching by zipcode is fine. – user1870400 Apr 01 '19 at 19:28

1 Answers1

1

The following query avoids producing a cartesian product of all 3 node labels (and will perform better if you have indexes for :Manager(name) and :Address(zipcode)):

MATCH (e:Employees)
OPTIONAL MATCH (m:Manager)
WHERE e.name = m.name
WITH e, COLLECT(m) AS mList
FOREACH(x IN mList | CREATE (x)-[:REL_NAME]->(e))
WITH e
OPTIONAL MATCH (a:Address)
WHERE e.zipcode = a.zipcode
WITH e, COLLECT(a) AS aList
FOREACH(y IN aList | CREATE (e)-[:ADDR_REL]->(y))
cybersam
  • 63,203
  • 6
  • 53
  • 76
  • Do I really need `FOREACH`? I am new to Cypher but my understanding from this post https://stackoverflow.com/questions/55354623/how-to-create-edges-based-on-the-equality-check-on-vertex-attributes-in-cypher/55355216?noredirect=1#comment97434704_55355216 is that `WITH` clause can collect all the pairs. No? – user1870400 Apr 01 '19 at 20:20
  • Also, is there a material online you can point me on how to design vertex labels that can lead to better perforamce when querying? – user1870400 Apr 01 '19 at 20:21
  • You don't absolutely need to use `FOREACH`, but in your case it is probably the simplest approach. Also, this approach uses a pattern that could be repeated any number of times for the same `e` node (should you want to create even more relationship types). The Cypher manual has a [query tuning](https://neo4j.com/docs/cypher-manual/current/query-tuning/) section. – cybersam Apr 01 '19 at 20:28
  • Great this example is the simplest version of the problem I am trying to solve and you are correct I am looking for a pattern to solve the big picture of my problem. The generic version of my problem is this imagine I have 10K messages(you can think each message is a vertex) and each pair of message is connected by different id. so say message1, message2 are connect by id1 where id1 is one of the fields in both messages and message2, message 3 are connected by id2 , message3, message 4 are connected by id3 and so on..and I want to create a graph out of it. – user1870400 Apr 01 '19 at 20:37