Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.
09-13-2021 01:03 PM
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:
type Client {
id: String!
login: String!
level: Int!
name: String!
sponsor: Client @relation(name: "HAS_SPONSOR", direction: OUT)
}
`
and Query 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)?
09-13-2021 01:26 PM
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
""")
09-13-2021 02:39 PM
Yes, I tried that. I am getting the same error:
Expected to find a node at ' client@891' but found Map instead
for the query:
downline(login: "c4" ) {
id
name
sponsor {
id
name
}
}
}
I also find the very similar question posted here, without an answer:
Any other idea? Is this doable at all?
Thanks!
09-14-2021 03:28 AM
@michaeldgraham, @William_Lyon
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 @William_Lyon 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).
Many thanks in advance,
Milan
09-14-2021 08:05 AM
Rather than using the Cypher directive on the Query field, could you move the custom Cypher to the level
and typeLevel
fields? So something like
type Client {
id: String!
login: String!
name: String!
sponsor: Client @relation(name: "HAS_SPONSOR", direction: OUT)
type: String!
level: Int @cypher(statement: "RETURN SIZE( (:Client)-[:HAS_SPONSOR*0..2]->(this) )"
typeLevel: String @cypher(statement: "MATCH (c:Client)-[hs:HAS_SPONSOR*0..2]->(this) RETURN (SIZE(hs) + c.type)"
}
and then query using the generated Client query field and filtering for login?
{
Client(login: "foo") {
level
typeLevel
...
}
}
09-15-2021 01:53 AM
Thanks @William_Lyon, interesting solution, but I am afraid, it won't work for me.
In order to calculate level
like you suggest, I would need 2 things:
id
or login
)recruits
recruits: [Client] @relation(name: "HAS_SPONSOR", direction: IN)
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?
Thanks again for your time and help.
09-15-2021 07:01 AM
but
1
is a problem, since apparently I cannot pass$login
to the cypher query attached tolevel
andtypeLevel
field. The only node I can pass isthis
.
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.
09-20-2021 01:23 PM
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.
Thanks for trying to help!
All the sessions of the conference are now available online