graphql icon indicating copy to clipboard operation
graphql copied to clipboard

Cypher queries are not executed and return null for nested results in lists

Open kristjanmik opened this issue 2 years ago • 4 comments

Describe the bug Properties in nodes are not executing the cypher query for nested nodes in a list

Type definitions Let's use the following simplified example.

type Person {
  id: ID! @id
  name: String
  sayHi: String
    @cypher(
      statement: """
      RETURN \"HELLO\"
      """
    )
}

type Query {
  search(searchTerm: String!): [SearchResult]
    @auth(rules: [{ isAuthenticated: true }])
    @cypher(
      statement: """
      MATCH (p:Person {id: $auth.jwt.sub})
      WITH p, apoc.text.join(split($searchTerm,\" \"),\"~ \") as search
      CALL db.index.fulltext.queryNodes(\"PersonSearchIndex\", \"name:\" + search) YIELD node, score
      WHERE node <> p
      RETURN {person:node{ __resolveType: \"Person\", .*}, score:score}
      LIMIT 20
      """
    )
}

type SearchResult @exclude {
  person: Person!
  score: Float!
}

With the following index

CALL db.index.fulltext.createNodeIndex("PersonSearchIndex",["Person"],["name"]);

To Reproduce Let's execute the following graphql query

query Search {
  search(searchTerm: "John Doe") {
    person {
      id
      name
      sayHi
    }
    score
  }
}

We get the following results:

{
  "data": {
    "search": [
      {
        "person": {
          "id": "person1",
          "name": "John Doe",
          "sayHi": null,
        },
        "score": 0.9042884111404419
      }
    ]
  }
}

Notice how sayHi is null... strange.

When investigating what queries are being run in debug mode we see the following:

CALL apoc.util.validate(NOT(apoc.util.validatePredicate(NOT($auth.isAuthenticated = true), "@neo4j/graphql/UNAUTHENTICATED", [0])), "@neo4j/graphql/FORBIDDEN", [0])

WITH apoc.cypher.runFirstColumn("MATCH (p:Person {id: $auth.jwt.sub})
WITH p, apoc.text.join(split($searchTerm,\" \"),\"~ \") as search
CALL db.index.fulltext.queryNodes(\"PersonSearchIndex\", \"name:\" + search) YIELD node, score
WHERE node <> p
RETURN {person:node{ __resolveType: \"Person\", .*}, score:score}
LIMIT 20", {auth: $auth, searchTerm: $searchTerm}, true) as x
UNWIND x as this
WITH this
RETURN this { .person, .score } AS this

The sayHi custom cypher not even executed

Expected behavior sayHi should be executed and should return HELLO.

As a bonus, if it were to be executed properly and this would be applied, we have to make sure it refers to the same object during execution. The property sayHi should be able to work with this format as well.

sayHi: String
    @cypher(
      statement: """
      RETURN \"HELLO to my\" + size((this)-[:IS_FRIEND]->(:Person)) + \"friends\"
      """
    )

kristjanmik avatar Oct 02 '21 15:10 kristjanmik

Thanks for the bug report @kristjanmik, looks like we should have enough information here to reproduce and take a look. Sorry for the slow reply, been a busy few weeks! I've tracked the bug internally so we can prioritise appropriately. 🙂

darrellwarde avatar Oct 18 '21 12:10 darrellwarde

I can confirm that this is a bug, but I actually get another bug before I even reach that point because my Cypher is:

CALL apoc.util.validate(NOT(apoc.util.validatePredicate(NOT($auth.isAuthenticated = true), "@neo4j/graphql/UNAUTHENTICATED", [0])), "@neo4j/graphql/FORBIDDEN", [0])

WITH apoc.cypher.runFirstColumn("MATCH (p:Person {id: $auth.jwt.sub})
WITH p, apoc.text.join(split($searchTerm," "),"~ ") as search
CALL db.index.fulltext.queryNodes("PersonSearchIndex", "name:" + search) YIELD node, score
WHERE node <> p
RETURN {person:node{ __resolveType: "Person", .*}, score:score}
LIMIT 20", {auth: $auth, searchTerm: $searchTerm}, true) as x
UNWIND x as this
WITH this
RETURN this { .person, .score } AS this

Note that all of the double quotes within apoc.cypher.runFirstColumn are not escaped. @angrykoala, do we have an issue with the wrapping functionality here? 👀

darrellwarde avatar Jan 25 '22 11:01 darrellwarde

We've been able to confirm this bug using the steps to reproduce that you provided - many thanks @kristjanmik! :pray: We will now prioritise the bug and address it appropriately.

neo4j-team-graphql avatar Jan 25 '22 11:01 neo4j-team-graphql

@darrellwarde The wrapping functionality is not used here

We could refactor the resolver to start using wrapInApocRunFirstColumn or, for a quicker workaround, use the escapeQuery utility on the statement directly.

angrykoala avatar Jan 25 '22 15:01 angrykoala

I think we need to be realistic here about the complexity of custom Cypher queries that we'll ever be able to support. If this was returning a node and that node had a custom Cypher field, it gets projected fine. It's just because it goes through a intermediate type, we can't translate the Cypher. Closing because it's not gonna get done.

darrellwarde avatar Feb 22 '24 16:02 darrellwarde