Sorting GraphQL results

Problem

You want to sort results by a particular field in a GraphQL query.

Solution

You can sort GraphQL query results using an index and a user-defined function.

The following example uses the following components:

The GraphQL schema schema.gql looks like this:

graphqlCopied!
type Posts {
  title: String!
  content: String!
}

type Query {
  allPostsSortedByTitle: [Posts!]! @resolver(name: "sort_by_title", paginated: true)
}

When you import the above schema, the GraphQL API automatically creates a collection named Posts and a UDF named sort_by_title.

For more information about importing GraphQL schemas, see the GraphQL quick start.

We need an index to sort our documents:

Copied!
client.query(
  q.({
    name: 'all_posts_sorted_by_title',
    source: q.('Posts'),
    values: [
      { field: ['data', 'title'] },
      { field: ['ref'] },
    ],
  })
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: ("all_posts_sorted_by_title"),
  ts: 1626709622330000,
  active: true,
  serialized: true,
  name: 'all_posts_sorted_by_title',
  source: ("Posts"),
  values: [ { field: [ 'data', 'title' ] }, { field: [ 'ref' ] } ],
  partitions: 8
}
Query metrics:
  •    bytesIn:   174

  •   bytesOut:   339

  • computeOps:     1

  •    readOps:     0

  •   writeOps:     1

  •  readBytes: 1,257

  • writeBytes:   473

  •  queryTime:  54ms

  •    retries:     0

We need some documents to sort. We can do this in either FQL or GraphQL. In FQL, the document creation command looks like this:

Copied!
client.query(
  q.(
    [
      { title: 'my second post', content: 'more placeholder content' },
      { title: 'my first post', content: 'placeholder content' },
      { title: 'my third post', content: 'even more placeholder content' },
    ],
    q.(
      'doc',
      q.(
        q.('Posts'),
        { data: q.('doc') },
      )
    )
  )
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
[
  {
    ref: (("Posts"), "305856701000581632"),
    ts: 1627946530260000,
    data: { title: 'my second post', content: 'more placeholder content' }
  },
  {
    ref: (("Posts"), "305856701000582656"),
    ts: 1627946530260000,
    data: { title: 'my first post', content: 'placeholder content' }
  },
  {
    ref: (("Posts"), "305856701000583680"),
    ts: 1627946530260000,
    data: {
      title: 'my third post',
      content: 'even more placeholder content'
    }
  }
]
Query metrics:
  •    bytesIn:   346

  •   bytesOut:   666

  • computeOps:     1

  •    readOps:     0

  •   writeOps:     3

  •  readBytes:     0

  • writeBytes: 1,253

  •  queryTime:  43ms

  •    retries:     0

Or to create the documents with GraphQL, use the following mutation:

graphqlCopied!
mutation {
  post2: createPosts( data:{
  	title: "my second post"
    content: "more placeholder content"
  }) {
    _id
  },
  post1: createPosts( data:{
  	title: "my first post"
    content: "placeholder content"
  }) {
    _id
  },
  post3: createPosts( data:{
  	title: "my third post"
    content: "even more placeholder content"
  }) {
    _id
  }
}

Next, update the stub function sort_by_title:

Copied!
client.query(
  q.(
    q.('sort_by_title'),
    {
      body: q.(
        q.(
          ['size', 'after', 'before'],
          q.(
            {
              match: q.(q.('all_posts_sorted_by_title')),
              page: q.(
                q.(q.('before'), null),
                q.(
                  q.(q.('after'), null),
                  q.(
                    q.('match'),
                    { size: q.('size') },
                  ),
                  q.(
                    q.('match'),
                    { size: q.('size'), after: q.('after') },
                  )
                ),
                q.(
                  q.('match'),
                  { size: q.('size'), before: q.('before') },
                ),
              ),
            },
            q.(
              q.('page'),
              q.(
                'values',
                q.(q.(1, q.('values')))
              )
            )
          )
        )
      ),
    }
  )
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: ("sort_by_title"),
  ts: 1626882814550000,
  name: 'sort_by_title',
  data: { gql: "not included for brevity" },
  body: ((["size", "after", "before"], ({"match": (("all_posts_sorted_by_title")), "page": ((("before"), null), ((("after"), null), (("match"), {"size": ("size")}), (("match"), {"after": ("after"), "size": ("size")})), (("match"), {"before": ("before"), "size": ("size")}))}, (("page"), ("values", ((1, ("values"))))))))
}
Query metrics:
  •    bytesIn:  637

  •   bytesOut:  771

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    1

  •  readBytes:  274

  • writeBytes:  686

  •  queryTime: 23ms

  •    retries:    0

Now you have a function which gets results from the all_posts_sorted_by_title index and arranges them in alphabetical order.

The following GraphQL query uses the allPostsSortedByTitle query specified in the schema:

graphqlCopied!
query FindAllPosts {
  allPostsSortedByTitle {
    data {
      title
      content
    }
  }
}

The above query generates the following results, in alphabetical order by title:

{
  "data": {
    "allPostsSortedByTitle": {
      "data": [
        {
          "title": "my first post",
          "content": "placeholder content"
        },
        {
          "title": "my second post",
          "content": "more placeholder content"
        },
        {
          "title": "my third post",
          "content": "even more placeholder content"
        }
      ]
    }
  }
}

Is this article helpful? 

Tell Fauna how the article can be improved:
Visit Fauna's forums or email docs@fauna.com

Thank you for your feedback!