

# 글로벌 보조 인덱스로 작업: .NET
<a name="GSILowLevelDotNet"></a>

AWS SDK for .NET 하위 수준 API를 사용하여 하나 이상의 글로벌 보조 인덱스가 포함된 Amazon DynamoDB 테이블을 만들고, 테이블의 인덱스를 설명하고, 인덱스를 사용하여 쿼리를 수행할 수 있습니다. 이러한 작업은 해당 DynamoDB 작업에 매핑됩니다. 자세한 내용은 [Amazon DynamoDB API 참조](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/)를 참조하세요.

다음은 .NET 하위 수준 API를 사용하여 테이블 작업을 할 때 따라야 할 공통 단계입니다.

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

1. 해당하는 요청 객체를 만들어 작업의 필수 및 선택적 파라미터를 제공합니다.

   예를 들어 `CreateTableRequest` 객체를 만들어 테이블을 생성하거나 `QueryRequest` 객체를 만들어 테이블 또는 인덱스를 쿼리합니다.

1. 이전 단계에서 만든 클라이언트가 제공한 적절한 메서드를 실행합니다.

**Topics**
+ [글로벌 보조 인덱스가 있는 테이블 생성](#GSILowLevelDotNet.CreateTableWithIndex)
+ [글로벌 보조 인덱스가 있는 테이블 설명](#GSILowLevelDotNet.DescribeTableWithIndex)
+ [글로벌 보조 인덱스 쿼리](#GSILowLevelDotNet.QueryAnIndex)
+ [예: AWS SDK for .NET 하위 수준 API를 사용하는 글로벌 보조 인덱스](GSILowLevelDotNet.Example.md)

## 글로벌 보조 인덱스가 있는 테이블 생성
<a name="GSILowLevelDotNet.CreateTableWithIndex"></a>

글로벌 보조 인덱스는 테이블을 만들 때 동시에 만들 수 있습니다. 이렇게 하려면 `CreateTable`을 사용하고 하나 이상의 글로벌 보조 인덱스에 대한 사양을 제공합니다. 다음에 예로 나오는 C\$1 코드는 날씨 데이터에 대한 정보를 담고 있는 테이블을 만듭니다. 파티션 키는 `Location`이고 정렬 키는 `Date`입니다. `PrecipIndex`라는 이름의 글로벌 보조 인덱스를 사용하면 다양한 위치의 강수 데이터에 빠르게 액세스할 수 있습니다.

다음은 .NET 하위 수준 API를 사용하여 글로벌 보조 인덱스가 포함된 테이블을 생성하는 단계입니다.

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

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

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

1. 요청 객체를 파라미터로 입력하여 `CreateTable` 메서드를 실행합니다.

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

`Precipitation`은 해당 테이블의 키 속성이 아니므로 필요하지 않습니다. 그러나 `WeatherData`이 없는 `Precipitation` 항목은 `PrecipIndex`에 표시되지 않습니다.

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

// Attribute definitions
var attributeDefinitions = new List<AttributeDefinition>()
{
    {new AttributeDefinition{
        AttributeName = "Location",
        AttributeType = "S"}},
    {new AttributeDefinition{
        AttributeName = "Date",
        AttributeType = "S"}},
    {new AttributeDefinition(){
        AttributeName = "Precipitation",
        AttributeType = "N"}
    }
};

// Table key schema
var tableKeySchema = new List<KeySchemaElement>()
{
    {new KeySchemaElement {
        AttributeName = "Location",
        KeyType = "HASH"}},  //Partition key
    {new KeySchemaElement {
        AttributeName = "Date",
        KeyType = "RANGE"}  //Sort key
    }
};

// PrecipIndex
var precipIndex = new GlobalSecondaryIndex
{
    IndexName = "PrecipIndex",
    ProvisionedThroughput = new ProvisionedThroughput
    {
        ReadCapacityUnits = (long)10,
        WriteCapacityUnits = (long)1
    },
    Projection = new Projection { ProjectionType = "ALL" }
};

var indexKeySchema = new List<KeySchemaElement> {
    {new KeySchemaElement { AttributeName = "Date", KeyType = "HASH"}},  //Partition key
    {new KeySchemaElement{AttributeName = "Precipitation",KeyType = "RANGE"}}  //Sort key
};

precipIndex.KeySchema = indexKeySchema;

CreateTableRequest createTableRequest = new CreateTableRequest
{
    TableName = tableName,
    ProvisionedThroughput = new ProvisionedThroughput
    {
        ReadCapacityUnits = (long)5,
        WriteCapacityUnits = (long)1
    },
    AttributeDefinitions = attributeDefinitions,
    KeySchema = tableKeySchema,
    GlobalSecondaryIndexes = { precipIndex }
};

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

DynamoDB에서 테이블을 만들고 테이블 상태가 `ACTIVE`로 설정될 때까지 기다려야 합니다. 그런 다음 테이블에 데이터 항목을 입력할 수 있습니다.

## 글로벌 보조 인덱스가 있는 테이블 설명
<a name="GSILowLevelDotNet.DescribeTableWithIndex"></a>

테이블의 글로벌 보조 인덱스에 관한 자세한 내용은 `DescribeTable` 단원을 참조하세요. 각 인덱스에 대해 인덱스의 이름, 키 스키마 및 프로젝션된 속성에 액세스할 수 있습니다.

다음은 .NET 하위 수준 API를 사용하여 테이블의 글로벌 보조 인덱스 정보에 액세스하는 단계입니다.

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

1. 요청 객체를 파라미터로 입력하여 `describeTable` 메서드를 실행합니다.

   `DescribeTableRequest` 클래스 인스턴스를 만들어 요청 정보를 입력합니다. 테이블 이름을 입력해야 합니다.

다음 C\$1 코드 예제에서는 이전 단계를 설명합니다.

**Example**  

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

DescribeTableResponse response = client.DescribeTable(new DescribeTableRequest { TableName = tableName});

List<GlobalSecondaryIndexDescription> globalSecondaryIndexes =
response.DescribeTableResult.Table.GlobalSecondaryIndexes;

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

foreach (GlobalSecondaryIndexDescription gsiDescription in globalSecondaryIndexes) {
     Console.WriteLine("Info for index " + gsiDescription.IndexName + ":");

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

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

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

## 글로벌 보조 인덱스 쿼리
<a name="GSILowLevelDotNet.QueryAnIndex"></a>

테이블을 `Query`할 때와 거의 동일한 방식으로 글로벌 보조 인덱스에서 `Query`를 사용할 수 있습니다. 인덱스 이름, 인덱스 파티션 키 및 정렬 키(있는 경우)의 쿼리 기준, 반환하려는 속성을 지정해야 합니다. 이 예제에서 인덱스는 `PrecipIndex`이고, 해당 파티션 키는 `Date`이고, 정렬 키는 `Precipitation`입니다. 인덱스 쿼리에서는 특정 날짜에 대해 강수량이 0보다 큰 모든 날씨 데이터를 반환합니다.

다음은 .NET 하위 수준 API를 사용하여 글로벌 보조 인덱스를 쿼리하는 단계입니다.

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

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

1. 요청 객체를 파라미터로 입력하여 `query` 메서드를 실행합니다.

속성 이름 `Date`는 DynamoDB 예약어입니다. 따라서 `KeyConditionExpression`에서 표현식 속성 이름을 자리 표시자로 사용해야 합니다.

다음 C\$1 코드 예제에서는 이전 단계를 설명합니다.

**Example**  

```
client = new AmazonDynamoDBClient();

QueryRequest queryRequest = new QueryRequest
{
    TableName = "WeatherData",
    IndexName = "PrecipIndex",
    KeyConditionExpression = "#dt = :v_date and Precipitation > :v_precip",
    ExpressionAttributeNames = new Dictionary<String, String> {
        {"#dt", "Date"}
    },
    ExpressionAttributeValues = new Dictionary<string, AttributeValue> {
        {":v_date", new AttributeValue { S =  "2013-08-01" }},
        {":v_precip", new AttributeValue { N =  "0" }}
    },
    ScanIndexForward = true
};

var result = client.Query(queryRequest);

var items = result.Items;
foreach (var currentItem in items)
{
    foreach (string attr in currentItem.Keys)
    {
        Console.Write(attr + "---> ");
        if (attr == "Precipitation")
        {
            Console.WriteLine(currentItem[attr].N);
    }
    else
    {
        Console.WriteLine(currentItem[attr].S);
    }

         }
     Console.WriteLine();
}
```

# 예: AWS SDK for .NET 하위 수준 API를 사용하는 글로벌 보조 인덱스
<a name="GSILowLevelDotNet.Example"></a>

다음 C\$1 코드 예제는 글로벌 보조 인덱스로 작업하는 방법을 보여줍니다. 이 예에서는 `Issues`라는 테이블을 만듭니다. 이 테이블은 소프트웨어 개발을 위한 간단한 버그 추적 시스템에서 사용할 수 있습니다. 파티션 키는 `IssueId`이고 정렬 키는 `Title`입니다. 이 테이블에는 세 개의 글로벌 보조 인덱스가 있습니다.
+ `CreateDateIndex` - 파티션 키는 `CreateDate`이고 정렬 키는 `IssueId`입니다. 테이블 키 외에도 `Description` 및 `Status` 속성도 인덱스로 프로젝션됩니다.
+ `TitleIndex` - 파티션 키는 `Title`이고 정렬 키는 `IssueId`입니다. 테이블 키 이외의 속성은 인덱스로 프로젝션되지 않습니다.
+ `DueDateIndex` - 파티션 키는 `DueDate`이고 정렬 키는 없습니다. 모든 테이블 속성이 인덱스로 프로젝션되지 않습니다.

`Issues` 테이블이 생성되면 프로그램에서 소프트웨어 버그 보고서를 나타내는 데이터와 함께 테이블을 로드합니다. 그런 다음 글로벌 보조 인덱스를 사용하여 데이터를 쿼리합니다. 마지막으로 프로그램에서 `Issues` 테이블을 삭제합니다.

다음 샘플을 테스트하기 위한 단계별 지침은 [.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 LowLevelGlobalSecondaryIndexExample
    {
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();
        public static String tableName = "Issues";

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

            QueryIndex("CreateDateIndex");
            QueryIndex("TitleIndex");
            QueryIndex("DueDateIndex");

            DeleteTable(tableName);

            Console.WriteLine("To continue, press enter");
            Console.Read();
        }

        private static void CreateTable()
        {
            // Attribute definitions
            var attributeDefinitions = new List<AttributeDefinition>()
        {
            {new AttributeDefinition {
                 AttributeName = "IssueId", AttributeType = "S"
             }},
            {new AttributeDefinition {
                 AttributeName = "Title", AttributeType = "S"
             }},
            {new AttributeDefinition {
                 AttributeName = "CreateDate", AttributeType = "S"
             }},
            {new AttributeDefinition {
                 AttributeName = "DueDate", AttributeType = "S"
             }}
        };

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

            // Initial provisioned throughput settings for the indexes
            var ptIndex = new ProvisionedThroughput
            {
                ReadCapacityUnits = 1L,
                WriteCapacityUnits = 1L
            };

            // CreateDateIndex
            var createDateIndex = new GlobalSecondaryIndex()
            {
                IndexName = "CreateDateIndex",
                ProvisionedThroughput = ptIndex,
                KeySchema = {
                new KeySchemaElement {
                    AttributeName = "CreateDate", KeyType = "HASH" //Partition key
                },
                new KeySchemaElement {
                    AttributeName = "IssueId", KeyType = "RANGE" //Sort key
                }
            },
                Projection = new Projection
                {
                    ProjectionType = "INCLUDE",
                    NonKeyAttributes = {
                    "Description", "Status"
                }
                }
            };

            // TitleIndex
            var titleIndex = new GlobalSecondaryIndex()
            {
                IndexName = "TitleIndex",
                ProvisionedThroughput = ptIndex,
                KeySchema = {
                new KeySchemaElement {
                    AttributeName = "Title", KeyType = "HASH" //Partition key
                },
                new KeySchemaElement {
                    AttributeName = "IssueId", KeyType = "RANGE" //Sort key
                }
            },
                Projection = new Projection
                {
                    ProjectionType = "KEYS_ONLY"
                }
            };

            // DueDateIndex
            var dueDateIndex = new GlobalSecondaryIndex()
            {
                IndexName = "DueDateIndex",
                ProvisionedThroughput = ptIndex,
                KeySchema = {
                new KeySchemaElement {
                    AttributeName = "DueDate",
                    KeyType = "HASH" //Partition key
                }
            },
                Projection = new Projection
                {
                    ProjectionType = "ALL"
                }
            };



            var createTableRequest = new CreateTableRequest
            {
                TableName = tableName,
                ProvisionedThroughput = new ProvisionedThroughput
                {
                    ReadCapacityUnits = (long)1,
                    WriteCapacityUnits = (long)1
                },
                AttributeDefinitions = attributeDefinitions,
                KeySchema = tableKeySchema,
                GlobalSecondaryIndexes = {
                createDateIndex, titleIndex, dueDateIndex
            }
            };

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

            WaitUntilTableReady(tableName);
        }

        private static void LoadData()
        {
            Console.WriteLine("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");
        }

        private static void putItem(
            String issueId, String title,
            String description,
            String createDate, String lastUpdateDate, String dueDate,
            Int32 priority, String status)
        {
            Dictionary<String, AttributeValue> item = new Dictionary<string, AttributeValue>();

            item.Add("IssueId", new AttributeValue
            {
                S = issueId
            });
            item.Add("Title", new AttributeValue
            {
                S = title
            });
            item.Add("Description", new AttributeValue
            {
                S = description
            });
            item.Add("CreateDate", new AttributeValue
            {
                S = createDate
            });
            item.Add("LastUpdateDate", new AttributeValue
            {
                S = lastUpdateDate
            });
            item.Add("DueDate", new AttributeValue
            {
                S = dueDate
            });
            item.Add("Priority", new AttributeValue
            {
                N = priority.ToString()
            });
            item.Add("Status", new AttributeValue
            {
                S = status
            });

            try
            {
                client.PutItem(new PutItemRequest
                {
                    TableName = tableName,
                    Item = item
                });
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

        private static void QueryIndex(string indexName)
        {
            Console.WriteLine
                ("\n***********************************************************\n");
            Console.WriteLine("Querying index " + indexName + "...");

            QueryRequest queryRequest = new QueryRequest
            {
                TableName = tableName,
                IndexName = indexName,
                ScanIndexForward = true
            };


            String keyConditionExpression;
            Dictionary<string, AttributeValue> expressionAttributeValues = new Dictionary<string, AttributeValue>();

            if (indexName == "CreateDateIndex")
            {
                Console.WriteLine("Issues filed on 2013-11-01\n");

                keyConditionExpression = "CreateDate = :v_date and begins_with(IssueId, :v_issue)";
                expressionAttributeValues.Add(":v_date", new AttributeValue
                {
                    S = "2013-11-01"
                });
                expressionAttributeValues.Add(":v_issue", new AttributeValue
                {
                    S = "A-"
                });
            }
            else if (indexName == "TitleIndex")
            {
                Console.WriteLine("Compilation errors\n");

                keyConditionExpression = "Title = :v_title and begins_with(IssueId, :v_issue)";
                expressionAttributeValues.Add(":v_title", new AttributeValue
                {
                    S = "Compilation error"
                });
                expressionAttributeValues.Add(":v_issue", new AttributeValue
                {
                    S = "A-"
                });

                // Select
                queryRequest.Select = "ALL_PROJECTED_ATTRIBUTES";
            }
            else if (indexName == "DueDateIndex")
            {
                Console.WriteLine("Items that are due on 2013-11-30\n");

                keyConditionExpression = "DueDate = :v_date";
                expressionAttributeValues.Add(":v_date", new AttributeValue
                {
                    S = "2013-11-30"
                });

                // Select
                queryRequest.Select = "ALL_PROJECTED_ATTRIBUTES";
            }
            else
            {
                Console.WriteLine("\nNo valid index name provided");
                return;
            }

            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 == "Priority")
                    {
                        Console.WriteLine(attr + "---> " + currentItem[attr].N);
                    }
                    else
                    {
                        Console.WriteLine(attr + "---> " + currentItem[attr].S);
                    }
                }
                Console.WriteLine();
            }
        }

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

        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;
                }
            }
        }
    }
}
```