cypheropencypherredisgraph

Exists function in RedisGraph


I have a cypher query that works as expected in Neo4j but is failing for me in RedisGraph.

So I have a query like this:

MATCH (dp:DataProduct {name: 'DataProduct1'})-[:CONTAINS]->(t:Table {name: 'output_table'})-[:HAS_COLUMN]->(c:Column {name: 'foo'})
RETURN exists((c)-[:TRANSFORMATION]->())

If column c does not have an outgoing TRANSFORMATION relationship I expect this query to return false. However, in RedisGraph this seems to return true always if the first part of the query matches.

To replicate, run the following cypher:

MERGE (dp:DataProduct {name: 'DataProduct1'}) RETURN dp
MATCH (dp:DataProduct {name: 'DataProduct1'}) MERGE (dp)-[:CONTAINS]->(t:Table {name: 'output_table'})
MATCH (dp:DataProduct {name: 'DataProduct1'})-[:CONTAINS]->(t:Table {name: 'output_table'}) MERGE (t)-[:HAS_COLUMN]->(c:Column {name: 'foo'})

MERGE (dp:DataProduct {name: 'DataProduct2'}) RETURN dp
MATCH (dp:DataProduct {name: 'DataProduct2'}) MERGE (dp)-[:CONTAINS]->(t:Table {name: 'input_table'})
MATCH (dp:DataProduct {name: 'DataProduct2'})-[:CONTAINS]->(t:Table {name: 'input_table'}) MERGE (t)-[:HAS_COLUMN]->(c:Column {name: 'bar'})

MATCH (sourceDp:DataProduct {name: 'DataProduct2'})-[:CONTAINS]->(sourceT:Table {name: 'input_table'})-[:HAS_COLUMN]->(sourceC:Column {name: 'bar'})
MATCH (targetDp:DataProduct {name: 'DataProduct1'})-[:CONTAINS]->(targetT:Table {name: 'output_table'})-[:HAS_COLUMN]->(targetC:Column {name: 'foo'})
MERGE (sourceC)-[r:TRANSFORMATION]->(targetC)
SET r.type = 'function',
r.code = 'CONCAT(foo and bar)'

This results in this graph: sample graph

So with this data setup, I would expect this query to return true:

MATCH (dp:DataProduct {name: 'DataProduct2'})-[:CONTAINS]->(t:Table {name: 'input_table'})-[:HAS_COLUMN]->(c:Column {name: 'bar'})
RETURN exists((c)-[:TRANSFORMATION]->())

And this query to return false:

MATCH (dp:DataProduct {name: 'DataProduct1'})-[:CONTAINS]->(t:Table {name: 'output_table'})-[:HAS_COLUMN]->(c:Column {name: 'foo'})
RETURN exists((c)-[:TRANSFORMATION]->())

However, in RedisGraph both return true. In fact if you create some unconnected nodes it even returns true:

MERGE (dp:DataProduct {name: 'DataProduct3'}) RETURN dp
MATCH (dp:DataProduct {name: 'DataProduct3'}) MERGE (dp)-[:CONTAINS]->(t:Table {name: 'whatever'})
MATCH (dp:DataProduct {name: 'DataProduct3'})-[:CONTAINS]->(t:Table {name: 'whatever'}) MERGE (t)-[:HAS_COLUMN]->(c:Column {name: 'foobar'})

So here foobar has neither an incoming nor an outgoing TRANSFORMATION relationship. But still this query returns true:

MATCH (dp:DataProduct {name: 'DataProduct3'})-[:CONTAINS]->(t:Table {name: 'whatever'})-[:HAS_COLUMN]->(c:Column {name: 'foobar'})
RETURN exists((c)-[:TRANSFORMATION]->())

Any idea what I am doing wrong?

EDIT: I have rewritten the query to use count() instead of exists(), this seems to work. Still interested in how to use exists correctly, but just wanted to add this in case anyone out there is running into the same problem.

MATCH (dp:DataProduct {name: 'DataProduct1'})-[:CONTAINS]->(t:Table {name: 'output_table'})-[:HAS_COLUMN]->(c:Column {name: 'foo'})-[:TRANSFORMATION]->() RETURN COUNT(dp) > 0

Solution

  • Explanation

    Redisgraph only covers a subset of the Cypher language, called openCypher.

    The openCypher language spec states:

    exists() returns true if the specified property exists in the node, relationship or map.

    Therefore, in openCypher the EXISTS() function only supports testing for property existence, and does not support testing for the existence of paths.

    Workaround

    The workaround at the bottom of your question does work, but it requires the server to find all matches before returning. For efficiency you want the query to return as soon the first match is found (if any). Here is a modified version of your workaround that does that:

    MATCH (:DataProduct {name: 'DataProduct1'})-[:CONTAINS]->(:Table {name: 'output_table'})-[:HAS_COLUMN]->(:Column {name: 'foo'})-[:TRANSFORMATION]->(t)
    WITH t
    LIMIT 1
    RETURN COUNT(*) > 0