Search code examples
neo4jcypherdivisiondivide-by-zero

Neo4j Divide ( / ) by Zero ( 0 )


In neo4j I am querying

MATCH (n)-[t:x{x:"1a"}]->()
WHERE n.a > 1 OR n.b > 1 AND toFloat(n.a) / (n.a+n.b) * 100 < 90
RETURN DISTINCT n, toFloat(n.a) / (n.a + n.b) * 100
ORDER BY toFloat(n.a) / (n.a + n.b) * 100 DESC
LIMIT 10

but I got / by zero error.

Since I declared one of n.a or n.b should be 1, if both zero it should skip that row and I shouldn't get this error. This looks like a logic issue in Neo4j. There is no problem when I delete AND toFloat(n.a)/(n.a+n.b)*100 < 90 from WHERE clause. But I want the results only lower than 90. How can I overcome this?


Solution

  • It seems the second condition, toFloat(na) / (na + nb) * 100 < 90, is tested before the first. Look at the Filter(1) operator in this execution plan:

    +--------------+---------------+------+--------+--------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    |     Operator | EstimatedRows | Rows | DbHits |                                            Identifiers |                                                                                                                                                                                      Other |
    +--------------+---------------+------+--------+--------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    |   Projection |             1 |    3 |      0 | anon[111], anon[138], n, toFloat(n.a)/(n.a + n.b)* 100 |                                                                                                                                                                       anon[111]; anon[138] |
    |          Top |             1 |    3 |      0 |                                   anon[111], anon[138] |                                                                                                                                                                             {  AUTOINT6};  |
    |     Distinct |             0 |    3 |     24 |                                   anon[111], anon[138] |                                                                                                                                                                       anon[111], anon[138] |
    |    Filter(0) |             0 |    3 |      6 |                                         anon[29], n, t |                                                                                                                                                                     t.x == {  AUTOSTRING0} |
    |  Expand(All) |             1 |    3 |      6 |                                         anon[29], n, t |                                                                                                                                                                          (  n@7)-[t:x]->() |
    |    Filter(1) |             1 |    3 |     34 |                                                      n | (Ors(List(n@7.a > {  AUTOINT1}, Multiply(Divide(ToFloatFunction(  n@7.a),Add(  n@7.a,  n@7.b)),{  AUTOINT3}) < {  AUTOINT4})) AND Ors(List(  n@7.a > {  AUTOINT1},   n.b > {  AUTOINT2}))) |
    | AllNodesScan |             4 |    4 |      5 |                                                      n |                                                                                                                                                                                            |
    +--------------+---------------+------+--------+--------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    

    You can get around this by force breaking the filter into two clauses.

    MATCH (n)-[t:x { x:"1a" }]->()
    WHERE n.a > 1 OR n.b > 1
    WITH n
    WHERE toFloat(n.a) / (n.a + n.b) * 100 < 90
    RETURN DISTINCT n, toFloat(n.a) / (n.a + n.b) * 100
    ORDER BY toFloat(n.a) / (n.a + n.b) * 100 DESC 
    LIMIT 10
    

    I found this behavior surprising, but as I think about it I suppose it isn't wrong for the execution engine to rearrange the filter in this way. There may be the assumption that the condition will abandon early on failing the first declared condition, but Cypher is exactly that: declarative. So we express the "what", not the "how", and in terms of the "what" A and B is equivalent to B and A.

    Here is the query and a sample graph, you can check if it translates to your actual data:
    http://console.neo4j.org/r/f6kxi5