Inherit properties from a node with relationship to another node to its child in neo4j

巧了我就是萌 提交于 2019-12-11 04:27:01

问题


Inherit properties from all the parents.

Consider I have a graph with below format. I want the properties of a node (which will be in account node, if it has a relation) to be inherited by its child node. Assume Parent and child node relationship is maintained by [r:CHILD] and account information by [r2:ACCOUNT]. If node has more than one parent, it needs to inherit from all its parent with the first account :

       (a0:ACCOUNT)<-[:HAS_ACCOUNT]-Morpheus
                                     \
         (a1:ACCOUNT)<-[:HAS_ACCOUNT]-Neo
                                       \
                                       alpha    
                                        \ 
                                       gamma    beta - [:HAS_ACCOUNT]->(a2:ACCOUNT)
                                          \    /
                                            A 
                                          /   \
            (a3:ACCOUNT)<-[:HAS_ACCOUNT]-B     C
                                       /  \   /  \
                                      D    E  F   G

I want to extract the data from the above graph something like this:

Problem: Given a node, get all its children and also its account (if it has account , e.g: see node B) or its inherited account information. AccountID is part of account node

Consider input is node A

OUTPUT:

|Node | CurrentNode|     Account   |Inherited_Account|
 - - - - - -- - - - - -- - - - - -- - - - - -- - - - - 
|  A  |    A       |     -         |   a1.accountID ,| 
|     |            |               |   a2.accountID  |
 - - - - - -- - - - - -- - - - - -- - - - - -- - - - - 
|  A  |    B       |  a3.accountID |        -        | 
 - - - - - -- - - - - -- - - - - -- - - - - -- - - - - 
|  A  |    D       |               |    a3.accountID | 
 - - - - - -- - - - - -- - - - - -- - - - - -- - - - - 
|  A  |    E       |               |    a3.accountID | 
 - - - - - -- - - - - -- - - - - -- - - - - -- - - - -
|  A  |    C       |               |   a1.accountID ,| 
|     |            |               |   a2.accountID  |
 - - - - - -- - - - - -- - - - - -- - - - - -- - - - - 
|  A  |    F       |               |   a1.accountID ,| 
|     |            |               |   a2.accountID  |
 - - - - - -- - - - - -- - - - - -- - - - - -- - - - - 
|  A  |    G       |               |   a1.accountID ,| 
|     |            |               |   a2.accountID  |
 - - - - - -- - - - - -- - - - - -- - - - - -- - - - - 

This was my cypher to retrive that I came up with, gets me all the accounts of all the parents. It doesnt work sometimes

MATCH (node:Person{personID:"A"})
MATCH (account:ACCOUNT)
MATCH p =(parent:Person)-[:CHILD*1..]->(node)
where (parent)-[:HAS_ACCOUNT]->(account)
UNWIND RELATIONSHIPS(p) AS rel 
WITH p, account, COUNT(DISTINCT rel) AS nRoutes 
RETURN account,p, nRoutes 
ORDER BY nRoutes

回答1:


This is a tricky one.

A pure Cypher solution exists, but it's a complicated query and requires some potentially heavy filtering to weed out paths to account-holding nodes that are beyond closer account-holding nodes along the same path.

However, I've found a better alternate using Cypher and APOC's path expander, plus some pre-processing of adding a label to nodes that are account holders.

APOC's path expander has a means of expanding while respecting a label filter, and there is a means to define a label which should prune any further traversal, but be included as a solution. We'll use this to limit our expansion when getting account-holding ancestors for nodes.

Here's a creation query to recreate the graph in your example (though I'm labeling non-ACCOUNT nodes as :Node):

// create inherited account graph
create (morpheus:Node{name:'Morpheus'})
create (neo:Node{name:'Neo'})
create (alpha:Node{name:'alpha'})
create (gamma:Node{name:'gamma'})
create (beta:Node{name:'beta'})

create (A:Node{name:'A'})
create (B:Node{name:'B'})
create (C:Node{name:'C'})
create (D:Node{name:'D'})
create (E:Node{name:'E'})
create (F:Node{name:'F'})
create (G:Node{name:'G'})

create (morpheus)-[:CHILD]->(neo)
create (neo)-[:CHILD]->(alpha)
create (alpha)-[:CHILD]->(gamma)
create (gamma)-[:CHILD]->(A)
create (beta)-[:CHILD]->(A)
create (A)-[:CHILD]->(B)
create (A)-[:CHILD]->(C)
create (B)-[:CHILD]->(D)
create (B)-[:CHILD]->(E)
create (C)-[:CHILD]->(F)
create (C)-[:CHILD]->(G)

create (morpheus)-[:HAS_ACCOUNT]->(a0:ACCOUNT{name:'a0'})
create (neo)-[:HAS_ACCOUNT]->(a1:ACCOUNT{name:'a1'})
create (beta)-[:HAS_ACCOUNT]->(a2:ACCOUNT{name:'a2'})
create (B)-[:HAS_ACCOUNT]->(a3:ACCOUNT{name:'a3'})

Next, we label account-holding nodes.

MATCH (acc:ACCOUNT)
WITH acc
MATCH (acc)<-[:HAS_ACCOUNT]-(holder)
SET holder:ACCOUNT_HOLDER

Once that's in place, we can use the following query to get your desired output:

// parameterize this in your own query
with 'A' as nodeName
match (node:Node{name:nodeName})-[r:CHILD*0..]->(currentNode)
with node, currentNode, size(r) as distance
optional match (currentNode)-[:HAS_ACCOUNT]->(acc)
with node, currentNode, distance, collect(acc) as accounts
// we now have all child nodes of the given node and their accounts, if they exist.

// this expands up from each currentNode, 
// stopping each expansion at the closest ACCOUNT_HOLDER node
call apoc.path.expand(currentNode, '<CHILD', '/ACCOUNT_HOLDER', -1, -1)
yield path
with node, currentNode, distance, accounts, last(nodes(path)) as holder
// get the account for each holder, 
// but only if the current doesn't have its own accounts
optional match (holder)-[:HAS_ACCOUNT]->(acc)
where size(accounts) = 0
with node, currentNode, accounts, collect(acc) as inherited, distance
order by distance asc
return node, currentNode, accounts, inherited

However, note that even with this approach, the query will not build up and reuse solutions (for example, once we've found the account-holding ancestors for node A, that solution is not referenced or reused when we have to get the account-holding ancestors for nodes, C, F, or G). You may want to consider a custom procedure to perform this complicated matching operation in code rather than Cypher for maximum efficiency.



来源:https://stackoverflow.com/questions/42523140/inherit-properties-from-a-node-with-relationship-to-another-node-to-its-child-in

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!