Sorting by aggregate

What I want to do

Imagine I have a shopping list app that lets you create multiple lists of items with a cost property. I’d like to sort the lists by the totals of the items in the list. Is this something that can be done in GraphQL or even DQL?

This may seem like a trivial problem with a few lists, but we have something similar for a use case at our company, and some clients have thousands of “shopping lists” with many items. We’d like to create a table where they can easily sort these lists by item total with pagination.

What I did

I’ve searched and found this: Possible to sort by count of Aggregate field?

It looks like this might not be possible.

To start off with I was playing around with dgraph cloud and came up with this query:

query GetUserAndLists {
  getUser(id: "0x2") {
    lists {
      title
      itemsAggregate {
        count
        costSum
      }
    }
  }
}

This produces this result:

{
  "getUser": {
    "lists": [
      {
        "id": "0x3",
        "title": "List1",
        "itemsAggregate": {
          "count": 3,
          "costSum": 617.65
        }
      },
      {
        "id": "0x4",
        "title": "List2",
        "itemsAggregate": {
          "count": 1,
          "costSum": 980.43
        }
      }
    ]
  }
}

Is there a way to sort the lists by costSum in itemsAggregate?

Dgraph metadata

dgraph version v21.03.0-48-ge3d3e6290

So I’ve been poking around a bit more and found this: https://dgraph.io/docs/graphql/custom/dql/

I think if I did something like this combined with a DQL query that lets me do this, that it might actually be possible. Here’s the DQL query I came up with:

query {
  var(func: type(User)) @filter(uid(0x2)) {
    User.lists {
      listId as uid
      List.items {
        cost as Item.cost 
      }
      total as sum(val(cost))
    }
  }
  lists(func: uid(listId), orderdesc: val(total)) {
    title: List.title
    total: val(total)
  }
}

It returns exactly what I need, and in the order that I need it. I’m still not sure how to use the UID instead of the search string provided in the example

I was able to figure it out. I just needed to make sure the DQL query variable had a dollar sign in front of it in both the query function and the filter:

query q($userId: string) {
  var(func: type(User)) @filter(uid($userId)) {
  ...
  }