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.

Hello, how can I return the Path without repetitive results?

Hello, everyone.
I'm trying to search the similar subgraphs from the original graph database according to the expected subgraph described by the following cypher:

match p=(n:Building{buildingid:19})-[]-(r:Road) 
with p,r
match p2=(r)-[]-(:Junction)
return p,p2

The expected subgraph is shown in the figure below:

The corressponding text table in browser is shown below:

I want to parse the result within .net driver, however the "p" path is repetitive as shown above. By the way, when I use the cypher:

return distinct p 

The problem is solved while the path "p2" is missed. And the cypher below is still repetitive:

return distinct p, p2

Anyway, I wish the result is non-repetitive. In addition, I am not sure whether the interim path "p" would be filtered by the subsequent cypher like the cypher code "with", because I want the final result all influenced by the whole cypher.

Does anyone know how to realize it ?

Feel free to comment if you see any other issue, I am a beginner and I welcome the feedback.

Thank you!

1 ACCEPTED SOLUTION

I think we are making progress. A few questions:

  1. I assume there is only one relationship between a junction and a road of type LINK_WITH. If so, I would think link4 and link9 are the same, as well as, link5 and link6 are the same, and link7 and link8 are the same. If true, and since your new query is not concerned with paths but just the collection of relationships, you could simplify even further by making those three matches just between each road and the common junction.

  2. You are accumulating all the nodes and relations across all subgraphs into collections. There is no structure in your result to associate nodes and relationships to a particular subgraph. This is not obvious when you display your results in the browser, because the browser shows all the result nodes and shows all the connections between the nodes, whether the relationship is in your result or not. What seems to make sense is grouping the results by junction, so you get the result per subgraph.

  3. Is it important to differentiate road1, road2, and road3, or is a collection of the roads ok?

Just a note, the linkx variables are relationships. Each contains the relationships’s start node, end node, the relationship type, and the relationship’s properties. Given link1, you would get the start node from startNode(link1), end node from endNode(link1), properties from properties(link2), and type from type(link1). As such, you can reduce the result payload returned by not returning the whole relationship, but only the node ‘id’ of the start and end nodes, along with the relationship properties (if you have any) and type. You could use this information to reconstruct the subgraphs in your client code, since you are also returning all the nodes. Would this work for you?

The ‘with’ clause has two purpose that is see. One is grouping variable values for aggregating other entities. The aggregating operations would be collect, sum, min, max, count, etc. The ‘with’ clause allows you to explicitly define the grouping, which is defined by all the parameters of the ‘with’ clause outside the aggregate functions. The result of the ‘with’ clause will be a row for every unique combination of the grouping parameters and the result of the aggregate functions applied to only those rows that have that same combination of grouping values. It’s the same as ‘Group by’ in sql.

The other use is to pass results from one phase of a query to the next phase in a multistage query. This is required when you transition between certain cypher clauses. For example, you can have a sequence of match statements to get values, then follow them with a sequence of create or merge statements using the match results results. If you wanted to match again using the results of your match, merge, and create clause, you would need to transition with a ‘with’ clause, passing the previous results you need to the next set of match, merge, create operations. I think you also need to transition with a ‘with’ clause when go from a ‘call’ clause to a match, merge, create. You will be notified in the browser if a ‘with’ is needed in the these case or with an error when executing with the driver.

I just thought of another use. If you want to derive new values from the current result and add them to the results so they are available in subsequent clauses. For example, adding two properties, manipulating an array, etc.

When you match a result is only returned if there is a match, so if you have multiple match’s and at least one does not match, then you will get no results. The optional match is different, as it returns null when there is not a match, thus allowing you to return a result. An example use, would be if you want to search for a related node that may or may not exists.

match (n:Person{id: 100})
optional match (n)-[:EMPLOYED_AT]->(c:Company)
return n.name, c.name

In the above, you will get a result either of the person has a relationship to a company or not. c.name will be null if no match is found.

