Neo4j/Graphql relationship filtering with union of targets

Hi Folks,

I run into the following issue/question while evaluating the capabilities of the new neo4j/graphql.
When I setup my graphql model as follows and wanna query some relationships with specific ns0__hasSortKey value, I dont seem to be offered with the proper syntax by the Graphiql. If I use unions without properties attribute at the relationship directive, the retrieval works fine and no filtering capabilities (as expected).

>>>>>>>>>snippet of model starts >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
type ns1__Work @exclude(operations: [CREATE, UPDATE, DELETE]){
    ns0__relatesToChild: [relatesToChildTarget] @relationship(type: "ns0__relatesToChild", properties: "ns0__CHORelateProps"  ,direction: OUT)
    ns0__relatesToParent: [relatesToParentTarget] @relationship(type: "ns0__relatesToParent", properties: "ns0__CHORelateProps"  ,direction: OUT)
} 

interface ns0__CHORelateProps{
    ns0__hasSortKey: String!
    dct__isPartOf: String
    dct__type: String
}

union relatesToChildTarget = ns1__Expression | ns1__Work | ns0__WorkCollection
union relatesToParentTarget = ns1__Work | ns0__WorkCollection

<<<<<<<<<<<<<<<<<<<Snippet of model ends<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

See what Graphiql offers:


I think this has something to do with the fact that I would like to use union and property at the same time in the relationship.

In contrast - If I dont use Graphql union, but a graphql type in the return type of the relationship, this is what I can achieve.


Note this latter one properly resolves into a Cypher query and it retrieves the expected result.

My question is whether union is supported as return type of the relationship with properties and could you refer to the correct syntax?

My references:
https://neo4j.com/docs/graphql-manual/current/type-definitions/unions-and-interfaces/

Thanks,

1 Like

Hey @kovacjo, Darrell from the GraphQL team here.

Conceptually, we treat unions as something of a "collection of not necessarily related entities". As such, when filtering them, we divide them up into their member types as early as possible.

As such, I believe the syntax for your query should be:

{
  resources {
    uri
    ns0__relatesToChildAsResourceConnection(where: { 
      ns1__Work: { edge: { ns0__hasSortKey: "2.1" } }, 
      ns0__WorkCollection: { edge: { ns0__hasSortKey: "2.1" } }, 
      ns1__Expression: { edge: { ns0__hasSortKey: "2.1" } }
    }) {
      edges {
        ns0__hasSortKey
      }
    }
  }
}

I'm not 100% on that, and I don't have your full type definitions to verify, but can you give it a go and report back?

Looking at this now I realise that "the split" shouldn't happen until the node level, but this is what we will be doing when we introduce interfaces, and I think this is the important thing that divides the two. This has certainly given me some food for thought!

Hi @darrellwarde,

Please see the graphql model attached:

Model
type ns1__Work @exclude(operations: [CREATE, UPDATE, DELETE]){
    uri: ID!
    ns0__relatesToChild: [relatesToChildTarget] @relationship(type: "ns0__relatesToChild", properties: "ns0__CHORelateProps"  ,direction: OUT)
    ns0__relatesToParent: [relatesToParentTarget] @relationship(type: "ns0__relatesToParent", properties: "ns0__CHORelateProps"  ,direction: OUT)
    ns0__relatesToChildAsResource: [Resource] @relationship(type: "ns0__relatesToChild", properties: "ns0__CHORelateProps"  ,direction: OUT)
    ns0__relatesToParentAsResource: [Resource] @relationship(type: "ns0__relatesToParent", properties: "ns0__CHORelateProps"  ,direction: OUT)
}

interface ns0__CHORelateProps{
    ns0__hasSortKey: String!
}

type ns0__WorkCollection @exclude(operations: [CREATE, UPDATE, DELETE]){
    uri: ID!
    ns0__relatesToChild: [relatesToChildTarget] @relationship(type: "ns0__relatesToChild", properties: "ns0__CHORelateProps"  ,direction: OUT)
    ns0__relatesToParent: [relatesToParentTarget] @relationship(type: "ns0__relatesToParent", properties: "ns0__CHORelateProps"  ,direction: OUT)
    ns0__relatesToChildAsResource: [Resource] @relationship(type: "ns0__relatesToChild", properties: "ns0__CHORelateProps"  ,direction: OUT)
    ns0__relatesToParentAsResource: [Resource] @relationship(type: "ns0__relatesToParent", properties: "ns0__CHORelateProps"  ,direction: OUT)
}

type ns1__Expression @exclude(operations: [CREATE, UPDATE, DELETE]){
    uri: ID!
    ns0__relatesToParent: [relatesToParentTarget] @relationship(type: "ns0__relatesToParent", properties: "ns0__CHORelateProps"  ,direction: OUT)
    ns0__relatesToParentAsResource: [Resource] @relationship(type: "ns0__relatesToParent", properties: "ns0__CHORelateProps"  ,direction: OUT)
}

union relatesToChildTarget = ns1__Expression | ns1__Work | ns0__WorkCollection

union relatesToParentTarget = ns1__Work | ns0__WorkCollection

type Resource @exclude(operations: [CREATE, UPDATE, DELETE]){
    uri: ID!
    ns0__relatesToChildAsResource: [Resource] @relationship(type: "ns0__relatesToChild", properties: "ns0__CHORelateProps"  ,direction: OUT)
    ns0__relatesToParentAsResource: [Resource] @relationship(type: "ns0__relatesToParent", properties: "ns0__CHORelateProps"  ,direction: OUT)
}

A variant of your suggestion worked!!! :) Thanks. Looks like I gave up trying some combinations too early. See below.

{
  ns1Works(where: {uri: "URN:NodeB_IRI"}) {
    uri
    ns0__relatesToChildConnection(where: {ns1__Work: {edge: {ns0__hasSortKey: "2.1"}}, ns0__WorkCollection: {edge: {ns0__hasSortKey: "2.1"}}, ns1__Expression: {edge: {ns0__hasSortKey: "2.1"}}}) {
      edges {
        ns0__hasSortKey
        node{
          __typename
          ... on ns1__Work{
            uri
          }
        }
      }
      totalCount
      
    }
  }
}

Now I can simplify my model thanks to this.

Have a lovely day!

1 Like