Cascade directive not properly working on fragments if types have the same field names

Version: 20.11.0

Schema:

interface  Meta {
  id: String! @id @search(by: [hash])
}

interface Generic {
  names: [String!]!
}

type Movie implements Meta & Generic {
  description: String!
  rating: Int!
}

type Comic implements Meta & Generic {
  description: String!
}

Mutation:

 mutation{
    addMovie(input:[{id:"abc",names:["xyz"],description:"dgraph",rating:1}]){
      movie{
            id
            names
            description
            rating
      }
    }
  addComic(input:[{id:"def",names:["uvw"],description:"comic-dgraph"}]){
      comic{
            id
            names
            description
      }
    }
}

Query:

query {
   queryMeta(first:2) @cascade{
       ... on Movie {
           names
       }
   }
}

Expected behavior:
Because we asked to expand only Movie, we expect the result to be only the name “xyz”.

Actual behavior:

{
  "data": {
    "queryMeta": [
      {
        "names": [
          "xyz"
        ]
      },
      {}
    ]
  },
  "extensions": {
    "touched_uids": 6,
    "tracing": {
      "version": 1,
      "startTime": "2020-12-20T14:14:27.8401613Z",
      "endTime": "2020-12-20T14:14:27.8415112Z",
      "duration": 1350000,
      "execution": {
        "resolvers": [
          {
            "path": [
              "queryMeta"
            ],
            "parentType": "Query",
            "fieldName": "queryMeta",
            "returnType": "[Meta]",
            "startOffset": 115700,
            "duration": 1219700,
            "dgraph": [
              {
                "label": "query",
                "startOffset": 159200,
                "duration": 1152000
              }
            ]
          }
        ]
      }
    }
  }
}

The cascade seems to not remove the empty result of the Comic, behaving in the same way of the query without the @cascade.


Running a query which ask for a field present only in Movie (rating), cascade behaves correctly
Query:

query {
   queryMeta(first:2) @cascade{
       ... on Movie {
           rating
       }
   }
}

Result

{
  "data": {
    "queryMeta": [
      {
        "rating": 1
      }
    ]
  },
  "extensions": {
    "touched_uids": 6,
    "tracing": {
      "version": 1,
      "startTime": "2020-12-20T14:17:19.6258508Z",
      "endTime": "2020-12-20T14:17:19.6271512Z",
      "duration": 1300500,
      "execution": {
        "resolvers": [
          {
            "path": [
              "queryMeta"
            ],
            "parentType": "Query",
            "fieldName": "queryMeta",
            "returnType": "[Meta]",
            "startOffset": 103900,
            "duration": 1179700,
            "dgraph": [
              {
                "label": "query",
                "startOffset": 182200,
                "duration": 1081000
              }
            ]
          }
        ]
      }
    }
  }
}

Hi @Luscha, Nice catch !!Preformatted text
The reason for this is the way cascade work in dgraph and graphql. Currrently all the filtering of objects related to @cascade is done in dgraph only and in graphql we filter result on basis of what’s been asked in query.So according to it , it’s expected behaviour. Note that field name is present in both types Movie and Comic.

As we are querying on Meta, we will get all the objects which implemented Meta and in this case, we will get both. But since we have asked to expand object of type Movie only , we get the other one empty. See the below graphql and the generated dgraph query.

GraphQL

query {
   queryMeta(first:2) @cascade{
       ... on Movie {
           names
       }
   }
}

Dgraph

query {
  queryMeta(func: type(Meta), first: 2) @cascade {
    dgraph.type
    names : Generic.names
    dgraph.uid : uid
  }
}

For the second case we have asked for field rating and only object of type “Movie” have it, So we don’t get any extra empty result because that is already filtered by cascade in dgraph.

I was suspecting the reason was both types had Generic.names and I see the logic behind the result.
I fully understand that returning empty objects if the fragment expands only some types is spec-compliant, but I feel like this behaviour does not reflect the @cascade’s definition.

With the @cascade directive, nodes that don’t have all fields specified in the query are removed.

Is it an intended behaviour and will never be “fixed”, or it might be possible that a postprocess of the data will be added in the GrapQL resolution of the directive?

Yeah, by the definition of cascade it doesn’t seem expected behavior. But now we are not asking cascade on fields but on fragments+fields and fragments are entirely graphql features.

So first, we need to support cascade on fragments, that will be added in graphql. And then this case will be handed. Currently we don’t have any plan for it but we will discuss it internally and then see if we need to prioritize it.