问题
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