Node.jsでGlobal Secondary Indexを使用して特定条件のレコードを抽出する


環境

  • Nodejs
  • AWS Lambda
  • AWS DynamoDB

はじめに

DynamoDBではパーティションキーもしくはレンジキーを使用してしか基本的には抽出が出来ないのですが、任意のキーを元に抽出を行いたいという時は、Global Secondary Index(以下GSI)を使用する事で任意のキーでレコードを抽出することが出来ます。

注意点

以下の点に気をつける必要があります。
* GSIは1テーブルにつき20件までしか作成出来ない
* GSIのキーに指定出来るカラムのデータ型は文字列、数値、バイナリのいずれかである必要があります
* Serverless Frameworkではまだそこまで自由なGSIは作成出来ないようなので、DynamoDBコンソールからGSIを作成する必要がある(ハッシュキー or レンジキー以外でのGSIの作成がうまくいかなかった)

例えばboolean型のカラムをキーにGSIは作成出来ません(私がやろうとしていました...)

参考情報:
* DynamoDB での制限

前提

適当ですが、以下のようなUsersテーブルがあるとします。

カラム名 データ型 キー
username string hash
age integer range
isPremier integer

isPremierカラムは01が整数で入るとします。
そしてisPremier0のレコードを全件取得したいというのが実現したいことです。

動作サンプルの手順

  1. AWS DynamoDB ConsoleからGSIを作成
  2. queryを行うLambdaを作成

AWS DynamoDB ConsoleからGSIを作成

AWSのDynamoDB Consoleページ > テーブル > インデックス にアクセスします

そしてインデックスの作成ボタンから新規のGSIを以下のように作成します。

queryを行うLambdaを作成

NodejsでLambda関数を作成します。
先ほど作成したGSIの以下の情報を使用します。
* テーブル名:Users
* インデックス名:isPremier-index
抽出条件はisPremierが0のレコードを全件取得

const AWS = require('aws-sdk')
const DynamoDB = new AWS.DynamoDB.DocumentClient({region: "ap-northeast-1"})

module.exports.hello = async (event, context) => {
  try {
    const params = {
      TableName: 'Users', // テーブル名
      IndexName: 'isPremier-index', // 作成したGSI名
      KeyConditionExpression: '#indexKey = :indexValue', // 条件を指定
      ExpressionAttributeNames : {
        "#indexKey"  : 'isPremier' // GSIの作成時に指定したキー名を設定
      },
      ExpressionAttributeValues: {
        ':indexValue': 0 // isPremierの0を抽出
      }
    }
    let items = [] // 抽出したレコードを格納するための空の配列を定義

    const query = async () => {
      console.log('start query')
      let result = await DynamoDB.query(params).promise()
      items.push(...result.Items)

      // LastEvaluatedKeyが存在していたら再帰的にqueryを実行
      if(result.LastEvaluatedKey){
        params.ExclusiveStartKey = result.LastEvaluatedKey
        await query()
      }
    }
    await query()
    console.log(`Execution result: ${items.length}`)
  } catch (err) {
    console.error(`[Error]: ${JSON.stringify(err)}`)
    return err
  }
}

以上で条件にあったレコードを全件取得出来ます。