graphql
graphql copied to clipboard
Custom Connections
Problem
Setup
type Node {
children: [Node!]! @relationship(type: "CHILDREN", type: OUT)
user: [User!]! @relationship(type: "MEMBER_OF", type: IN)
// This is the connection I want
allUserOfChildren: AllUserOfChildrenConnection
}
type User {
node: Node! @relationshp(type: "MEMBER_OF", type: OUT)
}
We have a Tree structure Node
, that can have many children, in many nesting levels (nodes can be in more than one place in the Tree). Each Node
has some Users
assigned to it.
Now I want to get all User
s, that are in all children of one Node
. Ideally (because we use Relay) capturing all these Users
In one Connection
, so I can use the same Relay mechanisms, as with all the other Connections.
Describe the solution you'd like
I would somehow mark a connection as "custom" and supply some way of getting the Items I want, so this library can generate the correct connection out of it.
One Idea:
type Node {
allUserOfChildren: [Users!]! @relationship(query: """
MATCH (this) -[:CHILDREN*]->(node:Node)<-[:MEMBER_OF]-(user:User)
RETURN user
""")
}
I am not to familiar with the internal parts of this library though, so take this as an early stage idea. It could very well be a different directive, and require the Cypher query to meet certain requirements.
Cheers
@reckter
This can actually be achieved through the current implementation if connection fields were to be decoupled from @relationship
fields. There really is no reason why this coupling should exist as connection fields are simply Relay style connections built from related nodes.
The @cypher
directive can already return a list of nodes related in an arbitrary (or custom) way when given a statement
. If one were to create a connection field from this @cypher
field similar to how a connection field is created for each @relationship
field, it should "just work". A simple subquery generated from the statement
would suffice.
Here is a rough demonstration of this: https://github.com/neo4j/graphql/compare/dev...dmoree:feature/cypher-connection.
- Unions and Interfaces aren't accounted for (more subqueries)
- Nested connection have an
apoc.cypher.runFirstColumn
call that maybe needs to be reworked (although it should probably all be subqueries) - Having something like a
$$source
node and a$$target
node in thestatement
may allow for more reliable translation for the additional labels, unions, interfaces, etc.
Perhaps @darrellwarde or @danstarns could comment on how they see these types of connections fitting into the library.
I added some tests based on the below graph: https://github.com/neo4j/graphql/commit/8b5e18834b01940b0f7a59503a90c6192e9b7983. Let me know if I misinterpreted something.
@dmoree Big 👍
If I understood it correctly, it generated a allUsersOfChildrenConnection
, because User
has a relationship to Object
and allUsersOfChildren
returns [Users!]!
?
So I wouldn't even have to tell the library that I want a connection, it would always do it based on those conditions in the schema?
That sounds awesome, but I could also imagine people getting confused why these connections are being generated. It might warrant and "enable" flag on the @cypher
directive, or a different directive all together, but that's a judgment call for maintainers for sure.
If I understood it correctly, it generated a
allUsersOfChildrenConnection
, becauseUser
has a relationship toObject
andallUsersOfChildren
returns[Users!]!
? So I wouldn't even have to tell the library that I want a connection, it would always do it based on those conditions in the schema?
That's right. The above will generate a {field}Connection
anytime the field
returns an array of nodes, unions, or interfaces. So you'll have both an allUsersOfChildren
field and an allUsersOfChildrenConnection
field.
That sounds awesome, but I could also imagine people getting confused why these connections are being generated. It might warrant and "enable" flag on the
@cypher
directive, or a different directive all together, but that's a judgment call for maintainers for sure.
I agree. This is also true of @relationship
although there really is no way of working with the properties of the relationship without it. But if you didn't need to work with those and, as with this, wanted to skip the generation of a connection there probably should be a flag somewhere. Perhaps at the directive level? An enableConnection
argument that is defaulted to true
? I'll leave that for the maintainers as you pointed out.
I agree. This is also true of
@relationship
although there really is no way of working with the properties of the relationship without it. But if you didn't need to work with those and, as with this, wanted to skip the generation of a connection there probably should be a flag somewhere. Perhaps at the directive level? AnenableConnection
argument that is defaulted totrue
? I'll leave that for the maintainers as you pointed out.
This should be the case for all generated fields/resolvers.