I'm learning Cypher and I created a "Criminal investigation" project on Neo4j.
I'm trying to run a query that outputs each Person that has two children (Person) and both of the children must have committed a crime. To achieve this, I was testing some queries with a Person (p) called p.name = "Lillian" so I know this person has two children but just one of them has committed a crime.
In order to make this I execute this query (return something if Lillian has two sons that committed crimes or return nothing contrarily:
match (p:Person)-[r:FAMILY_REL]->(s:Person)
where p.name = "Lillian"
and size((p)-[:FAMILY_REL]->()-[:PARTY_TO]->(:Crime))=2 and size((p)-[:FAMILY_REL]->()) = 2
return p, s
As I already knew Lillian has only one son who committed a crime, the query should have not returned anything but it returned both of their children.
I'm guessing the wrong part of the query is here:
where /*...*/ and size((p)-[:FAMILY_REL]->()-[:PARTY_TO]->(:Crime))=2
I think this is counting just the number of children instead the number of children who have committed crimes.
What would be the correct way to do this?
Give this a try:
MATCH (p:Person)
WHERE p.name = "Lillian" AND size((p)-[:FAMILY_REL]->()) = 2
WITH p, [(p)-[:FAMILY_REL]->(child) WHERE (child)-[:PARTY_TO]->(:Crime) | child] as childCriminals
WHERE size(childCriminals) = 2
UNWIND childCriminals as s
RETURN p, s
Note that this will only work if Lillian has exactly two children, and both have been party to a crime.
As for why your query wasn't working, it's likely that one of the children was party to two crimes, that would produce results.
Related
I'm learning Cypher and I created a 'Crime investigation' project on Neo4j.
I'm trying to return as an output the parent that only has two sons/daughters in total and each member of the family must have committed a crime.
So, in order to get this in the graph, I executed this query:
match(:Crime)<-[:PARTY_TO]-(p:Person)-[:FAMILY_REL]->(s:Person)-[:PARTY_TO]->(:Crime)
where size((p)-[:FAMILY_REL]->())=2
return p, s
FAMILY_REL relation shows the sons the Person (p) and PARTY_TO relation shows the Crime nodes a Person have committed.
The previous query it's not working as it should. It shows parents with more than two sons and also sons that have just one son.
What is wrong with the logic of the query?
SIZE((p)-[:FAMILY_REL]->()) counts all children of p, including ones who had committed no crimes.
This query should work better, as it only counts children who are criminals:
MATCH (:Crime)<-[:PARTY_TO]-(p:Person)-[:FAMILY_REL]->(s:Person)-[:PARTY_TO]->(:Crime)
WITH p, COLLECT(s) AS badKids
WHERE SIZE(badKids) = 2
RETURN p, badKids
how do i add relationships to nodes returned by a cypher query?
I have written a query that returns me all the person nodes who have the same surname who live at the same address. I now want to add a relationship between these person nodes to indicate they are the same person. The query below returns me 3 person nodes and I want to add a relationship from the first node (returned by the ORDER BY) to the other 2.
MATCH (a:Address) <-[LIVES_AT]-(p:Person)
WITH a as addnode, p.surname as psurname, COUNT(p.name_urn) as c
WHERE c > 1
MATCH (a2:Address{address_urn:addnode.address_urn})<-[LIVES_AT]- (p2:Person{surname:psurname})
WITH p2 as p2node
ORDER BY CASE
WHEN p2node.master_record = 'Y'
THEN
1
ELSE
2
END
WITH collect(p2node) as colp2node
RETURN colp2node
Hope this makes sense? Please advise if there is a better way of doing this.
Something like this should work for you:
MATCH (a:Address)<-[LIVES_AT]-(p:Person)
WITH a, p.surname AS psurname, COUNT(p.name_urn) AS c
WHERE c > 1
MATCH (a2:Address { address_urn:a.address_urn })<-[LIVES_AT]-(p2:Person { surname:psurname })
WITH p2
ORDER BY CASE WHEN p2.master_record = 'Y' THEN 1 ELSE 2 END
WITH collect(p2) AS colp2
WITH colp2[0] AS master, colp2[1..] AS others
UNWIND others AS other
MERGE (master)-[:HAS_ALIAS]->(other);
I use MERGE to avoid duplicate relationships.
By the way, just because two people have the same surname and live at the same address, that does not normally mean they are the same person. I hope you are sure that what you are doing is appropriate.
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 need a cypher query to search for something like this:
I have a graph (a)-[r]->(b) and (a)-[r]->(c) were a is a person and b and c are 2 different skill nodes.
Let's suppose I am looking for someone knowing both java and fortran.
Say b has property name:“java” and c has property name:“fortran”.
How do I find a person that has ALL specified skill nodes?
It'd be useful if the query was scalable, i.e. if I had 20 skill nodes, it would be also easy to execute it.
Thanks a lot in advance!
One way would be to MATCH your Person nodes to Skill nodes, filter Skill nodes for your properties and count the number of nodes per Person. If it's as large as the array of properties your filtering, the Person has all the Skills
MATCH (p:Person)-[r:HAS]->(s:Skill)
WHERE s.name IN ['java', 'fortran', 'cypher']
RETURN DISTINCT p, count(s)
I think you can combine this with a CASE statement to return the data:
MATCH (p:Person)-[r:HAS]->(s:Skill)
WHERE s.name IN ['java', 'fortran', 'cypher']
RETURN
CASE
WHEN count(s) = 3
THEN p
ELSE 0
END
I have set up a graph gist to show my problem: http://gist.neo4j.org/?dropbox-2900504%2Fnames.adoc
I have the problem that if I don't specifically return the person node, or person id, two of my person nodes get merged into one for the return. They both have the same second name and the same labels on the person node (id 3 and 4, Tom and Sarah Smith).
If I add a label to the person node, as with James Smith (id 1) in this example, there is no problem. If I were to remove his :Foo label he would also be merged in with Sarah and Tom in query 2.
If this is not a bug, is there a way for me to return these people distinctly without the person id or node being returned?
I have shown the problem in the above gist, with the only difference between the two queries being that the second one also returns the person id.
Many thanks for your help,
tekiegirl
Edit:
How I want my results to look (basically like query 3 in the gist, but without the person id):
labels names
[Person, Bar] [Sally, Jones]
[Person, Foo] [James, Smith]
[Person] [Sarah, Smith]
[Person] [Tom, Smith]
I think maybe you're not expecting the aggregation behavior you get with collect. Is this what you're trying to get?
MATCH (:Club { name:'FooFighters' })-[:MEMBER]->(p:Person)-[r:NAMED]->(n:Name)
RETURN labels(p) AS labels, n.content AS names
ORDER BY r.order, names
Update with more info, and now I understand what you were doing with your multiple names and order by in the WITH:
collect actually does an implicit group by on the other terms, making them distinct and grouping on them. If you want to group on person, then you need to include person p in the WITH/RETURN that you're collecting in. Here's a rewrite. You can avoid returning p if you want, in the last return statement:
MATCH (:Club{name:'FooFighters'})-[:MEMBER]->(p:Person)-[r:NAMED]->(n:Name)
WITH p, n, r
ORDER BY r.order
WITH p, labels(p) as labels, collect(n.content) as names
RETURN labels, names
ORDER BY names[length(names)-1], names[0]
http://gist.neo4j.org/?8008646