Amplify-cli: Sort single field API

Created on 22 May 2019  路  13Comments  路  Source: aws-amplify/amplify-cli

Is your feature request related to a problem? Please describe.
I'm unsure whether this is already possible in AWS Amplify, but here is my problem. I'm building a simple contact list app.

type Contact @model @auth(rules: [{ allow: owner }]) {
  id: ID!
  firstName: String
  lastName: String
  city: String
  email: String
  telephone: String
}

Until now, I was using Amplify's helper methods for the query.

API.graphql(graphqlOperation(listContacts, { limit: 1000 }));

Afterwards I would sort the contacts on the client side by lastName and then by firstName. Now the first user has over a thousand contacts so he doesn't get all his contacts. People have told me to use pagination, which is good, but the problem I have is, let's say I over 11 pages and each page has 100 contacts. Then I could only sort each page by first and last Name, but not all contacts.

Describe the solution you'd like
A good solution would be, if you could sort the contacts in the query by some fields. E.g.

API.graphql(graphqlOperation(listContacts, { limit: 100, nextToken, sortBy: { firstName: 'gte', lastName: 'gte' }));

This way the query would be sorted and fetch the correct contacts and the nextToken would be useful.

Describe alternatives you've considered
The only alternative I have considered is fetching all contacts prior to loading the list, but this approach will cause longer and longer loading times as the amount of entities grow.

And alternative API design would be to let user describe the sorting in the schema

type Contact @model @auth(rules: [{ allow: owner }]) {
  id: ID!
  firstName: String @sort(2, "gte")
  lastName: String @sort(1, "gte")
  city: String
  email: String
  telephone: String
}

Where the number determines the order of sorts and the string the way to sort. But this would be way less flexible than using the dynamic approach described above.

graphql-transformer pending-response question

Most helpful comment

Yeah where do we add the sortDirection?

All 13 comments

This is part of my generated graphql/queries.js:

export const listUsers = `query ListUsers(
  $filter: ModelUserFilterInput
  $limit: Int
  $nextToken: String
) {
  listUsers(filter: $filter, limit: $limit, nextToken: $nextToken) {
    items {
      id

It would be great if listUsers had an argument sortBy

@janhesters We are actively working on the @key directive that will help solve this use case for you. The code (https://github.com/aws-amplify/amplify-cli/pull/1463) and docs (https://github.com/aws-amplify/docs/pull/689/files) are currently in PR.

The general idea is that you will be able to define a schema like this:

type Contact 
  @model 
  @key(name: "ByOwnerLastNameFirstName", fields: ["owner", "lastName", "firstName"], queryField: "contactsByOwner")
  @auth(rules: [{ allow: owner }]) 
{
  id: ID!
  firstName: String
  lastName: String
  city: String
  email: String
  telephone: String
  owner: String
}

This will create a GSI named "ByOwnerLastNameFirstName" with a hash key of owner and a composite sort key of lastName#firstName that is managed on your behalf by AppSync resolvers. This will allow you to run a query:

query {
  contactByOwner(owner: "myuser") {
    items {
      id
      owner
      firstName
      lastName
    }
  }
}

The sorting will happen automatically because the @key specifies that the lastName/firstName combinations of fields should be used as the sort key.

@mikeparisstuff Awesome, thanks for letting me know! I have three questions:

  1. Do you know when that PR will merge and the directive will go live?
  2. Why did you choose to go only for a hardcoded sort, and not a dynamically one (like API.graphql(graphqlOperation(listContacts, { limit: 100, nextToken, sortBy: { firstName: 'gte', lastName: 'gte' }));) ?
  3. Could you also include other useful directives like the max_length one?

Why not use a local secondary index聽instead of a global secondary index?

@dancomanlive What do you mean by that?

Was referring to "This will create a GSI named "ByOwnerLastNameFirstName" with a hash key of owner and a composite sort key of lastName#firstName that is managed on your behalf by AppSync resolvers."

A primary key can be a partition key or a combination of a partition(hash) key and sort(range) key. A local secondary index has the same partition key and different sort keys. A global secondary index can be a partition key(different from the partition key of the primary key and all other GSIs) or a combination of a partition key and sort key.

We need to return "all the items", which means a scan. It is not possible to order the results of a scan so the solution is to use a GSI. Ok I get it now.

We launched support for custom indexes today. You can find docs for the same out here - https://aws-amplify.github.io/docs/cli/graphql#key
Please let us know if you're still not able to solve your problem through this solution and we'll re-open this issue for you.

Thanks for the update! If I understand correctly, if I wanted to be able to query a whole table by each field, I would do:

type Foo @model
    @key(name: "Field1", fields: ["[any field]", "field1"], queryField: "fooSortedByField1")
    @key(name: "Field2", fields: ["[any field]", "field2"], queryField: "fooSortedByField2")
    @key(name: "Field3", fields: ["[any field]", "field3"], queryField: "fooSortedByField3")
{
    id: ID!
    field1: Int
    field2: Int
    field3: Int
}

and query eg:

query FooByField1 {
  fooSortedByField1() {
    items {
      id
      field1
      field2
      field3
    }
    nextToken
  }
}

I'm not sure how to say that I want it sorted ascending vs descending.

Yeah where do we add the sortDirection?

Wanted to echo the comments by @lorensr and @nino-moreton : Any word on how to add sortDirection?

Also, when we set up a GSI as @mikeparisstuff describes, is there any way to remove the "owner" arg in the query? It is redundant because we're using auth, so we're only getting contacts by that user anyway. For the example contact list api, I would like to run a query "contactByOwner" without any args, and have automatically return all contacts that are owned by the user, sorted by name.

I thought maybe changing the global sort key would do it by:

@key(fields: ["id", "lastName", "firstName"])

But when I run a listContacts, even though I just get the contacts owned by the user, the result is not sorted by name.

Hi guys, did the question about sortDirection ever get answered or do I really have to make custom resolver just for that?

@voidarg there's an open feature request for this at #1629

Like @khusmann mentioned, it would be great to be able to omit the first field entirely, and just sort the entire list of values.

Was this page helpful?
0 / 5 - 0 ratings