Hello, could someone please help, it's rather urgent. I am using an old version of neo4j-graphql-js (with grandstack) but I imagine it could be the same problem with neo4j-graphql. My graph is (Client)-[:HAS_SPONSOR]->(Client) Client type in schema:
downline(login: String!): [Client] @cypher(statement: """
MATCH (me:Client) WHERE me.login=$login
WITH me
MATCH client:Client)-[hs:HAS_SPONSOR*0..2]->(me)
WITH SIZE(hs) AS level
RETURN client{.*, level:level} ORDER BY level
""")
Note that I am adding a calculated level field in map projection This query works great:
{
downline(login: "c4") {
id
name
level
}
}
but I need to get the sponsor as well and I am not able to. The query:
{
downline(login: "c4") {
id
name
sponsor {
id
name
}
}
}
error:
Expected to find a node at ' client@901' but found Map
I am getting the same if I return Client node instead of Map projection in cypher (custom query). Do you have any idea how I can get sponsor data (if specified in graphql query)?
You'd need to project out the sponsor object in the @cypher query as well, instead of returning the node object.
Something like this if I understand your model:
downline(login: String!): [Client] @cypher(statement: """
MATCH (me:Client) WHERE me.login=$login
WITH me
MATCH (client:Client)-[hs:HAS_SPONSOR*0..2]->(me)
WITH SIZE(hs) AS level
RETURN client {.*, level: level, sponsor: me {.*}} ORDER BY level
""")
@michaeldgraham, @lyonwj
I feel I am stuck and really need help. I have debugged the queries generated by neo4j-graphql-js (latest version 2.19.4) with neo4j-driver 4.2.2 against neo4j-community-server 4.3.3.
For completeness, here is my schema.graphql
type Client {
id: String!
login: String!
name: String!
sponsor: Client @relation(name: "HAS_SPONSOR", direction: OUT)
type: String!
level: Int
typeLevel: String
}
type Query {
team(login: String!): [Client] @cypher(statement: """
MATCH (me:Client) WHERE me.login=$login
WITH me
MATCH (client:Client)-[hs:HAS_SPONSOR*0..2]->(me)
WITH me, SIZE(hs) AS level, client, (SIZE(hs) + client.type) AS typeLevel
RETURN client{.*, level:level, typeLevel: typeLevel, sponsor: me {.*}} ORDER BY level
""")
}
typeLevel and level are the calculated fields where the calculation happens in custom cypher query during sponsorship tree traversal.
When I fire a generic (non-custom query) for Client:
query{
Client (filter: {login: "c4"}) {
id
name
sponsor {
id
name
}
}
}
Generated query by neo4j-graphql-js in the console is:
MATCH (`client`:`Client`) WHERE (`client`.login = 'c4') RETURN `client` { .id , .name ,sponsor: [(`client`)-[:`HAS_SPONSOR`]->(`client_sponsor`:`Client`) | `client_sponsor` { .id , .name }] } AS `client`
and it works ok.
But since I need to calculate fields: typeLevel and level, I am using custom query team.
And for the graphql query:
query{
team (login: "c4") {
id
name
sponsor {
id
name
}
}
}
I am getting error:
Expected to find a node at ' client@369' but found Map
Generated query by neo4j-graphql-js in the console is:
WITH apoc.cypher.runFirstColumn("MATCH (me:Client) WHERE me.login='c4'
WITH me
MATCH (client:Client)-[hs:HAS_SPONSOR*0..2]->(me)
WITH me, SIZE(hs) AS level, client, (SIZE(hs) + client.type) AS typeLevel
RETURN client{.*, level:level, typeLevel: typeLevel} ORDER BY level", {}) AS x
UNWIND x AS `client` RETURN `client` { .id , .name ,sponsor: head([(`client`)-[:`HAS_SPONSOR`]->(`client_sponsor`:`Client`) | `client_sponsor` { .id , .name }]) } AS client
I can see that the result of UNWIND is not the Client node, but Map which seems like a cause of the error I am getting.
The return statement in custom query:
RETURN client{.*, level:level, typeLevel: typeLevel, sponsor: me {.*}} ORDER BY level
is per @lyonwj advice in this thread.
I have also tried
RETURN client{.*, level:level, typeLevel: typeLevel} ORDER BY level
and I am getting the same error.
Again, the result of the UNWIND is a Map not Client node.
I have been browsing the github issues and community forums and found the similar problem reported in 2 Open issues:
without any possible solution mentioned.
Could you please tell me what options I have (very tight deadline is in front of me).
2 is not a problem, although it is somehow redundant, since I need only sponsor field for mutations
but 1 is a problem, since apparently I cannot pass $login to the cypher query attached to level and typeLevel field. The only node I can pass is this.
It would be much more convenient/efficient if there is a way to return Client node + additional fields in the custom query posted above:
type Query {
team(login: String!): [Client] @cypher(statement: """
MATCH (me:Client) WHERE me.login=$login
WITH me
MATCH (client:Client)-[hs:HAS_SPONSOR*0..2]->(me)
WITH me, SIZE(hs) AS level, client, (SIZE(hs) + client.type) AS typeLevel
RETURN client{.*, level:level, typeLevel: typeLevel, sponsor: me {.*}} ORDER BY level
""")
}
so that in graphql request I can specify sponsor and get its data through mapped relationship.
Is there any trick to make that work?
Is it a bug/limitation or I am doing something wrong here?
but 1 is a problem, since apparently I cannot pass $login to the cypher query attached to level and typeLevel field. The only node I can pass is this .
Any field arguments defined in a Cypher directive field are passed to the Cypher statement as Cypher parameters. So if you wanted a reference to $login in the Cypher query you could add it to the field definition and pass the login value at query time:
type Client {
...
level(login: String!): Int @cypher(statement: "RETURN SIZE ( (:Client {login: $login})-[:HAS_SPONSOR*0..2]->(this) )
}
The Cypher directive was originally intended for usecases like computed fields and custom mutations, so there may be some cases with returning a nested projected object that aren't properly handeled in neo4j-graphql.js.
Another option might be to implement a custom resolver that uses the Neo4j JavaScript driver directly to execute the Cypher query (the driver object will be available in the context object).
Have you tried this with the official Neo4j GraphQL Library? I haven't tested this specifically yet, but you may have better results with this and the new library.
Hi, sorry for the late response.
Cypher directive on a field doesn't seem to be an option, since I would need to traverse the whole sponsorship tree on both level and typeLevel.
Custom resolver, too, since I need a portable solution (we are using spring boot with neo4j-graphql-java in prod, and neo4j-graphql-js with grandstack for development only).
I haven't tried official neo4j-graphql js library recently. The problem with that is incompatibility with neo4j-graphql-java (which dev sadly seems to be stopped).
I ended up extracting fields from connected nodes and using map projection in data field on Client node. This way I cannot specify in graphql request which fields from connected nodes I want (all of them are returned when data is specified in the request) but I simply could not find any other way.