sourcenetwork / defradb

DefraDB is a Peer-to-Peer Edge Database. It's the core data storage system for the Source Network Ecosystem, built with IPLD, LibP2P, CRDTs, and Semantic open web properties.
438 stars 43 forks source link

Alias targeting #681

Open jsimnz opened 2 years ago

jsimnz commented 2 years ago

GraphQL supports using a field multiple times in a selection set. For scalars, this is a non-issue, but for our "meta fields" like aggregates, it is a valid use case, as you might want to return two different _avg results in the same query.

The problem becomes when we want to use these fields in other operations, like groupBy: [_avg]. If our query has two _avg functions, it is ambiguous which we want to use.

I propose Alias Targeting. Using gql Aliases, we can be specific of which field we want to refer to if its ambigious (or not).

Given the query:

query {
  User(groupBy: [Verified]) {
    Verified
    _sum(_group: {field: _avg})
    _group(groupBy: [Name]) {
      Verified
      _avg(_group: {field: Points})
      _avg(_group: {field: Points, filter: {Points: {_gt: 100}}})
    }
  }
}

It will actually complain that there is a conflict with _avg and it won't execute, requires an alias.

So if we raname the fields with an alias, we should then be able to use the alias when targeting that field for future use.

Eg:

query {
  User(groupBy: [Verified]) {
    Verified
    _sum(_group: {field: $pointsAvg})
    _group(groupBy: [Name]) {
      Verified
      $pointsAvg: _avg(_group: {field: Points})
      $pointsAvgFilter: _avg(_group: {field: Points, filter: {Points: {_gt: 100}}})
    }
  }
}

We can see that the _group argument on the first _sum now targets the alias field $pointsAvg. If we wanted to be strict about the GQL spec, we would also have to define the variable, but that can be up for debate.

Using variables in this way is at best, against the spirit of the GraphQL spec, but it does not technically violate the spec as far as I can tell, as we are just adding special meaning/semantics to a valid GQL query.

AndrewSisley commented 2 years ago

There are multiple tasks in this ticket.

That said, I am highly doubtful that the suggestion in the description is within the GQL spec:

If not defined as constant (for example, in DefaultValue), a Variable can be supplied for an input value.

It also looks like the suggestion is using a (string?) typed variable as both an enum var (the field input param is an enum type), and an identity (the alias definition). It seems very likely to me that this will not work in the proposed form.

AndrewSisley commented 1 year ago

Is a discussion on this in the discord thread dev-db/"Alias targeting syntax" https://discord.com/channels/427944769851752448/1036675536971374694

Alternative syntax suggestion:

query {
  User(groupBy: [Verified]) {
    Verified
    _sum(_group: {alias: "pointsAvg"})
    _group(groupBy: [Name]) {
      Verified
      pointsAvg: _avg(_group: {field: Points})
      pointsAvgFilter: _avg(_group: {field: Points, filter: {Points: {_gt: 100}}})
    }
  }
}