Can I collect properties down a path of nodes into a virtual node (or map)

Hi, there.
I wonder if I can select a starting node and then walk through related nodes and on my way collect all the properties. apoc.refactor.mergeNodes() seems to look quite good, but I don't want do destroy the existing structure. Instead get a virtual node with all the collected properties.

As example:
CREATE (:test {title: "test1", name: "hello"})-[:_RELATED]->(:test {title: "test2", weight: 12})-[:_RELATED]->(:test {title: "test3", age: 61, weight: 11})

should return a node (:test {name:'hello' title:'test3', age:61, weight:11 })

Thanks for your help

Frank

Yes, you can create virtual nodes and relationship for the purpose of display or analysis.

What are you collecting in your example? You have duplicate name and weight properties across the nodes in your path.

Hi,
as you see in my question the result should be a node with all found properties, whereas the values are always taken from the nodes that are found last on the way down the path. They overwrite the ones that were also found earlier. So 'apoc.refactor.mergeNodes() ' is really doing it, but destroys my structure. I am thinking of copying the original path into a virtual one and then start the function on the virtual nodes.
Maybe there is an other way of putting the properties in a map and collect/override the entries in the map along the path.

As a first step I wanted to return all nodes involved on a path and used:

match p = (n)-[*]->() where n.name = "hallo"
return nodes(p)

The result (watched in text display) is not what I expected. I expected a plain list with three nodes.
But it gives me two results. How do I get the three nodes?

I also tried the sample on this page:

and for the length function it says it gives a length of 2, but in reality length returns two records instead of one. Am I doing something wrong?

If you create your test data with:

CREATE (:test {title: "test1", name: "hello"})-[:_RELATED]->(:test {title: "test2", weight: 12})-[:_RELATED]->(:test {title: "test3", age: 61, weight: 11})

And execute this query:

Match (n:test{name:’hello’})
Match p=(n)-[:_RELATED*]->()
Return nodes(p) as nodes, length(p) as length

Then, you should get a list of three nodes and a length equal to two (path length equals number of hops, not nodes). This assumes your data does not contain addition paths like these that were created during your testing.

Sorry, I can’t run it and post the results, as I don’t have my computer. Please try it and confirm.

Try this to get your result.

Match (n:test{name:’hello’})
Match p=(n)-[:_RELATED*]->()
Unwind nodes(p) as node
Unwind keys(node) as key
With collect([key,node[key]]) as pairs
With apoc.map.fromPairs(pairs) as properties
Return apoc.create.vNode([“test”], properties) as vNode

Again, I don’t have my computer, so let me know how it works.

Hi Gary, thanks for your help. The last query does the job. I can return the properties directly instead of putting it in a virtual node. (although I believe some part will be executed multiple times)

The reason: The middle query already doesn't simply deliver the expected three nodes and a count of 2. Instead it returns two results. One with two nodes and a length of 1 and then all thre nodes and the length of 2. Hmm???

The result looks like this:

[
{
"nodes": [
{
"identity": 31,
"labels": [
"test"
],
"properties": {
"name": "hallo",
"title": "test1"
},
"elementId": "4:30de0956-b6ad-4b29-80d3-f70d1ad8f28e:31"
},
{
"identity": 32,
"labels": [
"test"
],
"properties": {
"gewicht": 12,
"title": "test2"
},
"elementId": "4:30de0956-b6ad-4b29-80d3-f70d1ad8f28e:32"
}
],
"length": 1
},
{
"nodes": [
{
"identity": 31,
"labels": [
"test"
],
"properties": {
"name": "hallo",
"title": "test1"
},
"elementId": "4:30de0956-b6ad-4b29-80d3-f70d1ad8f28e:31"
},
{
"identity": 32,
"labels": [
"test"
],
"properties": {
"gewicht": 12,
"title": "test2"
},
"elementId": "4:30de0956-b6ad-4b29-80d3-f70d1ad8f28e:32"
},
{
"identity": 33,
"labels": [
"test"
],
"properties": {
"gewicht": 11,
"title": "test3",
"alter": 61
},
"elementId": "4:30de0956-b6ad-4b29-80d3-f70d1ad8f28e:33"
}
],
"length": 2
}
]

Oh, my oversight. You are getting each segment of the full path, as those subpaths match the path pattern. Thus, you get two row of results. The first with a path of length 1 and the second with a path of length 2. We can add a constrain to the query to only return the path whose last node does not have an outgoing relationship, thus it is the last node along the path.

Match (n:test{name:’hello’})
Match p=(n)-[:_RELATED*]->(m)
Where not exists( (m)-[:_RELATED]->() )
Return nodes(p) as nodes, length(p) as length

Another method is to order the results by length and return only the longest.

Match (n:test{name:’hello’})
Match p=(n)-[:_RELATED*]->(m)
Return nodes(p) as nodes
Order by length(p) desc
Limit 1

Here is the fix for this one.

Match (n:test{name:’hello’})
Match p=(n)-[:_RELATED*]->(m)
Where not exists( (m)-[:_RELATED]->() )
Unwind nodes(p) as node
Unwind keys(node) as key
With collect([key,node[key]]) as pairs
Return  apoc.map.fromPairs(pairs) as properties
1 Like

I didn't think about detecting the end of a path. Now I understand what and why it happened.... always a good feeling!

Thanks a lot!!!

1 Like