

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