Optional match is equivalent to an outer join in sql.

View solution in original post

15 REPLIES 15

Can someone help me with my problem? Thanks!

Cypher returns rows, so the result of the second match is appended to the results passed with the ‘with r, p’. Since the first query results in one path and the second query two results, you get a total of two rows with the value of ‘p’ in common.

What output do you specifically want to pass to the driver?

The following will return ‘p’ and a list of the junction nodes:

match p=(n:Building{buildingid:19})-[]-(r:Road) 
match (r)-[]-(j:Junction)
return p, collect(j) as junctions

Hi, glilienfield.

Thanks for your reply!
In my topic, the cypher is excepted to return the paths(such as the flow) , which group the candidate subgraph. Then I will parse the paths within .net driver.

{"type":"other_building","buildingid":19},{},{"roadid":235}

For example, as the subgraph below, the excepted result is composed of two kinds of paths("[345--235, 340--235], [235--19]")
3X_5_f_5f5b7afe9ce5216f1257ec08f838a68af9c60fa9.png

Therefore, I wish the cypher result non-repetitive and can return the whole possible subgraphs from database, which is composed of paths or nodes.

In summmary, I want to match the possible similar/ isomorphic subgraphs from the database, and wish the results with nodes and relationships of each subgraph. Would you know how to solve it?

Thanks again!

Still not clear, so I am giving some ideas. If these don’t work, can you explicitly show what you want?

Return as paths. Paths consist of nodes and relationships. Do you have relationship properties?

match p1=(:Building{buildingid:19})-[]-(r:Road) 
match p2=(r)-[]-(:Junction)
return p1 as mainPath, collect(p2) as subPaths

Returning just the nodes:

match p1=(:Building{buildingid:19})-[]-(r:Road) 
match p2=(r)-[]-(:Junction)
return nodes(p1) as mainPathNodes, collect(nodes(p2)) as subPathNodesList

Returning as json

match (b:Building{buildingid:19})-[]-(r:Road) 
match (r)-[]-(j:Junction)
With b, r, collect(j{.*}) as junctionMaps
return {road: r{.*}, building: b{.*}, junctions: junctionMaps}

Hello, glilienfield, forgive me for not expressing myself clearly.

(1)My target: To Search the isomophic subgraphs from the database with the cypher.
Inputing the cypher queries to return the nodes and relationships of all matched subgraphs.

(2)For example
The target subgraph is:
3X_f_7_f74038cf4cba12554803ac47e718623585758936.png

There are two types of nodes: road and junction and one type of relationship: LINK_WITH. The three road nodes are linked with each other, and they are all linked to the node junction.

Therefore, the cypher describes the nodes and relationships of the target subgraph, which restricts the types of nodes and the specified connected relationships (also specified nodes and relationship properties).

The cypher is:

match p1=(road1:Road)-[link1:LINK_WITH]-(road2:Road)-[link2:LINK_WITH]-(road3:Road),p2=(road1)-[link3:LINK_WITH]-(road3)
where 100<=link1.angle<=120 and 100<=link2.angle<=120 and 100<=link3.angle<=140
with road1, road2, road3, p1,p2
match p3=(road1)-[:LINK_WITH]-(jun:Junction)-[:LINK_WITH]-(road2)
with road1, road2, road3, jun,p1,p2,p3
match p4=(road2)-[:LINK_WITH]-(jun)-[:LINK_WITH]-(road3)
with road1,road2,road3, jun,p2,p1,p3,p4
match p5=(road3)-[:LINK_WITH]-(jun)-[:LINK_WITH]-(road1)
with road1,road2,road3, jun,p3,p1,p2,p4,p5
with collect(distinct road1) as cr1, collect(distinct road2) as cr2, collect(distinct road3) as cr3,collect(distinct jun) as cj, collect(distinct p1) as cp1,collect(distinct p2) as cp2,collect(distinct p3) as cp3,collect(distinct p4) as cp4,collect(distinct p5) as cp5
return cr1,cr2,cr3,cj,cp1,cp2,cp3,cp4,cp5

