neo4j / graphql

A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations.
https://neo4j.com/docs/graphql-manual/current/
Apache License 2.0
510 stars 150 forks source link

Generate filters for primitive/relational cypher fields #554

Open rcbevans opened 3 years ago

rcbevans commented 3 years ago

The library does not currently generate filters for cypher fields.

Given a simple schema like

type Author {
    mostRecentBook: Book @cypher(statement: "MATCH (this)-[:AUTHORED_BOOK]->(b:Book) RETURN b ORDERED BY b.year DESC LIMIT 1")
    lastPublishedYear: Int @cypher(statement: "MATCH (this)...")
    books: [Book!] @relationship(type: "AUTHORED_BOOK", direction: OUT)
}

type Book {
    name: String!
    year: Int
    authors: [Author!]! @relationship:type: "AUTHORED_BOOK", direction: IN)
}

A caller may want to filter authors by those whose most recent book year was later that a certain year, either based on the date on the Book returned by the "mostRecentBook" cypher statement, or the lastPublishedYear field which returns the year as a primitive.

Currently filters are not generated for cypher fields, requiring the lastPublishedYear value to be denormalized into a property on the parent Author(s) in order to have the graphQL filter generated.

From looking at the code, it seems that cypher fields are evaluated in the final return statement whereas filters are evaluated as part of the initial MATCH against the type being queried. To support filtering on cypher queries, any cypher field which is being filtered against would need to be evaluated earlier in the executed query, and then included in the return result. It would be desirable to evaluate filter cypher fields to be evaluated as early as possible to stop further evaluation for matches which are filtered out.

Describe the solution you'd like The library should generate GraphQL filters for both primitive and relational cypher fields as it does for non-cypher fields and relationships.

Describe alternatives you've considered Currently the only solution I have found is to denormalize fields I wish to filter on into the related nodes and maintain these denormalized values every time the ground truth nodes are updated.

cramatt commented 2 years ago

Here is another example use case, currently not possible but seemingly common:

type Design {
  pv_count: Int @cypher(statement:"""
    MATCH (this)-[:HAS_SYSTEM]->(p:PressureVessel)
    RETURN count(p)
  """)
}
{
  designs(where:{pv_count_GT:0}) {
    pv_count
  }
}
Tanner-Scadden commented 2 years ago

Is there an alternative we should do for these fields in the meantime? Would it help if we make those @ computed instead or use @ callback on fields or other nodes they require to update their values?

vpctorr commented 1 year ago

Any updates on this since the cypher directive no longer relies on runFirstColumnMany?