neo4j / graphql

A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations.
https://neo4j.com/docs/graphql-manual/current/
Apache License 2.0
507 stars 149 forks source link

Cannot read property 'name' of undefined when querying interfaces #1121

Closed cramatt closed 2 years ago

cramatt commented 2 years ago

Is your feature request related to a problem? Please describe.

As implemented in v3, interfaces have a limited where filtering capability compared to unions. This is because the InterfaceWhere that is generated only contains the interface properties and not the properties of implementers. If this is intentional, then I open this ticket as a feature request. If unintentional, then consider this a good repro. If this DOES work and i'm missing it, then consider this a documentation suggestion 🤣

Note I also scanned through the existing Interface bugs, and saw several maybe related issues but nothing stood out as the same issue or containing the same level of clarity in the example:

Schema

type food {
  name: String
  ingredients_interface: [Ingredient_Interface!]!    @relationship(type: "has_Ingredient_Interface", direction: OUT)
  ingredients_union: [Ingredient_Union!]!            @relationship(type: "has_Ingredient_Union", direction: OUT)
}

interface Ingredient_Interface {
  id: ID! @id(autogenerate: true)
  name: String
}

type Banana implements Ingredient_Interface {
  id: ID! @id(autogenerate: true)
  name: String
  sweetness: String
  qty_ozs: Float
}

union Ingredient_Union = Sugar | Syrup

type Sugar {
  id: ID! @id(autogenerate: true)
  name: String
  sweetness: String
  qty_ozs: Float
}

type Syrup {
  id: ID! @id(autogenerate: true)
  name: String
  sweetness: String
  qty_ozs: Float
}

Seed some data

mutation {
  createFoods(
    input: [
      {
        name: "Cake"
        ingredients_interface: {
          create: [
            { node: { Banana: { name: "Golden Banana", qty_ozs: 100 } } }
          ]
        }
        ingredients_union: {
          Sugar: {
            create: [
              { node: { name: "Simple Sugar", qty_ozs: 100 } }
              { node: { name: "Brown Sugar", qty_ozs: 10 } }
            ]
          }
          Syrup: { create: { node: { name: "Maple Syrup", qty_ozs: 100 } } }
        }
      }
    ]
  ) {
    foods {
      name
    }
  }
}

Query showing limitations of interface where

{
  foods(where:{
    ingredients_unionConnection_ALL:{
      Sugar:{
        node:{
          qty_ozs_GT: 1
        }
      }
    }
    ingredients_interfaceConnection_ALL:{
      node:{
        # qty_ozs_GT not available here
        # only interfacfe level fields on where
        # in this example name and id
      }
      # Banana not available here like with unionWhere
    }
  }) {
    name
  }
}

Describe the solution you'd like In the above example, I'd expect to be able to filter on interfaces like I do on unions. For example adding a query something like this:

Banana :{
    node:{
      qty_ozs_GT: 1
  }
}

In practice this would make interfaces MUCH more useful when querying data.

Describe alternatives you've considered

None

Additional context

Versions:

"@neo4j/graphql": "3.0.1", "@neo4j/graphql-ogm": "3.0.1", Neo4j: 4.3.2

dmoree commented 2 years ago

@cramatt You will only get the interface fields at that level in the where input. There exists a special key _on that allows for access to the fields on the implementations. In other words, if you wanted to do something like your example you would need:

{
  foods(where:{
    ingredients_interfaceConnection_ALL:{
      node:{
        _on: {
          Banana: {
            qty_ozs_GT: 1
          }
        }
      }
    }
  }) {
    name
  }
}

This has implications for fetching the data and overriding shared fields.

This is documented here: https://neo4j.com/docs/graphql-manual/current/type-definitions/interfaces/#type-definitions-interfaced-types-querying

cramatt commented 2 years ago

@dmoree ah, ok, so its supported (although in a different way than unions) and I missed it in the docs - thanks for pointing me in the right direction.

However now I'm running into another issue...

I can followup with a more detail repro, but off the bat I'm getting an error trying to run the query you provided. Maybe this is related to one of the known issues??

Heres a screenshot showing the data has the name property:

image

But running this query

{
  foods(
    where: {
      ingredients_interfaceConnection_ALL: {
        node: { _on: { Banana: { qty_ozs_GT: 1 } } }
      }
    }
  ) {
    name
    ingredients_interface {
      name
      ... on Banana {
        qty_ozs
      }
    }
  }
}

Results in this error:

{
  "errors": [
    {
      "message": "Cannot read property 'name' of undefined",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "foods"
      ],
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR",
        "exception": {
          "stacktrace": [
            "TypeError: Cannot read property 'name' of undefined",
            "    at /website/node_modules/@neo4j/graphql/src/translate/where/create-where-and-params.ts:189:55",
            "    at Array.forEach (<anonymous>)",
            "    at reducer (/website/node_modules/@neo4j/graphql/src/translate/where/create-where-and-params.ts:183:41)",
            "    at Array.reduce (<anonymous>)",
            "    at createWhereAndParams (/website/node_modules/@neo4j/graphql/src/translate/where/create-where-and-params.ts:264:60)",
            "    at translateTopLevelMatch (/website/node_modules/@neo4j/graphql/src/translate/translate-top-level-match.ts:92:43)",
            "    at translateRead (/website/node_modules/@neo4j/graphql/src/translate/translate-read.ts:51:49)",
            "    at resolve (/website/node_modules/@neo4j/graphql/src/schema/resolvers/read.ts:31:47)",
            "    at Object.foods (/website/node_modules/@neo4j/graphql/src/schema/resolvers/wrapper.ts:98:16)",
            "    at field.resolve (/website/node_modules/apollo-server-core/src/utils/schemaInstrumentation.ts:106:18)"
          ]
        }
      }
    }
  ],
  "data": null
}

Thoughts?

cramatt commented 2 years ago

Maybe related to https://github.com/neo4j/graphql/issues/1049 ?

darrellwarde commented 2 years ago

No more errors encountered here for this bug, so this can be considered fixed in the latest release! Sorry for the slow updates, this may have been fixed for a while through some major refactors that we've done and we just haven't reconfirmed.