モジュール 3: クエリとグローバルセカンダリインデックス
DynamoDB で単一の API コールを使用して複数の項目を取得する簡単な例を見ていきます。
概要
このモジュールでは、DynamoDB で単一の API コールを使用して複数の項目を取得する簡単な例を見ていきます。セカンダリインデックスを使用して DynamoDB テーブルで別のクエリパターンを有効にする方法もご説明します。
アプリケーションのユースケース
モジュール 2 では、GetItem API コールを使用して DynamoDB テーブルから一冊の本を取得する方法をご紹介しました。このアクセスパターンは便利ですが、アプリケーションでは 1 回の呼び出しで複数の項目を取得できることも必要です。例えば、ジョン・グリシャム作のすべての本を取り出してユーザーに表示できるようにします。このモジュールのステップ 1 では、Query API を使用して特定の著者のすべての本を取得します。
1 冊の本を取得する GetItem API コールと、1 人の著者のすべての本を取得する Query API 呼び出しはどちらも、[Books] (本) テーブルで特定のプライマリキーを使用します。ただし、歴史や伝記などの特定のカテゴリですべての本を取得するといった、別のアクセスパターンを有効にすることもできます。カテゴリーはテーブルのプライマリキーではありませんが、セカンダリインデックスを作成して別のアクセスパターンを有効にできます。このモジュールのステップ 2 および 3 ではセカンダリインデックスを作成し、セカンダリインデックスのクエリを実行します。
所要時間
15 分
実装
-
クエリを使用して複数の項目を取得する
テーブルで複合プライマリキーを使用すると、Query API 呼び出しを使って同じハッシュキーですべての項目を取り出せます。このアプリケーションでは、同じ著者属性ですべての本を取り出せる、ということです。
AWS Cloud9 ターミナルで次のコマンドを実行します。
$ python query_items.py
このコマンドでジョン・グリシャム作のすべての本を取り出す次のスクリプトを実行します。
import boto3 from boto3.dynamodb.conditions import Key # boto3 is the AWS SDK library for Python. # The "resources" interface allows for a higher-level abstraction than the low-level client interface. # For more details, go to http://boto3.readthedocs.io/en/latest/guide/resources.html dynamodb = boto3.resource('dynamodb', region_name='us-east-1') table = dynamodb.Table('Books') # When making a Query API call, you use the KeyConditionExpression parameter to specify the hash key on which you want to query. # You’re using the Key object from the Boto 3 library to specify that you want the attribute name ("Author") # to equal "John Grisham" by using the ".eq()" method. resp = table.query(KeyConditionExpression=Key('Author').eq('John Grisham')) print("The query returned the following items:") for item in resp['Items']: print(item)
スクリプトを実行すると、ジョン・グリシャムの『法律事務所』と『レインメーカー』という 2 冊の本が表示されます。
$ python query_items.py The query returned the following items: {'Title': 'The Firm', 'Formats': {'Hardcover': 'Q7QWE3U2', 'Paperback': 'ZVZAYY4F', 'Audiobook': 'DJ9KS9NM'}, 'Author': 'John Grisham', 'Category': 'Suspense'} {'Title': 'The Rainmaker', 'Formats': {'Hardcover': 'J4SUKVGU', 'Paperback': 'D7YF4FCX'}, 'Author': 'John Grisham', 'Category': 'Suspense'}
DynamoDB で単一の呼び出しを使用して複数の項目を取得するのは一般的なパターンであり、Query API コールを使用すれば簡単にこれを実行できます。
-
セカンダリインデックスの作成
DynamoDB では、アカウントにセカンダリインデックスを作成して、テーブルで別のデータアクセスパターンに対応できるようになります。セカンダリインデックスは、DynamoDB テーブルにクエリの柔軟性を加える強力な方法です。
DynamoDB には 2 つのセカンダリインデックスがあります。グローバルセカンダリインデックスとローカルセカンダリインデックスです。このセクションでは、カテゴリー属性にグローバルセカンダリインデックスを追加して、特定のカテゴリーのすべての本を取り出せるようにします。
次のサンプルスクリプトで、既存のテーブルにグローバルセカンダリインデックスを追加します。
import boto3 # Boto3 is the AWS SDK library for Python. # You can use the low-level client to make API calls to DynamoDB. client = boto3.client('dynamodb', region_name='us-east-1') try: resp = client.update_table( TableName="Books", # Any attributes used in your new global secondary index must be declared in AttributeDefinitions AttributeDefinitions=[ { "AttributeName": "Category", "AttributeType": "S" }, ], # This is where you add, update, or delete any global secondary indexes on your table. GlobalSecondaryIndexUpdates=[ { "Create": { # You need to name your index and specifically refer to it when using it for queries. "IndexName": "CategoryIndex", # Like the table itself, you need to specify the key schema for an index. # For a global secondary index, you can use a simple or composite key schema. "KeySchema": [ { "AttributeName": "Category", "KeyType": "HASH" } ], # You can choose to copy only specific attributes from the original item into the index. # You might want to copy only a few attributes to save space. "Projection": { "ProjectionType": "ALL" }, # Global secondary indexes have read and write capacity separate from the underlying table. "ProvisionedThroughput": { "ReadCapacityUnits": 1, "WriteCapacityUnits": 1, } } } ], ) print("Secondary index added!") except Exception as e: print("Error updating table:") print(e)
グローバルセカンダリインデックスの作成は、テーブル作成と多くの共通点があります。インデックスの名前、インデックスに含まれる属性、インデックスのキースキーマ、プロビジョンドスループット (アプリケーションがテーブルまたはインデックスで使用できる性能の最大値) を指定します。各インデックスのプロビジョンドスループットはテーブルのプロビジョンドスループットとは異なります。これによりスループットをきめ細かく定義して、アプリケーションのニーズを満たすことができます。
ターミナルで次のコマンドを実行し、グローバルセカンダリインデックスを追加します。
$ python add_secondary_index.py
このスクリプトで、[Books] (本) テーブルにCategoryIndex というグローバルセカンダリインデックスを追加します。
-
セカンダリインデックスのクエリ
CategoryIndex ができましたので、これを使用して特定のカテゴリーのすべての本を取り出せます。セカンダリインデックスを使用してテーブルに対してクエリを実行するのは、Query API 呼び出しを使用するのと似ています。ここでは API 呼び出しにインデックス名を追加します。
グローバルセカンダリインデックスを既存のテーブルに追加する場合、DynamoDB では同期せずに、そのテーブルの既存の項目をインデックスに対してバックフィルします。すべての項目がバックフィルされたら、インデックスをクエリに使用することができます。バックフィルの時間はテーブルサイズよって異なります。
query_with_index.py スクリプトを使用して、新しいインデックスに対してクエリを実行できます。ターミナルで次のコマンドを使用してスクリプトを実行します。
$ python query_with_index.py
このコマンドでは次のスクリプトを実行して、[Suspense] (サスペンス) の [Category] (カテゴリ) に含まれるストアのすべての本を取得します。
import time import boto3 from boto3.dynamodb.conditions import Key # Boto3 is the AWS SDK library for Python. # The "resources" interface allows for a higher-level abstraction than the low-level client interface. # For more details, go to http://boto3.readthedocs.io/en/latest/guide/resources.html dynamodb = boto3.resource('dynamodb', region_name='us-east-1') table = dynamodb.Table('Books') # When adding a global secondary index to an existing table, you cannot query the index until it has been backfilled. # This portion of the script waits until the index is in the “ACTIVE” status, indicating it is ready to be queried. while True: if not table.global_secondary_indexes or table.global_secondary_indexes[0]['IndexStatus'] != 'ACTIVE': print('Waiting for index to backfill...') time.sleep(5) table.reload() else: break # When making a Query call, you use the KeyConditionExpression parameter to specify the hash key on which you want to query. # If you want to use a specific index, you also need to pass the IndexName in our API call. resp = table.query( # Add the name of the index you want to use in your query. IndexName="CategoryIndex", KeyConditionExpression=Key('Category').eq('Suspense'), ) print("The query returned the following items:") for item in resp['Items']: print(item)
一部のスクリプトはインデックスをクエリの実行に利用できるようになるまで待機することにご注意ください。
ターミナルに次のような出力が表示されます。
$ python query_with_index.py The query returned the following items: {'Title': 'The Firm', 'Formats': {'Hardcover': 'Q7QWE3U2', 'Paperback': 'ZVZAYY4F', 'Audiobook': 'DJ9KS9NM'}, 'Author': 'John Grisham', 'Category': 'Suspense'} {'Title': 'The Rainmaker', 'Formats': {'Hardcover': 'J4SUKVGU', 'Paperback': 'D7YF4FCX'}, 'Author': 'John Grisham', 'Category': 'Suspense'} {'Title': 'Along Came a Spider', 'Formats': {'Hardcover': 'C9NR6RJ7', 'Paperback': '37JVGDZG', 'Audiobook': '6348WX3U'}, 'Author': 'James Patterson', 'Category': 'Suspense'}
このクエリは 2 人の異なる著者の 3 冊の本を返します。これはテーブルの主なキースキーマでは難しかったと思われるクエリパターンですが、セカンダリインデックスの機能を利用すれば簡単に実行できます。
次のモジュールでは、UpdateItem API を使用してテーブルに既に存在する項目の属性を更新する方法をご説明します。