

# ローカルセカンダリインデックス
<a name="LSI"></a>

ベーステーブルのプライマリキーを使用してデータのクエリを実行する必要があるのは、一部のアプリケーションだけです。ただし、代替のソートキーが役に立つ場合があります。アプリケーションでソートキーを選択できるようにするには、Amazon DynamoDB テーブルに 1 つ以上のローカルセカンダリインデックスを作成し、これらのインデックスに対して `Query` または `Scan` のリクエストを発行できます。

**Topics**
+ [シナリオ: ローカルセカンダリインデックスの使用](#LSI.Scenario)
+ [属性の射影](#LSI.Projections)
+ [ローカルセカンダリインデックスの作成](#LSI.Creating)
+ [ローカルセカンダリインデックスからのデータの読み込み](#LSI.Reading)
+ [項目の書き込みとローカルセカンダリインデックス](#LSI.Writes)
+ [ローカルセカンダリインデックスに対するプロビジョニングされたスループットに関する考慮事項](#LSI.ThroughputConsiderations)
+ [ローカルセカンダリインデックスのストレージに関する考慮事項](#LSI.StorageConsiderations)
+ [ローカルセカンダリインデックス内の項目コレクション](#LSI.ItemCollections)
+ [ローカルセカンダリインデックスの操作: Java](LSIJavaDocumentAPI.md)
+ [ローカルセカンダリインデックスの操作: .NET](LSILowLevelDotNet.md)
+ [DynamoDB AWS CLI でのローカルセカンダリインデックスの操作](LCICli.md)

## シナリオ: ローカルセカンダリインデックスの使用
<a name="LSI.Scenario"></a>

例として、`Thread` テーブルを検討します。このテーブルは、[AWS ディスカッションフォーラム](https://forums.aws.amazon.com/)のようなアプリケーションで役立ちます。次の図は、テーブル内の項目の構成を示しています。一部表示されていない属性もあります。

![\[フォーラム名、件名、最後の投稿時刻、および返信数のリストを含む Thread テーブル。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/LSI_01.png)


DynamoDB には、同じパーティションキー値を持つすべての項目が継続的に保存されます。この例では、特定の `ForumName` を指定すると、`Query` オペレーションはそのフォーラムのすべてのスレッドをすばやく見つけることができます。同じパーティションキー値を持つ項目のグループでは、項目がソートキーの値によって並べ替えられます。ソートキー (`Subject`) がクエリでも提供される場合、DynamoDB は返される結果を絞り込むことができます。例えば、「S3」フォーラムで、文字「a」で始まる `Subject` を持つすべてのスレッドを返すことができます。

リクエストによっては、より複雑なデータアクセスパターンが要求される場合があります。以下に例を示します。
+ ビューと返信の数が最も多いのはどのフォーラムか?
+ 特定のフォーラムでメッセージ数が最も多いのはどのスレッドか?
+ 特定の期間中に特定のフォーラムに投稿されたスレッド数は?

これらの質問に答えるには、`Query` アクションでは十分ではありません。代わりに、テーブル全体の `Scan` を実行する必要があります。膨大な数の項目があるテーブルでは、これにより、プロビジョニングされた読み込みスループットが大量に消費されることになり、完了するまでに長時間かかります。

ただし、`Replies` や `LastPostDateTime` などの非キー属性に 1 つ以上のローカルセカンダリインデックスを指定することができます。

*ローカルセカンダリインデックス*は特定のパーティションキー値の代替ソートキーを維持します。またローカルセカンダリインデックスには、ベーステーブルの一部またはすべての属性のコピーが含まれます。テーブルを作成する際に、ローカルセカンダリインデックスに射影する属性を指定します。ローカルセカンダリインデックスのデータは、ベーステーブルと同じパーティションキーと、異なるソートキーで構成されます。これにより、この異なるディメンションにわたってデータ項目に効率的にアクセスできます。クエリまたはスキャンの柔軟性を向上させるために、テーブルごとに最大 5 つのローカルセカンダリインデックスを作成できます。

たとえば、あるアプリケーションで、特定のフォーラムに過去 3 か月以内に投稿されたすべてのスレッドを検索する必要があるとします。ローカルセカンダリインデックスがない場合、アプリケーションは `Scan` テーブル全体の `Thread` を実行して、指定された時間枠から外れた投稿を破棄する必要があります。ローカルセカンダリインデックスがあれば、`Query` オペレーションでは `LastPostDateTime` をソートキーとして使用し、データをすばやく見つけることができます。

次の図は、`LastPostIndex` という名前のローカルセカンダリインデックスを示しています。パーティションキーは `Thread` テーブルのパーティションキーと同じですが、ソートキーは `LastPostDateTime` です。

![\[フォーラム名、件名、および最後の投稿時刻のリストを含む LastPostIndex テーブル。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/LSI_02.png)


ローカルセカンダリインデックスは、すべて次の条件を満たす必要があります。
+ パーティションキーはそのベーステーブルのパーティションキーと同じである。
+ ソートキーは完全に 1 つのスカラー属性で構成されている。
+ ベーステーブルのソートキーがインデックスに射影され、非キー属性として機能する。

この例で、パーティションキーは `ForumName`、ローカルセカンダリインデックスのソートキーは `LastPostDateTime` です。さらに、ベーステーブルのソートキーの値 (この例では `Subject`) がインデックスに射影されますが、その値はインデックスキーの一部ではありません。アプリケーションが `ForumName` と `LastPostDateTime` に基づくリストを必要とする場合は、`Query` に対して `LastPostIndex` リクエストを実行できます。クエリ結果は `LastPostDateTime` によってソートされ、昇順または降順で返すことができます。このクエリは、特定の時間枠内に `LastPostDateTime` がある項目だけを返すなどのキー条件を適用することもできます。

すべてのローカルセカンダリインデックスには、そのベーステーブルからのパーティションキーとソートキーが自動的に格納されます。必要に応じて、非キー属性をインデックスに射影できます。インデックスのクエリを行うと、DynamoDB ではこれらの射影された属性を効率的に取り出すことができます。ローカルセカンダリインデックスにクエリを実行すると、インデックスに射影されて*いない*属性もクエリで取得できます。DynamoDB では、これらの属性をベーステーブルから自動的にフェッチしますが、レイテンシーが大きくなり、プロビジョニングされたスループットコストが高くなります。

任意のローカルセカンダリインデックスについて、異なるパーティションキーの値ごとに最大 10 GB のデータを保存できます。この数字には、ベーステーブル内のすべての項目に加えて、同じパーティションキーの値を持つインデックス内のすべての項目も含まれています。詳細については、「[ローカルセカンダリインデックス内の項目コレクション](#LSI.ItemCollections)」を参照してください。

## 属性の射影
<a name="LSI.Projections"></a>

`LastPostIndex` では、アプリケーションはクエリの基準として `ForumName` と `LastPostDateTime` を使用できます。ただし、追加の属性を取得する場合、DynamoDB は `Thread` テーブルに対して追加の読み取りオペレーションを実行する必要があります。このような追加の読み込みを*フェッチ*と言い、それによってクエリにプロビジョニングするスループットの合計量が増加することがあります。

ウェブページに「S3」内のすべてのスレッドと各スレッドの返信のリストを表示し、最新の返信の最後の返信日時で並べ替えるとします。このリストに入力するには、次の属性が必要になります。
+ `Subject`
+ `Replies`
+ `LastPostDateTime`

このデータのクエリを最も効率的に行い、フェッチオペレーションを回避するには、次の図に示すように、ローカルセカンダリインデックスにテーブルからの `Replies` 属性を射影します。

![\[フォーラム名、最後の投稿時刻、件名、および返信のリストを含む LastPostIndex テーブル。\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/LSI_03.png)




*射影*とは、テーブルからセカンダリインデックスにコピーされる属性のセットです。テーブルのパーティションキーとソートキーは常にインデックスに射影されます。アプリケーションのクエリ要件をサポートするために、他の属性を射影できます。インデックスをクエリすると、Amazon DynamoDB は、その属性が自身のテーブル内にあるかのように、プロジェクション内の任意の属性にアクセスできます。

セカンダリインデックスを作成するときは、インデックスに射影される属性を指定する必要があります。DynamoDB には、そのために次の 3 つのオプションが用意されています。
+ *KEYS\$1ONLY* – インデックス内の各項目は、テーブルパーティションキーとソートキーの値、およびインデックスキーの値のみで構成されます。`KEYS_ONLY` オプションを指定すると、セカンダリインデックスが最小になります。
+ *INCLUDE* – `KEYS_ONLY` の属性に加えて、セカンダリインデックスにその他の非キー属性が含まれるように指定できます。
+ *ALL* – セカンダリインデックスには、ソーステーブルのすべての属性が含まれます。すべてのテーブルデータがインデックスに複製されるため、`ALL` の射影にすると、セカンダリインデックスが最大になります。

前の図では、非キー属性の `Replies` が `LastPostIndex` に射影されています。アプリケーションでは `LastPostIndex` テーブル全体ではなく `Thread` に対するクエリを実行し、ウェブページに `Subject`、`Replies`、`LastPostDateTime` を表示することができます。他の非キー属性がリクエストされた場合、DynamoDB はそれらの属性を `Thread` テーブルからフェッチする必要があります。

アプリケーションの観点から見ると、ベーステーブルから追加の属性をフェッチする処理は、自動的で透過的なので、アプリケーションロジックを書き直す必要はありません。ただし、このようなフェッチによって、ローカルセカンダリインデックスを使用することで得られるパフォーマンスの利点が大幅に小さくなる可能性があります。

ローカルセカンダリインデックスに射影する属性を選択する場合には、プロビジョニングされるスループットコストとストレージコストのトレードオフを考慮する必要があります。
+ ごく一部の属性だけに最小のレイテンシーでアクセスする必要がある場合は、それらの属性だけをローカルセカンダリインデックスに射影することを検討してください。インデックスが小さいほど少ないコストで格納でき、書き込みコストも低くなります。まれにしかフェッチされない属性がある場合には、それらの属性を格納するコストよりも、プロビジョニングされるスループットのコストのほうが長期的に大きくなる可能性があります。
+ アプリケーションが非キー属性に頻繁にアクセスする場合には、それらの属性をローカルセカンダリインデックスに射影することを検討してください。ローカルセカンダリインデックスのストレージコストの増加は、頻繁にテーブルスキャンを実行するコストの減少で相殺されます。
+ ほとんどの非キー属性に頻繁にアクセスする場合は、それらの属性を (場合によっては、ベーステーブル全体を) ローカルセカンダリインデックスに射影することができます。それによってフェッチが不要になるため、柔軟性が最大になり、プロビジョニングされるスループットが最小限になります。ただし、ストレージコストが増加し、すべての属性を射影する場合には 2 倍にまで増大します。
+ アプリケーションでテーブルのクエリを頻繁に行う必要がなく、テーブル内のデータに対する書き込みや更新が多数になる場合は、*KEYS\$1ONLY* を射影することを検討してください。ローカルセカンダリインデックスは、最小サイズになりますが、それでもクエリのアクティビティに必要な場合は利用可能です。

## ローカルセカンダリインデックスの作成
<a name="LSI.Creating"></a>

テーブルで 1 つ以上のローカルセカンダリインデックスを作成するには、`CreateTable` オペレーションの `LocalSecondaryIndexes` パラメータを使用します。テーブルのローカルセカンダリインデックスは、テーブルが作成された時点で作成されます。テーブルを削除すると、そのテーブルにあるローカルセカンダリインデックスも削除されます。

ローカルセカンダリインデックスのソートキーにとなる 1 つの非キー属性を指定する必要があります。選択する属性はスカラー `String`、`Number`、または `Binary` である必要があります。その他のスカラー型、ドキュメント型、およびセット型は許可されません。データ型の詳細なリストについては、「[データ型](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes)」を参照してください。

**重要**  
ローカルセカンダリインデックスがあるテーブルには、パーティションキーの値ごとに 10 GB のサイズ制限があります。ローカルセカンダリインデックスがあるテーブルには、1 つのパーティションキー値の合計サイズが 10 GB を超えない限り、任意の数の項目を格納できます。詳細については、「」を参照してください[項目コレクションのサイズ制限](#LSI.ItemCollections.SizeLimit)

ローカルセカンダリインデックスには、任意のデータ型の属性を射影できます。これには、スカラー、ドキュメント、およびセットが含まれます。データ型の詳細なリストについては、「[データ型](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes)」を参照してください。

## ローカルセカンダリインデックスからのデータの読み込み
<a name="LSI.Reading"></a>

`Query` および `Scan` オペレーションを使用して、ローカルセカンダリインデックスから項目を取得できます。`GetItem` および `BatchGetItem` オペレーションは、ローカルセカンダリインデックスでは使用できません。

### ローカルセカンダリインデックスのクエリ
<a name="LSI.Querying"></a>

DynamoDB テーブルでは、各項目のパーティションキー値とソートキー値の組み合わせは、一意である必要があります。ただし、ローカルセカンダリインデックスでは、ソートキーの値は、特定のパーティションキーの値に対して一意である必要がありません。ローカルセカンダリインデックスに同じソートキーを持つ複数の項目がある場合、`Query` オペレーションは、同じパーティションキーの値を持つすべての項目を返します。レスポンスでは、一致する項目は特定の順序で返されません。

結果整合性の読み込みまたは完全整合性の読み込みを使用して、ローカルセカンダリインデックスのクエリを行うことができます。必要な整合性のタイプを指定するには、`ConsistentRead` オペレーションの `Query` パラメータを使用します。ローカルセカンダリインデックスからの完全整合性の読み込みでは、常に最新の更新された値が返されます。クエリがベーステーブルからさらに属性をフェッチする必要がある場合、それらの属性はインデックスについて整合性があることになります。

**Example**  
特定のフォーラムのディスカッションスレッドにあるデータをリクエストする `Query` から返される次のデータを考えてみます。  

```
{
    "TableName": "Thread",
    "IndexName": "LastPostIndex",
    "ConsistentRead": false,
    "ProjectionExpression": "Subject, LastPostDateTime, Replies, Tags",
    "KeyConditionExpression": 
        "ForumName = :v_forum and LastPostDateTime between :v_start and :v_end",
    "ExpressionAttributeValues": {
        ":v_start": {"S": "2015-08-31T00:00:00.000Z"},
        ":v_end": {"S": "2015-11-31T00:00:00.000Z"},
        ":v_forum": {"S": "EC2"}
    }
}
```
このクエリでは次のようになっています。  
+ DynamoDB は `ForumName` パーティションキーを使用して `LastPostIndex` にアクセスし、「EC2」のインデックス項目を特定します。このキーを持つすべてのインデックス項目が、すばやく取り出せるように隣り合わせに格納されます。
+ このフォーラム内で、DynamoDB はインデックスを使用して、指定された `LastPostDateTime` 条件に一致するキーを検索します。
+ `Replies` 属性はインデックスに射影されているため、DynamoDB は追加でプロビジョニングされたスループットを使用することなく、この属性を取得できます。
+ `Tags` 属性はインデックスに射影されていないため、DynamoDB は `Thread` テーブルにアクセスしてこの属性をフェッチする必要があります。
+ 結果が、`LastPostDateTime` によってソートされ、返されます。インデックスのエントリはまずパーティションキーの値によって、次にソートキーの値によって並べ替えられ、保存される順序で `Query` から返されます （`ScanIndexForward` パラメータを使用すると、結果が降順で返されます）。
ローカルセカンダリインデックスには `Tags` 属性が射影されていないため、DynamoDB は、読み取りキャパシティユニットを追加で使用して、ベーステーブルからこの属性をフェッチする必要があります。このクエリを頻繁に実行する必要がある場合は、`Tags` 属性を `LastPostIndex` に射影して、ベーステーブルからフェッチされないようにする必要があります。ただし、`Tags` 属性をまれにしか使用しない場合は、`Tags` 属性をインデックスに射影するためにストレージを追加することが有効でない可能性があります。

### ローカルセカンダリインデックスのスキャン
<a name="LSI.Scanning"></a>

`Scan` を使用して、ローカルセカンダリインデックスからすべてのデータを取得できます。リクエストにはベーステーブル名とインデックス名を指定する必要があります。`Scan` では、DynamoDB はインデックスのすべてのデータを読み込み、それをアプリケーションに返します。また、データの一部のみを返し、残りのデータを破棄するようにリクエストすることもできます。これを行うには、`FilterExpression` API の `Scan` パラメータを使用します。詳細については、「[Scan のフィルタ式](Scan.md#Scan.FilterExpression)」を参照してください。

## 項目の書き込みとローカルセカンダリインデックス
<a name="LSI.Writes"></a>

DynamoDB によって、すべてのローカルセカンダリインデックスがそれぞれのベーステーブルと自動的に同期されます。アプリケーションがインデックスに直接書き込むことはありません。ただし、DynamoDB でこれらのインデックスがどのように維持されるかを理解することは重要です。

ローカルセカンダリインデックスを作成する場合は、インデックスのソートキーになる属性を指定します。その属性のデータ型も指定します。つまり、ベーステーブルに項目を書き込むとき、その項目にインデックスキー属性が定義されている場合は、その型がインデックスキースキーマのデータ型に一致する必要があります。`LastPostIndex` の場合、インデックス内の `LastPostDateTime` ソートキーは、`String` データ型として定義されています。`Thread` テーブルに項目を追加し、`LastPostDateTime` に対して別のデータ型を指定する場合 (`Number` など)、データ型の不一致により DynamoDB によって `ValidationException` が返されます。

ベーステーブル内の項目とローカルセカンダリインデックス内の項目を 1 対 1 の関係にする必要はありません。この動作は、多くのアプリケーションにとってメリットになります。

多数のローカルセカンダリインデックスがあるテーブルは、インデックス数が少ないテーブルに比べて書き込みアクティビティに多くのコストを要します。詳細については、「[ローカルセカンダリインデックスに対するプロビジョニングされたスループットに関する考慮事項](#LSI.ThroughputConsiderations)」を参照してください。

**重要**  
ローカルセカンダリインデックスがあるテーブルには、パーティションキーの値ごとに 10 GB のサイズ制限があります。ローカルセカンダリインデックスがあるテーブルには、1 つのパーティションキー値の合計サイズが 10 GB を超えない限り、任意の数の項目を格納できます。詳細については、「[項目コレクションのサイズ制限](#LSI.ItemCollections.SizeLimit)」を参照してください。

## ローカルセカンダリインデックスに対するプロビジョニングされたスループットに関する考慮事項
<a name="LSI.ThroughputConsiderations"></a>

DynamoDB にテーブルを作成する場合には、テーブルで予想されるワークロードに応じた読み込み/書き込みキャパシティーユニットをプロビジョニングします。このワークロードには、テーブルのローカルセカンダリインデックスの読み込み/書き込みアクティビティが含まれます。

プロビジョニングされたスループット容量の現在の料金を確認するには、「[Amazon DynamoDB 料金](https://aws.amazon.com/dynamodb/pricing)」を参照してください。

### 読み込みキャパシティユニット
<a name="LSI.ThroughputConsiderations.Reads"></a>

ローカルセカンダリインデックスに対してクエリを実行する場合、使用される読み込みキャパシティーユニットの数は、データのアクセス方法によって異なります。

テーブルに対するクエリと同様に、インデックスクエリでは `ConsistentRead` の値に応じて、結果整合性のある読み込みまたは強力な整合性のある読み込みを実行できます。1 回の強力な整合性のある読み込みでは、1 つの読み込みキャパシティーユニットが消費され、結果整合性のある読み込みではその半分が消費されます。したがって結果整合性のある読み込みを選択することで、読み込みキャパシティーユニットのコストを削減できます。

インデックスキーと射影された属性だけをリクエストするインデックスクエリの場合、DynamoDB はテーブルに対するクエリの場合と同じ方法で、プロビジョニングされた読み込みアクティビティを計算します。唯一の違いは、ベーステーブル内の項目のサイズではなくインデックスエントリのサイズに基づいて計算が行われることです。読み込みキャパシティーユニットの数は、返されたすべての項目について射影されたすべての属性のサイズの合計です。結果は、次の 4 KB 境界まで切り上げられます。DynamoDB がプロビジョニングされたスループットの利用率を計算する方法の詳細については、「[DynamoDB プロビジョンドキャパシティモード](provisioned-capacity-mode.md)」を参照してください。

ローカルセカンダリインデックスに射影されていない属性を読み込むインデックスクエリの場合、DynamoDB は射影された属性をインデックスから読み込むのに加えて、それらの属性をベーステーブルからフェッチする必要があります。これらのフェッチは、`Select` オペレーションの `ProjectionExpression` または `Query` パラメータに、射影されていない属性を含めた場合に実行されます。フェッチによってクエリ応答のレイテンシーが増加し、プロビジョニングされるスループットのコストも増加します。前述のローカルセカンダリインデックスからの読み込みに加えて、フェッチされるベーステーブル項目それぞれについて、読み込みキャパシティーユニットの料金がかかります。この料金は、リクエストした属性だけでなく、項目全体をテーブルから読み込むために発生するものです。

`Query` オペレーションによって返される結果の最大サイズは 1 MB です。これには、返されるすべての項目にわたる、すべての属性の名前と値のサイズが含まれます。ただし、ローカルセカンダリインデックスに対するクエリによって、DynamoDB がベーステーブルから項目の属性をフェッチする場合には、結果で示されるデータの最大サイズが小さくなる可能性があります。この場合、結果のサイズは次の合計になります。
+ インデックス内で一致する項目のサイズ (次の 4 KB に切り上げ)
+ ベーステーブル内で一致する各項目のサイズ (項目ごとに次の 4 KB に切り上げ)

この式を使用すると、クエリオペレーションによって返される結果の最大サイズは依然として 1 MB になります。

たとえば、各項目のサイズが 300 バイトであるテーブルがあるとします。そのテーブルにはローカルセカンダリインデックスがありますが、各項目のうち 200 バイトだけがインデックスに射影されます。このインデックスに対して `Query` を行うときに各項目についてテーブルのフェッチが必要で、クエリによって 4 つの項目が返されるとします。DynamoDB では、次のように合計されます。
+ インデックス内で一致する項目のサイズは 200 バイト × 4 項目 = 800 バイトになり、それが 4 KB に切り上げられます。
+ ベーステーブル内で一致する項目のサイズ: (300 バイト、4 KB に切り上げ) × 4 項目 = 16 KB。

それによって、結果データの合計サイズは 20 KB になります。

### 書き込みキャパシティユニット
<a name="LSI.ThroughputConsiderations.Writes"></a>

テーブル内の項目が追加、更新、または削除された場合にローカルセカンダリインデックスを更新すると、テーブルにプロビジョニングされた書き込みキャパシティーユニットが消費されます。書き込み用にプロビジョニングされたスループットの合計コストは、テーブルに対する書き込みと、ローカルセカンダリインデックスの更新で消費された書き込みキャパシティーユニットの合計になります。

ローカルセカンダリインデックスに項目を書き込むコストは、いくつかの要因に左右されます。
+ インデックス付き属性が定義されたテーブルに新しい項目を書き込む場合、または既存の項目を更新して未定義のインデックス付き属性を定義する場合には、インデックスへの項目の挿入に 1 回の書き込みオペレーションが必要です。
+ テーブルに対する更新によってインデックス付きキー属性の値が（A から B に）変更された場合には、インデックスから既存の項目を削除し、インデックスに新しい項目を挿入するために、2 回の書き込みが必要です。  
+ インデックス内に既存の項目があって、テーブルに対する書き込みによってインデックス付き属性が削除された場合は、インデックスから古い項目の射影を削除するために、1 回の書き込みが必要です。
+ 項目の更新の前後にインデックス内に項目が存在しない場合は、インデックスで追加の書き込みコストは発生しません。

これらすべての要因は、インデックス内の各項目のサイズが 1 KB 以下であるという前提で書き込みキャパシティーユニット数を算出します。インデックスエントリがそれよりも大きい場合は、書き込みキャパシティーユニットを追加する必要があります。クエリが返す必要がある属性を特定し、それらの属性だけをインデックスに射影することで、書き込みコストは最小になります。

## ローカルセカンダリインデックスのストレージに関する考慮事項
<a name="LSI.StorageConsiderations"></a>

アプリケーションがテーブルに項目を書き込むと、DynamoDB では正しい属性のサブセットが、それらの属性が表示されるローカルセカンダリインデックスに自動的にコピーされます。AWS アカウントでは、テーブル内の項目のストレージと、そのベーステーブルのローカルセカンダリインデックスにある属性のストレージに対して課金されます。

インデックス項目が使用するスペースの量は、次の量の合計になります。
+ ベーステーブルのプライマリキー (パーティションキーとソートキー) のサイズのバイト数
+ インデックスキー属性のサイズのバイト数
+ 射影された属性（存在する場合）のサイズのバイト数
+ インデックス項目あたり 100 バイトのオーバーヘッド

ローカルセカンダリインデックスに必要なストレージは、インデックス内の 1 項目の平均サイズの見積もり値に、インデックス内の項目数を掛けて見積もることができます。

特定の属性が定義されていない項目がテーブルに含まれており、その属性がインデックスソートキーとして定義されている場合、DynamoDB はその項目に関連するデータをインデックスに書き込みません。

## ローカルセカンダリインデックス内の項目コレクション
<a name="LSI.ItemCollections"></a>

**注記**  
このセクションは、ローカルセカンダリインデックスがあるテーブルだけに関係します。

DynamoDB において、*項目コレクション*とは、テーブルおよびそのローカルセカンダリインデックス全体で同じパーティションキーの値を持つ項目のグループです。このセクションで使用する例では、`Thread` テーブルのパーティションキーは `ForumName` で、`LastPostIndex` のパーティションキーも `ForumName` です。同じ `ForumName` を持つテーブルとインデックス項目は、すべて同じ項目コレクションの一部です。例えば、`Thread` テーブルと `LastPostIndex` ローカルセカンダリインデックスには、フォーラム `EC2` 用の項目コレクションと、フォーラム `RDS` 用の別の項目コレクションがあります。

次の図は、フォーラム `S3` の項目コレクションを示しています。

![\[テーブル、および同じパーティションキー値 S3 を持つローカルセカンダリインデックスを含む DynamoDB 項目のコレクション。　\]](http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/images/LSI_04.png)


この図では、項目コレクションは、`Thread` および `LastPostIndex` 内のすべての項目で構成されていて、`ForumName` パーティションキーの値は「S3」です。テーブルにその他のローカルセカンダリインデックスがあった場合には、`ForumName` が「S3」であるそれらのインデックス内の項目も、項目コレクションの一部になります。

DynamoDB では次のオペレーションのいずれかを使用して、項目コレクションに関する情報を返すことができます。
+ `BatchWriteItem`
+ `DeleteItem`
+ `PutItem`
+ `UpdateItem`
+ `TransactWriteItems`

これらのオペレーションでは、それぞれ `ReturnItemCollectionMetrics` パラメータがサポートされています。このパラメータを `SIZE` に設定すると、インデックス内の各項目コレクションのサイズに関する情報が表示されます。

**Example**  
以下に、`UpdateItem` が `Thread` に設定されている、`ReturnItemCollectionMetrics` テーブルでの `SIZE` オペレーションの出力の例を示します。更新された項目には `ForumName` 値「EC2」があったため、出力にはその項目コレクションに関する情報が含まれています。  

```
{
    ItemCollectionMetrics: {
        ItemCollectionKey: {
            ForumName: "EC2"
        },
        SizeEstimateRangeGB: [0.0, 1.0]
    }
}
```
`SizeEstimateRangeGB` オブジェクトは、この項目コレクションのサイズが 0 GB と 1 GB の間であることを示します。DynamoDB では、このサイズ見積りが定期的に更新されるため、項目を変更した時には次回の数字が異なる場合があります。

### 項目コレクションのサイズ制限
<a name="LSI.ItemCollections.SizeLimit"></a>

1 つ以上のローカルセカンダリインデックスを含む項目コレクションのサイズは、すべて最大 10 GB です。これは、ローカルセカンダリインデックスのないテーブルの項目コレクションには適用されません。また、グローバルセカンダリインデックスの項目コレクションにも適用されません。1 つ以上のローカルセカンダリインデックスを含むテーブルのみに影響します。

項目コレクションが 10 GB の制限を超えると、DynamoDB は `ItemCollectionSizeLimitExceededException` を返す可能性があります。この場合、その項目コレクションに項目を追加したり、項目サイズを大きくしたりすることができなくなる場合があります。（項目コレクションのサイズを小さくする読み込み/書き込みオペレーションは、引き続き実行できます)。その他の項目コレクションには項目を追加することができます。

項目コレクションのサイズを小さくするには、次のいずれかを実行します。
+ 問題になっているパーティションキーの値を持つ不要な項目を削除します。ベーステーブルからこれらの項目を削除すると、DynamoDB では同じパーティションキーの値を持つインデックスエントリも削除されます。
+ 属性を削除するか属性のサイズを小さくすることで、項目を更新します。これらの属性がローカルセカンダリインデックスに射影されている場合には、DynamoDB では対応するインデックスエントリのサイズも小さくなります。
+ 同じパーティションキーおよびソートキーを使用して新しいテーブルを作成し、古いテーブルから新しいテーブルに項目を移動します。これは、頻繁にアクセスされない履歴データがテーブルに含まれている場合に適した方法です。また、この履歴データを Amazon Simple Storage Service (Amazon S3) にアーカイブすることを検討することもできます。

項目コレクションの合計サイズが 10 GB 未満になると、再び同じパーティションキーの値を使用して項目を追加できます。

項目コレクションのサイズを監視するようにアプリケーションを設定することをお勧めします。1 つの方法としては、`ReturnItemCollectionMetrics`、`SIZE`、`BatchWriteItem`、または `DeleteItem` を使用する場合に、`PutItem` パラメータを `UpdateItem` に設定するというものがあります。アプリケーションで出力内の `ReturnItemCollectionMetrics` オブジェクトを調査し、項目コレクションがユーザー定義の制限（たとえば 8 GB）を超えた場合にエラーメッセージを記録するようにします。制限を 10 GB より低く設定すれば早期警告システムになり、項目コレクションが上限に達する前に余裕をもって何らかの対処をとることができます。

### 項目コレクションおよびパーティション
<a name="LSI.ItemCollections.OnePartition"></a>

1 つ以上のローカルセカンダリインデックスを含むテーブルでは、各項目コレクションは 1 つのパーティションに保存されます。このような項目コレクションの合計サイズは、そのパーティションのキャパシティー (10 GB) に制限されます。データモデルにサイズに制限のない項目コレクションが含まれている場合や、一部の項目コレクションが将来に 10 GB を超えると合理的に予期されるアプリケーションでは、代わりにグローバルセカンダリインデックスの使用を検討します。

アプリケーションを設計する場合は、テーブルデータが異なるパーティションキー値に均一に分配されるようにする必要があります。ローカルセカンダリインデックス を持つテーブルについては、単一のパーティション内の単一の項目コレクションに読み込み/書き込みアクティビティの「ホットスポット」が作られないように、アプリケーションを設計します。

# ローカルセカンダリインデックスの操作: Java
<a name="LSIJavaDocumentAPI"></a>

AWS SDK for Java ドキュメント API を使用して、1 つ以上のローカルセカンダリインデックスを持つ Amazon DynamoDB テーブルを作成し、テーブルのインデックスを記述し、インデックスを使用してクエリを実行できます。

以下に、AWS SDK for Java ドキュメント API を使用したテーブルオペレーションの一般的な手順を示します。

1. `DynamoDB` クラスのインスタンスを作成します。

1. 対応するリクエストオブジェクトを作成して、オペレーションについて必要なパラメータとオプションパラメータを入力します。

1. 前のステップで作成したクライアントが提供する適切なメソッドを呼び出します。

**Topics**
+ [ローカルセカンダリインデックスを持つテーブルを作成する](#LSIJavaDocumentAPI.CreateTableWithIndex)
+ [ローカルセカンダリインデックスを持つテーブルの説明](#LSIJavaDocumentAPI.DescribeTableWithIndex)
+ [ローカルセカンダリインデックスのクエリ](#LSIJavaDocumentAPI.QueryAnIndex)
+ [例: Java ドキュメント API を使用したローカルセカンダリインデックス](LSIJavaDocumentAPI.Example.md)

## ローカルセカンダリインデックスを持つテーブルを作成する
<a name="LSIJavaDocumentAPI.CreateTableWithIndex"></a>

ローカルセカンダリインデックスは、テーブルの作成と同時に作成する必要があります。これを行うには、`createTable` メソッドを使用し、1 つ以上のローカルセカンダリインデックスの仕様を指定します。次の Java コード例では、ミュージックコレクション内の曲に関する情報を保持するためのテーブルを作成しています。パーティションキーは `Artist` で、ソートキーは `SongTitle` です。セカンダリインデックス `AlbumTitleIndex` は、アルバムタイトルによるクエリを容易にします。

DynamoDB ドキュメント API を使用して、ローカルセカンダリインデックスを持つテーブルを作成する手順を次に示します。

1. `DynamoDB` クラスのインスタンスを作成します。

1. リクエスト情報を指定する `CreateTableRequest` クラスのインスタンスを作成します。

   テーブル名、プライマリキー、およびプロビジョニングされたスループット値を指定する必要があります。ローカルセカンダリインデックスの場合は、インデックス名、インデックスソートキーの名前とデータ型、インデックスのキースキーマ、および属性射影を指定する必要があります。

1. リクエストオブジェクトをパラメータとして指定して、`createTable` メソッドを呼び出します。

以下の Java コード例は、前述のステップの例です。このコードは、`AlbumTitle` 属性に関するセカンダリインデックスを持つテーブル (`Music`) を作成します。テーブルパーティションキーとソートキー、およびインデックスソートキーのみが、インデックスに射影される属性です。

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

String tableName = "Music";

CreateTableRequest createTableRequest = new CreateTableRequest().withTableName(tableName);

//ProvisionedThroughput
createTableRequest.setProvisionedThroughput(new ProvisionedThroughput().withReadCapacityUnits((long)5).withWriteCapacityUnits((long)5));

//AttributeDefinitions
ArrayList<AttributeDefinition> attributeDefinitions= new ArrayList<AttributeDefinition>();
attributeDefinitions.add(new AttributeDefinition().withAttributeName("Artist").withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition().withAttributeName("SongTitle").withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition().withAttributeName("AlbumTitle").withAttributeType("S"));

createTableRequest.setAttributeDefinitions(attributeDefinitions);

//KeySchema
ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
tableKeySchema.add(new KeySchemaElement().withAttributeName("Artist").withKeyType(KeyType.HASH));  //Partition key
tableKeySchema.add(new KeySchemaElement().withAttributeName("SongTitle").withKeyType(KeyType.RANGE));  //Sort key

createTableRequest.setKeySchema(tableKeySchema);

ArrayList<KeySchemaElement> indexKeySchema = new ArrayList<KeySchemaElement>();
indexKeySchema.add(new KeySchemaElement().withAttributeName("Artist").withKeyType(KeyType.HASH));  //Partition key
indexKeySchema.add(new KeySchemaElement().withAttributeName("AlbumTitle").withKeyType(KeyType.RANGE));  //Sort key

Projection projection = new Projection().withProjectionType(ProjectionType.INCLUDE);
ArrayList<String> nonKeyAttributes = new ArrayList<String>();
nonKeyAttributes.add("Genre");
nonKeyAttributes.add("Year");
projection.setNonKeyAttributes(nonKeyAttributes);

LocalSecondaryIndex localSecondaryIndex = new LocalSecondaryIndex()
    .withIndexName("AlbumTitleIndex").withKeySchema(indexKeySchema).withProjection(projection);

ArrayList<LocalSecondaryIndex> localSecondaryIndexes = new ArrayList<LocalSecondaryIndex>();
localSecondaryIndexes.add(localSecondaryIndex);
createTableRequest.setLocalSecondaryIndexes(localSecondaryIndexes);

Table table = dynamoDB.createTable(createTableRequest);
System.out.println(table.getDescription());
```

DynamoDB がテーブルを作成し、テーブルのステータスを `ACTIVE` に設定するまで待機する必要があります。その後、テーブルへのデータ項目の入力を開始できます。

## ローカルセカンダリインデックスを持つテーブルの説明
<a name="LSIJavaDocumentAPI.DescribeTableWithIndex"></a>

テーブルのローカルセカンダリインデックスに関する情報を取得するには、`describeTable` メソッドを使用します。インデックスごとに、名前、キースキーマ、および射影された属性にアクセスできます。

AWS SDK for Java ドキュメント API を使用して、テーブルのローカルセカンダリインデックス情報にアクセスするためのステップを次に示します。

1. `DynamoDB` クラスのインスタンスを作成します。

1. `Table` クラスのインスタンスを作成します。テーブル名を入力する必要があります。

1. `Table` オブジェクトの `describeTable` メソッドを呼び出します。

以下の Java コード例は、前述のステップの例です。

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

String tableName = "Music";

Table table = dynamoDB.getTable(tableName);

TableDescription tableDescription = table.describe();

List<LocalSecondaryIndexDescription> localSecondaryIndexes 
    = tableDescription.getLocalSecondaryIndexes();

// This code snippet will work for multiple indexes, even though
// there is only one index in this example.

Iterator<LocalSecondaryIndexDescription> lsiIter = localSecondaryIndexes.iterator();
while (lsiIter.hasNext()) {

    LocalSecondaryIndexDescription lsiDescription = lsiIter.next();
    System.out.println("Info for index " + lsiDescription.getIndexName() + ":");
    Iterator<KeySchemaElement> kseIter = lsiDescription.getKeySchema().iterator();
    while (kseIter.hasNext()) {
        KeySchemaElement kse = kseIter.next();
        System.out.printf("\t%s: %s\n", kse.getAttributeName(), kse.getKeyType());
    }
    Projection projection = lsiDescription.getProjection();
    System.out.println("\tThe projection type is: " + projection.getProjectionType());
    if (projection.getProjectionType().toString().equals("INCLUDE")) {
        System.out.println("\t\tThe non-key projected attributes are: " + projection.getNonKeyAttributes());
    }
}
```

## ローカルセカンダリインデックスのクエリ
<a name="LSIJavaDocumentAPI.QueryAnIndex"></a>

テーブルに `Query` を実行するのとほぼ同じ方法で、ローカルセカンダリインデックスに対する `Query` オペレーションを使用することができます。インデックス名、インデックスソートキーのクエリ条件、および返す属性を指定する必要があります。この例では、インデックスは `AlbumTitleIndex`、インデックスソートキーは `AlbumTitle` です。

返される属性は、インデックスに射影された属性だけです。このクエリを変更して非キー属性を選択することもできますが、これには比較的コストのかかるテーブルフェッチアクティビティが必要です。テーブルのフェッチの詳細については、「[属性の射影](LSI.md#LSI.Projections)」を参照してください。

AWS SDK for Java ドキュメント API を使用してローカルセカンダリインデックスをクエリする手順を次に示します。

1. `DynamoDB` クラスのインスタンスを作成します。

1. `Table` クラスのインスタンスを作成します。テーブル名を入力する必要があります。

1. `Index` クラスのインスタンスを作成します。インデックス名を入力する必要があります。

1. `Index` クラスの `query` メソッドを呼び出します。

以下の Java コード例は、前述のステップの例です。

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

String tableName = "Music";

Table table = dynamoDB.getTable(tableName);
Index index = table.getIndex("AlbumTitleIndex");

QuerySpec spec = new QuerySpec()
    .withKeyConditionExpression("Artist = :v_artist and AlbumTitle = :v_title")
    .withValueMap(new ValueMap()
        .withString(":v_artist", "Acme Band")
        .withString(":v_title", "Songs About Life"));

ItemCollection<QueryOutcome> items = index.query(spec);

Iterator<Item> itemsIter = items.iterator();

while (itemsIter.hasNext()) {
    Item item = itemsIter.next();
    System.out.println(item.toJSONPretty());
}
```

### ローカルセカンダリインデックスの整合性のある読み取り
<a name="LSIJavaDocumentAPI.ConsistentReads"></a>

結果整合性のある読み込みのみをサポートするグローバルセカンダリインデックスとは異なり、ローカルセカンダリインデックスは結果整合性のある読み込みと強力な整合性のある読み込みの両方をサポートします。ローカルセカンダリインデックスからの完全整合性の読み込みでは、常に最新の更新された値が返されます。クエリがベーステーブルからさらに属性をフェッチする必要がある場合、それらのフェッチされた属性もインデックスについて整合性があることになります。

デフォルトでは、`Query` は結果整合性のある読み込みを使用します。強力な整合性のある読み込みをリクエストするには、`QuerySpec` で `ConsistentRead` を `true` に設定します。次の例では、強力な整合性のある読み込みを使用して `AlbumTitleIndex` をクエリを実行します。

**Example**  

```
QuerySpec spec = new QuerySpec()
    .withKeyConditionExpression("Artist = :v_artist and AlbumTitle = :v_title")
    .withValueMap(new ValueMap()
        .withString(":v_artist", "Acme Band")
        .withString(":v_title", "Songs About Life"))
    .withConsistentRead(true);
```

**注記**  
強力な整合性のある読み込みでは、返されるデータ 4 KB ごとに 1 つの読み込みキャパシティーユニット (切り上げ) が消費されますが、結果整合性のある読み込みではその半分が消費されます。例えば、9 KB のデータを返す強力な整合性のある読み込みでは、3 つの読み込みキャパシティーユニット (9 KB / 4 KB = 2.25、3 に切り上げ) が消費されますが、結果整合性のある読み込みを使用した同じクエリでは、1.5 の読み込みキャパシティーユニットが消費されます。アプリケーションがわずかに古くなる可能性のあるデータの読み取りを許容できる場合は、結果整合性のある読み込みを使用して読み込みキャパシティーの使用量を減らします。詳細については、「[読み込みキャパシティユニット](LSI.md#LSI.ThroughputConsiderations.Reads)」を参照してください。

# 例: Java ドキュメント API を使用したローカルセカンダリインデックス
<a name="LSIJavaDocumentAPI.Example"></a>

次の Java コード例は、Amazon DynamoDB でローカルセカンダリインデックスを操作する方法を示しています。この例では、パーティションキーを `CustomerId` として 、ソートキーを `OrderId` として `CustomerOrders` という名前のテーブルを作成しています。このテーブルには、次の 2 つのローカルセカンダリインデックスがあります。
+ `OrderCreationDateIndex` – ソートキーは `OrderCreationDate` です。次の属性がインデックスに射影されます。
  + `ProductCategory`
  + `ProductName`
  + `OrderStatus`
  + `ShipmentTrackingId`
+ `IsOpenIndex` – ソートキーは `IsOpen` です。すべてのテーブル属性がインデックスに射影されます。

`CustomerOrders` テーブルが作成されると、プログラムは顧客の注文を表すデータをテーブルにロードします。次に、ローカルセカンダリインデックスを使用してデータにクエリを実行します。最後に、プログラムは `CustomerOrders` テーブルを削除します。

以下の例をテストするための詳細な手順については、「[Java コードの例](CodeSamples.Java.md)」を参照してください。

**Example**  

```
package com.example.dynamodb;

import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.waiters.DynamoDbWaiter;

import java.util.HashMap;
import java.util.Map;

public class DocumentAPILocalSecondaryIndexExample {

    static DynamoDbClient client = DynamoDbClient.create();
    public static String tableName = "CustomerOrders";

    public static void main(String[] args) {
        createTable();
        loadData();
        query(null);
        query("IsOpenIndex");
        query("OrderCreationDateIndex");
        deleteTable(tableName);
    }

    public static void createTable() {
        CreateTableRequest request = CreateTableRequest.builder()
            .tableName(tableName)
            .provisionedThroughput(ProvisionedThroughput.builder()
                .readCapacityUnits(1L)
                .writeCapacityUnits(1L)
                .build())
            .attributeDefinitions(
                AttributeDefinition.builder().attributeName("CustomerId").attributeType(ScalarAttributeType.S).build(),
                AttributeDefinition.builder().attributeName("OrderId").attributeType(ScalarAttributeType.N).build(),
                AttributeDefinition.builder().attributeName("OrderCreationDate").attributeType(ScalarAttributeType.N).build(),
                AttributeDefinition.builder().attributeName("IsOpen").attributeType(ScalarAttributeType.N).build())
            .keySchema(
                KeySchemaElement.builder().attributeName("CustomerId").keyType(KeyType.HASH).build(),
                KeySchemaElement.builder().attributeName("OrderId").keyType(KeyType.RANGE).build())
            .localSecondaryIndexes(
                LocalSecondaryIndex.builder()
                    .indexName("OrderCreationDateIndex")
                    .keySchema(
                        KeySchemaElement.builder().attributeName("CustomerId").keyType(KeyType.HASH).build(),
                        KeySchemaElement.builder().attributeName("OrderCreationDate").keyType(KeyType.RANGE).build())
                    .projection(Projection.builder()
                        .projectionType(ProjectionType.INCLUDE)
                        .nonKeyAttributes("ProductCategory", "ProductName")
                        .build())
                    .build(),
                LocalSecondaryIndex.builder()
                    .indexName("IsOpenIndex")
                    .keySchema(
                        KeySchemaElement.builder().attributeName("CustomerId").keyType(KeyType.HASH).build(),
                        KeySchemaElement.builder().attributeName("IsOpen").keyType(KeyType.RANGE).build())
                    .projection(Projection.builder()
                        .projectionType(ProjectionType.ALL)
                        .build())
                    .build())
            .build();

        System.out.println("Creating table " + tableName + "...");
        client.createTable(request);

        try (DynamoDbWaiter waiter = client.waiter()) {
            WaiterResponse<DescribeTableResponse> response = waiter.waitUntilTableExists(r -> r.tableName(tableName));
            response.matched().response().ifPresent(System.out::println);
        }
    }

    public static void query(String indexName) {
        System.out.println("\n***********************************************************\n");
        System.out.println("Querying table " + tableName + "...");

        if ("IsOpenIndex".equals(indexName)) {
            System.out.println("\nUsing index: '" + indexName + "': Bob's orders that are open.");
            System.out.println("Only a user-specified list of attributes are returned\n");

            Map<String, AttributeValue> values = new HashMap<>();
            values.put(":v_custid", AttributeValue.builder().s("bob@example.com").build());
            values.put(":v_isopen", AttributeValue.builder().n("1").build());

            QueryRequest request = QueryRequest.builder()
                .tableName(tableName)
                .indexName(indexName)
                .keyConditionExpression("CustomerId = :v_custid and IsOpen = :v_isopen")
                .expressionAttributeValues(values)
                .projectionExpression("OrderCreationDate, ProductCategory, ProductName, OrderStatus")
                .build();

            System.out.println("Query: printing results...");
            client.query(request).items().forEach(System.out::println);

        } else if ("OrderCreationDateIndex".equals(indexName)) {
            System.out.println("\nUsing index: '" + indexName + "': Bob's orders that were placed after 01/31/2015.");
            System.out.println("Only the projected attributes are returned\n");

            Map<String, AttributeValue> values = new HashMap<>();
            values.put(":v_custid", AttributeValue.builder().s("bob@example.com").build());
            values.put(":v_orddate", AttributeValue.builder().n("20150131").build());

            QueryRequest request = QueryRequest.builder()
                .tableName(tableName)
                .indexName(indexName)
                .keyConditionExpression("CustomerId = :v_custid and OrderCreationDate >= :v_orddate")
                .expressionAttributeValues(values)
                .select(Select.ALL_PROJECTED_ATTRIBUTES)
                .build();

            System.out.println("Query: printing results...");
            client.query(request).items().forEach(System.out::println);

        } else {
            System.out.println("\nNo index: All of Bob's orders, by OrderId:\n");

            Map<String, AttributeValue> values = new HashMap<>();
            values.put(":v_custid", AttributeValue.builder().s("bob@example.com").build());

            QueryRequest request = QueryRequest.builder()
                .tableName(tableName)
                .keyConditionExpression("CustomerId = :v_custid")
                .expressionAttributeValues(values)
                .build();

            System.out.println("Query: printing results...");
            client.query(request).items().forEach(System.out::println);
        }
    }

    public static void deleteTable(String tableName) {
        System.out.println("Deleting table " + tableName + "...");
        client.deleteTable(DeleteTableRequest.builder().tableName(tableName).build());

        try (DynamoDbWaiter waiter = client.waiter()) {
            waiter.waitUntilTableNotExists(r -> r.tableName(tableName));
        }
    }

    public static void loadData() {
        System.out.println("Loading data into table " + tableName + "...");

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("alice@example.com").build(),
            "OrderId", AttributeValue.builder().n("1").build(),
            "IsOpen", AttributeValue.builder().n("1").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150101").build(),
            "ProductCategory", AttributeValue.builder().s("Book").build(),
            "ProductName", AttributeValue.builder().s("The Great Outdoors").build(),
            "OrderStatus", AttributeValue.builder().s("PACKING ITEMS").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("alice@example.com").build(),
            "OrderId", AttributeValue.builder().n("2").build(),
            "IsOpen", AttributeValue.builder().n("1").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150221").build(),
            "ProductCategory", AttributeValue.builder().s("Bike").build(),
            "ProductName", AttributeValue.builder().s("Super Mountain").build(),
            "OrderStatus", AttributeValue.builder().s("ORDER RECEIVED").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("alice@example.com").build(),
            "OrderId", AttributeValue.builder().n("3").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150304").build(),
            "ProductCategory", AttributeValue.builder().s("Music").build(),
            "ProductName", AttributeValue.builder().s("A Quiet Interlude").build(),
            "OrderStatus", AttributeValue.builder().s("IN TRANSIT").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("176493").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("1").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150111").build(),
            "ProductCategory", AttributeValue.builder().s("Movie").build(),
            "ProductName", AttributeValue.builder().s("Calm Before The Storm").build(),
            "OrderStatus", AttributeValue.builder().s("SHIPPING DELAY").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("859323").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("2").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150124").build(),
            "ProductCategory", AttributeValue.builder().s("Music").build(),
            "ProductName", AttributeValue.builder().s("E-Z Listening").build(),
            "OrderStatus", AttributeValue.builder().s("DELIVERED").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("756943").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("3").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150221").build(),
            "ProductCategory", AttributeValue.builder().s("Music").build(),
            "ProductName", AttributeValue.builder().s("Symphony 9").build(),
            "OrderStatus", AttributeValue.builder().s("DELIVERED").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("645193").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("4").build(),
            "IsOpen", AttributeValue.builder().n("1").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150222").build(),
            "ProductCategory", AttributeValue.builder().s("Hardware").build(),
            "ProductName", AttributeValue.builder().s("Extra Heavy Hammer").build(),
            "OrderStatus", AttributeValue.builder().s("PACKING ITEMS").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("5").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150309").build(),
            "ProductCategory", AttributeValue.builder().s("Book").build(),
            "ProductName", AttributeValue.builder().s("How To Cook").build(),
            "OrderStatus", AttributeValue.builder().s("IN TRANSIT").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("440185").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("6").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150318").build(),
            "ProductCategory", AttributeValue.builder().s("Luggage").build(),
            "ProductName", AttributeValue.builder().s("Really Big Suitcase").build(),
            "OrderStatus", AttributeValue.builder().s("DELIVERED").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("893927").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("7").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150324").build(),
            "ProductCategory", AttributeValue.builder().s("Golf").build(),
            "ProductName", AttributeValue.builder().s("PGA Pro II").build(),
            "OrderStatus", AttributeValue.builder().s("OUT FOR DELIVERY").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("383283").build()));
    }

    private static void putItem(Map<String, AttributeValue> item) {
        client.putItem(PutItemRequest.builder().tableName(tableName).item(item).build());
    }
}
```

# ローカルセカンダリインデックスの操作: .NET
<a name="LSILowLevelDotNet"></a>

**Topics**
+ [ローカルセカンダリインデックスを持つテーブルを作成する](#LSILowLevelDotNet.CreateTableWithIndex)
+ [ローカルセカンダリインデックスを持つテーブルの説明](#LSILowLevelDotNet.DescribeTableWithIndex)
+ [ローカルセカンダリインデックスのクエリ](#LSILowLevelDotNet.QueryAnIndex)
+ [例: AWS SDK for .NET 低レベル API を使用したローカルセカンダリインデックス](LSILowLevelDotNet.Example.md)

AWS SDK for .NET 低レベル API を使用して、1 つ以上のローカルセカンダリインデックスを含む Amazon DynamoDB テーブルを作成し、テーブルのインデックスを記述し、インデックスを使用してクエリを実行できます。これらのオペレーションは、対応する低レベル DynamoDB API アクションにマッピングされます。詳細については、「[.NET コード例](CodeSamples.DotNet.md)」を参照してください。

以下に、.NET 低レベル API を使用したテーブルオペレーションの一般的な手順を示します。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. 対応するリクエストオブジェクトを作成して、オペレーションについて必要なパラメータとオプションパラメータを入力します。

   例えば、テーブルを作成するには `CreateTableRequest` オブジェクトを作成し、テーブルやインデックスにクエリを実行するには `QueryRequest` オブジェクトを作成します。

1. 前述のステップで作成したクライアントから提供された適切なメソッドを実行します。

## ローカルセカンダリインデックスを持つテーブルを作成する
<a name="LSILowLevelDotNet.CreateTableWithIndex"></a>

ローカルセカンダリインデックスは、テーブルの作成と同時に作成する必要があります。これを行うには、`CreateTable` を使用し、1 つ以上のローカルセカンダリインデックスの仕様を指定します。次の C\$1 コード例では、ミュージックコレクション内の曲に関する情報を保持するためのテーブルを作成しています。パーティションキーは `Artist` で、ソートキーは `SongTitle` です。セカンダリインデックス `AlbumTitleIndex` は、アルバムタイトルによるクエリを容易にします。

.NET 低レベル API を使用して、ローカルセカンダリインデックスを含むテーブルを作成する手順を次に示します。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. リクエスト情報を指定する `CreateTableRequest` クラスのインスタンスを作成します。

   テーブル名、プライマリキー、およびプロビジョニングされたスループット値を指定する必要があります。ローカルセカンダリインデックスの場合は、インデックス名、インデックスソートキーの名前とデータ型、インデックスのキースキーマ、および属性射影を指定する必要があります。

1. リクエストオブジェクトをパラメータとして指定して、`CreateTable` メソッドを実行します。

以下の C\$1 サンプルコードは、前述のステップの例です。このコードは、`AlbumTitle` 属性に関するセカンダリインデックスを持つテーブル (`Music`) を作成します。テーブルパーティションキーとソートキー、およびインデックスソートキーのみが、インデックスに射影される属性です。

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "Music";

CreateTableRequest createTableRequest = new CreateTableRequest()
{
    TableName = tableName
};

//ProvisionedThroughput
createTableRequest.ProvisionedThroughput = new ProvisionedThroughput()
{
    ReadCapacityUnits = (long)5,
    WriteCapacityUnits = (long)5
};

//AttributeDefinitions
List<AttributeDefinition> attributeDefinitions = new List<AttributeDefinition>();

attributeDefinitions.Add(new AttributeDefinition()
{
    AttributeName = "Artist",
    AttributeType = "S"
});

attributeDefinitions.Add(new AttributeDefinition()
 {
     AttributeName = "SongTitle",
     AttributeType = "S"
 });

attributeDefinitions.Add(new AttributeDefinition()
 {
     AttributeName = "AlbumTitle",
     AttributeType = "S"
 });

createTableRequest.AttributeDefinitions = attributeDefinitions;

//KeySchema
List<KeySchemaElement> tableKeySchema = new List<KeySchemaElement>();

tableKeySchema.Add(new KeySchemaElement() { AttributeName = "Artist", KeyType = "HASH" });  //Partition key
tableKeySchema.Add(new KeySchemaElement() { AttributeName = "SongTitle", KeyType = "RANGE" });  //Sort key

createTableRequest.KeySchema = tableKeySchema;

List<KeySchemaElement> indexKeySchema = new List<KeySchemaElement>();
indexKeySchema.Add(new KeySchemaElement() { AttributeName = "Artist", KeyType = "HASH" });  //Partition key
indexKeySchema.Add(new KeySchemaElement() { AttributeName = "AlbumTitle", KeyType = "RANGE" });  //Sort key

Projection projection = new Projection() { ProjectionType = "INCLUDE" };

List<string> nonKeyAttributes = new List<string>();
nonKeyAttributes.Add("Genre");
nonKeyAttributes.Add("Year");
projection.NonKeyAttributes = nonKeyAttributes;

LocalSecondaryIndex localSecondaryIndex = new LocalSecondaryIndex()
{
    IndexName = "AlbumTitleIndex",
    KeySchema = indexKeySchema,
    Projection = projection
};

List<LocalSecondaryIndex> localSecondaryIndexes = new List<LocalSecondaryIndex>();
localSecondaryIndexes.Add(localSecondaryIndex);
createTableRequest.LocalSecondaryIndexes = localSecondaryIndexes;

CreateTableResponse result = client.CreateTable(createTableRequest);
Console.WriteLine(result.CreateTableResult.TableDescription.TableName);
Console.WriteLine(result.CreateTableResult.TableDescription.TableStatus);
```

DynamoDB がテーブルを作成し、テーブルのステータスを `ACTIVE` に設定するまで待機する必要があります。その後、テーブルへのデータ項目の入力を開始できます。

## ローカルセカンダリインデックスを持つテーブルの説明
<a name="LSILowLevelDotNet.DescribeTableWithIndex"></a>

テーブルのローカルセカンダリインデックスに関する情報を取得するには、`DescribeTable` API を使用します。インデックスごとに、名前、キースキーマ、および射影された属性にアクセスできます。

.NET 低レベル API を使用してテーブルのローカルセカンダリインデックス情報にアクセスするには次の手順に従います。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. リクエスト情報を指定する `DescribeTableRequest` クラスのインスタンスを作成します。テーブル名を入力する必要があります。

1. リクエストオブジェクトをパラメータとして指定して、`describeTable` メソッドを実行します。

以下の C\$1 サンプルコードは、前述のステップの例です。

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
string tableName = "Music";

DescribeTableResponse response = client.DescribeTable(new DescribeTableRequest() { TableName = tableName });
List<LocalSecondaryIndexDescription> localSecondaryIndexes =
    response.DescribeTableResult.Table.LocalSecondaryIndexes;

// This code snippet will work for multiple indexes, even though
// there is only one index in this example.
foreach (LocalSecondaryIndexDescription lsiDescription in localSecondaryIndexes)
{
    Console.WriteLine("Info for index " + lsiDescription.IndexName + ":");

    foreach (KeySchemaElement kse in lsiDescription.KeySchema)
    {
        Console.WriteLine("\t" + kse.AttributeName + ": key type is " + kse.KeyType);
    }

    Projection projection = lsiDescription.Projection;

    Console.WriteLine("\tThe projection type is: " + projection.ProjectionType);

    if (projection.ProjectionType.ToString().Equals("INCLUDE"))
    {
        Console.WriteLine("\t\tThe non-key projected attributes are:");

        foreach (String s in projection.NonKeyAttributes)
        {
            Console.WriteLine("\t\t" + s);
        }

    }
}
```

## ローカルセカンダリインデックスのクエリ
<a name="LSILowLevelDotNet.QueryAnIndex"></a>

テーブルに `Query` を実行するのとほぼ同じ方法で、ローカルセカンダリインデックスに対する `Query` を使用することができます。インデックス名、インデックスソートキーのクエリ条件、および返す属性を指定する必要があります。この例では、インデックスは `AlbumTitleIndex`、インデックスソートキーは `AlbumTitle` です。

返される属性は、インデックスに射影された属性だけです。このクエリを変更して非キー属性を選択することもできますが、これには比較的コストのかかるテーブルフェッチアクティビティが必要です。テーブルのフェッチの詳細については、「[属性の射影](LSI.md#LSI.Projections)」を参照してください。

.NET 低レベル API を使用して、ローカルセカンダリインデックスを含むテーブルにクエリを実行する手順を次に示します。

1. `AmazonDynamoDBClient` クラスのインスタンスを作成します。

1. リクエスト情報を指定する `QueryRequest` クラスのインスタンスを作成します。

1. リクエストオブジェクトをパラメータとして指定して、`query` メソッドを実行します。

以下の C\$1 サンプルコードは、前述のステップの例です。

**Example**  

```
QueryRequest queryRequest = new QueryRequest
{
    TableName = "Music",
    IndexName = "AlbumTitleIndex",
    Select = "ALL_ATTRIBUTES",
    ScanIndexForward = true,
    KeyConditionExpression = "Artist = :v_artist and AlbumTitle = :v_title",
    ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
    {
        {":v_artist",new AttributeValue {S = "Acme Band"}},
        {":v_title",new AttributeValue {S = "Songs About Life"}}
    },
};

QueryResponse response = client.Query(queryRequest);

foreach (var attribs in response.Items)
{
    foreach (var attrib in attribs)
    {
        Console.WriteLine(attrib.Key + " ---> " + attrib.Value.S);
    }
    Console.WriteLine();
}
```

# 例: AWS SDK for .NET 低レベル API を使用したローカルセカンダリインデックス
<a name="LSILowLevelDotNet.Example"></a>

次の C\$1 コード例は、Amazon DynamoDB でローカルセカンダリインデックスを操作する方法を示しています。この例では、パーティションキーを `CustomerId` として 、ソートキーを `OrderId` として `CustomerOrders` という名前のテーブルを作成しています。このテーブルには、次の 2 つのローカルセカンダリインデックスがあります。
+ `OrderCreationDateIndex` – ソートキーは `OrderCreationDate` です。次の属性がインデックスに射影されます。
  + `ProductCategory`
  + `ProductName`
  + `OrderStatus`
  + `ShipmentTrackingId`
+ `IsOpenIndex` – ソートキーは `IsOpen` です。すべてのテーブル属性がインデックスに射影されます。

`CustomerOrders` テーブルが作成されると、プログラムは顧客の注文を表すデータをテーブルにロードします。次に、ローカルセカンダリインデックスを使用してデータにクエリを実行します。最後に、プログラムは `CustomerOrders` テーブルを削除します。

以下の例をテストするための詳細な手順については、「[.NET コード例](CodeSamples.DotNet.md)」を参照してください。

**Example**  

```
using System;
using System.Collections.Generic;
using System.Linq;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;
using Amazon.SecurityToken;

namespace com.amazonaws.codesamples
{
    class LowLevelLocalSecondaryIndexExample
    {
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();
        private static string tableName = "CustomerOrders";

        static void Main(string[] args)
        {
            try
            {
                CreateTable();
                LoadData();

                Query(null);
                Query("IsOpenIndex");
                Query("OrderCreationDateIndex");

                DeleteTable(tableName);

                Console.WriteLine("To continue, press Enter");
                Console.ReadLine();
            }
            catch (AmazonDynamoDBException e) { Console.WriteLine(e.Message); }
            catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
            catch (Exception e) { Console.WriteLine(e.Message); }
        }

        private static void CreateTable()
        {
            var createTableRequest =
                new CreateTableRequest()
                {
                    TableName = tableName,
                    ProvisionedThroughput =
                    new ProvisionedThroughput()
                    {
                        ReadCapacityUnits = (long)1,
                        WriteCapacityUnits = (long)1
                    }
                };

            var attributeDefinitions = new List<AttributeDefinition>()
        {
            // Attribute definitions for table primary key
            { new AttributeDefinition() {
                  AttributeName = "CustomerId", AttributeType = "S"
              } },
            { new AttributeDefinition() {
                  AttributeName = "OrderId", AttributeType = "N"
              } },
            // Attribute definitions for index primary key
            { new AttributeDefinition() {
                  AttributeName = "OrderCreationDate", AttributeType = "N"
              } },
            { new AttributeDefinition() {
                  AttributeName = "IsOpen", AttributeType = "N"
              }}
        };

            createTableRequest.AttributeDefinitions = attributeDefinitions;

            // Key schema for table
            var tableKeySchema = new List<KeySchemaElement>()
        {
            { new KeySchemaElement() {
                  AttributeName = "CustomerId", KeyType = "HASH"
              } },                                                  //Partition key
            { new KeySchemaElement() {
                  AttributeName = "OrderId", KeyType = "RANGE"
              } }                                                //Sort key
        };

            createTableRequest.KeySchema = tableKeySchema;

            var localSecondaryIndexes = new List<LocalSecondaryIndex>();

            // OrderCreationDateIndex
            LocalSecondaryIndex orderCreationDateIndex = new LocalSecondaryIndex()
            {
                IndexName = "OrderCreationDateIndex"
            };

            // Key schema for OrderCreationDateIndex
            var indexKeySchema = new List<KeySchemaElement>()
        {
            { new KeySchemaElement() {
                  AttributeName = "CustomerId", KeyType = "HASH"
              } },                                                    //Partition key
            { new KeySchemaElement() {
                  AttributeName = "OrderCreationDate", KeyType = "RANGE"
              } }                                                            //Sort key
        };

            orderCreationDateIndex.KeySchema = indexKeySchema;

            // Projection (with list of projected attributes) for
            // OrderCreationDateIndex
            var projection = new Projection()
            {
                ProjectionType = "INCLUDE"
            };

            var nonKeyAttributes = new List<string>()
        {
            "ProductCategory",
            "ProductName"
        };
            projection.NonKeyAttributes = nonKeyAttributes;

            orderCreationDateIndex.Projection = projection;

            localSecondaryIndexes.Add(orderCreationDateIndex);

            // IsOpenIndex
            LocalSecondaryIndex isOpenIndex
                = new LocalSecondaryIndex()
                {
                    IndexName = "IsOpenIndex"
                };

            // Key schema for IsOpenIndex
            indexKeySchema = new List<KeySchemaElement>()
        {
            { new KeySchemaElement() {
                  AttributeName = "CustomerId", KeyType = "HASH"
              }},                                                     //Partition key
            { new KeySchemaElement() {
                  AttributeName = "IsOpen", KeyType = "RANGE"
              }}                                                  //Sort key
        };

            // Projection (all attributes) for IsOpenIndex
            projection = new Projection()
            {
                ProjectionType = "ALL"
            };

            isOpenIndex.KeySchema = indexKeySchema;
            isOpenIndex.Projection = projection;

            localSecondaryIndexes.Add(isOpenIndex);

            // Add index definitions to CreateTable request
            createTableRequest.LocalSecondaryIndexes = localSecondaryIndexes;

            Console.WriteLine("Creating table " + tableName + "...");
            client.CreateTable(createTableRequest);
            WaitUntilTableReady(tableName);
        }

        public static void Query(string indexName)
        {
            Console.WriteLine("\n***********************************************************\n");
            Console.WriteLine("Querying table " + tableName + "...");

            QueryRequest queryRequest = new QueryRequest()
            {
                TableName = tableName,
                ConsistentRead = true,
                ScanIndexForward = true,
                ReturnConsumedCapacity = "TOTAL"
            };


            String keyConditionExpression = "CustomerId = :v_customerId";
            Dictionary<string, AttributeValue> expressionAttributeValues = new Dictionary<string, AttributeValue> {
            {":v_customerId", new AttributeValue {
                 S = "bob@example.com"
             }}
        };


            if (indexName == "IsOpenIndex")
            {
                Console.WriteLine("\nUsing index: '" + indexName
                          + "': Bob's orders that are open.");
                Console.WriteLine("Only a user-specified list of attributes are returned\n");
                queryRequest.IndexName = indexName;

                keyConditionExpression += " and IsOpen = :v_isOpen";
                expressionAttributeValues.Add(":v_isOpen", new AttributeValue
                {
                    N = "1"
                });

                // ProjectionExpression
                queryRequest.ProjectionExpression = "OrderCreationDate, ProductCategory, ProductName, OrderStatus";
            }
            else if (indexName == "OrderCreationDateIndex")
            {
                Console.WriteLine("\nUsing index: '" + indexName
                          + "': Bob's orders that were placed after 01/31/2013.");
                Console.WriteLine("Only the projected attributes are returned\n");
                queryRequest.IndexName = indexName;

                keyConditionExpression += " and OrderCreationDate > :v_Date";
                expressionAttributeValues.Add(":v_Date", new AttributeValue
                {
                    N = "20130131"
                });

                // Select
                queryRequest.Select = "ALL_PROJECTED_ATTRIBUTES";
            }
            else
            {
                Console.WriteLine("\nNo index: All of Bob's orders, by OrderId:\n");
            }
            queryRequest.KeyConditionExpression = keyConditionExpression;
            queryRequest.ExpressionAttributeValues = expressionAttributeValues;

            var result = client.Query(queryRequest);
            var items = result.Items;
            foreach (var currentItem in items)
            {
                foreach (string attr in currentItem.Keys)
                {
                    if (attr == "OrderId" || attr == "IsOpen"
                        || attr == "OrderCreationDate")
                    {
                        Console.WriteLine(attr + "---> " + currentItem[attr].N);
                    }
                    else
                    {
                        Console.WriteLine(attr + "---> " + currentItem[attr].S);
                    }
                }
                Console.WriteLine();
            }
            Console.WriteLine("\nConsumed capacity: " + result.ConsumedCapacity.CapacityUnits + "\n");
        }

        private static void DeleteTable(string tableName)
        {
            Console.WriteLine("Deleting table " + tableName + "...");
            client.DeleteTable(new DeleteTableRequest()
            {
                TableName = tableName
            });
            WaitForTableToBeDeleted(tableName);
        }

        public static void LoadData()
        {
            Console.WriteLine("Loading data into table " + tableName + "...");

            Dictionary<string, AttributeValue> item = new Dictionary<string, AttributeValue>();

            item["CustomerId"] = new AttributeValue
            {
                S = "alice@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "1"
            };
            item["IsOpen"] = new AttributeValue
            {
                N = "1"
            };
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130101"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Book"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "The Great Outdoors"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "PACKING ITEMS"
            };
            /* no ShipmentTrackingId attribute */
            PutItemRequest putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "alice@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "2"
            };
            item["IsOpen"] = new AttributeValue
            {
                N = "1"
            };
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130221"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Bike"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Super Mountain"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "ORDER RECEIVED"
            };
            /* no ShipmentTrackingId attribute */
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "alice@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "3"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130304"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Music"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "A Quiet Interlude"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "IN TRANSIT"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "176493"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "1"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130111"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Movie"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Calm Before The Storm"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "SHIPPING DELAY"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "859323"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "2"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130124"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Music"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "E-Z Listening"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "DELIVERED"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "756943"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "3"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130221"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Music"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Symphony 9"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "DELIVERED"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "645193"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "4"
            };
            item["IsOpen"] = new AttributeValue
            {
                N = "1"
            };
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130222"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Hardware"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Extra Heavy Hammer"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "PACKING ITEMS"
            };
            /* no ShipmentTrackingId attribute */
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "5"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130309"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Book"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "How To Cook"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "IN TRANSIT"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "440185"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "6"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130318"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Luggage"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Really Big Suitcase"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "DELIVERED"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "893927"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "7"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130324"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Golf"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "PGA Pro II"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "OUT FOR DELIVERY"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "383283"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);
        }

        private static void WaitUntilTableReady(string tableName)
        {
            string status = null;
            // Let us wait until table is created. Call DescribeTable.
            do
            {
                System.Threading.Thread.Sleep(5000); // Wait 5 seconds.
                try
                {
                    var res = client.DescribeTable(new DescribeTableRequest
                    {
                        TableName = tableName
                    });

                    Console.WriteLine("Table name: {0}, status: {1}",
                              res.Table.TableName,
                              res.Table.TableStatus);
                    status = res.Table.TableStatus;
                }
                catch (ResourceNotFoundException)
                {
                    // DescribeTable is eventually consistent. So you might
                    // get resource not found. So we handle the potential exception.
                }
            } while (status != "ACTIVE");
        }

        private static void WaitForTableToBeDeleted(string tableName)
        {
            bool tablePresent = true;

            while (tablePresent)
            {
                System.Threading.Thread.Sleep(5000); // Wait 5 seconds.
                try
                {
                    var res = client.DescribeTable(new DescribeTableRequest
                    {
                        TableName = tableName
                    });

                    Console.WriteLine("Table name: {0}, status: {1}",
                              res.Table.TableName,
                              res.Table.TableStatus);
                }
                catch (ResourceNotFoundException)
                {
                    tablePresent = false;
                }
            }
        }
    }
}
```

# DynamoDB AWS CLI でのローカルセカンダリインデックスの操作
<a name="LCICli"></a>

AWS CLI を使用して、1 つ以上のローカルセカンダリインデックスを含む Amazon DynamoDB テーブルを作成し、テーブルのインデックスを記述し、インデックスを使用してクエリを実行できます。

**Topics**
+ [ローカルセカンダリインデックスを持つテーブルを作成する](#LCICli.CreateTableWithIndex)
+ [ローカルセカンダリインデックスを持つテーブルの説明](#LCICli.DescribeTableWithIndex)
+ [ローカルセカンダリインデックスのクエリ](#LCICli.QueryAnIndex)

## ローカルセカンダリインデックスを持つテーブルを作成する
<a name="LCICli.CreateTableWithIndex"></a>

ローカルセカンダリインデックスは、テーブルの作成と同時に作成する必要があります。これを行うには、`create-table` パラメータを使用し、1 つ以上のローカルセカンダリインデックスの仕様を指定します。次の例では、ミュージックコレクション内の曲に関する情報を保持するためのテーブル (`Music`) を作成しています。パーティションキーは `Artist` で、ソートキーは `SongTitle` です。`AlbumTitle` 属性に関するセカンダリインデックス `AlbumTitleIndex` は、アルバムタイトルによるクエリを容易にします。

```
aws dynamodb create-table \
    --table-name Music \
    --attribute-definitions AttributeName=Artist,AttributeType=S AttributeName=SongTitle,AttributeType=S \
        AttributeName=AlbumTitle,AttributeType=S  \
    --key-schema AttributeName=Artist,KeyType=HASH AttributeName=SongTitle,KeyType=RANGE \
    --provisioned-throughput \
        ReadCapacityUnits=10,WriteCapacityUnits=5 \
    --local-secondary-indexes \
        "[{\"IndexName\": \"AlbumTitleIndex\",
        \"KeySchema\":[{\"AttributeName\":\"Artist\",\"KeyType\":\"HASH\"},
                      {\"AttributeName\":\"AlbumTitle\",\"KeyType\":\"RANGE\"}],
        \"Projection\":{\"ProjectionType\":\"INCLUDE\",  \"NonKeyAttributes\":[\"Genre\", \"Year\"]}}]"
```

DynamoDB がテーブルを作成し、テーブルのステータスを `ACTIVE` に設定するまで待機する必要があります。その後、テーブルへのデータ項目の入力を開始できます。[describe-table](https://docs.aws.amazon.com/cli/latest/reference/dynamodb/describe-table.html) を使用して、テーブル作成のステータスを判断できます。

## ローカルセカンダリインデックスを持つテーブルの説明
<a name="LCICli.DescribeTableWithIndex"></a>

テーブルのローカルセカンダリインデックスに関する情報を取得するには、`describe-table` パラメータを使用します。インデックスごとに、名前、キースキーマ、および射影された属性にアクセスできます。

```
aws dynamodb describe-table --table-name Music
```

## ローカルセカンダリインデックスのクエリ
<a name="LCICli.QueryAnIndex"></a>

テーブルに `query` を実行するのとほぼ同じ方法で、ローカルセカンダリインデックスに対する `query` オペレーションを使用することができます。インデックス名、インデックスソートキーのクエリ条件、および返す属性を指定する必要があります。この例では、インデックスは `AlbumTitleIndex`、インデックスソートキーは `AlbumTitle` です。

返される属性は、インデックスに射影された属性だけです。このクエリを変更して非キー属性を選択することもできますが、これには比較的コストのかかるテーブルフェッチアクティビティが必要です。テーブルのフェッチの詳細については、「[属性の射影](LSI.md#LSI.Projections)」を参照してください。

```
aws dynamodb query \
    --table-name Music \
    --index-name AlbumTitleIndex \
    --key-condition-expression "Artist = :v_artist and AlbumTitle = :v_title" \
    --expression-attribute-values  '{":v_artist":{"S":"Acme Band"},":v_title":{"S":"Songs About Life"} }'
```