

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

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

以下に、テーブルオペレーションの一般的な手順を示します。

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

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

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

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

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

グローバルセカンダリインデックスは、テーブルの作成と同時に作成できます。これを行うには、`CreateTable` を使用し、1 つ以上のグローバルセカンダリインデックスの仕様を指定します。次の Java コード例では、気象データに関する情報を保持するテーブルを作成しています。パーティションキーは `Location` で、ソートキーは `Date` です。グローバルセカンダリインデックス `PrecipIndex` は、さまざまな場所の降水データへの高速アクセスを可能にします。

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

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

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

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

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

以下の Java コード例は、前述のステップの例です。このコードは、グローバルセカンダリインデックス (`PrecipIndex`) を持つテーブル (`WeatherData`) を作成します。インデックスパーティションキーは `Date` で、ソートキーは `Precipitation` です。すべてのテーブル属性がインデックスに射影されます。ユーザーは、このインデックスに対するクエリを実行して、特定の日付の気象データを取得できます。必要に応じて、降水量によってデータを並べ替えることもできます。

`Precipitation` はテーブルのキー属性ではないため、必須ではありません。ただし、`Precipitation` のない `WeatherData` 項目は `PrecipIndex` に表示されません。

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

// Attribute definitions
ArrayList<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();

attributeDefinitions.add(new AttributeDefinition()
    .withAttributeName("Location")
    .withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition()
    .withAttributeName("Date")
    .withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition()
    .withAttributeName("Precipitation")
    .withAttributeType("N"));

// Table key schema
ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
tableKeySchema.add(new KeySchemaElement()
    .withAttributeName("Location")
    .withKeyType(KeyType.HASH));  //Partition key
tableKeySchema.add(new KeySchemaElement()
    .withAttributeName("Date")
    .withKeyType(KeyType.RANGE));  //Sort key

// PrecipIndex
GlobalSecondaryIndex precipIndex = new GlobalSecondaryIndex()
    .withIndexName("PrecipIndex")
    .withProvisionedThroughput(new ProvisionedThroughput()
        .withReadCapacityUnits((long) 10)
        .withWriteCapacityUnits((long) 1))
        .withProjection(new Projection().withProjectionType(ProjectionType.ALL));

ArrayList<KeySchemaElement> indexKeySchema = new ArrayList<KeySchemaElement>();

indexKeySchema.add(new KeySchemaElement()
    .withAttributeName("Date")
    .withKeyType(KeyType.HASH));  //Partition key
indexKeySchema.add(new KeySchemaElement()
    .withAttributeName("Precipitation")
    .withKeyType(KeyType.RANGE));  //Sort key

precipIndex.setKeySchema(indexKeySchema);

CreateTableRequest createTableRequest = new CreateTableRequest()
    .withTableName("WeatherData")
    .withProvisionedThroughput(new ProvisionedThroughput()
        .withReadCapacityUnits((long) 5)
        .withWriteCapacityUnits((long) 1))
    .withAttributeDefinitions(attributeDefinitions)
    .withKeySchema(tableKeySchema)
    .withGlobalSecondaryIndexes(precipIndex);

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

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

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

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

テーブルのグローバルセカンダリインデックス情報にアクセスする手順を次に示します。

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

1. 操作対象のインデックスを表すために、`Table` クラスのインスタンスを作成します。

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

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

**Example**  

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

Table table = dynamoDB.getTable("WeatherData");
TableDescription tableDesc = table.describe();
    

