IanBussieres
IanBussieres

Reputation: 1006

Sorting results in AWS Amplify GraphQL without filtering

Provided a very simple model in graphql.schema, how would I perform a simple sort query?

type Todo @model
  id: ID!
  text: String!
}

Which generates the following in queries.js.

export const listTodos = /* GraphQL */ `
  query ListTodos(
    $filter: ModelTodoFilterInput
    $limit: Int
    $nextToken: String
  ) {
    listTodos(filter: $filter, limit: $limit, nextToken: $nextToken) {
      items {
        id
        text
      }
      nextToken
    }
  }
`;

I have found multiple sources pointing me in the direction of the @key directive. This similar question addresses that approach (GraphQL with AWS Amplify - how to enable sorting on query).

While that may seem promising and successfully generates new queries I can use, all the approaches I have tried require that I filter the data before sorting it. All I want to do is sort my todo results on a given column name, with a given sort direction (ASC/DESC).

This is how I would perform a simple (unsorted) query: const todos = await API.graphql(graphqlOperation(listTodos));

I would be looking to do something along the lines of: const todos = await API.graphql(graphqlOperation(listTodos, {sortField: "text", sortDirection: "ASC"} )).

Upvotes: 5

Views: 4433

Answers (3)

Glenn Mason
Glenn Mason

Reputation: 66

Declaring @searchable incurs pointless extra server cost if all you need is straight forward sorting. It spins up an EBS and an OpenSearch that will be about $20 a month minumum.

Instead you need to use the @index directive.

As per the documentation here: https://docs.amplify.aws/guides/api-graphql/query-with-sorting/q/platform/js/

In your model, add the @index directive to one of the fields with a few parameters:

type Todo @model {
  id: ID!
  title: String!
  type: String! @index(name: "todosByDate", queryField: "todosByDate", sortKeyFields: ["createdAt"])
  createdAt: String!
}

By declaring the queryField and the sortKeyField you will now have a new query available to once you push your amplify config:

query todosByDate {
  todosByDate(
    type: "Todo"
    sortDirection: ASC
  ) {
    items {
      id
      title
      createdAt
    }
  }
}

The field you declare this directive on can not be empty (notice the ! after the field name)

This is a much better way of doing it as opposed to @searchable, which is massively overkill.

Upvotes: 4

IanBussieres
IanBussieres

Reputation: 1006

I've accepted MTran's answer because it feels to me it is the nearest thing to an actual solution, but I've also decided to actually opt for a workaround. This way, I avoid adding a dependency to ElasticSearch.

I ended up adding a field to my schema and every single entry has the same value for that field. That way, I can filter by that value and still have the entire table of values, that I can then sort against.

Upvotes: 1

MTran
MTran

Reputation: 1809

Decorate your model with the @searchable directive, like so:

type Todo @model @searchable
{
  id: ID!
  text: String!
}

After that, you can query your data with sorting capabilities like below:

import { searchToDos } from '../graphql/queries';
import { API, graphqlOperation } from 'aws-amplify';

const toDoData = await API.graphql(graphqlOperation(searchToDos, {
  sort: {
    direction: 'asc',
    field: 'text'
  }
}));
console.log(toDoData.data.searchToDos.items);

For more information, see

Upvotes: 7

Related Questions