The matched subgraphs are:


The table is:

(3)My problems:
First, I'm not sure whether the similar subgraphs within database are all matched .
Second, the cypher is complex, is there any concise and effective statement?
Third, the returned result is comolex, how to get nodes and relationships that can be phrased in .net driver simply.
Fourth, are there any tools that can match subgraphs directly and return the nodes and relationships. The apoc and gds library can't solve this problem, as far as I know, because they can't specify the relationships among specified nodes.
At last, thank you for sparing time reading my problems! I expect for your reply! Thanks again.

May you spare some time reading my problems, thanks!

sure, I would love to help.

You query is complex. You are returning a lot of redundant information. What is it exactly want you want to get from the data? You stated that you want to parse the results in your .NET driver. If so, what would the end result of that parsing be? I use the java driver and I usually try to do as much of the heavy lifting in cypher and return the results I want to the driver. Of course, it is not always possible. Can you explain what the result of your parsing will be?

BTW, you can remove all the 'with' clauses in your query. They are not necessary between 'match' statements. You can have sequential 'match' statements.

Also, the screenshot of the graph doesn't necessarily represent the data you returned from your query. The query returns a whole bunch of stuff that is rendered in the browser, but the browser by default will also show any relationship between any nodes shown in the browser, regardless of if they are in your result. You can turn the feature off in the browser settings. In the browser window, click on the gear icon on the bottom left, scroll down to find "Connect result nodes", and uncheck it.

Anyway, I am glad to help, but I still don't understand the end result you want to achieve.

Thank you for your reply!
Your suggestion is very useful. BTW, the end results are nodes and relationships of the matched subgraph.
For example, the excepted result is shown below:

......
return nodes, relationships

The nodes are the collections of distinct nodes of the whole matched subgraphs, and the relationships are the whole connection information among nodes.

The returned "nodes" include the identity id、properties and labels of matched subgraph nodes, and the "relationships" include the identity ids of corresponding start and end nodes、properties and labels of matched subgraph relationships.

Thank you very much!

ameyasoft
Graph Maven
Try this:
You can change the maxLevel number.

match (n:Building{buildingid:19}) 
CALL apoc.path.subgraphAll(n, {maxLevel: 2}) YIELD nodes, relationships 
RETURN nodes, relationships

WITH [ node in nodes | node {.*, label:labels(node)[0], id:tostring(id(node))}] as nodes, [rel in relationships 
| rel {.*, fromNode:{label:labels(startNode(rel))[0], id:tostring(id(startNode(rel)))},type:type(rel), toNode:{label:labels(endNode(rel))[0], id:tostring(id(endNode(rel)))}}] as rels 
WITH {nodes:nodes, relationships:rels} as json 
RETURN json

Now I get it, you just want the distance collection of nodes and relationships.

The thread has two scenarios, one with a building and it’s subgraphs and the latter one which has roads and a junction.

I believe @ameyasoft query gives you what you want to the building one, without the json formatting.

Copied from above

match (n:Building{buildingid:19}) 
CALL apoc.path.subgraphAll(n, {maxLevel: 2}) YIELD nodes, relationships 
RETURN nodes, relationships

We can work on the road one if that is what you want.

Thanks! I'll further improve my cypher statements.

