graphprotocol / graph-node

Graph Node indexes data from blockchains such as Ethereum and serves it over GraphQL
https://thegraph.com
Apache License 2.0
2.91k stars 977 forks source link

Filtering for `null` nested entities #4112

Open azf20 opened 2 years ago

azf20 commented 2 years ago

Given the following schema:

type BadgeSpec @entity {
  id: String!
  metadata: SpecMetadata
}

type SpecMetadata @entity {
  id: ID!
  name: String!
}

The BadgeSpec metadata field is populated with the ID of a SpecMetadata entity. At query time, that is used as a lookup to return a SpecMetadata entity, if there is one with a matching ID. If there is no matching ID, the metadata object returns as null.

Users might want to filter for BadgeSpecs with no connected metadata, and might expect the following query to do that:

{
  badgeSpecs(where: {metadata: null}) {
    id
    metadata {
      id
      name
    }
  }
}

However that is not necessarily the current behaviour - filtering badgeSpecs on metadata filters on the value of the metadata field. So if the field is populated with an ID, but there isn't a SpecMetadata entity with that ID, then that BadgeSpec will not be returned. BadgeSpecs will only be returned when the value of the metadata field is itself null. There is currently no way to filter for all badgeSpecs with no connected metadata, regardless of whether the metadata field is populated.

Potential solutions:

kamilkisiela commented 2 years ago

Maybe metadata_is_null: true / false? Sending null is kind of limiting and might result in special treating of it in graph-node.

type Query {
  feed: Article
}

type Article {
  id: ID!
  title: String!
  description: String
  body: String!
}

With description_is_null: true we show all articles without descriptions, description_is_null: false would result in the opposite, only articles with with descriptions.

azf20 commented 2 years ago

@kamilkisiela yes I think that can make sense as a more explicit filter. Would you make it a specific filter to the nested entity case from the OP?

dotansimha commented 2 years ago

@kamilkisiela yes I think that can make sense as a more explicit filter. Would you make it a specific filter to the nested entity case from the OP?

I think we can have it for all kinds of fields? (not just nested) because null can be set on first-level fields that are defined as nullable?

azf20 commented 2 years ago

As long as it works for the specific case mentioned (nested entities, where the value in the first-level table is not null, but there is no entity in the child table) - it is already possible to filter for null first-level fields

kamilkisiela commented 1 year ago

Given the schema

type BadgeSpec @entity {
  id: String!
  metadata: SpecMetadata
}

type SpecMetadata @entity {
  id: ID!
  name: String!
}

The following query

{ badgeSpecs(where: {metadata_exists: false}) { ... } }

will be translated to:

SELECT ... FROM badge_spec WHERE metadata IS NULL

If it's derived, then the SQL will be:

SELECT ... FROM badge_spec WHERE NOT EXISTS (
  SELECT 1 FROM spec_metadata WHERE badge_spec.metadata = spec_metadata.id
)

Does it make sense, that's what we try to achieve?

Another question. Should we support only 1-to-1 connection?

azf20 commented 1 year ago

hey @kamilkisiela that sounds good to me! I think at the outset 1-to-1 is a reasonable starting point

github-actions[bot] commented 1 year ago

Looks like this issue has been open for 6 months with no activity. Is it still relevant? If not, please remember to close it.