cancel
Showing results for 
Search instead for 
Did you mean: 

How to get all the connected nodes and relationship of a particular node?

poornima_g
Node

I have a graph with this hierarchy, so I want to find all nodes and relationships connected to a specific node.

I've tried this query :

MATCH path=(:Building {name: 'Trinity'})-[:CONTAIN_SITE*4]-()
RETURN path;

Which in turn is returning the path but not as expected, along with that remaining paths are also retrieved. So i need to get the particular entire path of a node named "Trinity" and should ends with "mdt1" and "device".

And also facing issue in retrieval of nodes in both(->, <-) directions.

Is there any way to achieve this?

15 REPLIES 15

glilienfield
Ninja
Ninja

When you query for a path, you will get a result for each path. Each path will contain all of the nodes and relationships along its path. The nodes and relationships in the collections of paths contain duplicates among the paths, due to the hierarchical nature of the data. The following query will get all the paths originating from one root node, consolidate all the nodes and relationships from all the paths, then perform some double unwinding to eliminate the duplicates using the DISTINCT clause. This is necessary since the collect(nodes(p)) operation results in a list of lists. The inner list is the nodes along one path. Same is true for the relationships. That explains why the double unwind is done twice.

match p=(n{name:'root'})-[*]-()
with n, collect(nodes(p)) as listOflistOfnodes, collect(relationships(p)) as listOfListOfrelationships
unwind listOflistOfnodes as list
unwind list as element
with n, collect(distinct element) as distinctNodes, listOfListOfrelationships
unwind listOfListOfrelationships as list
unwind list as element
return n.name, distinctNodes, collect(distinct element) as distinctRelationships

Here is my test data:

Results:

poornima_g
Node

Can you please explain it briefly, I'm not getting expected results.

glilienfield
Ninja
Ninja

Am I addressing your requirement? I am assuming you want the collection of nodes and relationships for the subgraph originating a root node.

The first line of the query results in rows representing each path anchored to the node of interest. The query binds the paths to the variable p, producing one result row for each path. You can get all the nodes along path p as a collection using the nodes(p) function and relationships using the relationships(p) function. Line 2 aggregates the node and relationship collections using the collect method to reduce the data from all the paths to one collection to meet your requirement. The issue here is that the collect method does not merge the individual lists from each path into a collection that represent the union of all the nodes and relationships. Instead, it appends each list into a new list representing. This is expected. The result is a list containing lists of nodes and the same for the relationships. The list of lists for the nodes and relationships also contain duplicates. The rest of the query manipulates theses lists to remove the duplicates and flatten them into lists of unique nodes and relationships.

Back to the query, step 2 passes the single root node to the next phase of the query, forcing the collect methods to aggregate over all the paths, like a group by clause in sql. The result of line 2 is a single row consisting of node n and two collections of lists. To flatten the list of lists and apply the distinct clause on rows, lines 3 and 4 unwinds the list of lists of nodes twice to get a stream of nodes. Line 5 recollects the stream of nodes with the distinct clause, resulting in a list of unique nodes. Lines 6 and 7 repeat the process for the relationships. The final result on line 8 consist of the root node, and one list of unique nodes from all the paths, and one list of the unique relationships from all the paths.

  1. match p=(n{name:'root'})-[*]-()
  2. with n, collect(nodes(p)) as listOflistOfnodes, collect(relationships(p)) as listOfListOfrelationships
  3. unwind listOflistOfnodes as list
  4. unwind list as element
  5. with n, collect(distinct element) as distinctNodes, listOfListOfrelationships
  6. unwind listOfListOfrelationships as list
  7. unwind list as element
  8. return n.name, distinctNodes, collect(distinct element) as distinctRelationships

Can you explain your results? Can you provide a cypher query to create the subgraphs so we can test the query on it?

poornima_g
Node

Here, to create the subgraphs firstly i'm creating each nodes and after that providing association by relating them.

//To create a node

CREATE (n:Location {name: 'Building'})
RETURN n;

CREATE (n:Building {buildingId: 111, name: 'Trinity'})
RETURN n;

After that creating a relationship between these two

//To create relationship

MATCH (s:Location) 
MATCH(e:Building) 
WHERE s.name = 'Building' AND e.name = 'Trinity' 
CREATE (s)-[:CONTAIN_SITE]->(e)

I'm getting this result as a graph

But expecting this as a results

If i provide the any parameter of particular node the path should be returned like here based on buildingId. So in this "Trinity" is the building name, "2" is floor number and "Abcd" & "Efgh" is room name with different room id. All these nodes comes under label known as Building.

And remaining nodes in the path device, device1, mdt1, gps and Tenant are created under different respective labels.

glilienfield
Ninja
Ninja

The reason you are getting a subset of the subgraph you want is because of the match in line 1. It specifies all nodes connected to your building node that are 1 relationship away. Change the match to p=(n:Building{name:'Trinity'})-[:CONTAIN_SITE*]-(). The '*' indicates any number of relationships between node 'n' and any other node connected through a series of 'CONTAIN_SITE' relationships.

You can learn more from the cypher documentation:

the cypher reference card is very handy too:

https://neo4j.com/docs/cypher-refcard/current/

Now getting all the connected paths

Is there any way to filter this query as to get the highlighted node path

glilienfield
Ninja
Ninja

