neo4j-graphql-js
neo4j-graphql-js copied to clipboard
Map projection error when using nested types
Hello.
I'm looking to be able to add some properties to the output of a particular type using a map projection. However, if I perform a query on a type that returns a projection and has a nested type, the this of the nested type returns an error like:
Failed to invoke function `apoc.cypher.runFirstColumn`: Caused by: org.neo4j.cypher.internal.util.v3_4.InternalException: Expected to find a node at 'this' but found Map{ ... } instead
This error makes sense, but I'm having some trouble figuring out a way to add properties to the output in certain instances.
Here's an example of the type that causes a failure when trying to access any nested types within Team -
type Event {
id: ID!
gameStartYMD: String
teams: [Team] @cypher(statement:"""
MATCH (this)<-[:AS_PARTICIPANT]-(inEvent:InEvent)<-[:HAS_DETAIL]-(team:Team)
RETURN team { .*, side: inEvent.association }
""")
}
Hey @hellopat - how is Event resolved in this case? Do you have a custom @cypher directive on a Query type field for Event that you're using here? Could you include that bit of the schema and the full GraphQL query (and generated Cypher query) as well?
Hi @johnymontana - here's the Query for returning an Event:
"Find an event by its provider ID"
eventByProviderId(providerId: Float): Event @cypher(statement:"""
MATCH (event:Event {id: $providerId}) RETURN event
""")
Here's the GraphQL query:
query {
eventByProviderId(providerId: 1234) {
id
gameStartYMD
teams {
name
}
}
}
Here's the generated Cypher query:
WITH apoc.cypher.runFirstColumn("MATCH (event:Event {id: $providerId}) RETURN event", {providerId:$providerId}, True) AS x UNWIND x AS event
RETURN event {
.id,
.gameStartYMD,
teams: [ event_teams IN apoc.cypher.runFirstColumn("MATCH (event:Event)<-[:AS_PARTICIPANT]-(inEvent:InEvent)<-[:HAS_DETAIL]-(team:Team) RETURN team { .*, side: inEvent.association }", {this: event}, true) | event_teams {
.name
}]
} AS event SKIP $offset
hey @hellopat - I think you may want to change your schema a bit to take advantage of the GraphQL feature of nested types, which you can define using the @relation directive. See this section for a bit more detail, but I think instead of using @cypher to express the connections in your schema, try to define the type nesting in the GraphQL schema. I'm not sure I understand your domain perfectly but perhaps something like this:
type InEvent {
association: String
teams: [Team] @relation(name:"HAS_DETAIL", direction: "IN")
}
type Team {
name: String
}
type Event {
id: ID!
gameStartYMD: String
inEvent: [InEvent] @relation(name: "AS_PARTICIPANT", direction: "IN")
}
and then your query looks something like this:
{
Event(id:1234) {
id
gameStartYMD
inEvent {
association
teams {
name
}
}
}
}
Relationship solution
Replacing cypher directive to relation as above is not solution in case that your node structure is not exactly same as what you need to return. That's why map projections exists and it is very often situation of using them.
Cause
Problem here is that event you are returning is not node, but map projection, so generated query of nested type cannot use this keyword, because it expects that this is a node. If you have to return map projection rather than node, you need some workaround here.
Workaround
You can workaround this by match again desired node by map projection params as follows:
event sample query:
MATCH (e:Event) RETURN e{id:id(e), .*}
nested sample type query:
MATCH (event)-[:EVENT_HAS_TEAM]->(t:Team)
WHERE id(event) = this.id
RETURN t {id:id(t), .* } AS team
This approach is not so effective like passing node as this instead of map projection, so it is more db expensive - you are matching again node which has already been matched.
Improvements
It would be great if neo4j-graphql-js comes with some solution, for instance let you pass some variables between auto-composed queries. But how to achieve it? I do not know. I can imagine a solution with a directive telling what variables shell be passed to sub query. I hope that clever guys from neo4j-graphql-js will come with some elegant solution, because I love this 'framework'.
https://github.com/neo4j-graphql/neo4j-graphql-js/issues/608