

# 글로벌 보조 인덱스로 작업: Java
<a name="GSIJavaDocumentAPI"></a>

AWS SDK for Java Document API를 사용하여 하나 이상의 글로벌 보조 인덱스가 포함된 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`을 사용하고 하나 이상의 글로벌 보조 인덱스에 대한 사양을 제공합니다. 다음에 예로 나오는 Java 코드는 날씨 데이터에 대한 정보를 담고 있는 테이블을 만듭니다. 파티션 키는 `Location`이고 정렬 키는 `Date`입니다. `PrecipIndex`라는 이름의 글로벌 보조 인덱스를 사용하면 다양한 위치의 강수 데이터에 빠르게 액세스할 수 있습니다.

다음은 DynamoDB Document API를 사용하여 글로벌 보조 인덱스가 있는 테이블을 생성하는 단계입니다.

1. `DynamoDB` 클래스의 인스턴스를 만듭니다. 

1. `CreateTableRequest` 클래스 인스턴스를 만들어 요청 정보를 입력합니다.

   이때 입력해야 하는 정보는 테이블 이름, 기본 키, 그리고 프로비저닝된 처리량 값입니다. 글로벌 보조 인덱스의 경우 인덱스 이름, 프로비저닝된 처리량 설정, 인덱스 정렬 키의 속성 정의, 인덱스의 키 스키마, 속성 프로젝션을 제공해야 합니다.

1. 요청 객체를 파라미터로 입력하여 `createTable` 메서드를 호출합니다.

다음 Java 코드 예는 앞의 단계를 보여줍니다. 이 코드는 글로벌 보조 인덱스(`PrecipIndex`)가 있는 테이블(`WeatherData`)을 생성합니다. 인덱스 파티션 키는 `Date`이고 정렬 키는 `Precipitation`입니다. 모든 테이블 속성이 인덱스로 프로젝션되지 않습니다. 이 인덱스를 쿼리하여 특정 날짜에 대한 날씨 데이터를 가져와서 필요에 따라 강수량을 기준으로 데이터를 정렬할 수 있습니다.

`Precipitation`은 해당 테이블의 키 속성이 아니므로 필요하지 않습니다. 그러나 `WeatherData`이 없는 `Precipitation` 항목은 `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. `describe` 객체의 `Table` 메서드를 호출합니다.

다음 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`입니다. 인덱스 쿼리에서는 특정 날짜에 대해 강수량이 0보다 큰 모든 날씨 데이터를 반환합니다.

다음은 AWS SDK for Java Document API를 사용하여 글로벌 보조 인덱스를 쿼리하는 단계입니다.

1. `DynamoDB` 클래스의 인스턴스를 만듭니다. 

1. `Table` 클래스의 인스턴스를 만들어 사용할 인덱스를 표시합니다.

1. 쿼리할 인덱스에 대한 `Index` 클래스의 인스턴스를 만듭니다.

1. `query` 객체의 `Index` 메서드를 호출합니다.

속성 이름 `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`입니다. 이 테이블에는 세 개의 글로벌 보조 인덱스가 있습니다.
+ `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);
    }

}
```