First, I noticed you changed the query not to return the list of relationships. With that change, you can simplify the query you are using because it is processing the relationships, which are no longer needed. Make the following changes:

  1. Line 2: Remove collect(relationships(p)) as listOfListOfRelationships
  2. Line 5: Change 'Unwind' to 'Return', remove ListOfLiistOfRelationships, and change 'n' to 'n.name' if you want just the name
  3. Delete lines 6-8

To answer your question, if you just want the list of nodes originating from 'Trinity' and including 'Trinity', you can just change the condition for your anchor node bound to 'n.' this would give you the collection of nodes for the subgraph rooted on 'Trinity.'

If you still want the paths originating from building 111, but want to limit your answer to those paths that contain the 'Trinity' site, then you can modify the query two ways to get the answer. As I have with cypher, there may be multiple way to accomplish the same goal.

Method 1: Modify the pattern match criteria to include an instance of 'Trinity'
a. Add Match(t:Site{name:'Trinity'}) clause at the beginning to retrieve the Trinity node and bind to variable 't.'
b. Modify the match on current line 1 to the following:
match p=(n:Building{buildingId:'111'})-[:CONTAIN_SITE*]-(t)-[:CONTAIN_SITE*0..]-()

Method 2: Add a 'where' clause to exclude paths that match and do not contain the 'Trinity' node

  1. Add Match(t:Site{name:'Trinity'}) at the beginning
  2. Add the following 'where' clause after the current line 1 (following the match)
    where (t in nodes(p))

Even after trying the Method 1 as you said getting all the paths that are associated with it.

Match(t:Building {name:'Trinity'}) 
Match p=(n:Building{buildingId:'111'})-[:CONTAIN_SITE*]-(t)-[:CONTAIN_SITE*0..]-()
with n, collect(nodes(p)) as listOflistOfnodes 
unwind listOflistOfnodes as list 
unwind list as element 
return n.name, collect(distinct element) as distinctNodes;

Please help me to get the desired results.

Sorry i have specified the same label names in both match statements.

Match(t:Building {buildingId:'111'}) 
Match p=(n:Location{name:'Building'})-[:CONTAIN_SITE*]-(t)-[:CONTAIN_SITE*0..]-()
with n, collect(nodes(p)) as listOflistOfnodes 
unwind listOflistOfnodes as list 
unwind list as element 
return n.name, collect(distinct element) as distinctNodes;

Now got the expected results.
Thank you.

glilienfield
Ninja
Ninja

BTW, I just ran across this while randomly reading the APOC documentation. It has a method to convert a collection of paths to a tree representation. Perhaps you may find this useful, since you are dealing with subgraphs. The following query will return a tree representation of the subgraph connected to the anchor node:

match p=(n{name:'root'})-[*]-()
with collect(p) as paths
call apoc.convert.toTree(paths)
YIELD value
RETURN value;

poornima_g
Node

Thanks for the query it is working fine in the neo4j browser. But, facing issue in spring boot jpa code level. We have a REST api with buildingId as a parameter when it is invoked the method should be called and it should return the path of the graph in json.

Attached the screenshot for your reference where query has written in the repository class and highlighted the error which i'm facing.

org.springframework.data.neo4j.core.mapping.NoRootNodeMappingException: Could not find mappable nodes or relationships inside Record<{n.name: "Building", distinctNodes: [node<36>, node<44>, node<46>, node<47>, node<45>, node<49>, node<50>, node<43>]}> for org.springframework.data.neo4j.core.mapping.DefaultNeo4jPersistentEntity@28dc9501

And the query:-

Match(t:Building {buildingId:'111'}) 
Match p=(n:Location{name:'Building'})-[:CONTAIN_SITE*]-(t)-[:CONTAIN_SITE*0..]-()
with n, collect(nodes(p)) as listOflistOfnodes 
unwind listOflistOfnodes as list 
unwind list as element 
return n.name, collect(distinct element) as distinctNodes;

Need your help to resolve this.

glilienfield
Ninja
Ninja

what does your 'Building' object look like?

poornima_g
Node

Previously, Building entity class contains only building node properties (id, name, tenantCode and locationType). Now, i just altered by declaring Floor, Room and Device properties to retrieve this query path.

Am I missing anything

glilienfield
Ninja
Ninja

Are all the nodes along the path representing Buildings? At first guess, it looks like your query method returning a collection of buildings, but your query result is returning a string and a list of nodes (which I am assuming each is a building). It think spring is confused on how to map your query results to a collection of buildings. Try removing the n.name from the return statement, so you are only returning a collection of nodes.

From your path definition, I can see the starting node is a Location. Is that a type of Building, with a Building Label? The result should only return nodes with the 'Building' label, as you are asking them to be mapped to a collection of Building objects.

glilienfield
Ninja
Ninja

I thought of one more thing. The result should be a list of Buildings, so the query can be refactored to return a list of distinct nodes:

Match(t:Building {buildingId:'111'})
Match p=(n:Location{name:'Building'})-[:CONTAIN_SITE*]-(t)-[:CONTAIN_SITE*0..]-()
unwind nodes(p) as nodes
return distinct nodes

If all the nodes are not of type Building, you can filter them with the following alternate query:
Match(t:Building {buildingId:'111'})
Match p=(n:Location{name:'Building'})-[:CONTAIN_SITE*]-(t)-[:CONTAIN_SITE*0..]-()
unwind nodes(p) as nodes
with distinct nodes as nodes
where nodes:Building
return nodes