How to Order by Nested Data using GraphQL

We have discussed filtering on edges multiple times mot recently: Filter on non-scalar fields or relations (Types) - #4 by abhijit-kar, which is closely related to the topic of ordering on edges.

Given sample schema:

type Contact {
  name: String!
  tags: [Tag]
  category: Category
}
type Tag {
  tag @id
}
type Category {
  cat @id
}

With the query:

{
  queryContact(first: 5) {
    name
    tags { tag }
    category { cat }
  }
}

I could get a response back like:

data.queryContact: [
  { name: "A", tags: [{ tag: "n" }], category: { cat: null } },
  { name: "B", tags: [{ tag: "m" }], category: { cat: "z" } },
  { name: "C", tags: [{ tag: "p" }, { tag: "o" }], category: { cat: "x" } },
  { name: "E", tags: [], category: { cat: "y" } },
  { name: "D", tags: [], category: { cat: null } },
]

I can sort by name, but I cannot sort the above results by tags.tag nor category.cat

I am needing to this level of sorting. Is it possible without DQL and without doing it completely client side?

And just to be clear. I understand that I can order the subgraph, but what is wanted and needed is ordering the parent graph based upon the data from the sub graph.

This is not possible natively in GraphQL and requires using DQL. Out of curiosity, since tags.tag can have multiple values per Contact, how do you expect this to be sorted? Is it like you’d want to sort by the minimum possible tag per contact, so if contact had tags a and z, you’d use a when doing ascending sort and z while doing a descending sort?

That is sort of the wildcard that I threw in there to show some complexity to the issue. The use cases really vary on this and it could be sorted by length of the array, or alphabetically of the first item in the set. Really I just wanted to show that a simple sort by Contact.cat would not be the same in essence of sorting by Tags.tag. Ideally one could supply a sort function. But that is lending towards a solution in a JS hook which again has its own complexities. Sorting already queried data would not honor pagination. I don’t have the perfect answer for this, just wanted to throw out there what I am trying to wrap my head around. Sorting in a relational database with joining data is very different from sorting in a graph.

Is it possible to provide a sample query on how to do this in DQL? For now I was only able to find this in the documentation, while I was hoping for something more like this.

Let’s say the schema is the following:

type Asset {
  price: [Price]
}

type Price {
  date: [Date]
}

type Date {
  date: datetime
}

and we want to get the latest price of every asset (so sorting the prices by Date desc).

1 Like

Did you figure out how to do this @Matthias_Baetens ? I’m struggling with this too.

EDIT: Nevermind, figured it out. Converted my working code so it maps to your example, this should work:

{
  assetData as var(func: type(Asset)) @filter(uid("0x11e88")) @cascade {
    Asset.price {
      Price.date (orderdesc: Date.date) {
        dateVar as Date.date
      }
      dateValMap as min(val(dateVar))
    }
  }

  q(func: uid(assetData)) @cascade {
    price: Asset.price (orderdesc: val(dateValMap), first: 10, offset: 0) {
      date: Price.date (orderdesc: Date.date) {
        date: Date.date
      }
    }
  }
}

My understanding is that the @cascade directive forces the order from the nested nodes onto the parent—so in this case (orderdesc: val(dateValMap)) combined with @cascade means that the order of price: level is ‘filtered’ back up to the assetData level. Actually that doesn’t make sense because there is only one Asset in the above example, but the prices should be ordered by the nested dates. I don’t understand how this works, but it works on my machine™.

1 Like

The project was put on ice for now so didn’t investigate further, but thanks for sharing @BenW will definitely try this when we pick it up again!

1 Like

Is there a reason why this can’t be done using GraphQL?

The complexity of rewriting the GraphQL to DQL would probably entail quite a bit. Very similar to why nested filtering is not in GraphQL but is possible with DQL.

It is simple when first looked at it, but when trying to get the schema right for all possibilities in the strictly typed GraphQL inputs it becomes difficult in my understanding.

the orderable type input would have to be able to be nested within itself to allow this.

What now is a fairly simple rewrite from

{
  queryMyType(order: { asc: myField }) {
    id
  }
}

to

{
  queryMyType(func: type(MyType), orderasc: MyType.myField) {
    id: uid
  }
}

Would be really complex from

{
  queryMyType(order: { myEdge: { asc: myField } }) {
    id
  }
}

to

{
  var(func: type(MyType)) {
    MyType.myEdge {
      orderVar_1 as MyType.myField
    }
    orderVar_2 as min(val(orderVar_1))
  }
  queryMyType(func: type(MyType), orderasc: orderVar_2) {
    id: uid
  }
}