Jiahao Zhao
Jiahao Zhao

Reputation: 111

Neo4j / Cypher: Match nodes only if they have a relation with 1 or more other nodes

I'm having some trouble on this query I am trying to build. I have looked plenty of places over the internet and I couldn't seem to find an answer, so I am asking here.

so here is what my schema somewhat looks like. (sorry for shitty paint diagram)

enter image description here

The query I want is;

Starting from one Route node, I get multiple RS, and a single OMS from each RS

I want to find all Route nodes which eventually connects to the same (or more) OMS nodes

here is my current query:

MATCH (st)--(rs:RS)--(oms:OMS)
WHERE id(st) = 0
with st,rs, oms, collect(oms) as omsn

MATCH (ed:Route)--(rs2:RS)
WHERE ALL(x in omsn WHERE (ed)--(rs2)--(x))

RETURN *

And that returns this.

enter image description here

Which is almost correct, but notice that Route node 21 does not connect to BOTH OMS 4 and 2 so I do NOT want that strand

The result should be something like this instead.

enter image description here

How would I change my query to accomplish this instead? (Note that the RS node in the middle is necessary for some other stuff I am also doing, so I cannot remove those. I have gotten it working, though with just a 2 layer heiarchy, but that is not what I need.)

edit: simple insertion query

CREATE (a:Route)-[:rel]->(b:RS)-[:rel]->(c:OMS)

CREATE (a)-[:rel]->(d:RS)-[:rel]->(e:OMS)

CREATE (f:Route)-[:rel]->(g:RS)-[:rel]->(c)
CREATE (f)-[:rel]->(i:RS)-[:rel]->(e)
CREATE (f)-[:rel]->(k:RS)-[:rel]->(hfg:OMS)

CREATE (l:Route)-[:rel]->(m:RS)-[:rel]->(c)
CREATE (l)-[:rel]->(o:RS)-[:rel]->(e)
CREATE (l)-[:rel]->(asd:RS)-[:rel]->(dsf:OMS)
CREATE (l)-[:rel]->(ds:RS)-[:rel]->(gdg:OMS)

MATCH (m:OMS) WHERE id(m) = 4
CREATE (:Route)-[:rel]->(:RS)-[:rel]->(m)

RETURN *

Upvotes: 0

Views: 533

Answers (1)

Tezra
Tezra

Reputation: 8833

2 tips for working with Cypher.

1) Run a test with RETURN * after each WITH to verify you are returning what you want.

2) Look at the text table to verify the format of your return data.

The problem with your query is this part

MATCH (st)--(rs:RS)--(oms:OMS)
WHERE id(st) = 0
with st,rs, oms, collect(oms) as omsn
  • (st) is a single node, so 1 row of data
  • (rs) is two diffrent nodes, so now you have 2 rows of data
  • each (oms) node is one node off of an (rs) node. So 1 row for each (rs).
  • RETURN * will return 1 * 2 * COLLECT(1) as 1 = 2 rows
    • Note here that you have 2 rows of data, each with 1 distinct (oms) node

The solution here is to either do

  • Use COLLECT(rs) in the WITH to collapse those 2 rows into 1, so that the 2 (oms) nodes also get collected into the same list (note, this may contain duplicate oms nodes. use COLLECT(DISTINCT oms) if this will be a problem)
  • Only carry over COLLECT(oms) in the WITH part. The rest will be matched again in the following match anyways.

Final note. You know the direction of the relationships; Include it in the Cypher for better/cleaner performance.

Upvotes: 1

Related Questions