

# Índices secundarios locales
<a name="LSI"></a>

Algunas aplicaciones solo necesitan consultar datos mediante la clave principal de la tabla base. Sin embargo, puede haber situaciones en las que una clave de ordenación alternativa sería útil. Para que la aplicación disponga de diversas claves de ordenación entre las que elegir, puede crear uno o varios índices secundarios en una tabla de Amazon DynamoDB y emitir solicitudes `Query` o `Scan` para estos índices.

**Topics**
+ [Situación: Uso de un índice secundario local](#LSI.Scenario)
+ [Proyecciones de atributos](#LSI.Projections)
+ [Creación de un índice secundario local](#LSI.Creating)
+ [Lectura de datos de un índice secundario local](#LSI.Reading)
+ [Escrituras de elementos e índices secundarios locales](#LSI.Writes)
+ [Consideraciones sobre el rendimiento aprovisionado para los índices secundarios locales](#LSI.ThroughputConsiderations)
+ [Consideraciones sobre el almacenamiento para los índices secundarios locales](#LSI.StorageConsiderations)
+ [Colecciones de elementos en los índices secundarios locales](#LSI.ItemCollections)
+ [Trabajar con índices secundarios locales: Java](LSIJavaDocumentAPI.md)
+ [Trabajar con índices secundarios locales: .NET](LSILowLevelDotNet.md)
+ [Uso de índices secundarios locales en la AWS CLI de DynamoDB](LCICli.md)

## Situación: Uso de un índice secundario local
<a name="LSI.Scenario"></a>

Como ejemplo, veamos la tabla `Thread`. Esta tabla es útil para una aplicación como, por ejemplo, [Foros de debate de AWS](https://forums.aws.amazon.com/). En el siguiente diagrama se muestra cómo se organizarían los elementos de la tabla. No se muestran todos los atributos.

![\[Tabla de subprocesos que contiene una lista de nombres de foro, temas, hora de la última publicación y número de respuestas.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/LSI_01.png)


DynamoDB almacena todos los elementos que tienen el mismo valor de clave de partición de forma continua. En este ejemplo, para un valor determinado de `ForumName`, una operación `Query` podría localizar inmediatamente todas las conversaciones de ese foro. Dentro de un grupo de elementos con el mismo valor de clave de partición, los elementos se ordenan según el valor de la clave de ordenación. Si la clave de ordenación (`Subject`) también se incluye en la consulta, DynamoDB puede afinar los resultados que se devuelven; por ejemplo, devolvería todas las conversaciones del foro "S3" cuyo valor de `Subject` comience por la letra "a".

Algunas solicitudes podrían requerir patrones más complejos de acceso a los datos. Por ejemplo:
+ ¿Qué conversaciones del foro son las que suscitan más visualizaciones y respuestas?
+ ¿Qué conversación de un foro determinado tiene mayor cantidad de mensajes?
+ ¿Cuántas conversaciones se han publicado en un foro determinado dentro de un periodo concreto?

Para responder a estas preguntas, la acción `Query` no bastaría. En su lugar, habría que aplicar `Scan` a la tabla completa. Si la tabla contiene millones de elementos, esto consumiría una cantidad enorme de desempeño de lectura provisionado y tardaría mucho tiempo en completarse.

Sin embargo, puede especificar uno o más índices secundarios locales en atributos que no sean clave, tales como `Replies` or `LastPostDateTime`.

Un *índice secundario local* mantiene una clave de ordenación alternativa para un determinado valor de clave de partición. Un índice secundario local también contiene una copia parcial o total de los atributos de la tabla base. Al crear la tabla, se especifican qué atributos se proyectarán en el índice secundario local. Los datos de un índice secundario local se organizan según la misma clave de partición que la tabla base, pero con una clave de ordenación distinta. Esto le permite obtener acceso a los elementos de datos de forma eficiente en esta otra dimensión. Para lograr una mayor flexibilidad de consulta o examen, puede crear hasta cinco índices secundarios locales por tabla. 

Supongamos que una aplicación tiene que encontrar todas las conversaciones que se han publicado en los últimos tres meses en un foro particular. Sin un índice secundario local, la aplicación tendría que aplicar la operación `Scan` a toda la tabla `Thread` y, después, descartar aquellas publicaciones que no estuviesen comprendidos en el periodo especificado. Con un índice secundario local una operación `Query` podría usar `LastPostDateTime` como clave de ordenación para encontrar rápidamente los datos.

En el siguiente diagrama se muestra un índice secundario local denominado `LastPostIndex`. Tenga en cuenta que la clave de partición es la misma que para la tabla `Thread`, pero que la clave de ordenación es `LastPostDateTime`.

![\[La tabla LastPostIndex que contiene una lista de nombres de foro, temas y última hora de publicación.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/LSI_02.png)


Cada índice secundario local debe cumplir las condiciones siguientes:
+ La clave de partición ha de ser la misma que aquella de la tabla base.
+ La clave de ordenación consta exactamente de un atributo escalar.
+ La clave de ordenación de la tabla base se proyectará en el índice, donde actuará como atributo sin clave.

En este ejemplo, la clave de partición es `ForumName` y la clave de ordenación del índice secundario local es `LastPostDateTime`. Además, el valor de clave de ordenación de la tabla base (en este ejemplo, `Subject`) se proyecta en el índice, aunque no forma parte de la clave de índice. Si una aplicación requiere una lista basada en `ForumName` y `LastPostDateTime`, puede emitir una solicitud `Query` para `LastPostIndex`. Los resultados de la consulta se ordenan por `LastPostDateTime` y se pueden devolver en orden ascendente o descendente. La consulta también puede aplicar condiciones de clave, tales como devolver solamente aquellos elementos cuyo valor de `LastPostDateTime` esté comprendido en un periodo determinado.

Cada índice secundario local contiene automáticamente las claves de partición y ordenación de su tabla base. Si lo desea, puede proyectar atributos sin clave en el índice. Cuando se consulta el índice, DynamoDB puede recuperar estos atributos proyectados de forma eficiente. Cuando se consulta un índice secuandario local, la consulta también puede recuperar atributos *no* proyectados en el índice. DynamoDB recuperará automáticamente estos atributos de la tabla base, pero con una mayor latencia y costes más elevados de rendimiento aprovisionado.

Para cualquier índice secundario local, puede almacenar hasta 10 GB de datos por cada valor diferente de clave de partición. Esta cifra incluye todos los elementos de la tabla base, además de todos los elementos de los índices, que tengan el mismo valor de clave de partición. Para obtener más información, consulte [Colecciones de elementos en los índices secundarios locales](#LSI.ItemCollections).

## Proyecciones de atributos
<a name="LSI.Projections"></a>

Con `LastPostIndex`, una aplicación podría usar `ForumName` y `LastPostDateTime` como criterios de consulta. Sin embargo, para recuperar cualquier atributo adicional, DynamoDB debe realizar operaciones de lectura adicionales en la tabla `Thread`. Estas lecturas adicionales se denominan *recuperaciones* y pueden aumentar la cantidad total de desempeño provisionado necesario para una consulta.

Supongamos que desea rellenar una página web con una lista de todas las conversaciones del foro "S3" y el número de respuestas de cada conversación, ordenadas según la fecha y hora de la última respuesta y comenzando por la respuesta más reciente. Para rellenar esta lista, se requieren los siguientes atributos:
+ `Subject`
+ `Replies`
+ `LastPostDateTime`

La forma más eficiente de consultar estos datos y evitar operaciones de recuperación, sería proyectar el atributo `Replies`de la tabla en el índice secundario global, tal y como se muestra en este diagrama.

![\[Tabla LastPostIndex que contiene una lista de nombres de foro, horas de las últimas publicaciones, temas y respuestas.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/LSI_03.png)




Una *proyección* es el conjunto de atributos que se copia de una tabla en un índice secundario. La clave de partición y la clave de ordenación de la tabla siempre se proyectan en el índice; puede proyectar otros atributos para admitir los requisitos de consulta de la aplicación. Cuando consulta un índice, Amazon DynamoDB puede acceder a cualquier atributo de la proyección como si esos atributos estuvieran en una tabla propia.

Al crear un índice secundario, debe especificar los atributos que se proyectarán en el índice. DynamoDB ofrece tres opciones diferentes para esto:
+ *KEYS\$1ONLY*: cada elemento del índice consta únicamente de los valores de la clave de partición y la clave de ordenación de la tabla, así como de los valores de las claves del índice. La opción `KEYS_ONLY` da como resultado el índice secundario más pequeño posible.
+ *INCLUDE*: además de los atributos que se describen en `KEYS_ONLY`, el índice secundario incluirá otros atributos sin clave que se especifiquen.
+ *ALL*: el índice secundario incluye todos los atributos de la tabla de origen. Debido a que todos los datos de la tabla están duplicados en el índice, un resultado de proyección `ALL` en el índice secundario más grande posible.

En el diagrama anterior, el atributo sin clave `Replies` se proyecta en `LastPostIndex`. Una aplicación puede consultar `LastPostIndex` en lugar de `Thread` completo para rellenar una página web con `Subject`, `Replies` y `LastPostDateTime`. Si se solicitasen otros atributos sin clave, DynamoDB tendría que recuperarlos en la tabla `Thread`. 

Desde el punto de vista de una aplicación, la recuperación de atributos adicionales de la tabla base se lleva a cabo de forma automática y transparente, por lo que no es necesario volver a escribir la lógica de la aplicación. No obstante, este tipo de recuperaciones puede reducir en gran medida el efecto beneficioso para el desempeño que aporta el uso de un índice secundario local.

Cuando elija los atributos para proyectarlos en un índice secundario local, debe estudiar el equilibrio entre los costes de rendimiento aprovisionado y de almacenamiento:
+ Si solo necesita acceder a algunos atributos con la menor latencia posible, puede ser conveniente proyectar solamente esos atributos en un índice secundario local. Cuando menor es el índice, menos cuesta almacenarlo y menos son los costes de escritura. Si hay atributos que solo tiene que recuperar de vez en cuando, el costo del desempeño provisionado podría superar con creces el costo a largo plazo de almacenar esos atributos.
+ Si la aplicación va a obtener acceso con frecuencia a determinados atributos sin clave, puede ser interesante proyectarlos en un índice secundario local. Los costes del almacenamiento adicionales del índice secundario local compensarán el coste que supondrían los frecuentes exámenes de la tabla.
+ Si tiene que acceder a la mayoría de los atributos sin clave con frecuencia, puede proyectar estos atributos o, incluso, toda la tabla base, en un índice secundario local. De este modo, dispondrá de la máxima flexibilidad y el menor consumo de desempeño provisionado, porque no será preciso realizar recuperaciones. Sin embargo, el coste del almacenamiento aumentaría y podría llegar a duplicarse si se proyectan todos los atributos.
+ Si la aplicación tiene que consultar una tabla con poca frecuencia, pero tiene que realizar gran cantidad de escrituras o actualizaciones en los datos de la tabla, puede ser conveniente proyectar *KEYS\$1ONLY*. El índice secundario local tendrá el tamaño mínimo, pero estaría disponible siempre que se requiriese para actividades de consulta. 

## Creación de un índice secundario local
<a name="LSI.Creating"></a>

Para crear una tabla con uno o varios índices secundarios locales, use el parámetro `LocalSecondaryIndexes` de la operación `CreateTable`. Los índices secundarios locales de una tabla se crean cuando se crea la tabla. Al eliminar una tabla, todos los índices secundarios globales de esa tabla se eliminan también.

Debe especificar un atributo sin clave que actúe como clave de ordenación del índice secundario local. El atributo que elija debe ser un escalar `String`, `Number` o `Binary`. No se admite ningún otro tipo de escalar, documento ni conjunto. Para obtener una lista completa de los tipos de datos, consulte [Tipos de datos](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes).

**importante**  
Para las tablas con índices secundarios locales, existe un límite de tamaño de 10 GB por valor de clave de partición. Una tabla con índices secundarios locales puede almacenar cualquier número de elementos, siempre y cuando el tamaño total de cualquier valor de clave de partición individual no supere los 10 GB. Para obtener más información, consulte [Límite del tamaño de una colección de elementos](#LSI.ItemCollections.SizeLimit).

Puede proyectar atributos de cualquier tipo de datos en un índice secundario local. Esto incluye escalares, documentos y conjuntos. Para obtener una lista completa de los tipos de datos, consulte [Tipos de datos](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes).

## Lectura de datos de un índice secundario local
<a name="LSI.Reading"></a>

Puede recuperar elementos de un índice secundario local utilizando las operaciones `Query` y `Scan`. Las operaciones `GetItem` y `BatchGetItem` no se pueden usar en un índice secundario local.

### Consulta a un índice secundario local
<a name="LSI.Querying"></a>

En una tabla de DynamoDB, la combinación de valor de clave de partición y valor de clave de ordenación de cada elemento debe ser única. Sin embargo, en un índice secundario local, no es preciso que el valor de la clave de ordenación sea exclusivo para un determinado valor de clave de partición. Si un índice secundario local contiene varios elementos que tienen el mismo valor de clave de ordenación, una operación `Query` devolverá todos los elementos que tengan el mismo valor de clave de partición. En la respuesta, los elementos coincidentes no se devuelven en ningún orden concreto.

Puede consultar un índice secundario local usando lecturas consistentes finales o de consistencia alta. Para especificar qué tipo de consistencia desea aplicar, se utiliza el parámetro `ConsistentRead`Consistentead de la operación `Query`. Una lectura de consistencia alta de un índice secundario local siempre devolverá los valores actualizados más recientemente. Si la consulta tiene que recuperar atributos adicionales de la tabla base, estos serán consistentes respecto al índice.

**Example**  
Fíjese en los datos siguientes devueltos por una operación `Query` que solicita datos de las conversaciones de un foro determinado.  

```
{
    "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"}
    }
}
```
En esta consulta:  
+ DynamoDB accede a `LastPostIndex`. Utiliza la clave de partición `ForumName` para localizar los elementos del índice correspondientes a "EC2". Todos los elementos de índice que tienen esta clave se almacenan en posiciones adyacentes, para agilizar su recuperación.
+ En este foro, DynamoDB utiliza el índice para buscar las claves que coinciden con la condición `LastPostDateTime` especificada.
+ Dado que el atributo `Replies` se proyecta en el índice, DynamoDB puede recuperar este atributo sin consumir ningún rendimiento provisionado adicional.
+ El atributo `Tags` no se proyecta en el índice, de tal forma que DynamoDB debe acceder a la tabla `Thread` y recuperar este atributo.
+ Se devuelven los resultados ordenados según `LastPostDateTime`. Las entradas de índice se ordenan según el valor de la clave de partición y, a continuación, según el valor de la clave de ordenación; `Query` las devuelve en el orden en el que se encuentran almacenadas. Puede utilizar el parámetro `ScanIndexForward` para devolver los resultados en orden descendente.
Puesto que el atributo `Tags` no se ha proyectado en el índice secundario local, DynamoDB debe consumir unidades de capacidad de lectura adicionales para recuperar este atributo de la tabla base. Si necesita ejecutar esta consulta a menudo, debe proyectar `Tags` en `LastPostIndex` para evitar la obtención de la tabla base. Sin embargo, si necesitaba acceder a `Tags` sólo ocasionalmente, el coste de almacenamiento adicional para proyectar `Tags` en el índice puede no valer la pena.

### Análisis de un índice secundario local
<a name="LSI.Scanning"></a>

Puede utilizar `Scan` para recuperar todos los datos de un índice secundario local. Debe proporcionar el nombre de la tabla base y el nombre del índice en la solicitud. Con una operación `Scan`, DynamoDB lee todos los datos del índice y los devuelve a la aplicación. También puede solicitar que solo se devuelvan algunos de los datos y se descarten los demás. Para ello, se utiliza el parámetro `FilterExpression` del API `Scan`. Para obtener más información, consulte [Expresiones de filtro para el análisis](Scan.md#Scan.FilterExpression).

## Escrituras de elementos e índices secundarios locales
<a name="LSI.Writes"></a>

DynamoDB mantiene sincronizados automáticamente todos los índices secundarios locales con sus tablas base respectivas. Las aplicaciones nunca escriben directamente en un índice. Sin embargo, es importante que comprenda las implicaciones de cómo DynamoDB mantiene estos índices.

Al crear un índice secundario local, se especifica un atributo que actuará como clave de ordenación del índice. También se puede especificar el tipo de datos de ese atributo. Esto significa que, cada vez que se escribe un elemento en la tabla base, si el elemento define un atributo de clave de índice, su tipo debe coincidir con el tipo de datos del esquema de claves de índice. En el caso de `LastPostIndex`, el tipo de datos de la clave de ordenación `LastPostDateTime` del índice es `String`. Si intenta agregar un elemento a la tabla `Thread`, pero especifica un tipo de datos distinto para `LastPostDateTime` (tal como`Number`), DynamoDB devolverá una excepción `ValidationException`, porque los tipos de datos no coinciden.

No es obligatorio que exista una relación biunívoca entre los elementos de una tabla base y los elementos en un índice secundario local. De hecho, este comportamiento puede ser ventajoso para muchas aplicaciones. 

Una tabla con muchos índices secundarios locales devengará costes más elevados por la actividad de escritura que las tablas con menos índices. Para obtener más información, consulte [Consideraciones sobre el rendimiento aprovisionado para los índices secundarios locales](#LSI.ThroughputConsiderations).

**importante**  
Para las tablas con índices secundarios locales, existe un límite de tamaño de 10 GB por valor de clave de partición. Una tabla con índices secundarios locales puede almacenar cualquier número de elementos, siempre y cuando el tamaño total de cualquier valor de clave de partición individual no supere los 10 GB. Para obtener más información, consulte [Límite del tamaño de una colección de elementos](#LSI.ItemCollections.SizeLimit).

## Consideraciones sobre el rendimiento aprovisionado para los índices secundarios locales
<a name="LSI.ThroughputConsiderations"></a>

Al crear una tabla en DynamoDB, se provisionan unidades de capacidad de lectura y escritura para la carga de trabajo prevista de esa tabla. Esta carga de trabajo incluye la actividad de lectura y escritura en los índices secundarios locales de la tabla.

Para ver las tarifas vigentes de la capacidad de rendimiento aprovisionada, visite [Precios de Amazon DynamoDB](https://aws.amazon.com/dynamodb/pricing).

### Unidades de capacidad de lectura
<a name="LSI.ThroughputConsiderations.Reads"></a>

Cuando se realiza una consulta en un índice secundario local, el número de unidades de capacidad de lectura consumidas depende de cómo se obtiene acceso a los datos.

Al igual que ocurre con las consultas de tablas, las consultas de índices pueden utilizar lecturas consistentes finales o de consistencia alta, según el valor de `ConsistentRead`. Una lectura de consistencia alta consume una unidad de capacidad de lectura; una lectura eventualmente consistente consume solo la mitad. Por lo tanto, elegir la opción de lecturas consistentes finales permite reducir los cargos de unidades de capacidad de lectura.

Para las consultas de índices que solo solicitan claves de índice y atributos proyectados, DynamoDB calcula la actividad de lectura provisionada de la misma forma que para las consultas de tablas. La única diferencia es que el cálculo se basa en el tamaño de las entradas del índice, no en el tamaño del elemento en la tabla base. El número de unidades de capacidad de lectura es la suma de todos los tamaños de los atributos proyectados para todos los elementos devueltos; el resultado se redondea al múltiplo de 4 KB inmediatamente superior. Para obtener más información sobre cómo calcula DynamoDB el consumo de rendimiento aprovisionado, consulte [Modo de capacidad aprovisionada de DynamoDB](provisioned-capacity-mode.md).

Para indexar consultas que leen atributos no proyectados en el índice secundario, DynamoDB tendrá que recuperar esos atributos de la tabla base, además de leer los atributos proyectados en el índice. Estas recuperaciones tienen lugar cuando se incluyen atributos no proyectados en los parámetros `Select` o `ProjectionExpression` de la operación `Query`. Las recuperaciones provocan latencia adicional en las respuestas a las consultas y también devengan un mayor coste de desempeño provisionado. Esto se debe a que, además de las lecturas en el índice secundario local que se han descrito anteriormente, se le cobrarán unidades de capacidad de lectura por cada elemento recuperado de la tabla base. Este cargo incluye la lectura de cada elemento completo de la tabla, no solo de los atributos solicitados.

El tamaño máximo de los resultados que devuelve una operación `Query` es de 1 MB. Esta cifra incluye los tamaños de todos los nombres y valores de los atributos de todos los elementos devueltos. Sin embargo, si una consulta de un índice secundario local hace que DynamoDB recupere atributos de elementos de la tabla base, el tamaño máximo de los datos de los resultados podría ser inferior. En este caso, el tamaño del resultado es la suma de:
+ El tamaño de los elementos coincidentes del índice, redondeado al múltiplo de 4 KB inmediatamente superior.
+ El tamaño de cada elemento coincidente de la tabla base, redondeado individualmente al múltiplo de 4 KB inmediatamente superior.

Usando esta fórmula, el tamaño máximo de los resultados que devuelve una operación Query también es de 1 MB.

Por ejemplo, tomemos una tabla en la que el tamaño de cada elemento es de 300 bytes. Existe un índice secundario local en esa tabla, pero solo se proyectan en él 200 bytes de cada elemento. Ahora, supongamos que se ejecuta `Query` en este índice, que la consulta requiere recuperaciones en la tabla para cada elemento y que la consulta devuelve 4 elementos. DynamoDB calcula lo siguiente:
+ El tamaño de los elementos coincidentes en el índice: 200 bytes × 4 elementos = 800 bytes; este valor se redondea hasta 4 KB.
+ El tamaño de cada elemento coincidente de la tabla base: (300 bytes, redondeados al múltiplo de 4 KB inmediatamente superior) × 4 elementos = 16 KB.

Por lo tanto, el tamaño total de los datos del resultado es de 20 KB.

### Unidades de capacidad de escritura
<a name="LSI.ThroughputConsiderations.Writes"></a>

Cuando se agrega, actualiza o elimina un elemento de una tabla, la actualización de los índices secundarios locales consumirá unidades de capacidad de escritura provisionadas para la tabla. El coste total de rendimiento aprovisionado de una escritura es la suma de las unidades de capacidad de escritura consumidas al escribir en la tabla y aquellas consumidas al actualizar los índices secundarios locales.

El coste de escribir un elemento en un índice secundario local depende de varios factores:
+ Si escribe un nuevo elemento en la tabla que define un atributo indexado o actualiza un elemento existente para definir un atributo indexado no definido previamente, se requiere una operación de escritura para colocar el elemento en el índice.
+ Si al actualizar la tabla se cambia el valor de un atributo de clave indexado (de A a B), se requieren dos escrituras: una para eliminar el elemento anterior del índice y otra para colocar el nuevo elemento en el índice.  
+ Si ya hay un elemento en el índice, pero al escribir en la tabla se elimina el atributo indexado, se requiere una escritura para eliminar la proyección del elemento anterior del índice.
+ Si no hay ningún elemento presente en el índice antes o después de actualizar el elemento, no se devenga ningún coste de escritura adicional para el índice.

Al calcular las unidades de capacidad de escritura, en todos estos factores se presupone que el tamaño de cada elemento del índice es menor o igual que el tamaño de elemento de 1 KB. Las entradas de índice de mayor tamaño requieren más unidades de capacidad de escritura. Para minimizar los costes de escritura, es conveniente estudiar qué atributos tendrán que devolver las consultas y proyectar solamente esos atributos en el índice.

## Consideraciones sobre el almacenamiento para los índices secundarios locales
<a name="LSI.StorageConsiderations"></a>

Cuando una aplicación escribe un elemento en una tabla, DynamoDB copia automáticamente el subconjunto de atributos correcto en todos los índices secundarios locales en los que deban aparecer esos atributos. Se aplica un cargo en su cuenta de AWS por el almacenamiento del elemento en la tabla base y también por el almacenamiento de los atributos en todos los índices secundarios locales de esa tabla.

La cantidad de espacio utilizado por un elemento de índice es la suma de lo siguiente:
+ El tamaño en bytes de la clave principal (clave de partición y clave de ordenación) de la tabla base
+ El tamaño en bytes del atributo de clave de índice
+ El tamaño en bytes de los atributos proyectados (si procede)
+ 100 bytes de gastos generales por cada elemento de índice

Para calcular los requisitos de almacenamiento de un índice secundario local, puede calcular el tamaño medio de un elemento del índice y, a continuación, multiplicarlo por el número de elementos del índice.

Si una tabla contiene un elemento en el que no se ha definido un atributo determinado, pero ese atributo se ha definido como clave de ordenación del índice, DynamoDB no escribirá ningún dato para ese elemento en el índice. 

## Colecciones de elementos en los índices secundarios locales
<a name="LSI.ItemCollections"></a>

**nota**  
Esta sección solo concierne a las tablas que tienen índices secundarios locales.

En DynamoDB, una *colección de elementos* es cualquier grupo de elementos que contiene el mismo valor de clave de partición en una tabla y todos sus índices secundarios locales. En los ejemplos utilizados en esta sección, la clave de partición de la tabla `Thread` es `ForumName` y la clave de partición de `LastPostIndex`también es `ForumName`. Todos los elementos de la tabla y del índice que tienen el mismo valor de `ForumName` forman parte de la misma colección de elementos. Por ejemplo, en la tabla `Thread` y en el índice secundario local`LastPostIndex` hay una colección de elementos para el foro `EC2` y otra colección de elementos distinta para el foro `RDS`.

En el siguiente diagrama se muestra la colección de elementos del foro `S3`.

![\[Una colección de elementos de DynamoDB con elementos de tabla e índice secundario local que tienen el mismo valor de clave de partición de S3.\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/images/LSI_04.png)


En este diagrama, la colección de elementos consta de todos los elementos de `Thread` y `LastPostIndex` donde el valor de clave de partición `ForumName` es "S3". Si hay otros índices secundarios locales en la tabla, los elementos de esos índices cuyo valor de `ForumName` sea "S3" también formarán parte de la colección de elementos.

Puede utilizar cualquiera de las siguientes operaciones de DynamoDB para devolver información sobre las colecciones de elementos:
+ `BatchWriteItem`
+ `DeleteItem`
+ `PutItem`
+ `UpdateItem`
+ `TransactWriteItems`

Todas estas operaciones admiten el parámetro `ReturnItemCollectionMetrics`. Si establece este parámetro en `SIZE`, podrá ver información sobre el tamaño de cada colección de elementos en el índice.

**Example**  
A continuación se muestra un ejemplo del resultado de una operación `UpdateItem` en la tabla `Thread`, con `ReturnItemCollectionMetrics` configurado en `SIZE`. El elemento que se ha actualizado tenía un valor de `ForumName` de "EC2", por lo que el resultado incluye información acerca de esa colección de elementos.  

```
{
    ItemCollectionMetrics: {
        ItemCollectionKey: {
            ForumName: "EC2"
        },
        SizeEstimateRangeGB: [0.0, 1.0]
    }
}
```
El objeto `SizeEstimateRangeGB` muestra que el tamaño de esta colección de elementos está comprendido entre 0 y 1 GB. DynamoDB actualiza periódicamente este cálculo del tamaño, de modo que las cifras podrían ser distintas la próxima vez que se modifique el elemento.

### Límite del tamaño de una colección de elementos
<a name="LSI.ItemCollections.SizeLimit"></a>

El tamaño máximo de cualquier colección de elementos para una tabla que tenga uno o más índices secundarios locales es de 10 GB. Esto no se aplica a las colecciones de elementos en tablas sin índices secundarios locales, y tampoco se aplica a las colecciones de elementos en índices secundarios generales. Solo se ven afectadas las tablas que tienen uno o más índices secundarios locales.

Si una colección de elementos supera el límite de 10 GB, DynamoDB puede devolver una `ItemCollectionSizeLimitExceededException` y es posible que no pueda agregar más elementos a la colección de elementos ni incrementar los tamaños de los elementos contenidos en ella. (Las operaciones de lectura y escritura que reduzcan el tamaño de la colección de elementos sí se permitirán). También podrá agregar elementos a otras colecciones de elementos.

Para reducir el tamaño de una colección de elementos, puede elegir una de las siguientes opciones:
+ Eliminar todos los elementos innecesarios que tengan el valor de clave de partición de que se trate. Al eliminar estos elementos de la tabla base, DynamoDB también eliminará las entradas de índice cuyo valor de clave de partición sea el mismo.
+ Actualizar los elementos eliminando atributos o reduciendo el tamaño de estos últimos. Si estos atributos se han proyectado en uno o varios índices secundarios locales, DynamoDB también reducirá el tamaño de las entradas de índice correspondientes.
+ Crear una nueva tabla con las mismas clave de partición y clave de ordenación y, a continuación, mover elementos de la tabla anterior a la nueva. Esto puede ser conveniente si una tabla contiene datos históricos a los que se obtiene acceso con poca frecuencia. Puede considerar también el archivado de estos datos históricos en Amazon Simple Storage Service (Amazon S3).

Cuando el tamaño total de la colección de elementos disminuye por debajo de 10 GB, podrá volver a agregar elementos con el mismo valor de clave de partición.

Como práctica recomendada, es preferible instrumentar la aplicación de tal forma que monitorice los tamaños de las colecciones de elementos. Una forma de hacerlo es establecer el parámetro `ReturnItemCollectionMetrics` en `SIZE` siempre que utilice `BatchWriteItem`, `DeleteItem`, `PutItem` o `UpdateItem`. La aplicación debe examinar el objeto `ReturnItemCollectionMetrics` en los resultados y registrar un mensaje de error cada vez que una colección de elementos supere un límite definido por el usuario (8 GB, por ejemplo). Establecer un límite menor que 10 GB ofrece un mecanismo de advertencia precoz sistema que le permitirá saber qué colección de elementos está próxima a alcanzar el límite a tiempo para adoptar las medidas pertinentes.

### Colecciones de elementos y particiones
<a name="LSI.ItemCollections.OnePartition"></a>

En una tabla con uno o más índices secundarios locales, cada colección de elementos se almacena en una partición. El tamaño total de esa colección de elementos está limitado a la capacidad de esa partición: 10 GB. En el caso de una aplicación en la que el modelo de datos incluya colecciones de elementos cuyo tamaño no esté limitado, o en la que pueda esperar razonablemente que algunas colecciones de elementos aumenten más allá de los 10 GB en el futuro, debería considerar la posibilidad de utilizar un índice secundario general.

Debe diseñar las aplicaciones de tal forma que los datos de las tablas se distribuyan uniformemente entre los distintos valores de clave de partición. Para las tablas que tienen índices secundarios locales, las aplicaciones no deben crear "puntos calientes" de actividad de lectura y escritura dentro de una misma colección de elementos de una sola partición. 

# Trabajar con índices secundarios locales: Java
<a name="LSIJavaDocumentAPI"></a>

Puede utilizar la API de documentos AWS SDK para Java para crear una tabla Amazon DynamoDB con uno o varios índices secundarios locales, describir los índices de la tabla y utilizarlos para realizar consultas.

A continuación se indican los pasos comunes para las operaciones con tablas mediante el API de documentos del AWS SDK para Java.

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

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

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

**Topics**
+ [Creación de una tabla con un índice secundario local](#LSIJavaDocumentAPI.CreateTableWithIndex)
+ [Descripción de una tabla con un índice secundario local](#LSIJavaDocumentAPI.DescribeTableWithIndex)
+ [Consulta de un índice secundario local](#LSIJavaDocumentAPI.QueryAnIndex)
+ [Ejemplo: índices secundarios locales mediante la API de documentos de Java](LSIJavaDocumentAPI.Example.md)

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

Los índices secundarios locales se deben crear a la vez que se crea la tabla. Para ello, utilice el método `createTable` e indique las especificaciones para uno o varios índices secundarios locales. En el ejemplo de código Java siguiente, se crea una tabla para contener información sobre las canciones de una colección de música. La clave de partición es `Artist` y la de ordenación, `SongTitle`. Un índice secundario, `AlbumTitleIndex`, facilita las consultas por título de álbum. 

A continuación se indican los pasos que hay que seguir para crear una tabla con un índice secundario local mediante la API de documentos de DynamoDB. 

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

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 local, debe proporcionar el nombre de índice, el nombre y el tipo de datos de la clave de ordenación del índice, el esquema de claves del índice y la proyección de atributos.

1. Llame al método `createTable` proporcionando el objeto de solicitud como parámetro.

En el siguiente ejemplo de código Java se muestran los pasos anteriores. En el fragmento se crea una tabla (`Music`) con un índice secundario basado en el atributo `AlbumTitle`. La clave de partición y la clave de ordenación de la tabla, además de la clave de ordenación del índice, son los únicos atributos proyectados en el índice.

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

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 local
<a name="LSIJavaDocumentAPI.DescribeTableWithIndex"></a>

Para obtener información acerca de los índices secundarios locales en una tabla, utilice el método `describeTable`. Para cada índice, puede obtener acceso a su nombre, esquema de claves y atributos proyectados.

A continuación, se muestran los pasos que hay que seguir para obtener acceso a la información de un índice secundario local de una tabla mediante la API de documentos de AWS SDK para Java.

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

1. Cree una instancia de la clase `Table`. Debe proporcionar el nombre de la tabla.

1. Llame al método `describeTable` del objeto `Table`.

En el siguiente ejemplo de código Java se muestran los pasos anteriores.

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

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

Puede utilizar la operación `Query` en un índice secundario local de un modo bastante parecido a como `Query` se usa en una tabla. Debe especificar el nombre del índice, los criterios de consulta de la clave de ordenación del índice y los atributos que desea devolver. En este ejemplo, el índice es `AlbumTitleIndex` y la clave de ordenación del índice es `AlbumTitle`. 

Los únicos atributos devueltos son aquellos que se han proyectado en el índice. Puede modificar esta consulta de modo que también seleccione atributos sin clave, pero esto requeriría realizar actividad de recuperación en la tabla, lo que resulta relativamente costoso. Para obtener más información sobre recuperaciones de tablas, consulte [Proyecciones de atributos](LSI.md#LSI.Projections).

A continuación se indican los pasos que hay que seguir para consultar un índice secundario local con la API de documentos de AWS SDK para Java. 

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

1. Cree una instancia de la clase `Table`. Debe proporcionar el nombre de la tabla.

1. Cree una instancia de la clase `Index`. Debe proporcionar el nombre del índice.

1. Llame al método `query` de la clase `Index`.

En el siguiente ejemplo de código Java se muestran los pasos anteriores.

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

### Lecturas coherentes en un índice secundario local
<a name="LSIJavaDocumentAPI.ConsistentReads"></a>

A diferencia de los índices secundarios globales, que solo admiten lecturas coherentes posteriores, un índice secundario local admite lecturas coherentes posteriores y lecturas altamente coherentes. Una lectura de consistencia alta de un índice secundario local siempre devolverá los valores actualizados más recientemente. Si la consulta tiene que recuperar atributos adicionales de la tabla base, estos atributos recuperados también son coherentes con respecto al índice.

De forma predeterminada, `Query` usa lecturas coherentes posteriores. Para solicitar una lectura altamente coherente, establezca `ConsistentRead` en `true` en `QuerySpec`. A continuación, se muestran consultas `AlbumTitleIndex` mediante una lectura altamente coherente:

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

**nota**  
una lectura altamente coherente consume una unidad de capacidad de lectura por cada 4 KB de datos devueltos (redondeados al alza), mientras que una lectura coherente posterior consume la mitad. Por ejemplo, una lectura altamente coherente que devuelva 9 KB de datos consume 3 unidades de capacidad de lectura (9 KB/ 4 KB = 2,25, redondeada a 3), mientras que la misma consulta que utilice una lectura coherente posterior consume 1,5 unidades de capacidad de lectura. Si la aplicación puede tolerar la lectura de datos que puedan estar ligeramente obsoletos, utilice lecturas coherentes posteriores para reducir el uso de la capacidad de lectura. Para obtener más información, consulte [Unidades de capacidad de lectura](LSI.md#LSI.ThroughputConsiderations.Reads).

# Ejemplo: índices secundarios locales mediante la API de documentos de Java
<a name="LSIJavaDocumentAPI.Example"></a>

En el siguiente ejemplo de código Java se muestra cómo usar índices secundarios locales en Amazon DynamoDB. En el ejemplo se crea una tabla denominada `CustomerOrders` cuya clave de partición es `CustomerId` y cuya clave de ordenación es `OrderId`. Hay dos índices secundarios locales en esta tabla:
+ `OrderCreationDateIndex`: la clave de ordenación es `OrderCreationDate` y los atributos siguientes se proyectan en el índice:
  + `ProductCategory`
  + `ProductName`
  + `OrderStatus`
  + `ShipmentTrackingId`
+ `IsOpenIndex`: la clave de ordenación es `IsOpen` y todos los atributos de la tabla se proyectan en el índice.

Después de que se crea la tabla `CustomerOrders`, el programa carga la tabla con datos que representan pedidos de clientes. A continuación, consulta los datos utilizando los índices secundarios locales. Por último, el programa elimina la tabla `CustomerOrders`.

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

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

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

Puede utilizar la API de bajo nivel AWS SDK para .NET para crear una tabla Amazon DynamoDB con uno o varios índices secundarios locales, describir los índices de la tabla y utilizarlos para realizar consultas. Estas operaciones se mapean a las acciones correspondientes del API de bajo nivel de DynamoDB. Para obtener más información, consulte [Ejemplos de código .NET](CodeSamples.DotNet.md). 

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. 

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

Los índices secundarios locales se deben crear a la vez que se 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 las canciones de una colección de música. La clave de partición es `Artist` y la de ordenación, `SongTitle`. Un índice secundario, `AlbumTitleIndex`, facilita las consultas por título de álbum. 

A continuación se indican los pasos que hay que seguir para crear una tabla con un índice secundario local mediante la 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 local, debe proporcionar el nombre de índice, el nombre y el tipo de datos 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. En el fragmento se crea una tabla (`Music`) con un índice secundario basado en el atributo `AlbumTitle`. La clave de partición y la clave de ordenación de la tabla, además de la clave de ordenación del índice, son los únicos atributos proyectados en el índice.

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

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 local
<a name="LSILowLevelDotNet.DescribeTableWithIndex"></a>

Para obtener información acerca de los índices secundarios locales en una tabla, utilice la API `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 local de una tabla mediante el API de bajo nivel de .NET. 

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

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

1. Ejecute el método `describeTable` 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.

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

    }
}
```

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

Puede utilizar `Query` en un índice secundario local de un modo bastante parecido a como usa `Query` en una tabla. Debe especificar el nombre del índice, los criterios de consulta de la clave de ordenación del índice y los atributos que desea devolver. En este ejemplo, el índice es `AlbumTitleIndex` y la clave de ordenación del índice es `AlbumTitle`. 

Los únicos atributos devueltos son aquellos que se han proyectado en el índice. Puede modificar esta consulta de modo que también seleccione atributos sin clave, pero esto requeriría realizar actividad de recuperación en la tabla, lo que resulta relativamente costoso. Para obtener más información sobre recuperaciones de tablas, consulte [Proyecciones de atributos](LSI.md#LSI.Projections)

A continuación se indican los pasos que hay que seguir para consultar un índice secundario local mediante la 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.

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

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

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

En el siguiente ejemplo de código C\$1 se muestra cómo usar índices secundarios locales en Amazon DynamoDB. En el ejemplo se crea una tabla denominada `CustomerOrders` cuya clave de partición es `CustomerId` y cuya clave de ordenación es `OrderId`. Hay dos índices secundarios locales en esta tabla:
+ `OrderCreationDateIndex`: la clave de ordenación es `OrderCreationDate` y los atributos siguientes se proyectan en el índice:
  + `ProductCategory`
  + `ProductName`
  + `OrderStatus`
  + `ShipmentTrackingId`
+ `IsOpenIndex`: la clave de ordenación es `IsOpen` y todos los atributos de la tabla se proyectan en el índice.

Después de que se crea la tabla `CustomerOrders`, el programa carga la tabla con datos que representan pedidos de clientes. A continuación, consulta los datos utilizando los índices secundarios locales. Por último, el programa elimina la tabla `CustomerOrders`.

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

# Uso de índices secundarios locales en la AWS CLI de DynamoDB
<a name="LCICli"></a>

Puede usar la AWS CLI para crear una tabla de Amazon DynamoDB con uno o más índices secundarios locales, describir los índices de la tabla y utilizarlos para realizar consultas.

**Topics**
+ [Creación de una tabla con un índice secundario local](#LCICli.CreateTableWithIndex)
+ [Descripción de una tabla con un índice secundario local](#LCICli.DescribeTableWithIndex)
+ [Consulta de un índice secundario local](#LCICli.QueryAnIndex)

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

Los índices secundarios locales se deben crear al mismo tiempo que la tabla. Para ello, use el parámetro `create-table` y proporcione las especificaciones para uno o más índices secundarios locales. En el ejemplo siguiente, se crea una tabla (`Music`) para contener información sobre las canciones de una colección de música. La clave de partición es `Artist` y la de ordenación, `SongTitle`. Un índice secundario `AlbumTitleIndex` en el atributo `AlbumTitle` facilita las consultas por título de álbum. 

```
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\"]}}]"
```

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. Puede utilizar [describe-table](https://docs.aws.amazon.com/cli/latest/reference/dynamodb/describe-table.html) Para determinar el estado de la creación de tablas. 

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

Para obtener información acerca de los índices secundarios locales en una tabla, utilice el parámetro `describe-table`. Para cada índice, puede obtener acceso a su nombre, esquema de claves y atributos proyectados.

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

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

Puede usar la operación `query` en un índice secundario local de un modo bastante parecido a como `query` se usa en una tabla. Debe especificar el nombre del índice, los criterios de consulta de la clave de ordenación del índice y los atributos que desea devolver. En este ejemplo, el índice es `AlbumTitleIndex` y la clave de ordenación del índice es `AlbumTitle`. 

Los únicos atributos devueltos son aquellos que se han proyectado en el índice. Puede modificar esta consulta de modo que también seleccione atributos sin clave, pero esto requeriría realizar actividad de recuperación en la tabla, lo que resulta relativamente costoso. Para obtener más información sobre recuperaciones de tablas, consulte [Proyecciones de atributos](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"} }'
```