How to remove relations while iteration with UNWIND using cypher - neo4j

The following query getting all groups of specific user then
unwind on each result(each group) and should remove all incoming relations only if count relations to that group is 1.
example: group1<-user1 (will delete the incoming relationship to the group)
group1-<user1
group1-<user2 (will remain all incoming relationships to the group)
can assist complete it?
MATCH (me:userId{{1})-[rel:relation_group]-(allGroups:GROUP)
unwind userGroups as group
//how to use CASE or WHERE in order to check if this group
has only 1 relationship just remove it
Thanks.

You can use size in WHERE, example :
MATCH (me:userId{{1})-[rel:relation_group]-(allGroups:GROUP)
WHERE size((allGroups)<-[:relation_group]-()) = 1
DELETE rel
You don't need to iterate, by default the subsequent clauses after a MATCH will be executed for each row found in the MATCH, so let's say the first MATCH returns the following :
me rel allGroups
1 rel3 node5
1 rel4 node6
Then the DELETE will be executed for the first row, then for the second row, etc...

Related

How to obtain all direct parent-child relationships in a graph with Neo4j

I have a graph with multiple levels, and I need to create a query that would extract all direct child-parent relationships in a table.
For example, if I have a graph like this:
I want as an output, the following table:
Parent Child
1 2
1 3
2 4
2 5
3 6
For now, I am extracting these relations separately in multiple MATCH queries, but I'm sure there is a better way out there. Any help would be very much appreciated! Thank you!
Answer from norihide.shimatani is also valid. As an another option, you can try below one with union all.
MATCH (parent {name:'x'})-->(child)
RETURN parent.number as parentNumber, child.number as childNumber
UNION ALL
MATCH (parent {name:'x'})-->(child1)-->(child2)
RETURN child1.number as parentNumber, child2.number as childNumber
How about this?
MATCH(parent)-->(child)
RETURN DISTINCT
parent.id_of_node, child.id_of_node
ORDER BY
parent.id_of_node, child.id_of_node
;
second answer is
/* collect id_of_node of target nodes by the condition you want */
MATCH path=(parent)-[*0..]->(child)
WHERE parent.name = 'X'
WITH NODES(path) as nodes
UNWIND nodes AS node
WITH COLLECT(DISTINCT node.id_of_node) as ids_of_node
/* using ids_of_node as a condition, get final answer */
MATCH(parent)-->(child)
WHERE parent.id_of_node IN ids_of_node OR child.id_of_node IN ids_of_node
RETURN DISTINCT
parent.id_of_node , child.id_of_node
ORDER BY
parent.id_of_node , child.id_of_node
;

Do not return set of nodes from a specific path in Cypher

I am trying to return a set of a node from 2 sessions with a condition that returned node should not be present in another session (third session). I am using the following code but it is not working as intended.
MATCH (:Session {session_id: 'abc3'})-[:HAS_PRODUCT]->(p:Product)
UNWIND ['abc1', 'abc2'] as session_id
MATCH (target:Session {session_id: session_id})-[r:HAS_PRODUCT]->(product:Product)
where p<>product
WITH distinct product.products_id as products_id, r
RETURN products_id, count(r) as score
ORDER BY score desc
This query was supposed to return all nodes present in abc1 & abc2 but not in abc3. This query is not excluding all products present in abc3. Is there any way I can get it working?
UPDATE 1:
I tried to simplify it without UNWIND as this
match (:Session {session_id: 'abc3'})-[:HAS_PRODUCT]->(p:Product)
MATCH (target:Session {session_id: 'abc1'})-[r:HAS_PRODUCT]->(product:Product)
where product <> p
WITH distinct product.products_id as products_id
RETURN products_id
Even this is also not working. It is returning all items present in abc1 without removing those which are already in abc3. Seems like where product <> p is not working correctly.
I would suggest it would be best to check if the nodes are in a list, and to prove out the approach, start with a very simple example.
Here is a simple cypher showing one way to do it. This approach can then be extended into the complex query,
// get first two product IDs as a list
MATCH (p:Product)
WITH p LIMIT 2
WITH COLLECT(ID(p)) as list
RETURN list
// now show two more product IDs which not in that list
MATCH (p:Product)
WITH p LIMIT 2
WITH COLLECT(ID(p)) as list
MATCH (p2:Product)
WHERE NOT ID(p2) in list
RETURN ID(p2) LIMIT 2
Note: I'm using the ID() of the nodes instead of the entire node, same dbhits but may be more performant...

How to return single node with Multiple match for different labels in neo4j?

I am using neo4j 3.2.x. What I am try to do is write a query that will update relation between two nodes (User and Gender) and return single User node with InterestedInGender property pulled from relation as array. There is a problem with the below query as right now it returns multiple records with single value for interestedInGender. Relations are created properly but when returning data it is returning multiple records. I just want to return User node. Is there any way we can fix this query to just return single user node.
MATCH (u:User {_id:"1234"})
MATCH (ig:Gender) WHERE ig.value IN ["male", "female"]
WITH u, ig
OPTIONAL MATCH (u)-[igr:INTERESTED_IN_GENDER]->()
DELETE igr
with u, ig
MERGE (u)-[:INTERESTED_IN_GENDER]->(ig)
RETURN u{
._id,
interestedInGender: [(u)-[:INTERESTED_IN_GENDER]->(ig:Gender) | ig.value]
}
The reason you're getting multiple records (rows) is because your ig match to gender matches to two :Gender nodes...two rows where both rows have the same u node, but different ig nodes. That cardinality remains throughout the rest of your query, and so you get two rows back.
You need to shrink the cardinality of u back down to 1 after you MERGE the relationship, so add this after your MERGE but before your RETURN:
WITH distinct u

Grouping nodes by their relationship

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.

Cypher Query to get duplicate(same id) relationship between nodes

I have city nodes connected to each other by HAS_BUS relationships.
eg.
CREATE (:City{id:123,name:'Mumbai'})-[:HAS_BUS{id:22323,arr_time:234,dept_time:250}]->(:City{id:124,name:'Goa'}).
Though I wanted a bus id to be unique I mistakenly put bus with same id more than once i.e there are non unique HAS_BUS relationships.
Q1.How should I find out which ids are not unique
Q2.How should I find out which ids are not unique and delete them.
I wrote this query but got an unknown error
MATCH ()-[r:HAS_BUS]->() with count(r.id) as t match ()-[s:HAS_BUS]->() where s.id=t with count(s.id) as times,s.id as id where count(times)>1 return id,times
The database contains only 80 nodes and 6500 relationships.
I am actually missing GROUP BY feature of mySQL
DATABASE Can be downloaded from here 6MB
I am assuming that you want the bus id to be unique within your entire graph i.e. bus ID 22323 corresponds to exactly one HAS_BUS relation in the entire graph and is not repeated with different cities.
MATCH (c:City)-[r:HAS_BUS]->()
WITH r.id as busId, count(*) as idCount
WHERE idCount>1
RETURN busId,idCount
note: not tested
will give you all busIds repeated more than once.
Then you can figure out which places you want to delete the duplicates from, or delete all and re-create the correct one.
The group by you're looking for is documented at http://docs.neo4j.org/chunked/milestone/query-aggregation.html
Edit to remove all but one duplicate bus id: Make sure you have a backup of your database- this is NOT tested
MATCH (c:City)-[r:HAS_BUS]->()
WITH r.id as busId, count(*) as idCount
WHERE idCount>1 //Find all duplicate ids
MATCH (c:City)-[r2:HAS_BUS]->()
WHERE r2.id=busId
with COLLECT(r2) as ids,busId //find all relations for those duplicates
with head(ids) as firstId,ids,busId
with filter(x in ids where x<>firstId) as idsToDelete //keep the first id and collect the rest
foreach (d in idsToDelete | DELETE d); //delete the rest
The code to remove all duplicates but one may be a bit simplified using tail instead of head-filter:
MATCH (c:City)-[r:HAS_BUS]->()
WITH r.id as busId, count(*) as idCount
WHERE idCount>1 //Find all duplicate ids
MATCH (c:City)-[r2:HAS_BUS]->()
WHERE r2.id=busId
WITH tail(collect(r2)) as to_delete
FOREACH (d IN to_delete | DELETE d)

Resources