Prasad19sara
Prasad19sara

Reputation: 342

Get indirect relationships with nodes and relationship direction

I want to get all the nodes and relationships in a path in following format: node1, node2, relationship-type, relationship-direction.

I tried getting path using this query. From this I can get nodes and relationships. But cannot get the directions of relationships.

MATCH path = (e1:Entity)-[rel*1..4]-(e2:Entity)
WHERE e1._id = "222" AND  e2._id = "777"
RETURN path

I want the result to be similar to output of the following query which returns direct relationships.

MATCH (e1:Entity) WHERE e1._id = "222"
MATCH (e2:Entity) WHERE e2._id = "333"
MATCH (e1)-[rel]-(e2)
RETURN e1, e2, type(rel), (startNode(rel) = e1) as isOut

Upvotes: 1

Views: 864

Answers (2)

Gabor Szarnyas
Gabor Szarnyas

Reputation: 5057

It can be done, but I could not find a simple solution. So let's go with the complicated one.

First, create an example graph:

CREATE
  (a:Entity {name: 'a'}),
  (b:Entity {name: 'b'}),
  (c:Entity {name: 'c'}),
  (d:Entity {name: 'd'}),
  (e:Entity {name: 'e'}),
  (a)-[:REL {name: 'r1'}]->(b)
    <-[:REL {name: 'r2'}]-(c)
     -[:REL {name: 'r3'}]->(d)
     -[:REL {name: 'r4'}]->(e)

enter image description here

Use this query:

MATCH (e1 {name: 'a'})-[rels*1..4]-(e2 {name: 'e'})
WITH e1, e2, rels,
  extract(rel IN rels | startNode(rel)) AS startNodes,
  extract(rel IN rels | endNode(rel)) AS endNodes,
  range(1, size(rels)-1) AS indexes
WITH e1, e2, rels, startNodes, endNodes, indexes, startNodes[0] AS start
UNWIND indexes AS i
WITH e1, e2,
  e1 = start as isOutFirst, 
  (endNodes[i-1] = startNodes[i] OR
   startNodes[i-1] = startNodes[i]) AS isOut
WITH e1, e2, isOutFirst, collect(isOut) AS isOuts
RETURN e1, e2, [isOutFirst] + isOuts AS isOuts

The result is:

╒═════════╤═════════╤═════════════════════════╕
│e1       │e2       │isOuts                   │
╞═════════╪═════════╪═════════════════════════╡
│{name: a}│{name: e}│[true, false, true, true]│
└─────────┴─────────┴─────────────────────────┘

The main ideas are the following:

  1. We get the start and end node for each relationship using the extract method.
  2. We use the range function and UNWIND to generate a list of indexes and "iterate" on the lists.
  3. The i. relationship is outgoing (isOut) if it starts from any node of the previous (i-1.) relationship.
  4. The 0. relationship has no previous relationship, hence we treat it separately (isOutFirst).
  5. We collect the isOut values to an isOuts list.
  6. We combine the isOutFirst variable and the isOuts list to a single list.

You can UNWIND the results and add the corresponding relationship types:

MATCH (e1 {name: 'a'})-[rels*1..4]-(e2 {name: 'e'})
WITH e1, e2, rels,
  extract(rel IN rels | startNode(rel)) AS startNodes,
  extract(rel IN rels | endNode(rel)) AS endNodes,
  range(1, size(rels)-1) AS indexes
WITH e1, e2, rels, startNodes, endNodes, indexes, startNodes[0] AS start
UNWIND indexes AS i
WITH e1, e2, rels,
  e1 = start as isOutFirst, 
  (endNodes[i-1] = startNodes[i] OR
   startNodes[i-1] = startNodes[i]) AS isOut
WITH e1, e2, rels, isOutFirst, collect(isOut) AS isOuts
WITH e1, e2, rels, 
  [isOutFirst] + isOuts AS isOuts, 
  range(0, size(rels)-1) AS indexes2
UNWIND indexes2 AS i
RETURN e1, e2, type(rels[i]) AS relType, isOuts[i] AS isOut

This results in:

╒═════════╤═════════╤═══════╤═════╕
│e1       │e2       │relType│isOut│
╞═════════╪═════════╪═══════╪═════╡
│{name: a}│{name: e}│REL    │true │
├─────────┼─────────┼───────┼─────┤
│{name: a}│{name: e}│REL    │false│
├─────────┼─────────┼───────┼─────┤
│{name: a}│{name: e}│REL    │true │
├─────────┼─────────┼───────┼─────┤
│{name: a}│{name: e}│REL    │true │
└─────────┴─────────┴───────┴─────┘

Update: you can use the direction to extract the nodes in the correct order from the path:

MATCH (e1 {name: 'a'})-[rels*1..4]-(e2 {name: 'e'})
WITH e1, e2, rels,
  extract(rel IN rels | startNode(rel)) AS startNodes,
  extract(rel IN rels | endNode(rel)) AS endNodes,
  range(1, size(rels)-1) AS indexes
WITH e1, e2, rels, startNodes, endNodes, indexes, startNodes[0] AS start
UNWIND indexes AS i
WITH e1, e2, rels,
  e1 = start as isOutFirst, 
  (endNodes[i-1] = startNodes[i] OR
   startNodes[i-1] = startNodes[i]) AS isOut
WITH e1, e2, rels, isOutFirst, collect(isOut) AS isOuts
WITH e1, e2, rels, 
  [isOutFirst] + isOuts AS isOuts, 
  range(0, size(rels)-1) AS indexes2
UNWIND indexes2 AS i
RETURN e1, e2, type(rels[i]) AS relType,
  CASE isOuts[i]
    WHEN true THEN startNode(rels[i])
    ELSE endNode(rels[i])
  END AS node1,
  CASE isOuts[i]
    WHEN true THEN endNode(rels[i])
    ELSE startNode(rels[i])
  END AS node2

This results in:

╒═════════╤═════════╤═══════╤═════════╤═════════╕
│e1       │e2       │relType│node1    │node2    │
╞═════════╪═════════╪═══════╪═════════╪═════════╡
│{name: a}│{name: e}│REL    │{name: a}│{name: b}│
├─────────┼─────────┼───────┼─────────┼─────────┤
│{name: a}│{name: e}│REL    │{name: b}│{name: c}│
├─────────┼─────────┼───────┼─────────┼─────────┤
│{name: a}│{name: e}│REL    │{name: c}│{name: d}│
├─────────┼─────────┼───────┼─────────┼─────────┤
│{name: a}│{name: e}│REL    │{name: d}│{name: e}│
└─────────┴─────────┴───────┴─────────┴─────────┘

Limitations: the solutions above might not work if there is a 2-length circle in the graph, e.g. for a graph (a)<-[r1]-(b)<-[r2]-(a), they incorrectly mark (b)<-[r2]-(a) as outgoing for one of the paths.

Upvotes: 1

Prasad19sara
Prasad19sara

Reputation: 342

Seems my question was not clear.Sorry for that. Based on the previous answer I was able to get the relationships with nodes as follows for my use-case. In here I do not need isOut column since start and end nodes are there.

MATCH (e1 {_id: '222'})-[rels*1..4]-(e2 {_id: '777'}) WITH range(0, size(rels)-1) AS indexes,rels UNWIND indexes AS i RETURN startNode( rels[i]), type(rels[i]),endNode( rels[i])

Upvotes: 0

Related Questions