Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Neo4j: Conditions on Relationships with Depth

Tags:

neo4j

cypher

What I'm trying to do

Being relatively new to Neo4j, I'm trying to find certain nodes with Cypher in a Neo4j graph database. The nodes should be connected by a chain of relationships of a certain type with further conditions on the relationships:

// Cypher
START self = node(3413)
MATCH (self)<-[rel:is_parent_of*1..100]-(ancestors)
WHERE rel.some_property = 'foo'
RETURN DISTINCT ancestors

What goes wrong

If I drop the depth part *1..100, the query works, but of course, then allows only one relationship between self and the ancestors.

But, if I allow the ancestors to be several steps away from self by introducing the depth *1..100, the query fails:

Error: Expected rel to be a Map but it was a Collection

I thought, maybe this syntax defines rel to be is_parent_of*1..100 rather than defining rel to be a relationship of type is_parent_of and allowing a larger relationship depth.

So, I've tried to make my intentions clear by using parenthesis: [(rel:is_parent_of)*1..100. But this causes a syntax error.

I'd appreciate any help to fix this. Thanks!

like image 982
fiedl Avatar asked Nov 07 '13 21:11

fiedl


1 Answers

Nomenclature

Calling *1..100 depth originates in the nomenclature of the neography ruby gem, where this is done using the abstract depth method.

In neo4j, this is called variable length relationships, as can be seen here in the documentation: MATCH / Variable length relationships.

Reason for the error

The reason for the "Expected rel to be a Map but it was a Collection" error is, indeed, that rel does not refer to each single relationship but rather to the entire collection of matched relationships.

For an example, see here in the documentation: MATCH / Relationship variable in variable length relationships.

Solution

First, acknowledge that the identifier refers to a collection (i.e. a set of multiple items) and call it rels instead of rel. Then, in the WHERE clause, state that the condition has to apply to all rel items in the rels collection using the ALL predicate.

// Cypher
START self = node(3413)
MATCH (self)<-[rels:is_parent_of*1..100]-(ancestors)
WHERE ALL (rel in rels WHERE rel.some_property = 'foo')
RETURN DISTINCT ancestors

The ALL predicate is explained here in the documentation: Functions / Predicate Functions.

I was led to this solution by this stackoverflow answer of a related question.

Long query time

Unfortunately, asking for relationship properties does cost a lot of time. The above query with only a couple of nodes in the database, takes over 3000ms on my development machine.

like image 145
fiedl Avatar answered Sep 24 '22 00:09

fiedl