

# Trabajar con índices secundarios globales: .NET
<a name="GSILowLevelDotNet"></a>

Puede utilizar la API de bajo nivel AWS SDK para .NET para crear una tabla Amazon DynamoDB con uno o varios índices secundarios globales, describir los índices de la tabla y utilizarlos para realizar consultas. Estas operaciones se mapean a las operaciones correspondientes de DynamoDB. Para obtener más información, consulte la [Referencia de la API de Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/). 

A continuación se indican los pasos comunes para las operaciones con tablas mediante el API de bajo nivel de .NET. 

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Cree los objetos de solicitud correspondientes para proporcionar los parámetros obligatorios y opcionales de la operación.

   Por ejemplo, cree un objeto `CreateTableRequest` para crear una tabla y un objeto `QueryRequest` para consultar una tabla o un índice. 

1. Ejecute el método apropiado proporcionado por el cliente que ha creado en el paso anterior. 

**Topics**
+ [Creación de una tabla con un índice secundario global](#GSILowLevelDotNet.CreateTableWithIndex)
+ [Descripción de una tabla con un índice secundario global](#GSILowLevelDotNet.DescribeTableWithIndex)
+ [Consulta de un índice secundario local](#GSILowLevelDotNet.QueryAnIndex)
+ [Ejemplo: índices secundarios globales que usan la API de bajo nivel de AWS SDK para .NET](GSILowLevelDotNet.Example.md)

## Creación de una tabla con un índice secundario global
<a name="GSILowLevelDotNet.CreateTableWithIndex"></a>

Puede crear índices secundarios globales a la vez que crea la tabla. Para ello, utilice `CreateTable` e indique las especificaciones para uno o varios índices secundarios globales. En el ejemplo de código C\$1 siguiente, se crea una tabla para contener información sobre datos meteorológicos. La clave de partición es `Location` y la de ordenación, `Date`. Un índice secundario global denominado `PrecipIndex` permite obtener acceso rápido a los datos sobre precipitaciones de varias ubicaciones.

A continuación se indican los pasos que hay que seguir para crear una tabla con un índice secundario global mediante el API de bajo nivel de .NET. 

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Cree una instancia de la clase `CreateTableRequest` para proporcionar la información de solicitud. 

   Debe proporcionar el nombre de la tabla, su clave principal y los valores de rendimiento aprovisionado. Para el índice secundario global, debe proporcionar el nombre del índice, los ajustes de rendimiento aprovisionado, las definiciones de atributos de la clave de ordenación del índice, el esquema de claves del índice y la proyección de atributos.

1. Ejecute el método `CreateTable` proporcionando el objeto de solicitud como parámetro.

En el siguiente ejemplo de código C\$1 se ponen en práctica los pasos anteriores. El código crea una tabla (`WeatherData`) con un índice secundario global (`PrecipIndex`). La clave de partición del índice es `Date` y la de ordenación, `Precipitation`. Todos los atributos de la tabla se proyectan en el índice. Los usuarios pueden consultar este índice para obtener los datos meteorológicos de una determinada fecha y, si lo desean, ordenarlos según la cantidad de precipitación. 

Ya que `Precipitation` no es un atributo de clave para la tabla, no es obligatorio. Sin embargo, los elementos`WeatherData` sin `Precipitation` no aparecen en `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);
```

Debe esperar hasta que DynamoDB cree la tabla y establezca el estado de esta última en `ACTIVE`. A partir de ese momento, puede comenzar a incluir elementos de datos en la tabla.

## Descripción de una tabla con un índice secundario global
<a name="GSILowLevelDotNet.DescribeTableWithIndex"></a>

Para obtener información sobre los índices secundarios globales en una tabla, use `DescribeTable`. Para cada índice, puede obtener acceso a su nombre, esquema de claves y atributos proyectados.

A continuación se indican los pasos que hay que seguir para acceder a la información de un índice secundario global de una tabla mediante el API de bajo nivel de .NET. 

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Ejecute el método `describeTable` proporcionando el objeto de solicitud como parámetro.

   Cree una instancia de la clase `DescribeTableRequest` para proporcionar la información de solicitud. Debe proporcionar el nombre de la tabla.

En el siguiente ejemplo de código C\$1 se ponen en práctica los pasos anteriores.

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

## Consulta de un índice secundario local
<a name="GSILowLevelDotNet.QueryAnIndex"></a>

Puede utilizar `Query` en un índice secundario global, de un modo bastante similar al que realiza una `Query` en una tabla. Debe especificar el nombre del índice, los criterios de consulta de la clave de partición y la clave de ordenación (si procede) del índice y los atributos que desea devolver. En este ejemplo, el índice es `PrecipIndex`, cuyas clave de partición y ordenación son, respectivamente, `Date` y `Precipitation`. La consulta del índice devuelve todos los datos meteorológicos de una determinada fecha cuando el valor de Precipitation sea mayor que cero.

A continuación se indican los pasos que hay que seguir para consultar un índice secundario global mediante el API de bajo nivel de .NET. 

1. Cree una instancia de la clase `AmazonDynamoDBClient`.

1. Cree una instancia de la clase `QueryRequest` para proporcionar la información de solicitud.

1. Ejecute el método `query` proporcionando el objeto de solicitud como parámetro.

El nombre de atributo `Date` es una palabra reservada de DynamoDB. Por consiguiente, debe usar un nombre de atributo de expresión como marcador de posición en la expresión `KeyConditionExpression`.

En el siguiente ejemplo de código C\$1 se ponen en práctica los pasos anteriores.

**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();
}
```

# Ejemplo: índices secundarios globales que usan la API de bajo nivel de AWS SDK para .NET
<a name="GSILowLevelDotNet.Example"></a>

En el siguiente ejemplo de código C\$1 se muestra cómo usar índices secundarios globales. En el ejemplo se crea una tabla denominada `Issues`, que puede utilizarse en un sistema sencillo de seguimiento de errores con fines de desarrollo de software. La clave de partición es `IssueId` y la de ordenación, `Title`. Hay tres índices secundarios globales en esta tabla:
+ `CreateDateIndex`: la clave de partición es `CreateDate` y la de ordenación `IssueId`. Además de las claves de tabla, se proyectan en el índice los atributos `Description` y `Status`.
+ `TitleIndex`: la clave de partición es `Title` y la de ordenación `IssueId`. No se proyecta en el índice ningún otro atributo excepto las claves de tabla.
+ `DueDateIndex`: la clave de partición es `DueDate` y no hay clave de ordenación. Todos los atributos de la tabla se proyectan en el índice.

Una vez que se ha creado la tabla `Issues`, el programa carga la tabla con datos que representan informes de errores de software. A continuación, consulta los datos utilizando los índices secundarios globales. Por último, el programa elimina la tabla `Issues`.

Para obtener instrucciones paso a paso para probar el siguiente ejemplo, consulte [Ejemplos de código .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;
                }
            }
        }
    }
}
```