Good evening,
as beginner I struggle with the transfering my relational db knowledge towards a graph DB and its queries. Lets assume I have a graph with the following nodes:
a PERSON node
two group graphs like MAIN GROUP A and MAIN GROUP B
MAIN GROUP A has another node like SUB GROUP 1 which has another node DETAIL GROUP Z
MAIN GROUP B has another node like SUB GROUP 2
The user node is related to SUB GROUP 2 and DETAIL GROUP Z.
With the query
MATCH (user:PERSON {name: "user"})-[relation:IS_MEMBER_OF*0..]->(team:GROUP)
RETURN team
I find directly the groups the user belongs to.
Desired would be to know the groups the user is also connected to, as PERSON is by defintion also a member of SUB GROUP 1, MAIN GROUP A and MAIN GROUP B.
Anybody able to push me into the right direction? Thanks a lot.
Balael
Assuming you have a HAS_SUBGROUP relationship linking a parent group to each child group, this query should return each team the user is a direct member of, and for each team, the distinct collection of ancestor teams.
MATCH (:PERSON {name: "user"})-[:IS_MEMBER_OF*]->(team:GROUP)
OPTIONAL MATCH (team)<-[:HAS_SUBGROUP*]-(ancestor_team)
RETURN team, COLLECT(DISTINCT ancestor_team);
Related
I have nodes with label A. Some of them are connected with the relationship TEST (see Figure A).
I want to MATCH the groups of connected nodes, create a new node B for each group and create a relationship from each member of the group to the new node B (see Figure B). I know that the groups are small, never more then 3 steps of TEST relationships.
How can I MATCH the A nodes and return connected groups? Is there a graph algorithm implemented in APOC?
I found the answer, maybe it's still helpful for someone:
There are several algorithms for community detection in the graph algorithm package ()https://neo4j.com/docs/graph-algorithms/current/. In this case, we look for connected components: https://neo4j.com/docs/graph-algorithms/current/algorithms/connected-components/
The algorithm can find connected components and store an ID for the component on the nodes:
CALL algo.unionFind('A', 'TEST', {write:true, partitionProperty:"partition"})
YIELD nodes, setCount, loadMillis, computeMillis, writeMillis;
With this new property it's simple to MATCH all nodes belonging to a particular group:
MATCH (a:A)
WITH a.partition AS p, a
RETURN p, count(a)
algo.unionFind seems to be deprecated. The replacement is:
CALL gds.wcc.write('A', {nodeLabels: 'TEST', writeProperty: 'partition' });
Here is the syntax from the docs
CALL gds.wcc.write(
graphName: String,
configuration: Map
)
YIELD
componentCount: Integer,
nodePropertiesWritten: Integer,
preProcessingMillis: Integer,
computeMillis: Integer,
writeMillis: Integer,
postProcessingMillis: Integer,
componentDistribution: Map,
configuration: Map
See https://neo4j.com/docs/graph-data-science/current/algorithms/wcc/ for details
I have 2 Account Nodes, and several listing nodes, as seen below. The Match statement results in 2 Accounts being show with a relationship to each Listing thats associated to that account.
What im wanting to do is create a relationship between the two Accounts based on at least 1 of there listings each sharing the same phone number.
If possible im wanting to see the relationship between the two account nodes drawn and a relationship between the two listings so long as the listings are from different Account.
MERGE (:Account {account_id:11})
MERGE (:Listing {account_id:11, listing_id:1001, phone:99468320, author:'Paul'})
MERGE (:Account {account_id:12})
MERGE (:Listing {account_id:12, listing_id:1002, phone:97412521, author:'Sam'})
MERGE (:Listing {account_id:12, listing_id:1003, phone:97412521, author:'Sam'})
MERGE (:Listing {account_id:12, listing_id:1004, phone:99468320, author:'Sam'})
MERGE (:Listing {account_id:12, listing_id:1004, phone:0, author:'Same'})
MATCH (a:Account),(l:Listing)
WHERE a.account_id = l.account_id
CREATE (a)-[:LISTING]->(l)
RETURN a,l;
For the latter i did try the following but it went a bit crazy as it linked every listing to each other that had the same number appose to only doing so if the account_id was different.
match (p1:Listing)
with p1
match (p2:Listing)
where p2.phone = p1.phone and p1 <> p2
merge(p1)-[r:SHARED_PHONE]-(p2)
RETURN p1, p2
First of all, you should carefully consider if you really need the SHARED_PHONE relationships, as you will have to update the relationships every time a phone number is added, removed, or changed. That could complicate a lot of your queries and make your DB unnecessarily slower. Also, you could end up with a lot of SHARED_PHONE relationships (that you may not really need). Instead of creating the relationships, you could consider incorporating into the relevant queries the discovery of the nodes with the same phone numbers.
However, if you decide that you really need that relationship, here is one way to do what you want:
[UPDATED]
MATCH (n: Listing)
WITH n.phone AS phone, COLLECT(n) AS ns
FOREACH(i IN RANGE(0, SIZE(ns)-2) |
FOREACH(x IN [ns[i]] |
FOREACH(y IN [z IN ns[i+1..] WHERE x.account_id <> z.account_id] |
MERGE (x)-[:SHARED_PHONE]-(y)
)))
The WITH clause collects all the (unique) Listing nodes that share the same phone, and the nested FOREACH clauses execute the minimum number of MERGEs needed to ensure that all the appropriate nodes are connected by a SHARED_PHONE relationship (in either direction). The innermost FOREACH also ensures that the nodes to be connected do not have the same account_id.
Within a graph there is a group G1 - this group G1 has 3 subgroups S1, S2 and S3. The relation is classified as IS_SUBGROUP_OF.
G1 itsself is again a subgroup of another group, lets call it D1. D1 has a lot of subgroups where G1 is only one.
Having a user U1 who is member of a Subgroup of G1 - here S1. I want to create a query which is able to gather all users of subgroup S1, traverse from user U1 to S1 and from there to G1, get the users of G1 and down from G1 to S2 and S3 and grab all users from S2 and S3 as well. The final result should be all users in the subgroups S1, S2 and S3 from the parent Group G1 including the users of G1.
I have tried:
MATCH (d:User) --> (S1:Subgroup)-[:IS_SUBGROUP_OF*0..]->(G1:Group)
WHERE d.name = "U1"
RETURN d
Unfortunately I traverse all groups and give back all users of any group in the graph. I tried to change the hop-level in the relation (e.g. 1 only) but didnt succeed. Do you have a hint how to create the query to get only this subset of users?
The name of the groups are just for the example and not known in the real world - all I know is the username (here: U1) - and from there I need to find various groups depending where the user is situated. So in the query I cannot work with names of groups but only with variables as they are not known.
* EDITED *
Sorry for the confusion, I labeld S1 wrongly as Subgroup, but only the relation mentions 'IS_SUBGROUP_OF', so all Group Nodes have the label 'Group', D1 would also have the label 'Group'. I also add the relation label for users, so the statement looks now like this:
MATCH (d:User) -[:IS_MEMBER_OF]-> (S1:Group)-[:IS_SUBGROUP_OF*0..]->(G1:Group)
WHERE d.name = "U1"
RETURN d
Let's try this, a minor tweak on Dave's answer (which should work fine, as far as I can tell...)
MATCH (:User {name: 'U1'})-[:IS_MEMBER_OF]->(:Group)-[:IS_SUBGROUP_OF]->(superGroup:Group)
WITH superGroup
MATCH (superGroup)<-[:IS_SUBGROUP_OF*0..1]-(:Group)<-[:IS_MEMBER_OF]-(users:User)
RETURN COLLECT(DISTINCT users)
Based upon the starting user, this finds the grandparent group or supergroup (G1 according to your example), then matches on users that are members of G1 or any of its immediate subgroups and returns the distinct collection. It will include the original matched user.
This answer assumes the user is identified as a member of a group by the relationship IS_MEMBER_OF.
The query first determines the parent group G1 based on the supplied user U1. It then determines all of the users of the child groups of G1 (S1, S2, S3) and returns the collection of distinct users accross the child groups.
This is a somewhat generalized approach that could be used to traverse more levels by modifying the number of levels to traverse in each situation.
// follow IS_MEMBER_OF or IS_SUBGROUP_OF relationships up
// the group/user hierarchy to find the parent group two
// levels up
match (u:User1 {name: 'U1'})-[:IS_MEMBER_OF|IS_SUBGROUP_OF*2]->(g:Group)
// using the parent group
with g
// follow the IS_MEMBER_OF or IS_SUBGROUP_OF relationships back down
// the hierarchy to find all of the peer users or the original user
match (g)<-[:IS_MEMBER_OF|IS_SUBGROUP_OF*2]-(u:User)
return collect(distinct u)
Would this work?
MATCH (d:User)-[*0..1]-(G1:Group)
WHERE d.name= 'U1'
RETURN DISTINCT d
I've built a simple graph of one node type and two relationship types: IS and ISNOT. IS relationships means that the node pair belongs to the same group, and obviouslly ISNOT represents the not belonging rel.
When I have to get the groups of related nodes I run the following query:
"MATCH (a:Item)-[r1:IS*1..20]-(b:Item) RETURN a,b"
So this returns a lot of a is b results and I added some code to group them after.
What I'd like is to group them modifying the query above, but given my rookie level I haven't yet figured it out. What I'd like is to get one row per group like:
(node1, node3, node5)
(node2,node4,node6)
(node7,node8)
I assume what you call groups are nodes present in a path where all these nodes are connected with a :IS relationship.
I think this query is what you want :
MATCH p=(a:Item)-[r1:IS*1..20]-(b:Item)
RETURN nodes(p) as nodes
Where p is a path describing your pattern, then you return all the nodes present in the path in a collection.
Note that, a simple graph (http://console.neo4j.org/r/ukblc0) :
(item1)-[:IS]-(item2)-[:IS]-(item3)
will return already 6 paths, because you use undericted relationships in the pattern, so there are two possible paths between item1 and item2 for eg.
I am trying to learn neo4j and was going to start with a basic employee / employer example where an employee has a manager unless at the top of the tree.
My structure looks like employee->MANAGEDBY->manager->MANAGEDBY->manager->MANAGEDBY->manager. However each manger can have multiple employees or managers under them.
What I would like to be able to do is to get a list of all managers between an employee and main boss(ceo/president/whatever).
Initially I started off with a query like this
MATCH(baseEmployee {Name: 'Josh'})-[:MANAGEDBY*0..]-(managers)
RETURN managers.Name;
This seems to not only show my manager and his/her manager and so on but it also seems to show everyone that they manage when what I want to show up is a list like
Josh,
Boss,
BossBoss,
BossBossBoss,
CEO
After some searching and some luck I managed to get closer by using the following query
MATCH p=(baseEmployee {Name: 'Josh'})-[:MANAGEDBY*0..]-(managers)
WHERE NOT(managers-[:MANAGEDBY]->())
RETURN p;
I am just learning neo4j so my best guess of what is happening here is that I get the path from myself to the first manager who doesn't have a MANAGEDBY relationship. My issue is that this returns a path and I would prefer to just have a list of managers.
Is it possible to do this without doing the p= query?
You must add the direction of the relationship to your path query!
Otherwise you also explore downwards from Josh.
And if you start your varlength query at zero 0.. then you return Josh too.
The default is 1.. so you can also leave it off.
You can return the nodes of the path with nodes(p).
MATCH p=(baseEmployee {Name: 'Josh'})-[:MANAGEDBY*]->(managers)
WHERE NOT(managers-[:MANAGEDBY]->())
RETURN nodes(p);
If you want to have one row per node you can do one of two:
Either unwind the collection back into rows.
MATCH p=(baseEmployee {Name: 'Josh'})-[:MANAGEDBY*]->(managers)
WHERE NOT(managers-[:MANAGEDBY]->())
UNWIND nodes(p) as n
RETURN n;
Or return the last node of each path up to the root.
MATCH p=(baseEmployee {Name: 'Josh'})-[:MANAGEDBY*]->(managers)
RETURN managers;