cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

Expand sets of multiple relations when querying for hierarchical tree structure

joonashak
Node

I am trying to ultimately get a hierarchical tree from a more complex graph structure that better matches the underlying problem. While I can easily enough represent the desired end result as Systems and CONNECTS relations, I get stuck when trying to add a new node type Signature between the Systems and CONNECTS relations and query over that.

Say I have this data:

CREATE (jita:System {name: 'Jita'})
CREATE (ikuchi:System {name: 'Ikuchi'})
CREATE (ansila:System {name: 'Ansila'})
CREATE (jita)-[:CONNECTS]->(ikuchi)
CREATE (ikuchi)-[:CONNECTS]->(ansila)

Then what I'm ultimately after can be queried with:

MATCH (root:System {name: 'Jita'})<-[:CONNECTS*..5]->(destination:System)
RETURN root, destination

Resulting in this graph:

jqt2GOw

However, in reality (pun intended, as this is about a videogame...) Systems have Signatures and some Signatures are connected together effectively forming the CONNECTS relationship seen above.

Now, take this dataset:

CREATE (jita:System {name: 'Jita'})
CREATE (ikuchi:System {name: 'Ikuchi'})
CREATE (ansila:System {name: 'Ansila'})
CREATE (sig1:Signature {id: 'JIT-001'})
CREATE (sig2:Signature {id: 'IKU-001'})
CREATE (sig3:Signature {id: 'IKU-002'})
CREATE (sig4:Signature {id: 'ANS-001'})
CREATE (jita)-[:HAS]->(sig1)
CREATE (ikuchi)-[:HAS]->(sig2)
CREATE (ikuchi)-[:HAS]->(sig3)
CREATE (ansila)-[:HAS]->(sig4)
CREATE (sig1)-[:CONNECTS]->(sig2)
CREATE (sig4)-[:CONNECTS]->(sig3)

Which looks like this when querying for all nodes:

2i1ylTz

With this, I can query one level of connection between Systems:

MATCH (root:System {name: 'Jita'})-[:HAS]->(sigOrigin:Signature)<-[:CONNECTS]->(sigDestination:Signature)<-[:HAS]-(destination:System)
RETURN root, destination, sigOrigin, sigDestination

Resulting in what I would expect:

UjCZItD

 Can this query be expanded in a similar fashion to the first example query's [:CONNECTS*..5] part? I guess in a way I would like to take the

-[:HAS]->(sigOrigin:Signature)<-[:CONNECTS]->(sigDestination:Signature)<-[:HAS]-(destination:System)

part and expand/repeat it an arbitrary number of times. I should add that, for this problem, I can reduce the Signatures away from between the Systems in code so that does not necessarily need to be done in Cypher.

Thank you for any help!

PS. Some background: This is for my hobby project which is a navigation tool for the videogame EVE Online. I recently decided to switch to Neo4j for the connection tree generation bit. It's my first time using any graph database and so far I've been loving it!

 

 

1 ACCEPTED SOLUTION

glilienfield
Ninja
Ninja

I do not know of a method of specifying the repetition of a pattern sequence in a cypher query. The documentation for the APOC method 'apoc.path.expandConfig' discusses specifying sequences of label/relationships that must occur along each path. You will still need to extract the information you need along the path that is returned. 

We can take an alternative approach realizing that it looks like any path from system-to-system nodes will have repeating sequences of '(:System)-[:HAS]->(:Signature)-[:CONNECTS]->(:Signature)<-[:HAS]-(:System)' sequences. As such, we can find the longest path starting from a specific System node and terminating on a System node with the following query:

 

match p=(System {name: 'Jita'})-[*]-(s:System)
where size([(s)-[:HAS]->()|1]) = 1
return p

 

 From this path, we can extract each CONNECTS relationships and retrieve the starting and ending Signature nodes, along with the System nodes each is related to. The following query does that.

 

match p=(:System {name: 'Jita'})-[*]-(s:System)
where size([(s)-[:HAS]->()|1]) = 1
unwind [x in relationships(p) where type(x)='CONNECTS'] as connRel
with startNode(connRel) as startNode, endNode(connRel) as endNode
match(startSystem:System)-[:HAS]->(startNode)
match(endSystem:System)-[:HAS]->(endNode)
return {start_signature: {id: startNode.id, system: startSystem.name}, end_signature: {id: endNode.id, end_system: endSystem.name}} as `CONNECT Relationships`

 

The following are the results using your test data, where each row is a map of the data for one CONNECT relationship. As shown, there are two CONNECT relationships. Each shows the start and end Signature nodes related by this CONNECT, with the Signature node's 'id' and the name of the System node that HAS this signature. 

 

{
  "start_signature": {
    "system": "Jita",
    "id": "JIT-001"
  },
  "end_signature": {
    "end_system": "Ikuchi",
    "id": "IKU-001"
  }
},
{
  "start_signature": {
    "system": "Ansila",
    "id": "ANS-001"
  },
  "end_signature": {
    "end_system": "Ikuchi",
    "id": "IKU-002"
  }
}

 

Is this the type of information you are looking for?

View solution in original post

2 REPLIES 2

glilienfield
Ninja
Ninja

I do not know of a method of specifying the repetition of a pattern sequence in a cypher query. The documentation for the APOC method 'apoc.path.expandConfig' discusses specifying sequences of label/relationships that must occur along each path. You will still need to extract the information you need along the path that is returned. 

We can take an alternative approach realizing that it looks like any path from system-to-system nodes will have repeating sequences of '(:System)-[:HAS]->(:Signature)-[:CONNECTS]->(:Signature)<-[:HAS]-(:System)' sequences. As such, we can find the longest path starting from a specific System node and terminating on a System node with the following query:

 

match p=(System {name: 'Jita'})-[*]-(s:System)
where size([(s)-[:HAS]->()|1]) = 1
return p

 

 From this path, we can extract each CONNECTS relationships and retrieve the starting and ending Signature nodes, along with the System nodes each is related to. The following query does that.

 

match p=(:System {name: 'Jita'})-[*]-(s:System)
where size([(s)-[:HAS]->()|1]) = 1
unwind [x in relationships(p) where type(x)='CONNECTS'] as connRel
with startNode(connRel) as startNode, endNode(connRel) as endNode
match(startSystem:System)-[:HAS]->(startNode)
match(endSystem:System)-[:HAS]->(endNode)
return {start_signature: {id: startNode.id, system: startSystem.name}, end_signature: {id: endNode.id, end_system: endSystem.name}} as `CONNECT Relationships`

 

The following are the results using your test data, where each row is a map of the data for one CONNECT relationship. As shown, there are two CONNECT relationships. Each shows the start and end Signature nodes related by this CONNECT, with the Signature node's 'id' and the name of the System node that HAS this signature. 

 

{
  "start_signature": {
    "system": "Jita",
    "id": "JIT-001"
  },
  "end_signature": {
    "end_system": "Ikuchi",
    "id": "IKU-001"
  }
},
{
  "start_signature": {
    "system": "Ansila",
    "id": "ANS-001"
  },
  "end_signature": {
    "end_system": "Ikuchi",
    "id": "IKU-002"
  }
}

 

Is this the type of information you are looking for?

Thank you for your reply! Didn't get a chance to take a proper look into this but now that I did, it seems that your solution works perfectly even when I expand the testing dataset to include multiple "chains" (as we call them in-game), loopbacks, etc.

The first query you posted results in exactly the visual graph (in Neo4j browser) I was expecting, and the expanded version is a very nice touch and a good lesson for me 🙂

Thanks again!