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.
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
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.
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
Can you explain your results? Can you provide a cypher query to create the subgraphs so we can test the query on it?
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.
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.
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:
Line 2: Remove collect(relationships(p)) as listOfListOfRelationships
Line 5: Change 'Unwind' to 'Return', remove ListOfLiistOfRelationships, and change 'n' to 'n.name' if you want just the name
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
Add Match(t:Site{name:'Trinity'}) at the beginning
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;
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;
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;
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;
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.
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.
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