graphql: Something with interfaces generates invalid cypher
Describe the bug I’m not sure what actually triggers this error, but it triggers given the following schema
Type definitions
type SomeNode {
id: ID! @id
other: OtherNode! @relationship(type: "HAS_OTHER_NODES", direction: OUT)
}
type OtherNode {
id: ID! @id
interfaceField: MyInterface! @relationship(type: "HAS_INTERFACE_NODES", direction: OUT)
}
interface MyInterface {
id: ID! @id
}
type MyImplementation implements MyInterface {
id: ID! @id
}
To Reproduce Run the following query:
query {
someNodes {
id
other {
interfaceField {
id
}
}
}
}
and you should get an error, similar to #1535
Variable `interfaceField` not defined (line 2, column 117 (offset: 138))\n\"RETURN this { .id, other: head([ (this)-[:HAS_OTHER_NODES]->(this_other:OtherNode) | this_other { interfaceField: interfaceField } ]) } as this\"\n ^
System (please complete the following information):
- OS: macOS
- Version: @neo4j/graphql@3.2.0
- Node.js version: [e.g. 14.16.0]14
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 1
- Comments: 17 (8 by maintainers)
@chrisdostert We are planning on a release this week, so hopefully you’ll be unblocked pretty soon.
Thanks for the fork, @litewarp 👏
After some recent changes in the cypher projections, fixing this should indeed be easier. As @litewarp mentions, the logic is similar to unions, and in fact to normal relationships now that those are subqueries (#1971 and #1918)
I’ll be making a PR soon that should fix this issue, thanks for all the info and sorry for the long time this has been on triage
So I could be way off base here, but I think this fix might be easier than once thought. Or at least it should be theoretically.
If I understand the spec correctly, the only difference between an interface and union is where you can query the types - in an interface, you can group them together; in a union, you have to list them individually. In other words, this is really a concern for how the client queries the graphql server, but it doesn’t necessarily affect the query to the database.
To take the star wars example the difference between querying an interface and a union is about where you place the inline fragment.
In both cases, the database has to search for Droids that have
id, name, and hasRestrainingBolt
, and for Humans,id, name, and hasForce
.The way that
create-projection-and-params
currently handles unions is that it finds the nodes that comprise the union, and then filters out the ones that either don’t have any fields projected or are filtered by the where clause and then constructs parallel subqueries for them joined by the UNION statement.See, e.g.
However, since both abstract classes expect the same data returned, you could basically use the cypher built for unions to run the interface query (echoing a comment made waaaaay back in the day).
Most of the heavy lifting can be pulled from the
relationField.union
implementation linked above. You’d just have to change the logic to find all the Nodes that implement the Interface before passing it to the same subquery builder as used for unions. Then the resolver should accept the same shape of the returned data, but process it as an interface before returning it to the client.If I had the time, I’d give it a try. But hope this helps someone else get started.
@chrisdostert, sorry to hear that! That’s on me that the bug still has a high-priority label, I simply forgot to remove it. Given your input and some changes in the library in the meantime, I’ll also make sure that we in the team discuss and re-evaluate this bug report again, likely next week.