简体   繁体   中英

Is there a way to Traverse neo4j Path using Cypher for different relationships

I want to Traverse a PATH in neo4j (preferably using Cypher, but I can write neo4j managed extensions).

Problem - For any starting node (:Person) I want to traverse hierarchy like

(me:Person)-[:FRIEND|:KNOWS*]->(newPerson:Person)

if the :FRIEND outgoing relationship is present then the path should traverse that, and ignore any :KNOWS outgoing relationships, if :FRIEND relationship does not exist but :KNOWS relationship is present then the PATH should traverse that node.

Right now the problem with above syntax is that it returns both the paths with :FRIEND and :KNOWS - I am not able to filter out a specific direction based on above requirement.

1. Example data set

For the ease of possible further answers and solutions I note my graph creating statement:

CREATE
  (personA:Person {name:'Person A'})-[:FRIEND]->(personB:Person {name: 'Person B'}),
  (personB)-[:FRIEND]->(personC:Person {name: 'Person C'}),
  (personC)-[:FRIEND]->(personD:Person {name: 'Person D'}),
  (personC)-[:FRIEND]->(personE:Person {name: 'Person E'}),
  (personE)-[:FRIEND]->(personF:Person {name: 'Person F'}),
  (personA)-[:KNOWS]->(personG:Person {name: 'Person G'}),
  (personA)-[:KNOWS]->(personH:Person {name: 'Person H'}),
  (personH)-[:KNOWS]->(personI:Person {name: 'Person I'}),
  (personI)-[:FRIEND]->(personJ:Person {name: 'Person J'});

图1

2. Scenario "Optional Match"

2.1 Solution

MATCH (startNode:Person {name:'Person A'})
OPTIONAL MATCH friendPath = (startNode)-[:FRIEND*]->(:Person)
OPTIONAL MATCH knowsPath = (startNode)-[:KNOWS*]->(:Person)
RETURN friendPath, knowsPath;

If you do not need every path to all nodes of the entire path, but only the whole, I recommend using shortestPath() for performance reasons.

2.1 Result

Note the missing node 'Person J', because it owns a FRIENDS relationship to node 'Person I'.

图2

3. Scenario "Expand paths"

3.1 Solution

Alternatively you could use the Expand paths functions of the APOC user library . Depending on the next steps of your process you can choose between the identification of nodes, relationships or both.

MATCH (startNode:Person {name:'Person A'})
CALL apoc.path.subgraphNodes(startNode,
  {maxLevel: -1, relationshipFilter: 'FRIEND>', labelFilter: '+Person'}) YIELD node AS friendNodes
CALL apoc.path.subgraphNodes(startNode,
  {maxLevel: -1, relationshipFilter: 'KNOWS>', labelFilter: '+Person'}) YIELD node AS knowsNodes
WITH
  collect(DISTINCT friendNodes.name) AS friendNodes,
  collect(DISTINCT knowsNodes.name) AS knowsNodes
RETURN friendNodes, knowsNodes;

3.2 Explanation

  • line 1: defining your start node based on the name
  • line 2-3: Expand from the given startNode following the given relationships ( relationshipFilter: 'FRIEND>' ) adhering to the label filter ( labelFilter: '+Person' ).
  • line 4-5: Expand from the given startNode following the given relationships ( relationshipFilter: 'KNOWS>' ) adhering to the label filter ( labelFilter: '+Person' ).
  • line 7: aggregates all nodes by following the FRIEND relationship type (omit the .name part if you need the complete node)
  • line 8: aggregates all nodes by following the KNOWS relationship type (omit the .name part if you need the complete node)
  • line 9: render the resulting groups of nodes

3.3 Result

╒═════════════════════════════════════════════╤═════════════════════════════════════════════╕
│"friendNodes"                                │"knowsNodes"                                 │
╞═════════════════════════════════════════════╪═════════════════════════════════════════════╡
│["Person A","Person B","Person C","Person E",│["Person A","Person H","Person G","Person I"]│
│"Person D","Person F"]                       │                                             │
└─────────────────────────────────────────────┴─────────────────────────────────────────────┘
MATCH p = (me:Person)-[:FRIEND|:KNOWS*]->(newPerson:Person) 
WITH p, extract(r in relationships(p) | type(r)) AS types
RETURN p ORDER BY types asc LIMIT 1

This is a matter of interrogating the types of outgoing relationships for each node and then making a prioritized decision on which relationships to retain leveraging some nested case logic.

Using the small graph above

MATCH path = (a)-[r:KNOWS|FRIEND]->(b) WITH a, COLLECT([type(r),a,r,b]) AS rels WITH a, rels, CASE WHEN filter(el in rels WHERE el[0] = "FRIEND") THEN filter(el in rels WHERE el[0] = "FRIEND") ELSE CASE WHEN filter(el in rels WHERE el[0] = "KNOWS") THEN filter(el in rels WHERE el[0] = "KNOWS") ELSE [''] END END AS search UNWIND search AS s RETURN s[1] AS a, s[2] AS r, s[3] AS b

I believe this returns your expected result:

在此输入图像描述

Based on your logic, there should be no traversal to Person G or Person H from Person A, as there is a FRIEND relationship from Person A to Person B that takes precedence.

However there is a traversal from Person H to Person I because of the existence of the singular KNOWS relationship, and then a subsequent traversal from Person I to Person J.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM