neo4j-graphql-java
neo4j-graphql-java copied to clipboard
Use the projected fields of fragments only if applicable
Currently the projection of a fragment is applied to all nodes, no matter if they have the appropriate labels or not.
{
person {
name
... on Actor {
movies {
title
}
}
}
}
currently results in:
MATCH (person:`Person`)
RETURN person {
.name,
movies:[(person)-[:ACTED_IN]->(personMovies:Movie) | personMovies { .title }]
} AS person
but we should generate something like:
MATCH (person:Person)
WHERE ("Actor" IN labels(person)) OR ("User" IN labels(person))
RETURN head([person IN [person] WHERE "Actor" IN labels(person) | person {
.name,
movies:[(person)-[:ACTED_IN]->(personMovies:Movie) | personMovies { .title }]
}]
+ [person IN [person] WHERE "User" IN labels(person) | person {
.name
}]
) AS person
See also the tests of the js-library
Not sure what the definition for fragments is.
can't we just add the Actor
label as additional criteria here: (person:Actor)
MATCH (person:`Person`)
RETURN person {
.name,
movies:[(person:Actor)-[:ACTED_IN]->(personMovies:Movie) | personMovies { .title }]
} AS person
or
MATCH (person:`Person`)
RETURN person {
.name,
movies:[(person)-[:ACTED_IN]->(personMovies:Movie) WHERE person:Actor | personMovies { .title }]
} AS person
yes, we can add this, but:
- the client does not expect additional fields that were not requested
- the fragment may have additional fields (mapped as a property rather than a relationship) that would be present and null for all non-matching types, like the
actorProperty
in the following example:
{
person {
name
... on Actor {
actorProperty
movies {
title
}
}
}
}
but then that would be returned as part of the projection or?
In the JS-lib for each branch (type) the complete projection will be returned seperatly
What does that mean? /cc @johnymontana
this means that for request:
{
person {
name
... on Actor {
a
}
... on User {
b
}
}
}
the js-lib will generate a cypher like:
MATCH (person:Person)
WHERE ("Actor" IN labels(person)) OR ("User" IN labels(person))
RETURN head([person IN [person] WHERE "Actor" IN labels(person) | person {
.name,
.a
}]
+ [person IN [person] WHERE "User" IN labels(person) | person {
.name
.b
}]
) AS person
so the .name
property is projected for the User
branch and the Actor
branch
If so we should use much more compact code:
use identifier:Label
expression, e.g. `person:Actor``
and instead of the list comprehension + filter we can just case
head([person IN [person] WHERE "Actor" IN labels(person) | person { .name, .a }]
->
case when person:Actor then person { .name, .a } else null end
or if we need lists:
case when person:Actor then [person { .name, .a }] else []
Is there also a way to extract the common properties as well (.name
in the example)