neo4j-graphql-java icon indicating copy to clipboard operation
neo4j-graphql-java copied to clipboard

Use the projected fields of fragments only if applicable

Open Andy2003 opened this issue 4 years ago • 8 comments

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

Andy2003 avatar Aug 05 '20 10:08 Andy2003

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

jexp avatar Aug 05 '20 10:08 jexp

yes, we can add this, but:

  1. the client does not expect additional fields that were not requested
  2. 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
      }
    }
  }
}

Andy2003 avatar Aug 05 '20 11:08 Andy2003

but then that would be returned as part of the projection or?

jexp avatar Aug 05 '20 19:08 jexp

In the JS-lib for each branch (type) the complete projection will be returned seperatly

Andy2003 avatar Aug 05 '20 20:08 Andy2003

What does that mean? /cc @johnymontana

jexp avatar Aug 06 '20 22:08 jexp

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

Andy2003 avatar Aug 06 '20 23:08 Andy2003

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 []

jexp avatar Aug 07 '20 09:08 jexp

Is there also a way to extract the common properties as well (.name in the example)

Andy2003 avatar Aug 07 '20 09:08 Andy2003