match (road1:Road)-[link1:LINK_WITH]-(road2:Road)-[link2:LINK_WITH]-(road3:Road),(road1)-[link3:LINK_WITH]-(road3)
where 100<=link1.angle<=120 and 100<=link2.angle<=120 and 100<=link3.angle<=140
match (road1)-[link4:LINK_WITH]-(jun:Junction)-[link5:LINK_WITH]-(road2)
match (road2)-[link6:LINK_WITH]-(jun)-[link7:LINK_WITH]-(road3)
match (road3)-[link8:LINK_WITH]-(jun)-[link9:LINK_WITH]-(road1)
return collect(DISTINCT road1) as cr1, collect(DISTINCT road2) as cr2, 
collect(DISTINCT road3) as cr3, collect(DISTINCT jun) as cj,
collect(DISTINCT link1) as cl1, collect(DISTINCT link2) as cl2,
collect(DISTINCT link3) as cl3, collect(DISTINCT link4) as cl4,
collect(DISTINCT link5) as cl5, collect(DISTINCT link6) as cl6,
collect(DISTINCT link7) as cl7, collect(DISTINCT link8) as cl8, collect(DISTINCT link9) as cl9

In addition, would you explain when to use the "WITH" and "Optional Match" and what are the differences between them? Thank you for your patience.

I think we are making progress. A few questions:

  1. I assume there is only one relationship between a junction and a road of type LINK_WITH. If so, I would think link4 and link9 are the same, as well as, link5 and link6 are the same, and link7 and link8 are the same. If true, and since your new query is not concerned with paths but just the collection of relationships, you could simplify even further by making those three matches just between each road and the common junction.

  2. You are accumulating all the nodes and relations across all subgraphs into collections. There is no structure in your result to associate nodes and relationships to a particular subgraph. This is not obvious when you display your results in the browser, because the browser shows all the result nodes and shows all the connections between the nodes, whether the relationship is in your result or not. What seems to make sense is grouping the results by junction, so you get the result per subgraph.

  3. Is it important to differentiate road1, road2, and road3, or is a collection of the roads ok?

Just a note, the linkx variables are relationships. Each contains the relationships’s start node, end node, the relationship type, and the relationship’s properties. Given link1, you would get the start node from startNode(link1), end node from endNode(link1), properties from properties(link2), and type from type(link1). As such, you can reduce the result payload returned by not returning the whole relationship, but only the node ‘id’ of the start and end nodes, along with the relationship properties (if you have any) and type. You could use this information to reconstruct the subgraphs in your client code, since you are also returning all the nodes. Would this work for you?

The ‘with’ clause has two purpose that is see. One is grouping variable values for aggregating other entities. The aggregating operations would be collect, sum, min, max, count, etc. The ‘with’ clause allows you to explicitly define the grouping, which is defined by all the parameters of the ‘with’ clause outside the aggregate functions. The result of the ‘with’ clause will be a row for every unique combination of the grouping parameters and the result of the aggregate functions applied to only those rows that have that same combination of grouping values. It’s the same as ‘Group by’ in sql.

The other use is to pass results from one phase of a query to the next phase in a multistage query. This is required when you transition between certain cypher clauses. For example, you can have a sequence of match statements to get values, then follow them with a sequence of create or merge statements using the match results results. If you wanted to match again using the results of your match, merge, and create clause, you would need to transition with a ‘with’ clause, passing the previous results you need to the next set of match, merge, create operations. I think you also need to transition with a ‘with’ clause when go from a ‘call’ clause to a match, merge, create. You will be notified in the browser if a ‘with’ is needed in the these case or with an error when executing with the driver.

I just thought of another use. If you want to derive new values from the current result and add them to the results so they are available in subsequent clauses. For example, adding two properties, manipulating an array, etc.

When you match a result is only returned if there is a match, so if you have multiple match’s and at least one does not match, then you will get no results. The optional match is different, as it returns null when there is not a match, thus allowing you to return a result. An example use, would be if you want to search for a related node that may or may not exists.

match (n:Person{id: 100})
optional match (n)-[:EMPLOYED_AT]->(c:Company)
return n.name, c.name

In the above, you will get a result either of the person has a relationship to a company or not. c.name will be null if no match is found.

Optional match is equivalent to an outer join in sql.

Thanks! I have benefited a lot!

That is great. Let us know if you have further questions.

Thanks! I have no questions now.