Search code examples
neo4jtraversalneo4jphp

Neo4j - traversal to find specific connected component


Using neo4j 1.9.4, I'm trying to find the connected components (all reachable nodes) from a starting node where the relationship has a certain attribute ('since') and this attribute has a defined integer value, e.g. 20130101.

My initial approach was using a cypher query, but I got the feeling that this query loops to infinity if there is a loop within the graph? At least if I do not restrict the path length and restricting the length is not what I want to do.

So meanwhile I started using a traversal. Using neo4jphp a traversal looks like that:

$traversal->setOrder(Everyman\Neo4j\Traversal::OrderBreadthFirst)
    ->setPruneEvaluator(Everyman\Neo4j\Traversal::PruneNone)
    ->setReturnFilter(Everyman\Neo4j\Traversal::ReturnAll)
    ->setUniqueness(Everyman\Neo4j\Traversal::UniquenessNodeGlobal);

What I think I need is something like this:

->setPruneEvaluator('javascript', "position.RELATIONSHIP().getProperty('since').EQUALS(20130101)")

Obviously, RELATIONSHIP and EQUALS seem to be wrong. I adopted this from the example https://github.com/jadell/neo4jphp/wiki/Traversals, where the following valid and working pruneElevater is set:

->setPruneEvaluator('javascript', "position.endNode().getProperty('name').toLowerCase().contains('t')")

I'm absolutely not familiar with JavasScript, so I can't figure out how to do that. Additionally, how can I make sure the traversal does not result in an error if there is a relationship that does not have the property "since"? If I can achieve the same using a cypher query I would accept that, too.

EDIT: By the way, my approach using cypher was this:

START n=node({start_node}) MATCH p = n-[*]-m WHERE ALL(x IN RELATIONSHIPS(p) WHERE HAS(x.since) AND x.since = 20130101) RETURN DISTINCT m

EDIT2: Trying the suggested cypher query from ulkas give me the following error:

Invalid query
string matching regex ``(``|[^`])*`' expected but `*' found
Think we should have better error message here? Help us by sending this query to [email protected].
Thank you, the Neo4j Team.
"START n=node(40317) MATCH p = n-[r:*..]-m WHERE has(r.since) AND r.since = 20130101 RETURN DISTINCT m"
                                    ^

EDIT3: The suggestion of LameCode looked really promising, but still it returns an error:

Fatal error: Uncaught exception 'Everyman\Neo4j\Exception' with message 'Unable to execute traversal [400]: Headers: Array ( [Content-Length] => 5183 [Content-Type] => application/json; charset=UTF-8 [Access-Control-Allow-Origin] => * [Server] => Jetty(6.1.25) ) Body: Array ( [message] => Failed to execute script, see nested exception. [exception] => EvaluationException [fullname] => org.neo4j.server.rest.domain.EvaluationException [stacktrace] => Array ( [0] => org.neo4j.server.scripting.javascript.JavascriptExecutor.execute(JavascriptExecutor.java:118) [1] => org.neo4j.server.rest.domain.EvaluatorFactory$ScriptedEvaluator.evalPosition(EvaluatorFactory.java:140) [2] => org.neo4j.server.rest.domain.EvaluatorFactory$ScriptedPruneEvaluator.evaluate(EvaluatorFactory.java:161) [3] => org.neo4j.graphdb.traversal.Evaluator$AsPathEvaluator.evaluate(Evaluator.java:69) [4] => org.neo4j.kernel.impl.traversal.TraverserIterator.eva in /var/www/vendor/everyman/neo4jphp/lib/Everyman/Neo4j/Command.php on line 116

And I used the following pruneEvaluator:

->setPruneEvaluator('javascript', "position.lastRelationship().hasProperty('since') && position.lastRelationship().getProperty('since') == 20130101")

When changing from lastRelationship() to endNode() it at least doesn't return me an error, despite I am wondering about the many results it returns, as none of the nodes has exactly this since attribute?! So it seems even then, the prune evaluator does not get to work. I expected it to stop at each endNode if has no since property or if it is unqual the given date? What am I doing wrong, any ideas?


Solution

  • In regards to the Traverser that you are using. The javascript prune evaluator 'position' variable is a Path object. See: http://components.neo4j.org/neo4j/1.9.4/apidocs/org/neo4j/graphdb/Path.html

    Those methods should be available to you.

    Use lastRelationship() (because all the former relationships will have come through the prune evaluator already).

    The Relationship object inherits from Property Container and that has a hasProperty() method.

    setPruneEvaluator('javascript', "position.lastRelationship().hasProperty('since') && position.lastRelationship().getProperty('since') == 20130101")
    

    I'm not sure if you need to use the Equals method or not since it's javascript.