Mosselman
Mosselman

Reputation: 1748

Cypher returns exponential count result on 'join'

I am learning Cypher on Neo4j and I am having trouble in understanding how to perform an efficient 'join' equivalent in Cypher.

I am using the standard Matrix character example and I have added some nodes to the mix called 'Gun' with a relation of ':GIVEN_TO'. You can see the console with my query result here:

http://console.neo4j.org/r/rog2hv

The query I am using is:

MATCH (Neo:Crew { name: 'Neo' })-[:KNOWS*..]->(other:Crew),(other)<-[:GIVEN_TO]-(g:Gun),(Neo)<-[:GIVEN_TO]-(g2:Gun) RETURN count(g2);

I have given Neo 4 guns, but when I perform the above I get a count of '12'. This seems to be the case because there are 3 'others' and 3*4 = 12. So I get some exponential result.

What should my query look like to get the correct count ('4') from the example?

Edit: The reason I am not querying through Guns directly as suggested by @ceej is because in my real use case I have to do this traversal as described above. Adding DISTINCT does not do anything for my result.

Upvotes: 1

Views: 86

Answers (2)

Dave Bennett
Dave Bennett

Reputation: 11216

The reason you get 12 guns instead of 4 is because your query produces a cartesian product. This is because you have asked for items in the same match statement without joining them. @ceej rightly pointed out if you want to find Neo's guns you would do as he suggested in his first query.

If you wanted to get a list of the crew members and their guns then you could do something like this...

MATCH (crew:Crew)<-[:GIVEN_TO]-(g:Gun)
RETURN crew.name, collect(g.name)

Which finds all of the crew members with guns and returns their name and the guns that they were given.

If you wanted to invert it and get a list of the guns and the respective crew members they were give to you could do the following...

MATCH (crew:Crew)<-[:GIVEN_TO]-(g:Gun)
RETURN g.name, collect(crew.name)

If you wanted to find all of the crew that knew Neo multiple levels deep that were given a gun you could write the query like this...

MATCH (crew:Crew)<-[:GIVEN_TO]-(g:Gun)
WITH crew, g
MATCH (neo:Crew {name: 'Neo'})-[:KNOWS*0..]->(crew)
RETURN crew.name, collect(g.name)

That finds all the crew that were given guns and then determines which of them have a :KNOWS path to Neo.

Upvotes: 4

ceej
ceej

Reputation: 1893

Forgive me, but I am am unclear why you have the initial MATCH in your query. From your explanation it would appear that you are trying to get the number of :Gun nodes linked to Neo by the :GIVEN_TO relationship. In which case all you need is the latter part of your query. Which would give you something like

MATCH (neo:Crew { name: 'Neo' })<-[:GIVEN_TO]-(g:Gun)
RETURN count(g)

Furthermore, to make sure that you are only counting distinct :Gun nodes you can add DISTINCT to the RETURN statement.

MATCH (neo:Crew { name: 'Neo' })<-[:GIVEN_TO]-(g:Gun)
RETURN count( DISTINCT g )

This is possibly unnecessary in your case but can be helpful when the pattern that you are matching on can arrive at the same node by different traversals.

Have I misunderstood your requirement?

Upvotes: 2

Related Questions