Iterator<GlobalSecondaryIndexDescription> gsiIter = tableDesc.getGlobalSecondaryIndexes().iterator();
while (gsiIter.hasNext()) {
    GlobalSecondaryIndexDescription gsiDesc = gsiIter.next();
    System.out.println("Info for index "
         + gsiDesc.getIndexName() + ":");

    Iterator<KeySchemaElement> kseIter = gsiDesc.getKeySchema().iterator();
    while (kseIter.hasNext()) {
        KeySchemaElement kse = kseIter.next();
        System.out.printf("\t%s: %s\n", kse.getAttributeName(), kse.getKeyType());
    }
    Projection projection = gsiDesc.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="GSIJavaDocumentAPI.QueryAnIndex"></a>

テーブルに `Query` を実行するのとほぼ同じ方法で、グローバルセカンダリインデックスに対する `Query` を使用することができます。インデックス名、インデックスパーティションキーとソートキー (存在する場合) のクエリ条件、および返す属性を指定する必要があります。この例では、インデックスは `PrecipIndex` で、パーティションキーが `Date` で、ソートキーが `Precipitation` です。このインデックスクエリは、降水量がゼロより大きい特定の日付のすべての気象データを返します。

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

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

1. 操作対象のインデックスを表すために、`Table` クラスのインスタンスを作成します。

1. クエリするインデックスの `Index` クラスのインスタンスを作成します。

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

属性名 `Date` は DynamoDB の予約語です。したがって、式の属性名を `KeyConditionExpression` のプレースホルダーとして使用する必要があります。

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

**Example**  

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

Table table = dynamoDB.getTable("WeatherData");
Index index = table.getIndex("PrecipIndex");

QuerySpec spec = new QuerySpec()
    .withKeyConditionExpression("#d = :v_date and Precipitation = :v_precip")
    .withNameMap(new NameMap()
        .with("#d", "Date"))
    .withValueMap(new ValueMap()
        .withString(":v_date","2013-08-10")
        .withNumber(":v_precip",0));

ItemCollection<QueryOutcome> items = index.query(spec);
Iterator<Item> iter = items.iterator(); 
while (iter.hasNext()) {
    System.out.println(iter.next().toJSONPretty());
}
```

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

次の Java コード例は、グローバルセカンダリインデックスの操作方法を示しています。この例では、`Issues` というテーブルを作成しています。これは、ソフトウェア開発のためのシンプルなバグ追跡システムで使用できる可能性があります。パーティションキーは `IssueId` で、ソートキーは `Title` です。このテーブルには、次の 3 つのグローバルセカンダリインデックスがあります。
+ `CreateDateIndex` – パーティションキーは `CreateDate` で、ソートキーは `IssueId` です。テーブルキーに加えて、属性 `Description` および `Status` はインデックスに射影されます。
+ `TitleIndex` – パーティションキーは `Title` で、ソートキーは `IssueId` です。テーブルキー以外の属性はインデックスに射影されません。
+ `DueDateIndex` – パーティションキーは `DueDate` で、ソートキーはありません。すべてのテーブル属性がインデックスに射影されます。

`Issues` テーブルが作成されると、プログラムはソフトウェアのバグレポートを表すデータをテーブルにロードします。次に、グローバルセカンダリインデックスを使用してデータのクエリを実行します。最後に、プログラムは `Issues` テーブルを削除します。

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

**Example**  

```
package com.amazonaws.codesamples.document;

import java.util.ArrayList;
import java.util.Iterator;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Index;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.ItemCollection;
import com.amazonaws.services.dynamodbv2.document.QueryOutcome;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.spec.QuerySpec;
import com.amazonaws.services.dynamodbv2.document.utils.ValueMap;
import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
import com.amazonaws.services.dynamodbv2.model.GlobalSecondaryIndex;
import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
import com.amazonaws.services.dynamodbv2.model.KeyType;
import com.amazonaws.services.dynamodbv2.model.Projection;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;

public class DocumentAPIGlobalSecondaryIndexExample {

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

    public static String tableName = "Issues";

    public static void main(String[] args) throws Exception {

        createTable();
        loadData();

        queryIndex("CreateDateIndex");
        queryIndex("TitleIndex");
        queryIndex("DueDateIndex");

        deleteTable(tableName);

    }

    public static void createTable() {

        // Attribute definitions
        ArrayList<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();

        attributeDefinitions.add(new AttributeDefinition().withAttributeName("IssueId").withAttributeType("S"));
        attributeDefinitions.add(new AttributeDefinition().withAttributeName("Title").withAttributeType("S"));
        attributeDefinitions.add(new AttributeDefinition().withAttributeName("CreateDate").withAttributeType("S"));
        attributeDefinitions.add(new AttributeDefinition().withAttributeName("DueDate").withAttributeType("S"));

        // Key schema for table
        ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
        tableKeySchema.add(new KeySchemaElement().withAttributeName("IssueId").withKeyType(KeyType.HASH)); // Partition
                                                                                                           // key
        tableKeySchema.add(new KeySchemaElement().withAttributeName("Title").withKeyType(KeyType.RANGE)); // Sort
                                                                                                          // key

        // Initial provisioned throughput settings for the indexes
        ProvisionedThroughput ptIndex = new ProvisionedThroughput().withReadCapacityUnits(1L)
                .withWriteCapacityUnits(1L);

        // CreateDateIndex
        GlobalSecondaryIndex createDateIndex = new GlobalSecondaryIndex().withIndexName("CreateDateIndex")
                .withProvisionedThroughput(ptIndex)
                .withKeySchema(new KeySchemaElement().withAttributeName("CreateDate").withKeyType(KeyType.HASH), // Partition
                                                                                                                 // key
                        new KeySchemaElement().withAttributeName("IssueId").withKeyType(KeyType.RANGE)) // Sort
                                                                                                        // key
                .withProjection(
                        new Projection().withProjectionType("INCLUDE").withNonKeyAttributes("Description", "Status"));

        // TitleIndex
        GlobalSecondaryIndex titleIndex = new GlobalSecondaryIndex().withIndexName("TitleIndex")
                .withProvisionedThroughput(ptIndex)
                .withKeySchema(new KeySchemaElement().withAttributeName("Title").withKeyType(KeyType.HASH), // Partition
                                                                                                            // key
                        new KeySchemaElement().withAttributeName("IssueId").withKeyType(KeyType.RANGE)) // Sort
                                                                                                        // key
                .withProjection(new Projection().withProjectionType("KEYS_ONLY"));

        // DueDateIndex
        GlobalSecondaryIndex dueDateIndex = new GlobalSecondaryIndex().withIndexName("DueDateIndex")
                .withProvisionedThroughput(ptIndex)
                .withKeySchema(new KeySchemaElement().withAttributeName("DueDate").withKeyType(KeyType.HASH)) // Partition
                                                                                                              // key
                .withProjection(new Projection().withProjectionType("ALL"));

        CreateTableRequest createTableRequest = new CreateTableRequest().withTableName(tableName)
                .withProvisionedThroughput(
                        new ProvisionedThroughput().withReadCapacityUnits((long) 1).withWriteCapacityUnits((long) 1))
                .withAttributeDefinitions(attributeDefinitions).withKeySchema(tableKeySchema)
                .withGlobalSecondaryIndexes(createDateIndex, titleIndex, dueDateIndex);

        System.out.println("Creating table " + tableName + "...");
        dynamoDB.createTable(createTableRequest);

        // Wait for table to become active
        System.out.println("Waiting for " + tableName + " to become ACTIVE...");
        try {
            Table table = dynamoDB.getTable(tableName);
            table.waitForActive();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void queryIndex(String indexName) {

        Table table = dynamoDB.getTable(tableName);

        System.out.println("\n***********************************************************\n");
        System.out.print("Querying index " + indexName + "...");

        Index index = table.getIndex(indexName);

        ItemCollection<QueryOutcome> items = null;

        QuerySpec querySpec = new QuerySpec();

        if (indexName == "CreateDateIndex") {
            System.out.println("Issues filed on 2013-11-01");
            querySpec.withKeyConditionExpression("CreateDate = :v_date and begins_with(IssueId, :v_issue)")
                    .withValueMap(new ValueMap().withString(":v_date", "2013-11-01").withString(":v_issue", "A-"));
            items = index.query(querySpec);
        } else if (indexName == "TitleIndex") {
            System.out.println("Compilation errors");
            querySpec.withKeyConditionExpression("Title = :v_title and begins_with(IssueId, :v_issue)")
                    .withValueMap(
                            new ValueMap().withString(":v_title", "Compilation error").withString(":v_issue", "A-"));
            items = index.query(querySpec);
        } else if (indexName == "DueDateIndex") {
            System.out.println("Items that are due on 2013-11-30");
            querySpec.withKeyConditionExpression("DueDate = :v_date")
                    .withValueMap(new ValueMap().withString(":v_date", "2013-11-30"));
            items = index.query(querySpec);
        } else {
            System.out.println("\nNo valid index name provided");
            return;
        }

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

        System.out.println("Query: printing results...");

        while (iterator.hasNext()) {
            System.out.println(iterator.next().toJSONPretty());
        }

    }

    public static void deleteTable(String tableName) {

        System.out.println("Deleting table " + tableName + "...");

        Table table = dynamoDB.getTable(tableName);
        table.delete();

        // Wait for table to be deleted
        System.out.println("Waiting for " + tableName + " to be deleted...");
        try {
            table.waitForDelete();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void loadData() {

        System.out.println("Loading data into table " + tableName + "...");

        // IssueId, Title,
        // Description,
        // CreateDate, LastUpdateDate, DueDate,
        // Priority, Status

        putItem("A-101", "Compilation error", "Can't compile Project X - bad version number. What does this mean?",
                "2013-11-01", "2013-11-02", "2013-11-10", 1, "Assigned");

        putItem("A-102", "Can't read data file", "The main data file is missing, or the permissions are incorrect",
                "2013-11-01", "2013-11-04", "2013-11-30", 2, "In progress");

        putItem("A-103", "Test failure", "Functional test of Project X produces errors", "2013-11-01", "2013-11-02",
                "2013-11-10", 1, "In progress");

        putItem("A-104", "Compilation error", "Variable 'messageCount' was not initialized.", "2013-11-15",
                "2013-11-16", "2013-11-30", 3, "Assigned");

        putItem("A-105", "Network issue", "Can't ping IP address 127.0.0.1. Please fix this.", "2013-11-15",
                "2013-11-16", "2013-11-19", 5, "Assigned");

    }

    public static void putItem(

            String issueId, String title, String description, String createDate, String lastUpdateDate, String dueDate,
            Integer priority, String status) {

        Table table = dynamoDB.getTable(tableName);

        Item item = new Item().withPrimaryKey("IssueId", issueId).withString("Title", title)
                .withString("Description", description).withString("CreateDate", createDate)
                .withString("LastUpdateDate", lastUpdateDate).withString("DueDate", dueDate)
                .withNumber("Priority", priority).withString("Status", status);

        table.putItem(item);
    }

}
```