liveBook · Manning

Sort key

As explained in Amazon Web Services in Action, Third Edition: An in-depth guide to AWS

A sort key is the optional key attribute that orders items sharing a DynamoDB partition-key value and contributes to their composite primary key.

A sort key is the optional second attribute of a DynamoDB primary key. The first attribute is the partition key. Together, their values must uniquely identify an item, but either value can appear in multiple items. DynamoDB keeps sort keys ordered within each partition-key value. section 12.2.2 section 12.7

Tasks for each user

A to-do application stores tasks with a user ID (`uid`) as the partition key and a task ID (`tid`) as the sort key. Several tasks can share a `uid`, so the application can list one user’s tasks with a query. Because `uid` and `tid` must be unique together, using a millisecond timestamp for `tid` limits each user to one task per timestamp. section 12.2.2

$ aws dynamodb create-table --table-name todo-task \
 --attribute-definitions AttributeName=uid,AttributeType=S \   #1
 AttributeName=tid,AttributeType=N \
 --key-schema AttributeName=uid,KeyType=HASH \
 AttributeName=tid,KeyType=RANGE \                             #2
 --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5
#1 At least two attributes are needed for a partition key and sort key.
#2 The tid attribute is the sort key.

Create the task table with `uid` declared as the `HASH` key and numeric `tid` as the `RANGE` key. Wait for the table to become `ACTIVE` before using it. section 12.2.2

Use `getItem` with both key values to retrieve one task. Use `query` with the partition key to retrieve multiple tasks for a user; add a sort-key condition to narrow the results. A partition key requires an exact match, while sort-key conditions include comparisons and `BETWEEN`. You cannot query by sort key alone. section 12.4.1 section 12.7.2

Listing 12.11 nodetodo: Retrieving tasks (index.js)
if (input['task-ls'] === true) {
  const yyyymmdd = moment().format('YYYYMMDD');
  const params = {
    KeyConditionExpression: 'uid = :uid',                    #1
    ExpressionAttributeValues: {
      ':uid': {S: input['<uid>']}                            #2
    },
    TableName: 'todo-task',
    Limit: input['--limit']
  };
  if (input['--next'] !== null) {
    params.KeyConditionExpression += ' AND tid > :next';
    params.ExpressionAttributeValues[':next'] = {N: input['--next']};
  }
  if (input['--overdue'] === true) {
    params.FilterExpression = 'due < :yyyymmdd';             #3
    params.ExpressionAttributeValues
 [':yyyymmdd'] = {N: yyyymmdd};                            #4
  } else if (input['--due'] === true) {
    params.FilterExpression = 'due = :yyyymmdd';
    params.ExpressionAttributeValues[':yyyymmdd'] = {N: yyyymmdd};
  } else if (input['--withoutdue'] === true) {
    params.FilterExpression = 
 'attribute_not_exists(due)';                              #5
  } else if (input['--futuredue'] === true) {
    params.FilterExpression = 'due > :yyyymmdd';
    params.ExpressionAttributeValues[':yyyymmdd'] = {N: yyyymmdd};
  } else if (input['--dueafter'] !== null) {
    params.FilterExpression = 'due > :yyyymmdd';
    params.ExpressionAttributeValues[':yyyymmdd'] =
      {N: input['--dueafter']};
  } else if (input['--duebefore'] !== null) {
    params.FilterExpression = 'due < :yyyymmdd';
    params.ExpressionAttributeValues[':yyyymmdd'] =
      {N: input['--duebefore']};
  }
  if (input['<category>'] !== null) {
    if (params.FilterExpression === undefined) {
      params.FilterExpression = '';
    } else {
      params.FilterExpression += ' AND ';                    #6
    }
    params.FilterExpression += 'category = :category';
    params.ExpressionAttributeValues[':category'] =
      S: input['<category>']};
  }
  db.query(params, (err, data) => {                          #7
    if (err) {
      console.error('error', err);
    } else {
      console.log('tasks', data.Items.map(mapTaskItem));
      if (data.LastEvaluatedKey !== undefined) {
        console.log('more tasks available with --next=' +
          data.LastEvaluatedKey.tid.N);
      }
    }
  });
}
#1 Primary key query. The task table uses a partition and sort key. Only the partition key is defined in the query, so all tasks belonging to a user are returned.
#2 Query attributes must be passed this way.
#3 Filtering uses no index; it’s applied over all elements returned from the primary key query.
#4 Filter attributes must be passed this way.
#5 attribute_not_exists(due) is true when the attribute is missing (opposite of attribute_exists).
#6 Multiple filters can be combined with logical operators.
#7 Invokes the query operation on DynamoDB

The task-list command queries by `uid` and, when a starting point is supplied, adds `tid > :next` to its key condition. Its due-date checks are filters rather than sort-key conditions. section 12.4.2

Choose the sort key around the queries the application needs. A timestamp sort key, for example, lets a message query find one user’s newer or older messages in timestamp order. Filtering on a non-key attribute is less efficient because the filter inspects items returned by the key condition. section 12.7.2 section 12.4.2

If another access pattern needs different keys, a global secondary index can use its own partition and sort keys. A category-and-task-ID index supports task queries across users, but adds storage and write-capacity costs and is updated asynchronously. section 12.4.3 section 12.4.4

How Amazon Web Services in Action, Third Edition: An in-depth guide to AWS covers it

How deep it goes
Defines composite-key uniqueness, shows table creation and SDK queries, and explains sorted indexes without detailing their internals.
Examples
Uses user tasks keyed by user ID and task ID, plus timestamp-ordered messages and a category index.
What it assumes
Introduces partition-key and sort-key terminology through a to-do application rather than assuming familiarity with DynamoDB key schemas.
What you can do after
Apply it via the AWS CLI and DynamoDB SDK to create a composite-key table and query a user’s tasks.
How it connects
Connects partition keys and primary keys to key conditions, filters, and secondary indexes.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Amazon Web Services in Action, Third Edition: An in-depth guide to AWS. The section links point to the parts of the book it draws on.