

# Como usar índices secundários globais no DynamoDB
<a name="GSI"></a>

Alguns aplicativos talvez precisem executar muitos tipos de consultas, usando uma variedade de atributos diferentes como critérios de consulta. Para oferecer suporte a esses requisitos, você pode criar um ou mais *índices secundários globais* e emitir solicitações `Query` para esses índices no Amazon DynamoDB.

**Topics**
+ [Cenário: Uso de um índice secundário global](#GSI.scenario)
+ [Projeções de atributo](#GSI.Projections)
+ [Esquema de chave de vários atributos](#GSI.MultiAttributeKeys)
+ [Ler dados de um índice secundário global](#GSI.Reading)
+ [Sincronização de dados entre tabelas e índices secundários globais](#GSI.Writes)
+ [Classes de tabela com índice secundário global](#GSI.tableclasses)
+ [Considerações sobre throughput provisionado para índices secundários globais](#GSI.ThroughputConsiderations)
+ [Considerações sobre armazenamento para índices secundários globais](#GSI.StorageConsiderations)
+ [Padrões de design](GSI.DesignPatterns.md)
+ [Gerenciar índices secundários globais no DynamoDB](GSI.OnlineOps.md)
+ [Detectar e corrigir violações de chave de índice no DynamoDB](GSI.OnlineOps.ViolationDetection.md)
+ [Como trabalhar com índices secundários globais: Java](GSIJavaDocumentAPI.md)
+ [Como trabalhar com índices secundários globais: .NET](GSILowLevelDotNet.md)
+ [Trabalhar com índices secundários globais no DynamoDB usando a AWS CLI](GCICli.md)

## Cenário: Uso de um índice secundário global
<a name="GSI.scenario"></a>

Para ilustrar, considere uma tabela chamada `GameScores` que controla os usuários e os placares de um aplicativo de jogo móvel. Cada item em `GameScores` é identificado por uma chave de partição (`UserId`) e por uma chave de classificação (`GameTitle`). O diagrama a seguir mostra como os itens da tabela seriam organizados. (Nem todos os atributos são mostrados.)

![\[A tabela GameScores que contém uma lista de ID de usuários, títulos, pontuações, datas e vitórias/derrotas.\]](http://docs.aws.amazon.com/pt_br/amazondynamodb/latest/developerguide/images/GSI_01.png)


Agora, vamos supor que você quisesse gravar um aplicativo de placar para exibir as pontuações máximas de cada jogo. Uma consulta que especificasse os atributos chave (`UserId` e `GameTitle`) seria muito eficaz. No entanto, se o aplicativo precisasse recuperar dados de `GameScores` com base no `GameTitle` apenas, ele precisaria usar uma operação `Scan`. À medida que mais itens são adicionados à tabela, as verificações de todos os dados se tornam lentas e ineficientes. Isso dificulta responder a questões como as seguintes:
+ Qual é a pontuação máxima já registrada no jogo Meteor Blasters?
+ Qual usuário tinha a maior pontuação no Galaxy Invaders?
+ Qual era a maior proporção de vitórias versus derrotas?

Para acelerar as consultas em atributos que não são chave, você pode criar um índice secundário global. Um índice secundário global contém uma seleção de atributos da tabela-base, mas eles são organizados por uma chave primária que é diferente daquela da região da tabela. A chave do índice não precisa ter nenhum dos atributos de chaves da tabela. Ela não precisa nem ter o mesmo esquema de chaves que a tabela.

Por exemplo, você poderia criar um índice secundário global chamado `GameTitleIndex` com uma chave de partição `GameTitle` e uma chave de classificação `TopScore`. Os atributos de chave primária da tabela base são sempre projetados em um índice, portanto, o atributo `UserId` também está presente. O diagrama a seguir mostra qual é a aparência do índice `GameTitleIndex`.

![\[A tabela GameTitleIndex que contém uma lista de títulos, pontuações e IDs de usuários.\]](http://docs.aws.amazon.com/pt_br/amazondynamodb/latest/developerguide/images/GSI_02.png)


Agora você pode consultar `GameTitleIndex` e obter as pontuações do jogo Meteor Blasters com facilidade. Os resultados são ordenados por valores de chave de classificação, `TopScore`. Se você definir o parâmetro `ScanIndexForward` como falso, os resultados serão retornados em ordem decrescente, de modo que a maior pontuação é retornada primeiro.

Cada índice secundário global deve ter uma chave de partição e pode ter uma chave de classificação opcional. O esquema da chave de índice pode ser diferente do esquema da tabela base. Você poderia ter uma tabela com uma chave primária simples (chave de partição) e criar um índice secundário global com uma chave primária composta (chave de partição e chave de classificação), ou vice versa. Os atributos de chaves do índice podem consistir em quaisquer atributos de nível superior, `String`, `Number` ou `Binary` da tabela base. Outros tipos escalares, tipos de documento e tipos de conjunto não são permitidos.

Você pode projetar outros atributos da tabela-base para o índice, se quisesse. Quando você consultar o índice, o DynamoDB poderá recuperar esses atributos projetados com eficiência. No entanto, as consultas do índice secundário global buscam atributos da tabela-base. Por exemplo, se você consultar `GameTitleIndex` conforme mostrado no diagrama anterior, a consulta poderá não acessar nenhum atributo que não seja de chave além de `TopScore` (embora os atributos de chave `GameTitle` e `UserId` sejam projetados automaticamente).

Em uma tabela do DynamoDB, cada valor de chave deve ser exclusivo. No entanto, os valores de chave em um índice secundário global não precisam ser exclusivos. Para ilustrar, suponhamos que um jogo chamado Comet Quest seja muito difícil. Há vários novos usuários tentando obter uma pontuação acima de zero, mas não conseguem. Veja a seguir alguns dos dados que podem representar isso.


****  

| UserId | GameTitle | TopScore | 
| --- | --- | --- | 
| 123 | Comet Quest | 0 | 
| 201 | Comet Quest | 0 | 
| 301 | Comet Quest | 0 | 

Quando esses dados são adicionados à tabela `GameScores`, o DynamoDB os propaga para o `GameTitleIndex`. Se consultarmos o índice usando Comet Quest para `GameTitle` e 0 para `TopScore`, os seguintes dados serão retornados.

![\[A tabela que contém uma lista de títulos, pontuações superiores e IDs de usuários.\]](http://docs.aws.amazon.com/pt_br/amazondynamodb/latest/developerguide/images/GSI_05.png)


Apenas os itens com os valores de chaves especificados aparecem na resposta. Nesse conjunto de dados, os itens não estão em nenhuma ordem específica. 

Um índice secundário global controla apenas os itens de dados em que seus atributos de chave realmente existem. Por exemplo, suponha que você tenha adicionado um novo item à tabela `GameScores`, mas forneceu somente os atributos de chave primária necessários.


****  

| UserId | GameTitle | 
| --- | --- | 
| 400 | Comet Quest | 

Como você não especificou o atributo `TopScore`, o DynamoDB não propagará esse item para `GameTitleIndex`. Portanto, se você tivesse consultado `GameScores` para obter todos os itens do Comet Quest, obteria os quatro itens a seguir.

![\[A tabela que contém uma lista de 4 títulos, pontuações superiores e IDs de usuários.\]](http://docs.aws.amazon.com/pt_br/amazondynamodb/latest/developerguide/images/GSI_04.png)


Uma consulta semelhante em `GameTitleIndex` ainda retornaria três itens, em vez de quatro. Isso acontece porque o item com `TopScore` inexistente não é propagado para o índice.

![\[A tabela que contém uma lista de 3 títulos, pontuações superiores e IDs de usuários.\]](http://docs.aws.amazon.com/pt_br/amazondynamodb/latest/developerguide/images/GSI_05.png)


## Projeções de atributo
<a name="GSI.Projections"></a>

Uma *projeção* é o conjunto de atributos que é copiado de uma tabela para um índice secundário. A chave de partição e a chave de classificação da tabela são sempre projetadas no índice; você pode projetar outros atributos para suportar os requisitos de consulta da sua aplicação. Quando você consulta um índice, o Amazon DynamoDB pode acessar quaisquer atributos na projeção como se estivessem em uma tabela própria.

Quando você cria um índice secundário, é necessário especificar os atributos que serão projetados no índice. O DynamoDB proporciona três opções diferentes para fazer isso:
+ *KEYS\$1ONLY*: cada item do índice consiste apenas nos valores de chaves de partição e nas chaves de classificação da tabela, além dos valores de chaves do índice. A opção `KEYS_ONLY` resulta no menor índice secundário possível.
+ *INCLUDE*: além dos atributos descritos em `KEYS_ONLY`, o índice secundário incluirá outros atributos não chave que você especificar.
+ *ALL*: o índice secundário inclui todos os atributos da tabela de origem. Como todos os dados da tabela são duplicados no índice, uma projeção `ALL` resulta no maior índice secundário possível.

No diagrama anterior, `GameTitleIndex` tem apenas um atributo projetado: `UserId` Portanto, embora um aplicativo possa determinar eficientemente o `UserId` dos melhores marcadores para cada jogo usando `GameTitle` e `TopScore` nas consultas, ele não pode determinar com eficiência a maior proporção de vitórias versus derrotas dos maiores marcadores. Para fazer isso, a aplicação teria que realizar uma consulta adicional na tabela base para buscar os ganhos e perdas de cada um dos maiores artilheiros. A maneira mais eficiente de oferecer suporte a consultas nesses dados seria projetar esses atributos da tabela-base no índice secundário global, conforme mostrado neste diagrama. 

![\[Descrição da projeção de atributos não essenciais em um GSI para oferecer suporte a consultas eficientes.\]](http://docs.aws.amazon.com/pt_br/amazondynamodb/latest/developerguide/images/GSI_06.png)


Como os atributos não são de chave `Wins` e `Losses` são projetados no índice, um aplicativo pode determinar a proporção de vitórias versus derrotas de qualquer jogo ou de qualquer combinação de jogo e ID do usuário.

Ao escolher os atributos para projetar em um índice secundário global, você deve considerar a desvantagem entre os custos de throughput provisionado e os custos de armazenamento:
+ Se você precisar acessar apenas alguns atributos com a latência mais baixa possível, considere projetar apenas os atributos em um índice secundário global. Quanto menor o índice, menores serão os custos de armazenamento e de gravação.
+ Se sua aplicação acessar frequentemente alguns atributos não chave, considere projetar esses atributos em um índice secundário global. Os custos adicionais de armazenamento do índice secundário global compensarão o custo de executar verificações de tabelas frequentes.
+ Se precisar acessar a maioria dos atributos não chave com frequência, você poderá projetar esses atributos, inclusive a tabela-base inteira, em um índice secundário global. Isso dá flexibilidade máxima a você. No entanto, o custo do armazenamento aumentará ou até dobrará.
+ Se o aplicativo precisar consultar uma tabela com pouca frequência, mas precisar executar muitas gravações ou atualizações nos dados da tabela, considere projetar `KEYS_ONLY`. O índice secundário global seria de tamanho mínimo, mas ainda estaria disponível quando necessário para a atividade de consulta. 

## Esquema de chave de vários atributos
<a name="GSI.MultiAttributeKeys"></a>

Os índices secundários globais são compatíveis com chaves de vários atributos, permitindo que você componha chaves de partição e chaves de classificação com base em vários atributos. Com chaves de vários atributos, você pode criar uma chave de partição com até quatro atributos e uma chave de classificação com até quatro atributos, totalizando até oito atributos por esquema de chave.

As chaves de vários atributos simplificam seu modelo de dados, eliminando a necessidade de concatenar manualmente os atributos em chaves sintéticas. Em vez de criar strings compostas, como `TOURNAMENT#WINTER2024#REGION#NA-EAST`, você pode usar diretamente os atributos naturais do seu modelo de domínio. O DynamoDB processa a lógica da chave composta automaticamente, unindo vários atributos da chave de partição para distribuição de dados e mantendo a ordem de classificação hierárquica em vários atributos da chave de classificação.

Por exemplo, considere um sistema de torneios de jogos em que você deseja organizar partidas por torneio e região. Com chaves de vários atributos, você pode definir sua chave de partição como dois atributos separados: `tournamentId` e `region`. Da mesma forma, você pode definir sua chave de classificação usando vários atributos, como `round`, `bracket` e `matchId`, para criar uma hierarquia natural. Essa abordagem mantém seus dados tipados e seu código limpo, sem manipulação nem análise de strings.

Quando você consulta um índice secundário global com chaves de vários atributos, deve especificar todos os atributos da chave de partição usando condições de igualdade. Em relação a atributos de chave de classificação, é possível consultá-los da esquerda para a direita na ordem em que estão definidos no esquema de chaves. Isso significa que você pode consultar o primeiro atributo da chave de classificação sozinho, os dois primeiros atributos juntos ou todos os atributos juntos, mas não pode ignorar os atributos no meio. Condições de desigualdade, como `>`, `<`, `BETWEEN` ou `begins_with()`, devem ser a última condição em sua consulta.

As chaves de vários atributos funcionam particularmente bem quando você cria índices secundários globais em tabelas existentes. Você pode usar atributos que já existem na sua tabela sem precisar preencher chaves sintéticas em todos os seus dados. Isso facilita a inclusão de novos padrões de consulta à sua aplicação criando índices que reorganizam seus dados usando diferentes combinações de atributos.

Cada atributo em uma chave de vários atributos pode ter seu próprio tipo de dados: `String` (S), `Number` (N) ou `Binary` (B). Quando você escolhe os tipos de dados, pense que os atributos `Number` são classificados numericamente sem exigir preenchimento zero, enquanto os atributos `String` são classificados de modo lexicográfico. Por exemplo, se você usar um tipo `Number` para um atributo de pontuação, os valores 5, 50, 500 e 1000 serão classificados em ordem numérica natural. Os mesmos valores, se fossem do tipo `String`, seriam ordenados como “1000”, “5”, “50”, “500”, a menos que você os preenchesse com zeros à esquerda.

Ao criar chaves de vários atributos, ordene seus atributos do mais geral para o mais específico. Para chaves de partição, combine atributos que são sempre consultados juntos e que oferecem uma boa distribuição de dados. Para chaves de classificação, coloque os atributos frequentemente consultados em primeiro lugar na hierarquia para maximizar a flexibilidade da consulta. Essa ordenação permite que você consulte em qualquer nível de granularidade que corresponda aos seus padrões de acesso.

Consulte exemplos de implementação em [Chaves de vários atributos](GSI.DesignPattern.MultiAttributeKeys.md).

## Ler dados de um índice secundário global
<a name="GSI.Reading"></a>

Você pode recuperar itens de um índice secundário global usando as operações `Query` e `Scan`. As operações `GetItem` e `BatchGetItem` não podem ser usadas em um índice secundário global.

### Como consultar um índice secundário global
<a name="GSI.Querying"></a>

Você pode usar a operação `Query` para acessar um ou mais itens em um índice secundário global. A consulta deve especificar o nome da tabela-base e o nome do índice que você deseja usar, os atributos a serem retornados nos resultados de consulta e quaisquer condições que você deseja aplicar. O DynamoDB pode retornar os resultados em ordem crescente ou decrescente.

Considere os seguintes dados retornados de uma `Query` que solicita dados de jogos para um aplicativo de placar.

```
{
    "TableName": "GameScores",
    "IndexName": "GameTitleIndex",
    "KeyConditionExpression": "GameTitle = :v_title",
    "ExpressionAttributeValues": {
        ":v_title": {"S": "Meteor Blasters"}
    },
    "ProjectionExpression": "UserId, TopScore",
    "ScanIndexForward": false
}
```

Nesta consulta:
+ O DynamoDB acessa *GameTitleIndex* usando a chave de partição *GameTitle* para localizar os itens de índice do Meteor Blasters. Todos os itens do índice com essa chave são armazenados lado a lado para rápida recuperação.
+ Nesse jogo, o DynamoDB usa o índice para acessar todos os IDs de usuário e as pontuações máximas.
+ Os resultados são retornados, classificados em ordem decrescente, pois o parâmetro `ScanIndexForward` está definido como falso.

### Como verificar um índice secundário global
<a name="GSI.Scanning"></a>

Você pode usar a operação `Scan` para recuperar todos os dados de um índice secundário global. Você deve fornecer o nome da tabela-base e o nome de índice na solicitação. Com uma operação `Scan`, o DynamoDB lê todos os dados do índice e os retorna para a aplicação. Você também pode solicitar que apenas alguns dos dados sejam retornados, e que os dados restantes sejam descartados. Para fazer isso, use o parâmetro `FilterExpression` da operação `Scan`. Para obter mais informações, consulte [Expressões de filtro para verificação](Scan.md#Scan.FilterExpression).

## Sincronização de dados entre tabelas e índices secundários globais
<a name="GSI.Writes"></a>

O DynamoDB sincroniza automaticamente cada índice secundário global com sua tabela-base. Quando uma aplicação grava ou exclui itens em uma tabela, quaisquer índices secundários globais nessa tabela são atualizados de forma assíncrona usando um modelo final consistente. Os aplicativos nunca gravam diretamente em um índice. No entanto, é importante compreender as implicações de como o DynamoDB mantém esses índices.

 Índices secundários globais herdam o modo de capacidade leitura/gravação da tabela base. Para obter mais informações, consulte [Considerações ao alternar os modos de capacidade no DynamoDB](bp-switching-capacity-modes.md). 

Ao criar um índice secundário global, você especifica um ou mais atributos de chave de índice e seus tipos de dados. Isso significa que sempre que você grava um item na tabela-base, os tipos de dados desses atributos devem corresponder aos tipos de dados do esquema de chaves do índice. No caso de `GameTitleIndex`, a chave de partição `GameTitle` no índice é definida como um tipo de dados `String`. A chave de classificação `TopScore` no índice é do tipo `Number`. Se você tentar adicionar um item à tabela `GameScores` e especificar um tipo de dados diferente para `GameTitle` ou `TopScore`, o DynamoDB retornará uma `ValidationException` devido à inconsistência do tipo de dados.

Quando você insere ou exclui itens em uma tabela, os índices secundários globais dessa tabela são atualizados de uma forma eventualmente consistente. As alterações na tabela são propagadas para os índices secundários globais em uma fração de segundo, sob condições normais. No entanto, em alguns cenários improváveis de falha, podem ocorrer atrasos de propagação mais longos. Consequentemente, as aplicações precisam prever e lidar com situações em que uma consulta em um índice secundário global retorna resultados que não estão atualizados.

Se você gravar um item em uma tabela, não será necessário especificar os atributos para qualquer chave de classificação do índice secundário global. Usando `GameTitleIndex` como um exemplo, você não precisa especificar um valor para o atributo `TopScore` para gravar um novo item na tabela `GameScores`. Neste caso, o DynamoDB não grava todos os dados no índice deste item específico.

Os custos das atividades de gravação em uma tabela com muitos índices secundários serão mais altos do que em uma tabela com um número menor de índices. Para obter mais informações, consulte [Considerações sobre throughput provisionado para índices secundários globais](#GSI.ThroughputConsiderations).

## Classes de tabela com índice secundário global
<a name="GSI.tableclasses"></a>

Um índice secundário global sempre usará a mesma classe de tabela que sua tabela-base. Sempre que um novo índice secundário global for adicionado para uma tabela, o novo índice usará a mesma classe de tabela que sua tabela base. Quando a classe de tabela de uma tabela é atualizada, todos os índices secundários globais associados também são atualizados.

## Considerações sobre throughput provisionado para índices secundários globais
<a name="GSI.ThroughputConsiderations"></a>

Ao criar um índice secundário global em uma tabela de modo provisionado, você deve especificar unidades de capacidade de leitura e gravação para a workload esperada no índice. As configurações de throughput provisionado de um índice secundário global são separadas daquelas de sua tabela-base. Uma operação `Query` em um índice secundário global consome unidades de capacidade de leitura do índice, não da tabela-base. Quando você insere ou exclui itens em uma tabela, os índices secundários globais dessa tabela também são atualizados. Essas atualizações de índice consomem unidades de capacidade do índice, não da tabela base.

Por exemplo, se você usar a operação `Query` em um índice secundário global e exceder sua capacidade de leitura provisionada, sua solicitação será limitada. Se você executar atividades de gravação pesadas na tabela, mas um índice secundário global da tabela tiver capacidade de gravação insuficiente, a atividade de gravação na tabela será limitada.

**Importante**  
 Para evitar o controle de utilização potencial, a capacidade de gravação provisionada para um índice secundário global deve ser igual ou maior que a capacidade de gravação da tabela base, porque as novas atualizações gravarão na tabela base e no índice secundário global. 

Para visualizar as configurações de throughput provisionado de um índice secundário global, use a operação `DescribeTable`. Informações detalhadas sobre todos os índices secundários globais da tabela são retornados.

### Unidades de capacidade de leitura
<a name="GSI.ThroughputConsiderations.Reads"></a>

Os índices secundários globais oferecem suporte a leituras eventualmente consistentes, cada uma delas consome metade de uma unidade de capacidade de leitura. Isso significa que uma única consulta de índice secundário global pode recuperar até 2 × 4 KB = 8 KB por unidade de capacidade de leitura.

Para consultas de índice secundário global, o DynamoDB calcula a atividade de leitura provisionada da mesma forma que para consultas em tabelas. A única diferença é que o cálculo é baseado no tamanho das entradas de índice, em vez do tamanho do item na tabela-base. O número de unidades de capacidade de leitura é a soma de todos os tamanhos de atributos projetados em todos os itens retornados. O resultado é arredondado para o próximo limite de 4 KB. Para obter mais informações sobre como o DynamoDB calcula a utilização de throughput provisionado, consulte [Modo de capacidade provisionada do DynamoDB](provisioned-capacity-mode.md).

O tamanho máximo dos resultados retornados por uma operação `Query` é 1 MB. Isso inclui os tamanhos de todos os nomes e valores de atributos de todos os itens retornados.

Por exemplo, considere um índice secundário global em que cada item contém 2.000 bytes de dados. Agora vamos supor que você use a operação `Query` nesse índice e que `KeyConditionExpression` da consulta retorne oito itens. O tamanho total dos itens correspondentes é 2.000 bytes × 8 itens = 16.000 bytes. O resultado é arredondado para o próximo limite de 4 KB. Como as consultas do índice secundário global são finais consistentes, o custo total é 0,5 × (16 KB / 4 KB), ou 2 unidades de capacidade de leitura.

### Unidades de capacidade de gravação
<a name="GSI.ThroughputConsiderations.Writes"></a>

Quando um item é adicionado, atualizado ou excluído em uma tabela, e um índice secundário global é afetado por isso, o índice secundário global consome unidades de capacidade de gravação provisionadas para a operação. O custo total do throughput provisionado para uma gravação consiste na soma das unidades de capacidade de gravação consumidas pela gravação na tabela base e pela atualização dos índices secundários globais. Se uma gravação em uma tabela não exigir uma atualização do índice secundário global, nenhuma capacidade de gravação será consumida no índice.

Para que uma gravação de tabela seja bem-sucedida, as configurações do throughput provisionado e todos os seus índices secundários globais devem ter capacidade de gravação suficiente para acomodar a gravação. Caso contrário, a gravação na tabela será limitada. 

**Importante**  
Ao criar um índice secundário global (GSI), as operações de gravação na tabela base poderão sofrer controle de utilização se a atividade do GSI resultante das gravações na tabela base exceder a capacidade de gravação provisionada do GSI. Esse controle de utilização afeta todas as operações de gravação, desde o processo de indexação até a possível interrupção de suas workloads de produção. Para ter mais informações, consulte [Problemas de controle de utilização no Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TroubleshootingThrottling.html).

O custo de gravar um item em um índice secundário global depende de vários fatores:
+ Se você gravar um novo item na tabela que define um atributo indexado, ou atualizar um item existente para definir um atributo indexado indefinido anteriormente, uma operação de gravação é necessária para inserir o item no índice.
+ Se uma atualização na tabela alterar o valor de um atributo de chave indexado (de A para B), duas gravações serão necessárias, uma para excluir o item anterior do índice e outra gravação para inserir o novo item no índice.  
+ Se um item estava presente no índice, mas uma gravação na tabela fez com que o atributo indexado fosse excluído, uma gravação é necessária para excluir a projeção do item antigo do índice.
+ Se um item não estiver presente no índice antes ou depois que o item é atualizado, não haverá custo de gravação adicionais para o índice.
+ Se uma atualização na tabela alterar somente o valor dos atributos projetados no esquema de chaves do índice, mas não alterar o valores de qualquer atributo de chave indexado, uma gravação será necessária para atualizar os valores dos atributos projetados no índice.

Todos esses fatores supõem que o tamanho de cada item no índice seja menor ou igual ao tamanho de item de 1 KB para calcular unidades de capacidade de gravação. Entradas de índice maiores exigirão unidades adicionais de capacidade de gravação. Você pode minimizar os custos de gravação, considerando quais atributos suas consultas precisarão retornar e projetar apenas esses atributos no índice.

## Considerações sobre armazenamento para índices secundários globais
<a name="GSI.StorageConsiderations"></a>

Quando uma aplicação grava um item em uma tabela, o DynamoDB copia automaticamente o subconjunto de atributos corretos para qualquer índice secundário global no qual esses atributos devem aparecer. Sua conta da AWS é cobrada pelo armazenamento do item na tabela-base e também pelo armazenamento de atributos em qualquer índice secundário global dessa tabela.

A quantidade de espaço usada por um item do índice é a soma do seguinte:
+ O tamanho em bytes da chave primária da tabela-base (chave de partição e chave de classificação)
+ O tamanho em bytes do atributo de chave do índice
+ O tamanho em bytes dos atributos projetados (se houver)
+ 100 bytes de sobrecarga por item de índice

Para estimar os requisitos de armazenamento de um índice secundário global, você pode estimar o tamanho médio de um item no índice e multiplicar pelo número de itens da tabela-base que têm os atributos de chave do índice secundário global.

Se uma tabela contiver um item em que determinado atributo não esteja definido, mas esteja configurado como uma chave de partição ou chave de classificação do índice, o DynamoDB não gravará nenhum dado desse item no índice.

# Padrões de design
<a name="GSI.DesignPatterns"></a>

Os padrões de design fornecem soluções comprovadas para desafios comuns quando você trabalha com índices secundários globais. Esses padrões ajudam você a criar aplicações eficientes e escaláveis, mostrando como estruturar seus índices para casos de uso específicos.

Cada padrão inclui um guia de implementação completo com exemplos de código, práticas recomendadas e casos de uso reais para ajudar você a aplicar o padrão em suas próprias aplicações.

**Topics**
+ [Chaves de vários atributos](GSI.DesignPattern.MultiAttributeKeys.md)

# Padrão de chaves de vários atributos
<a name="GSI.DesignPattern.MultiAttributeKeys"></a>

## Visão geral
<a name="GSI.DesignPattern.MultiAttributeKeys.Overview"></a>

As chaves de vários atributos permitem criar partições de Índice Secundário Global (GSI) e chaves de classificação compostas por até quatro atributos cada. Isso reduz o código do lado do cliente e facilita a modelagem inicial dos dados e a adição posterior de novos padrões de acesso.

Pense em um cenário comum: para criar um GSI que consulte itens por vários atributos hierárquicos, você tradicionalmente precisaria criar chaves sintéticas concatenando valores. Por exemplo, em uma aplicação de jogos, para consultar partidas de torneios por torneio, região e rodada, você pode criar uma chave de partição de GSI sintética, como TOURNAMENT\$1WINTER2024\$1REGION\$1NA-EAST, e uma chave de classificação sintética, como ROUND\$1SEMIFINALS\$1BRACKET\$1UPPER. Essa abordagem funciona, mas requer concatenação de strings ao gravar dados, analisar durante a leitura e preencher chaves sintéticas em todos os itens existentes se você estiver adicionando o GSI a uma tabela existente. Isso torna o código mais confuso e desafiador para manter a segurança de tipos em componentes de chave individuais.

As chaves de vários atributos resolvem esse problema para GSIs. Você define sua chave de partição de GSI usando vários atributos existentes, como tournamentId e region. O DynamoDB processa a lógica da chave composta automaticamente, juntando-as para distribuição de dados. Você grava itens usando atributos naturais do seu modelo de domínio e o GSI os indexa automaticamente. Sem concatenação, sem análise, sem preenchimento. Seu código permanece limpo, seus dados permanecem tipados e suas consultas permanecem simples. Essa abordagem é particularmente útil quando você tem dados hierárquicos com agrupamentos de atributos naturais (como torneio → região → rodada ou organização → departamento → equipe).

## Aplicação de exemplo
<a name="GSI.DesignPattern.MultiAttributeKeys.ApplicationExample"></a>

Este guia explica a criação de um sistema de rastreamento de partidas de torneios para uma plataforma de esportes eletrônicos. A plataforma precisa consultar partidas de forma eficiente em várias dimensões: por torneio e região para gerenciamento de chaves, por jogador para o histórico de partidas e por data para agendamento.

## Modelo de dados
<a name="GSI.DesignPattern.MultiAttributeKeys.DataModel"></a>

Neste passo a passo, o sistema de rastreamento de partidas do torneio comporta três padrões de acesso primários, cada um exigindo uma estrutura de chave diferente:

**Padrão de acesso 1:** procure uma correspondência específica por seu ID exclusivo.
+ **Solução:** tabela base com `matchId` como chave de partição.

**Padrão de acesso 2:** consulte todas as partidas de um torneio e região específicos, opcionalmente filtrando por rodada, chave ou partida.
+ **Solução:** índice secundário global com chave de partição de vários atributos (`tournamentId` \$1 `region`) e chave de classificação de vários atributos (`round` \$1 `bracket` \$1 `matchId`).
+ **Consultas de exemplo:** “Todas as partidas WINTER2024 na região NA-EAST” ou “Todas as partidas de SEMIFINALS na chave UPPER de WINTER2024/NA-EAST”.

**Padrão de acesso 3:** consulte o histórico de partidas de um jogador, opcionalmente filtrando por intervalo de datas ou rodada do torneio.
+ **Solução:** índice secundário global com uma chave de partição (`player1Id`) e uma chave de classificação de vários atributos (`matchDate` \$1 `round`).
+ **Consultas de exemplo:** “Todas as partidas do jogador 101" ou “Partidas do jogador 101 em janeiro de 2024".

A principal diferença entre as abordagens tradicional e de vários atributos fica clara ao examinar a estrutura do item:

**Abordagem tradicional do Índice Secundário Global (chaves concatenadas):**

```
// Manual concatenation required for GSI keys
const item = {
    matchId: 'match-001',                                          // Base table PK
    tournamentId: 'WINTER2024',
    region: 'NA-EAST',
    round: 'SEMIFINALS',
    bracket: 'UPPER',
    player1Id: '101',
    // Synthetic keys needed for GSI
    GSI_PK: `TOURNAMENT#${tournamentId}#REGION#${region}`,       // Must concatenate
    GSI_SK: `${round}#${bracket}#${matchId}`,                    // Must concatenate
    // ... other attributes
};
```

**Abordagem de Índice Secundário Global de vários atributos (chaves nativas):**

```
// Use existing attributes directly - no concatenation needed
const item = {
    matchId: 'match-001',                                          // Base table PK
    tournamentId: 'WINTER2024',
    region: 'NA-EAST',
    round: 'SEMIFINALS',
    bracket: 'UPPER',
    player1Id: '101',
    matchDate: '2024-01-18',
    // No synthetic keys needed - GSI uses existing attributes directly
    // ... other attributes
};
```

Com chaves de vários atributos, você grava itens uma vez com atributos de domínio naturais. O DynamoDB os indexa automaticamente em vários GSIs sem exigir chaves concatenadas sintéticas.

**Esquema da tabela base:**
+ Chave de partição: `matchId` (1 atributo)

**Esquema de Índice Secundário Global (TournamentRegionIndex com chaves de vários atributos):**
+ Chave de partição: `tournamentId`, `region` (2 atributos)
+ Chave de classificação: `round`, `bracket`, `matchId` (3 atributos)

**Esquema de Índice Secundário Global (PlayerMatchHistoryIndex com chaves de vários atributos):**
+ Chave de partição: `player1Id` (1 atributo)
+ Chave de classificação: `matchDate`, `round` (2 atributos)

### Tabela base: TournamentMatches
<a name="GSI.DesignPattern.MultiAttributeKeys.BaseTable"></a>


| matchId (PK) | tournamentId | região | round | bracket | player1Id | player2Id | matchDate | winner | pontuação | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| match-001 | WINTER2024 | NA-EAST | FINALS | CHAMPIONSHIP | 101 | 103 | 2024-01-20 | 101 | 3-1 | 
| match-002 | WINTER2024 | NA-EAST | SEMIFINALS | UPPER | 101 | 105 | 2024-01-18 | 101 | 3-2 | 
| match-003 | WINTER2024 | NA-EAST | SEMIFINALS | UPPER | 103 | 107 | 2024-01-18 | 103 | 3-0 | 
| match-004 | WINTER2024 | NA-EAST | QUARTERFINALS | UPPER | 101 | 109 | 2024-01-15 | 101 | 3-1 | 
| match-005 | WINTER2024 | NA-WEST | FINALS | CHAMPIONSHIP | 102 | 104 | 2024-01-20 | 102 | 3-2 | 
| match-006 | WINTER2024 | NA-WEST | SEMIFINALS | UPPER | 102 | 106 | 2024-01-18 | 102 | 3-1 | 
| match-007 | SPRING2024 | NA-EAST | QUARTERFINALS | UPPER | 101 | 108 | 2024-03-15 | 101 | 3-0 | 
| match-008 | SPRING2024 | NA-EAST | QUARTERFINALS | LOWER | 103 | 110 | 2024-03-15 | 103 | 3-2 | 

### GSI: TournamentRegionIndex (chaves de vários atributos)
<a name="GSI.DesignPattern.MultiAttributeKeys.TournamentRegionIndexTable"></a>


| tournamentId (PK) | region (PK) | round (SK) | bracket (SK) | matchId (SK) | player1Id | player2Id | matchDate | winner | pontuação | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| WINTER2024 | NA-EAST | FINALS | CHAMPIONSHIP | match-001 | 101 | 103 | 2024-01-20 | 101 | 3-1 | 
| WINTER2024 | NA-EAST | QUARTERFINALS | UPPER | match-004 | 101 | 109 | 2024-01-15 | 101 | 3-1 | 
| WINTER2024 | NA-EAST | SEMIFINALS | UPPER | match-002 | 101 | 105 | 2024-01-18 | 101 | 3-2 | 
| WINTER2024 | NA-EAST | SEMIFINALS | UPPER | match-003 | 103 | 107 | 2024-01-18 | 103 | 3-0 | 
| WINTER2024 | NA-WEST | FINALS | CHAMPIONSHIP | match-005 | 102 | 104 | 2024-01-20 | 102 | 3-2 | 
| WINTER2024 | NA-WEST | SEMIFINALS | UPPER | match-006 | 102 | 106 | 2024-01-18 | 102 | 3-1 | 
| SPRING2024 | NA-EAST | QUARTERFINALS | LOWER | match-008 | 103 | 110 | 2024-03-15 | 103 | 3-2 | 
| SPRING2024 | NA-EAST | QUARTERFINALS | UPPER | match-007 | 101 | 108 | 2024-03-15 | 101 | 3-0 | 

### GSI: PlayerMatchHistoryIndex (chaves de vários atributos)
<a name="GSI.DesignPattern.MultiAttributeKeys.PlayerMatchHistoryIndexTable"></a>


| player1Id (PK) | matchDate (SK) | round (SK) | tournamentId | região | bracket | matchId | player2Id | winner | pontuação | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| 101 | 2024-01-15 | QUARTERFINALS | WINTER2024 | NA-EAST | UPPER | match-004 | 109 | 101 | 3-1 | 
| 101 | 2024-01-18 | SEMIFINALS | WINTER2024 | NA-EAST | UPPER | match-002 | 105 | 101 | 3-2 | 
| 101 | 2024-01-20 | FINALS | WINTER2024 | NA-EAST | CHAMPIONSHIP | match-001 | 103 | 101 | 3-1 | 
| 101 | 2024-03-15 | QUARTERFINALS | SPRING2024 | NA-EAST | UPPER | match-007 | 108 | 101 | 3-0 | 
| 102 | 2024-01-18 | SEMIFINALS | WINTER2024 | NA-WEST | UPPER | match-006 | 106 | 102 | 3-1 | 
| 102 | 2024-01-20 | FINALS | WINTER2024 | NA-WEST | CHAMPIONSHIP | match-005 | 104 | 102 | 3-2 | 
| 103 | 2024-01-18 | SEMIFINALS | WINTER2024 | NA-EAST | UPPER | match-003 | 107 | 103 | 3-0 | 
| 103 | 2024-03-15 | QUARTERFINALS | SPRING2024 | NA-EAST | LOWER | match-008 | 110 | 103 | 3-2 | 

## Pré-requisitos
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites"></a>

Antes de começar, verifique se você tem:

### Conta e permissões
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites.AWSAccount"></a>
+ Uma conta da AWS ativa ([crie uma aqui](https://aws.amazon.com/free/), se necessário)
+ Permissões do IAM para operações do DynamoDB:
  + `dynamodb:CreateTable`
  + `dynamodb:DeleteTable`
  + `dynamodb:DescribeTable`
  + `dynamodb:PutItem`
  + `dynamodb:Query`
  + `dynamodb:BatchWriteItem`

**nota**  
**Nota de segurança:** para uso em produção, crie uma política do IAM personalizada com apenas as permissões necessárias. Para este tutorial, você pode usar a política gerenciada pela AWS `AmazonDynamoDBFullAccessV2`.

### Ambiente de desenvolvimento
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites.DevEnvironment"></a>
+ Node.js instalado em sua máquina
+ Credenciais da AWS configuradas usando um dos seguintes métodos:

**Opção 1: AWS CLI**

```
aws configure
```

**Opção 2: Variáveis de ambiente**

```
export AWS_ACCESS_KEY_ID=your_access_key_here
export AWS_SECRET_ACCESS_KEY=your_secret_key_here
export AWS_DEFAULT_REGION=us-east-1
```

### Instalar os pacotes obrigatórios
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites.InstallPackages"></a>

```
npm install @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
```

## Implementação
<a name="GSI.DesignPattern.MultiAttributeKeys.Implementation"></a>

### Etapa 1: criar tabela com GSIs usando chaves de vários atributos
<a name="GSI.DesignPattern.MultiAttributeKeys.CreateTable"></a>

Crie uma tabela com uma estrutura de chave básica simples e GSIs que usam chaves de vários atributos.

#### Exemplo de código
<a name="w2aac19c13c45c23b9c11b3b5b1"></a>

```
import { DynamoDBClient, CreateTableCommand } from "@aws-sdk/client-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });

const response = await client.send(new CreateTableCommand({
    TableName: 'TournamentMatches',
    
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'matchId', KeyType: 'HASH' }              // Simple PK
    ],
    
    AttributeDefinitions: [
        { AttributeName: 'matchId', AttributeType: 'S' },
        { AttributeName: 'tournamentId', AttributeType: 'S' },
        { AttributeName: 'region', AttributeType: 'S' },
        { AttributeName: 'round', AttributeType: 'S' },
        { AttributeName: 'bracket', AttributeType: 'S' },
        { AttributeName: 'player1Id', AttributeType: 'S' },
        { AttributeName: 'matchDate', AttributeType: 'S' }
    ],
    
    // GSIs with multi-attribute keys
    GlobalSecondaryIndexes: [
        {
            IndexName: 'TournamentRegionIndex',
            KeySchema: [
                { AttributeName: 'tournamentId', KeyType: 'HASH' },    // GSI PK attribute 1
                { AttributeName: 'region', KeyType: 'HASH' },          // GSI PK attribute 2
                { AttributeName: 'round', KeyType: 'RANGE' },          // GSI SK attribute 1
                { AttributeName: 'bracket', KeyType: 'RANGE' },        // GSI SK attribute 2
                { AttributeName: 'matchId', KeyType: 'RANGE' }         // GSI SK attribute 3
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'PlayerMatchHistoryIndex',
            KeySchema: [
                { AttributeName: 'player1Id', KeyType: 'HASH' },       // GSI PK
                { AttributeName: 'matchDate', KeyType: 'RANGE' },      // GSI SK attribute 1
                { AttributeName: 'round', KeyType: 'RANGE' }           // GSI SK attribute 2
            ],
            Projection: { ProjectionType: 'ALL' }
        }
    ],
    
    BillingMode: 'PAY_PER_REQUEST'
}));

console.log("Table with multi-attribute GSI keys created successfully");
```

**Principais decisões de design:**

**Tabela base:** a tabela base usa uma chave de partição `matchId` simples para pesquisas diretas de correspondência, mantendo a estrutura da tabela base simples, enquanto os GSIs fornecem padrões de consulta complexos.

**Índice Secundário Global TournamentRegionIndex:** o Índice Secundário Global `TournamentRegionIndex` usa `tournamentId` \$1 `region` como uma chave de partição de vários atributos, criando um isolamento da região do torneio em que os dados são distribuídos pelo hash de ambos os atributos combinados, permitindo consultas eficientes dentro de um contexto específico da região do torneio. A chave de classificação de vários atributos (`round` \$1 `bracket` \$1`matchId`) fornece classificação hierárquica que comporta consultas em qualquer nível da hierarquia com ordenação natural do geral (redondo) ao específico (ID de correspondência).

**Índice secundário global PlayerMatchHistoryIndex:** o Índice Secundário Global `PlayerMatchHistoryIndex` reorganiza os dados por jogador usando `player1Id` como chave de partição, permitindo consultas entre torneios para um jogador específico. A chave de classificação de vários atributos (`matchDate` \$1 `round`) fornece ordem cronológica com a capacidade de filtrar por intervalos de datas ou rodadas específicas do torneio.

### Etapa 2: inserir dados com atributos nativos
<a name="GSI.DesignPattern.MultiAttributeKeys.InsertData"></a>

Adicione dados da partida do torneio usando atributos naturais. O GSI indexará automaticamente esses atributos sem exigir chaves sintéticas.

#### Exemplo de código
<a name="w2aac19c13c45c23b9c11b5b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Tournament match data - no synthetic keys needed for GSIs
const matches = [
    // Winter 2024 Tournament, NA-EAST region
    {
        matchId: 'match-001',
        tournamentId: 'WINTER2024',
        region: 'NA-EAST',
        round: 'FINALS',
        bracket: 'CHAMPIONSHIP',
        player1Id: '101',
        player2Id: '103',
        matchDate: '2024-01-20',
        winner: '101',
        score: '3-1'
    },
    {
        matchId: 'match-002',
        tournamentId: 'WINTER2024',
        region: 'NA-EAST',
        round: 'SEMIFINALS',
        bracket: 'UPPER',
        player1Id: '101',
        player2Id: '105',
        matchDate: '2024-01-18',
        winner: '101',
        score: '3-2'
    },
    {
        matchId: 'match-003',
        tournamentId: 'WINTER2024',
        region: 'NA-EAST',
        round: 'SEMIFINALS',
        bracket: 'UPPER',
        player1Id: '103',
        player2Id: '107',
        matchDate: '2024-01-18',
        winner: '103',
        score: '3-0'
    },
    {
        matchId: 'match-004',
        tournamentId: 'WINTER2024',
        region: 'NA-EAST',
        round: 'QUARTERFINALS',
        bracket: 'UPPER',
        player1Id: '101',
        player2Id: '109',
        matchDate: '2024-01-15',
        winner: '101',
        score: '3-1'
    },
    
    // Winter 2024 Tournament, NA-WEST region
    {
        matchId: 'match-005',
        tournamentId: 'WINTER2024',
        region: 'NA-WEST',
        round: 'FINALS',
        bracket: 'CHAMPIONSHIP',
        player1Id: '102',
        player2Id: '104',
        matchDate: '2024-01-20',
        winner: '102',
        score: '3-2'
    },
    {
        matchId: 'match-006',
        tournamentId: 'WINTER2024',
        region: 'NA-WEST',
        round: 'SEMIFINALS',
        bracket: 'UPPER',
        player1Id: '102',
        player2Id: '106',
        matchDate: '2024-01-18',
        winner: '102',
        score: '3-1'
    },
    
    // Spring 2024 Tournament, NA-EAST region
    {
        matchId: 'match-007',
        tournamentId: 'SPRING2024',
        region: 'NA-EAST',
        round: 'QUARTERFINALS',
        bracket: 'UPPER',
        player1Id: '101',
        player2Id: '108',
        matchDate: '2024-03-15',
        winner: '101',
        score: '3-0'
    },
    {
        matchId: 'match-008',
        tournamentId: 'SPRING2024',
        region: 'NA-EAST',
        round: 'QUARTERFINALS',
        bracket: 'LOWER',
        player1Id: '103',
        player2Id: '110',
        matchDate: '2024-03-15',
        winner: '103',
        score: '3-2'
    }
];

// Insert all matches
for (const match of matches) {
    await docClient.send(new PutCommand({
        TableName: 'TournamentMatches',
        Item: match
    }));
    
    console.log(`Added: ${match.matchId} - ${match.tournamentId}/${match.region} - ${match.round} ${match.bracket}`);
}

console.log(`\nInserted ${matches.length} tournament matches`);
console.log("No synthetic keys created - GSIs use native attributes automatically");
```

**Estrutura de dados explicada:**

**Uso de atributos naturais:** cada atributo representa um conceito real de torneio sem necessidade de concatenação ou análise de strings, fornecendo mapeamento direto para o modelo de domínio.

**Indexação automática do Índice Secundário Global:** os GSIs indexam itens automaticamente usando os atributos existentes (`tournamentId`, `region`, `round`, `bracket`, `matchId` para TournamentRegionIndex e `player1Id`, `matchDate`, `round` para PlayerMatchHistoryIndex) sem exigir chaves concatenadas sintéticas.

**Sem necessidade de preenchimento:** quando você adiciona um novo Índice Secundário Global com chaves de vários atributos a uma tabela existente, o DynamoDB indexa automaticamente todos os itens existentes usando seus atributos naturais, sem a necessidade de atualizar os itens com chaves sintéticas.

### Etapa 3: consultar o Índice Secundário Global TournamentRegionIndex com todos os atributos da chave de partição
<a name="GSI.DesignPattern.MultiAttributeKeys.QueryAllPartitionKeys"></a>

Este exemplo consulta o Índice Secundário Global TournamentRegionIndex, que tem uma chave de partição de vários atributos (`tournamentId` \$1 `region`). Todos os atributos da chave de partição devem ser especificados com condições de igualdade nas consultas, ou seja, você não pode fazer consultas com apenas `tournamentId` sozinho ou usar operadores de desigualdade nos atributos da chave de partição.

#### Exemplo de código
<a name="w2aac19c13c45c23b9c11b7b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Query GSI: All matches for WINTER2024 tournament in NA-EAST region
const response = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region',
    ExpressionAttributeNames: {
        '#region': 'region',  // 'region' is a reserved keyword
        '#tournament': 'tournament'
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST'
    }
}));

console.log(`Found ${response.Items.length} matches for WINTER2024/NA-EAST:\n`);
response.Items.forEach(match => {
    console.log(`  ${match.round} | ${match.bracket} | ${match.matchId}`);
    console.log(`    Players: ${match.player1Id} vs ${match.player2Id}`);
    console.log(`    Winner: ${match.winner}, Score: ${match.score}\n`);
});
```

**Saída esperada:**

```
Found 4 matches for WINTER2024/NA-EAST:

  FINALS | CHAMPIONSHIP | match-001
    Players: 101 vs 103
    Winner: 101, Score: 3-1

  QUARTERFINALS | UPPER | match-004
    Players: 101 vs 109
    Winner: 101, Score: 3-1

  SEMIFINALS | UPPER | match-002
    Players: 101 vs 105
    Winner: 101, Score: 3-2

  SEMIFINALS | UPPER | match-003
    Players: 103 vs 107
    Winner: 103, Score: 3-0
```

**Consultas inválidas:**

```
// Missing region attribute
KeyConditionExpression: 'tournamentId = :tournament'

// Using inequality on partition key attribute
KeyConditionExpression: 'tournamentId = :tournament AND #region > :region'
```

**Performance:** as chaves de partição de vários atributos são combinadas por meio de uma função hash, fornecendo a mesma performance de pesquisa O(1) como chaves de atributo único.

### Etapa 4: consultar as chaves de classificação do Índice Secundário Global da esquerda para a direita
<a name="GSI.DesignPattern.MultiAttributeKeys.QuerySortKeysLeftToRight"></a>

Os atributos da chave de classificação devem ser consultados da esquerda para a direita na ordem em que estão definidos no Índice Secundário Global. Este exemplo demonstra a consulta do TournamentRegionIndex em diferentes níveis hierárquicos: filtrando por apenas `round`, por `round` \$1 `bracket` ou por todos os três atributos da chave de classificação. Você não pode ignorar atributos no meio, por exemplo, você não pode consultar por `round` e `matchId` e ignorar `bracket`.

#### Exemplo de código
<a name="w2aac19c13c45c23b9c11b9b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Query 1: Filter by first sort key attribute (round)
console.log("Query 1: All SEMIFINALS matches");
const query1 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round = :round',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'SEMIFINALS'
    }
}));
console.log(`  Found ${query1.Items.length} matches\n`);

// Query 2: Filter by first two sort key attributes (round + bracket)
console.log("Query 2: SEMIFINALS UPPER bracket matches");
const query2 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round = :round AND bracket = :bracket',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'SEMIFINALS',
        ':bracket': 'UPPER'
    }
}));
console.log(`  Found ${query2.Items.length} matches\n`);

// Query 3: Filter by all three sort key attributes (round + bracket + matchId)
console.log("Query 3: Specific match in SEMIFINALS UPPER bracket");
const query3 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round = :round AND bracket = :bracket AND matchId = :matchId',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'SEMIFINALS',
        ':bracket': 'UPPER',
        ':matchId': 'match-002'
    }
}));
console.log(`  Found ${query3.Items.length} matches\n`);

// Query 4: INVALID - skipping round
console.log("Query 4: Attempting to skip first sort key attribute (WILL FAIL)");
try {
    const query4 = await docClient.send(new QueryCommand({
        TableName: 'TournamentMatches',
        IndexName: 'TournamentRegionIndex',
        KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND bracket = :bracket',
        ExpressionAttributeNames: {
            '#region': 'region'  // 'region' is a reserved keyword
        },
        ExpressionAttributeValues: {
            ':tournament': 'WINTER2024',
            ':region': 'NA-EAST',
            ':bracket': 'UPPER'
        }
    }));
} catch (error) {
    console.log(`  Error: ${error.message}`);
    console.log(`  Cannot skip sort key attributes - must query left-to-right\n`);
}
```

**Saída esperada:**

```
Query 1: All SEMIFINALS matches
  Found 2 matches

Query 2: SEMIFINALS UPPER bracket matches
  Found 2 matches

Query 3: Specific match in SEMIFINALS UPPER bracket
  Found 1 matches

Query 4: Attempting to skip first sort key attribute (WILL FAIL)
  Error: Query key condition not supported
  Cannot skip sort key attributes - must query left-to-right
```

**Regras de consulta da esquerda para a direita:** você deve consultar os atributos na ordem da esquerda para a direita, sem ignorar nenhum.

**Padrões válidos:**
+ Somente o primeiro atributo: `round = 'SEMIFINALS'`
+ Primeiros dois atributos: `round = 'SEMIFINALS' AND bracket = 'UPPER'`
+ Todos os três atributos: `round = 'SEMIFINALS' AND bracket = 'UPPER' AND matchId = 'match-002'`

**Padrões inválidos:**
+ Ignorando o primeiro atributo: `bracket = 'UPPER'` (ignora uma rodada)
+ Consulta fora de ordem: `matchId = 'match-002' AND round = 'SEMIFINALS'`
+ Deixando lacunas: `round = 'SEMIFINALS' AND matchId = 'match-002'` (ignora a chave)

**nota**  
**Dica de design:** ordene os atributos da chave de classificação do mais geral para o mais específico para maximizar a flexibilidade da consulta.

### Etapa 5: usar condições de desigualdade nas chaves de classificação do Índice Secundário Global
<a name="GSI.DesignPattern.MultiAttributeKeys.InequalityConditions"></a>

Condições de desigualdade devem ser a última condição em sua consulta. Este exemplo demonstra o uso de operadores de comparação (`>=`, `BETWEEN`) e correspondência de prefixo (`begins_with()`) nos atributos da chave de classificação. Depois de usar um operador de desigualdade, você não pode adicionar nenhuma condição adicional de chave de classificação depois dele, ou seja, a desigualdade deve ser a condição final em sua expressão de condição de chave.

#### Exemplo de código
<a name="w2aac19c13c45c23b9c11c11b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Query 1: Round comparison (inequality on first sort key attribute)
console.log("Query 1: Matches from QUARTERFINALS onwards");
const query1 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round >= :round',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'QUARTERFINALS'
    }
}));
console.log(`  Found ${query1.Items.length} matches\n`);

// Query 2: Round range with BETWEEN
console.log("Query 2: Matches between QUARTERFINALS and SEMIFINALS");
const query2 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round BETWEEN :start AND :end',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':start': 'QUARTERFINALS',
        ':end': 'SEMIFINALS'
    }
}));
console.log(`  Found ${query2.Items.length} matches\n`);

// Query 3: Prefix matching with begins_with (treated as inequality)
console.log("Query 3: Matches in brackets starting with 'U'");
const query3 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'TournamentRegionIndex',
    KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round = :round AND begins_with(bracket, :prefix)',
    ExpressionAttributeNames: {
        '#region': 'region'  // 'region' is a reserved keyword
    },
    ExpressionAttributeValues: {
        ':tournament': 'WINTER2024',
        ':region': 'NA-EAST',
        ':round': 'SEMIFINALS',
        ':prefix': 'U'
    }
}));
console.log(`  Found ${query3.Items.length} matches\n`);

// Query 4: INVALID - condition after inequality
console.log("Query 4: Attempting condition after inequality (WILL FAIL)");
try {
    const query4 = await docClient.send(new QueryCommand({
        TableName: 'TournamentMatches',
        IndexName: 'TournamentRegionIndex',
        KeyConditionExpression: 'tournamentId = :tournament AND #region = :region AND round > :round AND bracket = :bracket',
        ExpressionAttributeNames: {
            '#region': 'region'  // 'region' is a reserved keyword
        },
        ExpressionAttributeValues: {
            ':tournament': 'WINTER2024',
            ':region': 'NA-EAST',
            ':round': 'QUARTERFINALS',
            ':bracket': 'UPPER'
        }
    }));
} catch (error) {
    console.log(`  Error: ${error.message}`);
    console.log(`  Cannot add conditions after inequality - it must be last\n`);
}
```

**Regras do operador de desigualdade:** você pode usar operadores de comparação (`>`, `>=`, `<`, `<=`) `BETWEEN` para consultas de intervalo e `begins_with()` para correspondência de prefixos. A desigualdade deve ser a última condição em sua consulta.

**Padrões válidos:**
+ Condições de igualdade seguidas de desigualdade: `round = 'SEMIFINALS' AND bracket = 'UPPER' AND matchId > 'match-001'`
+ Desigualdade no primeiro atributo: `round BETWEEN 'QUARTERFINALS' AND 'SEMIFINALS'`
+ Correspondência de prefixo como condição final: `round = 'SEMIFINALS' AND begins_with(bracket, 'U')`

**Padrões inválidos:**
+ Adicionando condições após uma desigualdade: `round > 'QUARTERFINALS' AND bracket = 'UPPER'`
+ Usando várias desigualdades: `round > 'QUARTERFINALS' AND bracket > 'L'`

**Importante**  
`begins_with()` é tratada como uma condição de desigualdade, portanto, nenhuma condição adicional de chave de classificação pode segui-la.

### Etapa 6: consultar o Índice Secundário Global PlayerMatchHistoryIndex com chaves de classificação vários atributos
<a name="GSI.DesignPattern.MultiAttributeKeys.QueryPlayerHistory"></a>

Este exemplo consulta o PlayerMatchHistoryIndex, que tem uma única chave de partição (`player1Id`) e uma chave de classificação de vários atributos (`matchDate` \$1 `round`). Isso permite a análise de vários torneios consultando todas as partidas de um jogador específico sem saber os IDs do torneio, enquanto a tabela base exigiria consultas separadas por combinação de torneio e região.

#### Exemplo de código
<a name="w2aac19c13c45c23b9c11c13b5b1"></a>

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

// Query 1: All matches for Player 101 across all tournaments
console.log("Query 1: All matches for Player 101");
const query1 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'PlayerMatchHistoryIndex',
    KeyConditionExpression: 'player1Id = :player',
    ExpressionAttributeValues: {
        ':player': '101'
    }
}));

console.log(`  Found ${query1.Items.length} matches for Player 101:`);
query1.Items.forEach(match => {
    console.log(`    ${match.tournamentId}/${match.region} - ${match.matchDate} - ${match.round}`);
});
console.log();

// Query 2: Player 101 matches on specific date
console.log("Query 2: Player 101 matches on 2024-01-18");
const query2 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'PlayerMatchHistoryIndex',
    KeyConditionExpression: 'player1Id = :player AND matchDate = :date',
    ExpressionAttributeValues: {
        ':player': '101',
        ':date': '2024-01-18'
    }
}));

console.log(`  Found ${query2.Items.length} matches\n`);

// Query 3: Player 101 SEMIFINALS matches on specific date
console.log("Query 3: Player 101 SEMIFINALS matches on 2024-01-18");
const query3 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'PlayerMatchHistoryIndex',
    KeyConditionExpression: 'player1Id = :player AND matchDate = :date AND round = :round',
    ExpressionAttributeValues: {
        ':player': '101',
        ':date': '2024-01-18',
        ':round': 'SEMIFINALS'
    }
}));

console.log(`  Found ${query3.Items.length} matches\n`);

// Query 4: Player 101 matches in date range
console.log("Query 4: Player 101 matches in January 2024");
const query4 = await docClient.send(new QueryCommand({
    TableName: 'TournamentMatches',
    IndexName: 'PlayerMatchHistoryIndex',
    KeyConditionExpression: 'player1Id = :player AND matchDate BETWEEN :start AND :end',
    ExpressionAttributeValues: {
        ':player': '101',
        ':start': '2024-01-01',
        ':end': '2024-01-31'
    }
}));

console.log(`  Found ${query4.Items.length} matches\n`);
```

## Variações de padrões
<a name="GSI.DesignPattern.MultiAttributeKeys.PatternVariations"></a>

### Dados de séries temporais com chaves de vários atributos
<a name="GSI.DesignPattern.MultiAttributeKeys.TimeSeries"></a>

Otimizar para consultas de séries temporais com atributos de tempo hierárquicos

#### Exemplo de código
<a name="w2aac19c13c45c23b9c13b3b5b1"></a>

```
{
    TableName: 'IoTReadings',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'readingId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'readingId', AttributeType: 'S' },
        { AttributeName: 'deviceId', AttributeType: 'S' },
        { AttributeName: 'locationId', AttributeType: 'S' },
        { AttributeName: 'year', AttributeType: 'S' },
        { AttributeName: 'month', AttributeType: 'S' },
        { AttributeName: 'day', AttributeType: 'S' },
        { AttributeName: 'timestamp', AttributeType: 'S' }
    ],
    // GSI with multi-attribute keys for time-series queries
    GlobalSecondaryIndexes: [{
        IndexName: 'DeviceLocationTimeIndex',
        KeySchema: [
            { AttributeName: 'deviceId', KeyType: 'HASH' },
            { AttributeName: 'locationId', KeyType: 'HASH' },
            { AttributeName: 'year', KeyType: 'RANGE' },
            { AttributeName: 'month', KeyType: 'RANGE' },
            { AttributeName: 'day', KeyType: 'RANGE' },
            { AttributeName: 'timestamp', KeyType: 'RANGE' }
        ],
        Projection: { ProjectionType: 'ALL' }
    }],
    BillingMode: 'PAY_PER_REQUEST'
}

// Query patterns enabled via GSI:
// - All readings for device in location
// - Readings for specific year
// - Readings for specific month in year
// - Readings for specific day
// - Readings in time range
```

**Benefícios:** a hierarquia de tempo natural (ano → mês → dia → carimbo de data/hora) permite consultas eficientes em qualquer momento, granularidade, sem análise ou manipulação de datas. O Índice Secundário Global indexa automaticamente todas as leituras usando seus atributos de tempo natural.

### Pedidos de comércio eletrônico com chaves de vários atributos
<a name="GSI.DesignPattern.MultiAttributeKeys.ECommerce"></a>

Monitorar pedidos com várias dimensões

#### Exemplo de código
<a name="w2aac19c13c45c23b9c13b5b5b1"></a>

```
{
    TableName: 'Orders',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'orderId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'orderId', AttributeType: 'S' },
        { AttributeName: 'sellerId', AttributeType: 'S' },
        { AttributeName: 'region', AttributeType: 'S' },
        { AttributeName: 'orderDate', AttributeType: 'S' },
        { AttributeName: 'category', AttributeType: 'S' },
        { AttributeName: 'customerId', AttributeType: 'S' },
        { AttributeName: 'orderStatus', AttributeType: 'S' }
    ],
    GlobalSecondaryIndexes: [
        {
            IndexName: 'SellerRegionIndex',
            KeySchema: [
                { AttributeName: 'sellerId', KeyType: 'HASH' },
                { AttributeName: 'region', KeyType: 'HASH' },
                { AttributeName: 'orderDate', KeyType: 'RANGE' },
                { AttributeName: 'category', KeyType: 'RANGE' },
                { AttributeName: 'orderId', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'CustomerOrdersIndex',
            KeySchema: [
                { AttributeName: 'customerId', KeyType: 'HASH' },
                { AttributeName: 'orderDate', KeyType: 'RANGE' },
                { AttributeName: 'orderStatus', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        }
    ],
    BillingMode: 'PAY_PER_REQUEST'
}

// SellerRegionIndex GSI queries:
// - Orders by seller and region
// - Orders by seller, region, and date
// - Orders by seller, region, date, and category

// CustomerOrdersIndex GSI queries:
// - Customer's orders
// - Customer's orders by date
// - Customer's orders by date and status
```

### Dados de organizações hierárquicas
<a name="GSI.DesignPattern.MultiAttributeKeys.Hierarchical"></a>

Hierarquias organizacionais de modelos

#### Exemplo de código
<a name="w2aac19c13c45c23b9c13b7b5b1"></a>

```
{
    TableName: 'Employees',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'employeeId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'employeeId', AttributeType: 'S' },
        { AttributeName: 'companyId', AttributeType: 'S' },
        { AttributeName: 'divisionId', AttributeType: 'S' },
        { AttributeName: 'departmentId', AttributeType: 'S' },
        { AttributeName: 'teamId', AttributeType: 'S' },
        { AttributeName: 'skillCategory', AttributeType: 'S' },
        { AttributeName: 'skillLevel', AttributeType: 'S' },
        { AttributeName: 'yearsExperience', AttributeType: 'N' }
    ],
    GlobalSecondaryIndexes: [
        {
            IndexName: 'OrganizationIndex',
            KeySchema: [
                { AttributeName: 'companyId', KeyType: 'HASH' },
                { AttributeName: 'divisionId', KeyType: 'HASH' },
                { AttributeName: 'departmentId', KeyType: 'RANGE' },
                { AttributeName: 'teamId', KeyType: 'RANGE' },
                { AttributeName: 'employeeId', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'SkillsIndex',
            KeySchema: [
                { AttributeName: 'skillCategory', KeyType: 'HASH' },
                { AttributeName: 'skillLevel', KeyType: 'RANGE' },
                { AttributeName: 'yearsExperience', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'INCLUDE', NonKeyAttributes: ['employeeId', 'name'] }
        }
    ],
    BillingMode: 'PAY_PER_REQUEST'
}

// OrganizationIndex GSI query patterns:
// - All employees in company/division
// - Employees in specific department
// - Employees in specific team

// SkillsIndex GSI query patterns:
// - Employees by skill and experience level
```

### Chaves esparsas de vários atributos
<a name="GSI.DesignPattern.MultiAttributeKeys.Sparse"></a>

Combinar chaves de vários atributos para criar um GSI esparso

#### Exemplo de código
<a name="w2aac19c13c45c23b9c13b9b5b1"></a>

```
{
    TableName: 'Products',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'productId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'productId', AttributeType: 'S' },
        { AttributeName: 'categoryId', AttributeType: 'S' },
        { AttributeName: 'subcategoryId', AttributeType: 'S' },
        { AttributeName: 'averageRating', AttributeType: 'N' },
        { AttributeName: 'reviewCount', AttributeType: 'N' }
    ],
    GlobalSecondaryIndexes: [
        {
            IndexName: 'CategoryIndex',
            KeySchema: [
                { AttributeName: 'categoryId', KeyType: 'HASH' },
                { AttributeName: 'subcategoryId', KeyType: 'HASH' },
                { AttributeName: 'productId', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'ReviewedProductsIndex',
            KeySchema: [
                { AttributeName: 'categoryId', KeyType: 'HASH' },
                { AttributeName: 'averageRating', KeyType: 'RANGE' },  // Optional attribute
                { AttributeName: 'reviewCount', KeyType: 'RANGE' }     // Optional attribute
            ],
            Projection: { ProjectionType: 'ALL' }
        }
    ],
    BillingMode: 'PAY_PER_REQUEST'
}

// Only products with reviews appear in ReviewedProductsIndex GSI
// Automatic filtering without application logic
// Multi-attribute sort key enables rating and count queries
```

### SaaS e multilocação
<a name="GSI.DesignPattern.MultiAttributeKeys.SaaS"></a>

Plataforma SaaS multilocatário com isolamento de clientes

#### Exemplo de código
<a name="w2aac19c13c45c23b9c13c11b5b1"></a>

```
// Table design
{
    TableName: 'SaasData',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'resourceId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'resourceId', AttributeType: 'S' },
        { AttributeName: 'tenantId', AttributeType: 'S' },
        { AttributeName: 'customerId', AttributeType: 'S' },
        { AttributeName: 'resourceType', AttributeType: 'S' }
    ],
    // GSI with multi-attribute keys for tenant-customer isolation
    GlobalSecondaryIndexes: [{
        IndexName: 'TenantCustomerIndex',
        KeySchema: [
            { AttributeName: 'tenantId', KeyType: 'HASH' },
            { AttributeName: 'customerId', KeyType: 'HASH' },
            { AttributeName: 'resourceType', KeyType: 'RANGE' },
            { AttributeName: 'resourceId', KeyType: 'RANGE' }
        ],
        Projection: { ProjectionType: 'ALL' }
    }],
    BillingMode: 'PAY_PER_REQUEST'
}

// Query GSI: All resources for tenant T001, customer C001
const resources = await docClient.send(new QueryCommand({
    TableName: 'SaasData',
    IndexName: 'TenantCustomerIndex',
    KeyConditionExpression: 'tenantId = :tenant AND customerId = :customer',
    ExpressionAttributeValues: {
        ':tenant': 'T001',
        ':customer': 'C001'
    }
}));

// Query GSI: Specific resource type for tenant/customer
const documents = await docClient.send(new QueryCommand({
    TableName: 'SaasData',
    IndexName: 'TenantCustomerIndex',
    KeyConditionExpression: 'tenantId = :tenant AND customerId = :customer AND resourceType = :type',
    ExpressionAttributeValues: {
        ':tenant': 'T001',
        ':customer': 'C001',
        ':type': 'document'
    }
}));
```

**Benefícios:** consultas eficientes no contexto locatário-cliente e na organização natural dos dados.

### Transações financeiras
<a name="GSI.DesignPattern.MultiAttributeKeys.Financial"></a>

Sistema bancário monitorando transações de contas usando GSIs

#### Exemplo de código
<a name="w2aac19c13c45c23b9c13c13b5b1"></a>

```
// Table design
{
    TableName: 'BankTransactions',
    // Base table: Simple partition key
    KeySchema: [
        { AttributeName: 'transactionId', KeyType: 'HASH' }
    ],
    AttributeDefinitions: [
        { AttributeName: 'transactionId', AttributeType: 'S' },
        { AttributeName: 'accountId', AttributeType: 'S' },
        { AttributeName: 'year', AttributeType: 'S' },
        { AttributeName: 'month', AttributeType: 'S' },
        { AttributeName: 'day', AttributeType: 'S' },
        { AttributeName: 'transactionType', AttributeType: 'S' }
    ],
    GlobalSecondaryIndexes: [
        {
            IndexName: 'AccountTimeIndex',
            KeySchema: [
                { AttributeName: 'accountId', KeyType: 'HASH' },
                { AttributeName: 'year', KeyType: 'RANGE' },
                { AttributeName: 'month', KeyType: 'RANGE' },
                { AttributeName: 'day', KeyType: 'RANGE' },
                { AttributeName: 'transactionId', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        },
        {
            IndexName: 'TransactionTypeIndex',
            KeySchema: [
                { AttributeName: 'accountId', KeyType: 'HASH' },
                { AttributeName: 'transactionType', KeyType: 'RANGE' },
                { AttributeName: 'year', KeyType: 'RANGE' },
                { AttributeName: 'month', KeyType: 'RANGE' }
            ],
            Projection: { ProjectionType: 'ALL' }
        }
    ],
    BillingMode: 'PAY_PER_REQUEST'
}

// Query AccountTimeIndex GSI: All transactions for account in 2023
const yearTransactions = await docClient.send(new QueryCommand({
    TableName: 'BankTransactions',
    IndexName: 'AccountTimeIndex',
    KeyConditionExpression: 'accountId = :account AND #year = :year',
    ExpressionAttributeNames: { '#year': 'year' },
    ExpressionAttributeValues: {
        ':account': 'ACC-12345',
        ':year': '2023'
    }
}));

// Query AccountTimeIndex GSI: Transactions in specific month
const monthTransactions = await docClient.send(new QueryCommand({
    TableName: 'BankTransactions',
    IndexName: 'AccountTimeIndex',
    KeyConditionExpression: 'accountId = :account AND #year = :year AND #month = :month',
    ExpressionAttributeNames: { '#year': 'year', '#month': 'month' },
    ExpressionAttributeValues: {
        ':account': 'ACC-12345',
        ':year': '2023',
        ':month': '11'
    }
}));

// Query TransactionTypeIndex GSI: Deposits in 2023
const deposits = await docClient.send(new QueryCommand({
    TableName: 'BankTransactions',
    IndexName: 'TransactionTypeIndex',
    KeyConditionExpression: 'accountId = :account AND transactionType = :type AND #year = :year',
    ExpressionAttributeNames: { '#year': 'year' },
    ExpressionAttributeValues: {
        ':account': 'ACC-12345',
        ':type': 'deposit',
        ':year': '2023'
    }
}));
```

## Exemplo completo
<a name="GSI.DesignPattern.MultiAttributeKeys.CompleteExample"></a>

O exemplo a seguir demonstra chaves de vários atributos, da configuração à limpeza:

### Exemplo de código
<a name="w2aac19c13c45c23b9c15b5b1"></a>

```
import { 
    DynamoDBClient, 
    CreateTableCommand, 
    DeleteTableCommand, 
    waitUntilTableExists 
} from "@aws-sdk/client-dynamodb";
import { 
    DynamoDBDocumentClient, 
    PutCommand, 
    QueryCommand 
} from "@aws-sdk/lib-dynamodb";

const client = new DynamoDBClient({ region: 'us-west-2' });
const docClient = DynamoDBDocumentClient.from(client);

async function multiAttributeKeysDemo() {
    console.log("Starting Multi-Attribute GSI Keys Demo\n");
    
    // Step 1: Create table with GSIs using multi-attribute keys
    console.log("1. Creating table with multi-attribute GSI keys...");
    await client.send(new CreateTableCommand({
        TableName: 'TournamentMatches',
        KeySchema: [
            { AttributeName: 'matchId', KeyType: 'HASH' }
        ],
        AttributeDefinitions: [
            { AttributeName: 'matchId', AttributeType: 'S' },
            { AttributeName: 'tournamentId', AttributeType: 'S' },
            { AttributeName: 'region', AttributeType: 'S' },
            { AttributeName: 'round', AttributeType: 'S' },
            { AttributeName: 'bracket', AttributeType: 'S' },
            { AttributeName: 'player1Id', AttributeType: 'S' },
            { AttributeName: 'matchDate', AttributeType: 'S' }
        ],
        GlobalSecondaryIndexes: [
            {
                IndexName: 'TournamentRegionIndex',
                KeySchema: [
                    { AttributeName: 'tournamentId', KeyType: 'HASH' },
                    { AttributeName: 'region', KeyType: 'HASH' },
                    { AttributeName: 'round', KeyType: 'RANGE' },
                    { AttributeName: 'bracket', KeyType: 'RANGE' },
                    { AttributeName: 'matchId', KeyType: 'RANGE' }
                ],
                Projection: { ProjectionType: 'ALL' }
            },
            {
                IndexName: 'PlayerMatchHistoryIndex',
                KeySchema: [
                    { AttributeName: 'player1Id', KeyType: 'HASH' },
                    { AttributeName: 'matchDate', KeyType: 'RANGE' },
                    { AttributeName: 'round', KeyType: 'RANGE' }
                ],
                Projection: { ProjectionType: 'ALL' }
            }
        ],
        BillingMode: 'PAY_PER_REQUEST'
    }));
    
    await waitUntilTableExists({ client, maxWaitTime: 120 }, { TableName: 'TournamentMatches' });
    console.log("Table created\n");
    
    // Step 2: Insert tournament matches
    console.log("2. Inserting tournament matches...");
    const matches = [
        { matchId: 'match-001', tournamentId: 'WINTER2024', region: 'NA-EAST', round: 'FINALS', bracket: 'CHAMPIONSHIP', player1Id: '101', player2Id: '103', matchDate: '2024-01-20', winner: '101', score: '3-1' },
        { matchId: 'match-002', tournamentId: 'WINTER2024', region: 'NA-EAST', round: 'SEMIFINALS', bracket: 'UPPER', player1Id: '101', player2Id: '105', matchDate: '2024-01-18', winner: '101', score: '3-2' },
        { matchId: 'match-003', tournamentId: 'WINTER2024', region: 'NA-WEST', round: 'FINALS', bracket: 'CHAMPIONSHIP', player1Id: '102', player2Id: '104', matchDate: '2024-01-20', winner: '102', score: '3-2' },
        { matchId: 'match-004', tournamentId: 'SPRING2024', region: 'NA-EAST', round: 'QUARTERFINALS', bracket: 'UPPER', player1Id: '101', player2Id: '108', matchDate: '2024-03-15', winner: '101', score: '3-0' }
    ];
    
    for (const match of matches) {
        await docClient.send(new PutCommand({ TableName: 'TournamentMatches', Item: match }));
    }
    console.log(`Inserted ${matches.length} tournament matches\n`);
    
    // Step 3: Query GSI with multi-attribute partition key
    console.log("3. Query TournamentRegionIndex GSI: WINTER2024/NA-EAST matches");
    const gsiQuery1 = await docClient.send(new QueryCommand({
        TableName: 'TournamentMatches',
        IndexName: 'TournamentRegionIndex',
        KeyConditionExpression: 'tournamentId = :tournament AND #region = :region',
        ExpressionAttributeNames: { '#region': 'region' },
        ExpressionAttributeValues: { ':tournament': 'WINTER2024', ':region': 'NA-EAST' }
    }));
    
    console.log(`  Found ${gsiQuery1.Items.length} matches:`);
    gsiQuery1.Items.forEach(match => {
        console.log(`    ${match.round} - ${match.bracket} - ${match.winner} won`);
    });
    
    // Step 4: Query GSI with multi-attribute sort key
    console.log("\n4. Query PlayerMatchHistoryIndex GSI: All matches for Player 101");
    const gsiQuery2 = await docClient.send(new QueryCommand({
        TableName: 'TournamentMatches',
        IndexName: 'PlayerMatchHistoryIndex',
        KeyConditionExpression: 'player1Id = :player',
        ExpressionAttributeValues: { ':player': '101' }
    }));
    
    console.log(`  Found ${gsiQuery2.Items.length} matches for Player 101:`);
    gsiQuery2.Items.forEach(match => {
        console.log(`    ${match.tournamentId}/${match.region} - ${match.matchDate} - ${match.round}`);
    });
    
    console.log("\nDemo complete");
    console.log("No synthetic keys needed - GSIs use native attributes automatically");
}

async function cleanup() {
    console.log("Deleting table...");
    await client.send(new DeleteTableCommand({ TableName: 'TournamentMatches' }));
    console.log("Table deleted");
}

// Run demo
multiAttributeKeysDemo().catch(console.error);

// Uncomment to cleanup:
// cleanup().catch(console.error);
```

**Estrutura de código mínima**

### Exemplo de código
<a name="w2aac19c13c45c23b9c15b9b1"></a>

```
// 1. Create table with GSI using multi-attribute keys
await client.send(new CreateTableCommand({
    TableName: 'MyTable',
    KeySchema: [
        { AttributeName: 'id', KeyType: 'HASH' }        // Simple base table PK
    ],
    AttributeDefinitions: [
        { AttributeName: 'id', AttributeType: 'S' },
        { AttributeName: 'attr1', AttributeType: 'S' },
        { AttributeName: 'attr2', AttributeType: 'S' },
        { AttributeName: 'attr3', AttributeType: 'S' },
        { AttributeName: 'attr4', AttributeType: 'S' }
    ],
    GlobalSecondaryIndexes: [{
        IndexName: 'MyGSI',
        KeySchema: [
            { AttributeName: 'attr1', KeyType: 'HASH' },    // GSI PK attribute 1
            { AttributeName: 'attr2', KeyType: 'HASH' },    // GSI PK attribute 2
            { AttributeName: 'attr3', KeyType: 'RANGE' },   // GSI SK attribute 1
            { AttributeName: 'attr4', KeyType: 'RANGE' }    // GSI SK attribute 2
        ],
        Projection: { ProjectionType: 'ALL' }
    }],
    BillingMode: 'PAY_PER_REQUEST'
}));

// 2. Insert items with native attributes (no concatenation needed for GSI)
await docClient.send(new PutCommand({
    TableName: 'MyTable',
    Item: {
        id: 'item-001',
        attr1: 'value1',
        attr2: 'value2',
        attr3: 'value3',
        attr4: 'value4',
        // ... other attributes
    }
}));

// 3. Query GSI with all partition key attributes
await docClient.send(new QueryCommand({
    TableName: 'MyTable',
    IndexName: 'MyGSI',
    KeyConditionExpression: 'attr1 = :v1 AND attr2 = :v2',
    ExpressionAttributeValues: {
        ':v1': 'value1',
        ':v2': 'value2'
    }
}));

// 4. Query GSI with sort key attributes (left-to-right)
await docClient.send(new QueryCommand({
    TableName: 'MyTable',
    IndexName: 'MyGSI',
    KeyConditionExpression: 'attr1 = :v1 AND attr2 = :v2 AND attr3 = :v3',
    ExpressionAttributeValues: {
        ':v1': 'value1',
        ':v2': 'value2',
        ':v3': 'value3'
    }
}));

// Note: If any attribute name is a DynamoDB reserved keyword, use ExpressionAttributeNames:
// KeyConditionExpression: 'attr1 = :v1 AND #attr2 = :v2'
// ExpressionAttributeNames: { '#attr2': 'attr2' }
```

## Recursos adicionais
<a name="GSI.DesignPattern.MultiAttributeKeys.AdditionalResources"></a>
+ [Práticas recomendadas do DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/best-practices.html)
+ [Trabalho com tabelas e dados](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithTables.html)
+ [Índices secundários globais](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html)
+ [Operações de consulta e verificação](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html)

# Gerenciar índices secundários globais no DynamoDB
<a name="GSI.OnlineOps"></a>

Esta seção descreve como criar, modificar e excluir índices secundários globais no Amazon DynamoDB.

**Topics**
+ [Criar uma tabela com índices secundários globais](#GSI.Creating)
+ [Descrever os índices secundários globais em uma tabela](#GSI.Describing)
+ [Adicionar um índice secundário global a uma tabela](#GSI.OnlineOps.Creating)
+ [Exclusão de um índice secundário global](#GSI.OnlineOps.Deleting)
+ [Modificar um índice secundário global durante a criação](#GSI.OnlineOps.Creating.Modify)

## Criar uma tabela com índices secundários globais
<a name="GSI.Creating"></a>

Para criar uma tabela com um ou mais índices secundários globais, use a operação `CreateTable` com o parâmetro `GlobalSecondaryIndexes`. Para obter a flexibilidade máxima de consultas, é possível criar até 20 índices secundários globais (cota padrão) por tabela. 

Você deve especificar um atributo que não atue como chave de partição do índice. Como opção, você pode especificar outro atributo para a chave de classificação do índice. Não é necessário que nenhum desses atributos de chave seja o mesmo que um atributo de chave na tabela. Por exemplo, na tabela *GameScores* (consulte [Como usar índices secundários globais no DynamoDB](GSI.md)), nem `TopScore` nem `TopScoreDateTime` são atributos chave. Você poderia criar um índice secundário global com uma chave de partição `TopScore` e uma chave de classificação `TopScoreDateTime`. Um índice desse tipo pode ser usado para determinar se há uma correlação entre pontuações altas e a hora do dia em que um jogo é jogado.

Cada atributo de chave de índice deve ser um escalar do tipo `String`, `Number` ou `Binary`. (Ele não pode ser um documento ou um conjunto.) Você pode projetar atributos de qualquer tipo de dados em um índice secundário global. Isso inclui escalares, documentos e conjuntos. Para obter uma lista completa de tipos de dados, consulte [Tipos de dados](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes).

Se estiver usando o modo provisionado, você deve fornecer configurações de `ProvisionedThroughput` para o índice, formadas por `ReadCapacityUnits` e `WriteCapacityUnits`. Essas configurações de throughput provisionado são distintas daquelas na tabela, mas se comportam de forma semelhante. Para obter mais informações, consulte [Considerações sobre throughput provisionado para índices secundários globais](GSI.md#GSI.ThroughputConsiderations).

 Índices secundários globais herdam o modo de capacidade leitura/gravação da tabela base. Para obter mais informações, consulte [Considerações ao alternar os modos de capacidade no DynamoDB](bp-switching-capacity-modes.md). 

**nota**  
 Ao criar um novo GSI, pode ser importante conferir se a escolha de chave de partição está gerando uma distribuição desigual ou estreita de dados ou tráfego nos valores de chave de partição do novo índice. Se isso ocorrer, você pode estar vendo operações de provisionamento e de gravação ocorrendo ao mesmo tempo e restringindo as gravações na tabela base. O serviço toma medidas para minimizar o potencial desse caso, mas não tem insights sobre o formato dos dados do cliente em relação à chave de partição de índice, à projeção escolhida ou à dispersão da chave primária do índice.  
Se você suspeitar que o novo índice secundário global tenha dados estreitos ou distorcidos ou distribuição de tráfego entre valores de chave de partição, considere o seguinte antes de adicionar novos índices a tabelas operacionalmente importantes.  
Talvez seja mais seguro adicionar o índice no momento em que a aplicação está gerando a menor quantidade de tráfego.
Considere habilitar o CloudWatch Contributor Insights em sua tabela e índices de base. Isso lhe proporcionará um valioso insight sobre sua distribuição de tráfego.
 Observe as métricas `WriteThrottleEvents`, `ThrottledRequests` e `OnlineIndexPercentageProgress` do CloudWatch em todo o processo. Ajuste a capacidade de gravação provisionada conforme necessário para concluir o provisionamento em tempo razoável, sem efeitos consideráveis de controle de utilização nas operações em andamento. `OnlineIndexConsumedWriteCapacity` e `OnlineThrottleEvents` devem mostrar 0 durante o preenchimento de índice.
Prepare-se para cancelar a criação do índice, em caso de impacto operacional devido ao controle de utilização de gravação.

## Descrever os índices secundários globais em uma tabela
<a name="GSI.Describing"></a>

Para ver o status de todos os índices secundários globais em uma tabela, use a operação `DescribeTable`. A parte `GlobalSecondaryIndexes` da resposta mostra todos os índices na tabela, juntamente com o status atual de cada (`IndexStatus`).

O `IndexStatus` para um índice secundário global será um dos seguintes:
+ `CREATING`: o índice está sendo criado e ainda não está disponível para uso.
+ `ACTIVE`: o índice está pronto para uso e as aplicações podem executar operações `Query` no índice.
+ `UPDATING`: as configurações de throughput provisionado do índice estão sendo alteradas.
+ `DELETING`: o índice está sendo excluído e não pode mais ser usado.

Quando o DynamoDB tiver terminado de criar um índice secundário global, o status do índice mudará de `CREATING` para `ACTIVE`.

## Adicionar um índice secundário global a uma tabela
<a name="GSI.OnlineOps.Creating"></a>

Para adicionar um índice secundário global a uma tabela existente, use a operação `UpdateTable` com o parâmetro `GlobalSecondaryIndexUpdates`. Você deve fornecer o seguinte:
+ Um nome de índice. O nome deve ser exclusivo entre todos os índices na tabela.
+ O esquema de chave do índice. É necessário especificar um atributo para a chave da partição de índice, mas existe a opção de especificar outro atributo para a chave de classificação de índice. Não é necessário que nenhum desses atributos de chave seja o mesmo que um atributo de chave na tabela. Os tipos de dados de cada atributo de esquema devem ser escalares: `String`, `Number` ou `Binary`.
+ Os atributos a serem projetados da tabela para o índice:
  + `KEYS_ONLY`: cada item do índice consiste apenas nos valores de chaves de partição e nas chaves de classificação da tabela, além dos valores de chaves do índice. 
  + `INCLUDE`: além dos atributos descritos em `KEYS_ONLY`, o índice secundário inclui outros atributos não chave que você especificar.
  + `ALL`:  o índice inclui todos os atributos da tabela de origem.
+ As configurações do throughput provisionado para o índice, formadas por `ReadCapacityUnits` e `WriteCapacityUnits`. Essas configurações de throughput provisionado são distintas daquelas na tabela.

Você pode criar somente um índice secundário global por operação `UpdateTable`.

### Fases da criação de um índice
<a name="GSI.OnlineOps.Creating.Phases"></a>

Quando você adiciona um novo índice secundário global a uma tabela existente, ela continua a estar disponível enquanto o índice está sendo construído. No entanto, o novo índice apenas estará disponível para operações de consulta quando seu status mudar de `CREATING` para `ACTIVE`.

**nota**  
A criação do índice secundário global não usa o Application Auto Scaling. Aumentar a capacidade `MIN` do Application Auto Scaling não diminuirá o tempo de criação do índice secundário global.

Nos bastidores, o DynamoDB constrói o índice em duas fases:

**Alocação de recursos**  
O DynamoDB aloca os recursos de computação e de armazenamento que são necessários para a construção do índice.  
Durante a fase de alocação de recursos, o atributo `IndexStatus` é `CREATING`, enquanto o atributo `Backfilling` é false. Use a operação `DescribeTable` para recuperar o status de uma tabela e todos os índices secundários dela.  
Enquanto o índice estiver na fase de alocação de recurso, você poderá excluí-lo ou a apagar a tabela principal dele. Você também não pode modificar o throughput provisionado do índice ou da tabela. Não é possível adicionar ou excluir outros índices na tabela. No entanto, você pode modificar o throughput provisionado desses outros índices.

**Aterramento**  
Para cada item na tabela, o DynamoDB determina qual conjunto de atributos deve ser gravado no índice com base em sua projeção (`KEYS_ONLY`, `INCLUDE` ou `ALL`). Em seguida, ele grava esses atributos no índice. Durante a fase de preenchimento, o DynamoDB rastreia os itens que estão sendo adicionados, excluídos ou atualizados na tabela. Os atributos desses itens também são adicionados, excluídos ou atualizados no índice conforme apropriado.  
Durante a fase de preenchimento, o atributo `IndexStatus` é definido como `CREATING`, e o atributo `Backfilling` é verdadeiro. Use a operação `DescribeTable` para recuperar o status de uma tabela e todos os índices secundários dela.  
Enquanto o índice está em processo de aterramento, não é possível excluir a tabela principal. No entanto, ainda é possível excluir o índice ou modificar o throughput provisionado da tabela e de qualquer um dos seus índices secundários globais.  
Durante a fase de aterramento, algumas gravações de itens infratores podem ter sucesso, enquanto outras serão rejeitadas. Após o aterramento, todas as gravações de itens que violarem o esquema de chaves do novo índice serão rejeitadas. Recomendamos executar a ferramenta Violation Detector após a conclusão da fase de preenchimento em segundo plano, para detectar e resolver qualquer infração de chave que possa ter ocorrido. Para obter mais informações, consulte [Detectar e corrigir violações de chave de índice no DynamoDB](GSI.OnlineOps.ViolationDetection.md).

Enquanto as fases de alocação de recurso e aterramento estão em andamento, o índice permanece no estado `CREATING`. Enquanto isso, o DynamoDB executa operações de leitura na tabela. Você não é cobrado pelas operações de leitura da tabela-base para preencher o índice secundário global.

Quando a construção do índice estiver concluída, seu status mudará para `ACTIVE`. Você não pode executar `Query` ou `Scan` no índice até que ele esteja no modo `ACTIVE`.

**nota**  
Em alguns casos, o DynamoDB não pode gravar dados da tabela no índice devido a violações de chaves de índice. Isso poderá ocorrer se:  
O tipo de dados de um valor de atributo não corresponder ao tipo de dados de um esquema de chaves de índice.
O tamanho de um atributo excede o tamanho máximo de um atributo de chave de índice.
Um atributo de chave de índice tem um valor binários ou de string vazio.
Violações de chaves de índice não interferem na criação do índice secundário global. No entanto, quando o índice ficar `ACTIVE`, as chaves infratoras não estarão presentes no índice.  
O DynamoDB fornece uma ferramenta autônoma para localizar e resolver esses problemas. Para obter mais informações, consulte [Detectar e corrigir violações de chave de índice no DynamoDB](GSI.OnlineOps.ViolationDetection.md).

### Adicionar um índice secundário global a uma tabela grande
<a name="GSI.OnlineOps.Creating.LargeTable"></a>

O tempo necessário para a construção de um índice secundário global depende de vários fatores, entre eles:
+ O tamanho da tabela
+ O número de itens na tabela que se qualificam para inclusão no índice
+ O número de atributos projetados no índice
+ A atividade de gravação na tabela principal durante a criação dos índices

Se você estiver adicionando um índice secundário global a uma tabela muito grande, a conclusão do processo de criação poderá ser demorada. Para monitorar o progresso e determinar se o índice tem capacidade de gravação suficiente, consulte as seguintes métricas do Amazon CloudWatch:
+ `OnlineIndexPercentageProgress`

Para obter mais informações sobre as métricas do CloudWatch relacionadas ao DynamoDB, consulte [Métricas do DynamoDB](metrics-dimensions.md#dynamodb-metrics).

**Importante**  
Talvez seja necessário incluir tabelas muito grandes na lista de permissões antes de criar ou atualizar um índice secundário global. Entre em contato com o AWS Support para incluir tabelas na lista de permissões.

Enquanto um índice está sendo preenchido em segundo plano, o DynamoDB usa a capacidade do sistema interno para fazer leituras na tabela. Isso é feito para minimizar o impacto da criação do índice e garantir que sua tabela não fique sem capacidade de leitura.

## Exclusão de um índice secundário global
<a name="GSI.OnlineOps.Deleting"></a>

Se você não precisa mais de um índice secundário global, pode excluí-lo usando a operação `UpdateTable`.

É possível excluir somente um índice secundário global por operação `UpdateTable`.

Enquanto o índice secundário global está sendo excluído, não há efeitos sobre atividades de leitura ou de gravação na tabela principal. Enquanto a exclusão está em andamento, você ainda pode modificar o throughput provisionado em outros índices.

**nota**  
Quando você exclui uma tabela usando a ação `DeleteTable`, todos os índices secundários globais nessa tabela também são excluídos.
Sua conta não será cobrada pela operação de exclusão do índice secundário global.

## Modificar um índice secundário global durante a criação
<a name="GSI.OnlineOps.Creating.Modify"></a>

Enquanto um índice está sendo construído, é possível usar a operação `DescribeTable` para determinar em qual fase ele se encontra. A descrição do índice inclui um atributo booleano, `Backfilling`, para indicar se o DynamoDB está carregando o índice com itens da tabela no momento. Se `Backfilling` for verdadeiro, a fase de alocação de recursos estará concluída, e o índice agora estará sendo preenchido. 

Durante a fase de aterramento, é possível excluir o índice que está sendo criado. Durante essa fase, não é possível adicionar ou excluir outros índices na tabela.

**nota**  
Para índices que foram criados como parte de uma operação `CreateTable`, o atributo `Backfilling` não aparece na saída de `DescribeTable`. Para obter mais informações, consulte [Fases da criação de um índice](#GSI.OnlineOps.Creating.Phases).

# Detectar e corrigir violações de chave de índice no DynamoDB
<a name="GSI.OnlineOps.ViolationDetection"></a>

Durante a fase de preenchimento em segundo plano da criação de um índice secundário global, o Amazon DynamoDB examina cada item na tabela para determinar se ele está qualificado para inclusão no índice. Alguns itens podem não ser qualificados, pois causariam violações de chave de índice. Nesses casos, os itens permanecerão na tabela, mas o índice não terá uma entrada correspondente para eles.

Uma *infração na chave do índice* ocorre nas seguintes situações:
+ Há uma inconsistência de tipo de dados entre um valor de atributo e o tipo de dados do esquema de chaves de índice. Por exemplo, suponha que um dos itens na tabela `GameScores` tivesse um valor `TopScore` do tipo `String`. Se você adicionasse um `TopScore` com uma chave de partição de do tipo `Number`, o item da tabela violaria a chave de índice.
+ Um valor de atributo da tabela excede o comprimento máximo para um atributo de chave de índice. O comprimento máximo de uma chave de partição é 2048 bytes, enquanto o comprimento máximo de uma chave de classificação é 1024 bytes. Se qualquer um dos valores de atributo correspondente na tabela exceder esses limites, o item da tabela violará a chave de índice.

**nota**  
Se um valor de atributo binário ou de string for definido para um atributo usado como uma chave de índice, o valor do atributo deverá ter um tamanho maior que zero. Caso contrário, o item da tabela violaria a chave de índice.  
Esta ferramenta não sinaliza esta infração na chave do índice neste momento.

Se uma infração na chave do índice ocorrer, a fase de preenchimento em segundo plano continuará sem interrupção. No entanto, os itens infratores não são incluídos no índice. Concluída a fase de aterramento, todas as gravações em itens que violam o esquema de chaves do novo índice serão rejeitadas.

Para identificar e corrigir os valores de atributos em uma tabela que violam uma chave de índice, use a ferramenta Violation Detector. Para executar o Violation Detector, crie um arquivo de configuração que especifique o nome de uma tabela a ser verificada, os nomes e os tipos de dados da chave de partição do índice secundário global e a chave de classificação, bem como quais ações deverão ser realizadas se violações de chave de índice forem encontradas. O Violation Detector pode ser executado em um destes dois diferentes modos:
+ **Modo de detecção**: detecta violações de chave de índice. Use o modo de detecção para informar os itens na tabela que causariam violações de chaves em um índice secundário global. (Você tem a opção de solicitar que esses itens de tabela infratores sejam excluídos imediatamente quando forem encontrados.) A saída do modo de detecção é gravada em um arquivo, que você pode usar para análise posterior.
+ **Modo de correção**: corrige violações de chaves de índice. No modo de correção, o Violation Detector lê um arquivo de entrada com o mesmo formato que o arquivo de saída do modo de detecção. O modo de correção lê os registros do arquivo de entrada e, para cada registro, ele exclui ou atualiza os itens correspondentes na tabela. (Observe que, se você optar por atualizar os itens, deverá editar o arquivo de entrada e definir os valores apropriados para essas atualizações.)

## Baixar e executar o Violation Detector
<a name="GSI.OnlineOps.ViolationDetection.Running"></a>

O Violation Detector está disponível como um arquivo Java executável (arquivo `.jar`) e pode ser executado em computadores Windows, Mac ou Linux. O Violation Detector requer o Java 1.7 (ou posterior) e o Apache Maven.
+ [Baixar o Violation Detector do GitHub](https://github.com/awslabs/dynamodb-online-index-violation-detector)

Siga as instruções no arquivo `README.md` para fazer download e instalar o Violation Detector usando o Maven.

Para iniciar o Violation Detector, acesse o diretório no qual você compilou o arquivo `ViolationDetector.java` e insira o seguinte comando:

```
java -jar ViolationDetector.jar [options]
```

A linha de comando do Violation Detector aceita as seguintes opções:
+ `-h | --help`: imprime um resumo de uso e opções do Violation Detector.
+ `-p | --configFilePath` `value`: o nome totalmente qualificado de um arquivo de configuração do Violation Detector. Para obter mais informações, consulte [O arquivo de configuração do Violation Detector](#GSI.OnlineOps.ViolationDetection.ConfigFile).
+ `-t | --detect` `value`: detecta violações de chave de índice na tabela e as grava no arquivo de saída do Violation Detector. Se o valor desse parâmetro for definido como `keep`, os itens com violações de chave não serão modificados. Se o valor for definido como `delete`, os itens com violações de chave serão excluídos da tabela.
+ `-c | --correct` `value`: lê violações de chave de índice de um arquivo de entrada e toma ações corretivas nos itens da tabela. Se o valor desse parâmetro for definido como `update`, os itens com violações de chave serão atualizados com valores novos não infratores. Se o valor for definido como `delete`, os itens com violações de chave serão excluídos da tabela.

## O arquivo de configuração do Violation Detector
<a name="GSI.OnlineOps.ViolationDetection.ConfigFile"></a>

Em tempo de execução, a ferramenta Violation Detector requer um arquivo de configuração Os parâmetros nesse arquivo determinam quais recursos do DynamoDB o Violation Detector pode acessar e quanto throughput provisionado ele pode consumir. A tabela a seguir descreve esses parâmetros.


****  

| Nome do parâmetro | Descrição | Obrigatório? | 
| --- | --- | --- | 
|  `awsCredentialsFile`  |  O nome totalmente qualificado de um arquivo que contém suas credenciais da AWS. O arquivo de credenciais deve estar no seguinte formato: <pre>accessKey = access_key_id_goes_here<br />secretKey = secret_key_goes_here </pre>  |  Sim  | 
|  `dynamoDBRegion`  |  A região da AWS na qual a tabela reside. Por exemplo: `us-west-2`.  |  Sim  | 
|  `tableName`  | O nome da tabela do DynamoDB a ser verificada. |  Sim  | 
|  `gsiHashKeyName`  |  O nome da chave de partição do índice.  |  Sim  | 
|  `gsiHashKeyType`  |  O tipo de dados da chave de partição do índice: `String`, `Number` ou `Binary` `S \| N \| B`  |  Sim  | 
|  `gsiRangeKeyName`  |  O nome da chave de classificação do índice. Não especifique esse parâmetro se o índice tiver apenas uma chave primária simples (chave de partição).  |  Não  | 
|  `gsiRangeKeyType`  |  O tipo de dados da chave de classificação do índice: `String`, `Number` ou `Binary` `S \| N \| B`  Não especifique esse parâmetro se o índice tiver apenas uma chave primária simples (chave de partição).  |  Não  | 
|  `recordDetails`  |  Se você deseja gravar os detalhes completos das violações de chaves de índice no arquivo de saída. Se a opção for definida como `true` (o padrão), todas as informações sobre os itens infratores serão relatadas. Se definido como `false`, apenas o número de violações será relatado.  |  Não  | 
|  `recordGsiValueInViolationRecord`  |  Se você deseja gravar os valores das chaves de índice infratoras no arquivo de saída. Se a opção for definida como `true` (padrão), os valores das chaves serão relatados. Se definido como `false`, os valores das chave não serão relatados.  |  Não  | 
|  `detectionOutputPath`  |  O caminho completo do arquivo de saída do Violation Detector. Esse parâmetro oferece suporte à gravação em um diretório local ou no Amazon Simple Storage Service (Amazon S3). Veja os exemplos a seguir: `detectionOutputPath = ``//local/path/filename.csv` `detectionOutputPath = ``s3://bucket/filename.csv` As informações no arquivo de saída são mostradas no formato CSV (valores separados por vírgula). Se você não definir `detectionOutputPath`, o arquivo de saída se chamará `violation_detection.csv` e será gravado no seu diretório de trabalho atual.  |  Não  | 
|  `numOfSegments`  | O número de segmentos de verificação paralela a serem usados quando o Violation Detector verificar a tabela. O valor padrão é 1, o que significa que a tabela é verificada de maneira sequencial. Se o valor for 2 ou superior, o Violation Detector dividirá a tabela no número correspondente de segmentos lógicos e em um número igual de threads de verificação. A configuração máxima para `numOfSegments` é 4096.Para tabelas maiores, uma verificação paralela é geralmente mais rápida do que uma verificação sequencial. Além disso, se a tabela for grande o suficiente para abranger várias partições, uma verificação paralela distribui sua atividade de leitura uniformemente entre essas várias partições.Para obter mais informações sobre verificações paralelas no DynamoDB, consulte [Verificar em paralelo](Scan.md#Scan.ParallelScan). |  Não  | 
|  `numOfViolations`  |  O limite superior de violações de chaves de índice a serem gravadas no arquivo de saída. Se definido como `-1` (o padrão), a tabela inteira é verificada. Se definido como um número inteiro positivo, o Violation Detector será interrompido quando encontrar esse número de violações.  |  Não  | 
|  `numOfRecords`  |  O número de itens na tabela a ser verificada. Se definido como -1 (o padrão), a tabela inteira é verificada. Se definido como um número inteiro positivo, o Violation Detector será interrompido após verificar o mesmo número de itens na tabela.  |  Não  | 
|  `readWriteIOPSPercent`  |  Regula a porcentagem de unidades de capacidade de leitura provisionada que são consumidas durante a verificação de tabela. Os valores válidos variam de `1` até `100`. O valor padrão (`25`) significa que o Violation Detector não consumirá mais de 25% do throughput provisionado da tabela.  |  Não  | 
|  `correctionInputPath`  |  O caminho completo do arquivo de entrada de correção do Violation Detector. Se você executar o Violation Detector no modo de correção, o conteúdo desse arquivo será usado para modificar ou excluir os itens de dados na tabela que violam o índice secundário global. O formato do arquivo `correctionInputPath` é o mesmo que o do arquivo `detectionOutputPath`. Isso permite que você processe a saída do modo de detecção como uma entrada no modo de correção.  |  Não  | 
|  `correctionOutputPath`  |  O caminho completo do arquivo de saída de correção do Violation Detector. Esse arquivo será criado somente se houver erros de atualização. Esse parâmetro oferece suporte à gravação em um diretório local ou no Amazon S3. Veja os exemplos a seguir: `correctionOutputPath = ``//local/path/filename.csv` `correctionOutputPath = ``s3://bucket/filename.csv` As informações no arquivo de saída são mostradas no formato CSV. Se você não definir `correctionOutputPath`, o arquivo de saída se chamará `violation_update_errors.csv` e será gravado no seu diretório de trabalho atual.  |  Não  | 

## Detecção
<a name="GSI.OnlineOps.ViolationDetection.Detection"></a>

Para detectar violações de chave de índice, use o Violation Detector com a opção de linha de comando `--detect`. Para mostrar como essa opção funciona, considere a tabela `ProductCatalog`. Veja a seguir uma lista de itens na tabela. Apenas a chave primária (`Id`) e o atributo `Price` são mostrados.


****  

| ID (chave primária) | Preço | 
| --- | --- | 
| 101 |  5  | 
| 102 |  20  | 
| 103 | 200  | 
| 201 |  100  | 
| 202 |  200  | 
| 203 |  300  | 
| 204 |  400  | 
| 205 |  500  | 

Todos os valores para `Price` são do tipo `Number`. No entanto, como o DynamoDB não tem esquema, é possível adicionar um item com um não numérico `Price`. Por exemplo, suponha que adicionemos outro item à tabela `ProductCatalog`:


****  

| ID (chave primária) | Preço | 
| --- | --- | 
| 999 | "Hello" | 

Agora, a tabela tem um total de nove itens.

Agora você adiciona um novo índice secundário global à tabela: `PriceIndex`. A chave primária desse índice é uma chave de partição, `Price`, do tipo `Number`. Depois que o índice tiver sido construído, ele conterá oito itens, mas a tabela `ProductCatalog` tem nove itens. O motivo dessa discrepância é que o valor `"Hello"` é do tipo `String`, mas `PriceIndex` tem uma chave primária do tipo `Number`. O valor de `String` viola a chave do índice secundário global e, por isso, não está presente no índice.

Para usar o Violation Detector nesse cenário, você primeiro deve criar um arquivo de configuração como este.

```
# Properties file for violation detection tool configuration.
# Parameters that are not specified will use default values.

awsCredentialsFile = /home/alice/credentials.txt
dynamoDBRegion = us-west-2
tableName = ProductCatalog
gsiHashKeyName = Price
gsiHashKeyType = N
recordDetails = true
recordGsiValueInViolationRecord = true
detectionOutputPath = ./gsi_violation_check.csv
correctionInputPath = ./gsi_violation_check.csv
numOfSegments = 1
readWriteIOPSPercent = 40
```

Em seguida, execute o Violation Detector como no exemplo a seguir.

```
$  java -jar ViolationDetector.jar --configFilePath config.txt --detect keep

Violation detection started: sequential scan, Table name: ProductCatalog, GSI name: PriceIndex
Progress: Items scanned in total: 9,    Items scanned by this thread: 9,    Violations found by this thread: 1, Violations deleted by this thread: 0
Violation detection finished: Records scanned: 9, Violations found: 1, Violations deleted: 0, see results at: ./gsi_violation_check.csv
```

Se o parâmetro de configuração `recordDetails` for definido como `true`, o Violation Detector gravará os detalhes de cada infração no arquivo de saída, como no exemplo a seguir.

```
Table Hash Key,GSI Hash Key Value,GSI Hash Key Violation Type,GSI Hash Key Violation Description,GSI Hash Key Update Value(FOR USER),Delete Blank Attributes When Updating?(Y/N) 

999,"{""S"":""Hello""}",Type Violation,Expected: N Found: S,,
```

O arquivo de saída está no formato CSV. A primeira linha do arquivo é um cabeçalho, seguida de um registro por item que viola a chave de índice. Os campos desses registros infratores são os seguintes:
+ **Table Hash Key** (Chave de hash da tabela): o valor da chave de partição do item na tabela.
+ **Table Range Key** (Chave de classificação da tabela): o valor da chave de classificação do item na tabela.
+ **GSI Hash Key Value** (Valor da chave de hash do GSI): o valor da chave de partição do índice secundário global.
+ **GSI Hash Key Violation Type** (Tipo de violação da chave de hash do GSI): `Type Violation` ou `Size Violation`.
+ **GSI Hash Key Violation Description** (Descrição da violação da chave de hash do GSI): a causa da infração.
+ **GSI Hash Key Update Value(FORUSER)** (Valor da atualização da chave de hash do GSI [PARA USUÁRIO]): no modo de correção, um novo valor fornecido pelo usuário para o atributo.
+ **GSI Hash Key Value** (Valor da chave de hash do GSI): o valor da chave de classificação do índice secundário global.
+ **GSI Range Key Violation Type** (Tipo de violação da chave de intervalo do GSI: `Type Violation` ou `Size Violation`.
+ **GSI Range Key Violation Description** (Descrição da violação da chave de intervalo do GSI): a causa da infração.
+ **GSI Range Key Update Value(FOR USER)** (Valor da atualização da chave de intervalo do GSI [PARA USUÁRIO]: no modo de correção, um novo valor fornecido pelo usuário para o atributo.
+ **Delete Blank Attribute When Updating(Y/N)** (Excluir atributo em branco ao atualizar[S/N]): no modo de correção, determina se o item infrator deve ser excluído (Y) ou mantido (N) na tabela, mas apenas se algum dos seguintes campos estiver em branco:
  + `GSI Hash Key Update Value(FOR USER)`
  + `GSI Range Key Update Value(FOR USER)`

  Se qualquer um desses campos não estiver em branco, `Delete Blank Attribute When Updating(Y/N)` não terá efeito.

**nota**  
O formato de saída pode variar dependendo do arquivo de configuração e das opções da linha de comando. Por exemplo, se a tabela tiver uma chave primária simples (sem uma chave de classificação), nenhum campo de chave de classificação estará presente na saída.  
Os registros de infração no arquivo podem não estar na ordem classificada.

## Correção
<a name="GSI.OnlineOps.ViolationDetection.Correction"></a>

Para corrigir violações de chave de índice, use o Violation Detector com a opção de linha de comando `--correct`. No modo de correção, o Violation Detector lê o arquivo de entrada especificado pelo parâmetro `correctionInputPath`. Esse arquivo tem o mesmo formato que o arquivo `detectionOutputPath` e, portanto, você pode usar a saída da detecção como a entrada para a correção.

O Violation Detector fornece duas maneiras diferentes para corrigir violações de chave de índice:
+ **Excluir violações**: exclua os itens da tabela que possuem valores de atributo infratores.
+ **Atualizar violações**: atualize os itens da tabela substituindo os atributos infratores por valores não infratores.

Em ambos os casos, você pode usar o arquivo de saída do modo de detecção como uma entrada para o modo de correção.

Continuando com o exemplo de `ProductCatalog`, suponha que queiramos excluir o item infrator da tabela. Para fazer isso, use a seguinte linha de comando:

```
$  java -jar ViolationDetector.jar --configFilePath config.txt --correct delete
```

Neste ponto, você será solicitado a confirmar se deseja excluir os itens infratores.

```
Are you sure to delete all violations on the table?y/n
y
Confirmed, will delete violations on the table...
Violation correction from file started: Reading records from file: ./gsi_violation_check.csv, will delete these records from table.
Violation correction from file finished: Violations delete: 1, Violations Update: 0
```

Agora, tanto `ProductCatalog` quanto `PriceIndex` têm o mesmo número de itens.

# Como trabalhar com índices secundários globais: Java
<a name="GSIJavaDocumentAPI"></a>

Você pode usar a API de documentos do AWS SDK para Java para criar uma tabela do Amazon DynamoDB com um ou mais índices secundários globais na tabela e executar consultas usando os índices. 

Veja a seguir as etapas comuns para as operações de tabela. 

1. Crie uma instância da classe `DynamoDB`.

1. Forneça os parâmetros obrigatórios e opcionais para a operação, criando os objetos de solicitação correspondentes. 

1. Chame o método apropriado fornecido pelo cliente que você criou na etapa anterior. 

**Topics**
+ [Criar uma tabela com um índice secundário global](#GSIJavaDocumentAPI.CreateTableWithIndex)
+ [Descrever uma tabela com um índice secundário global](#GSIJavaDocumentAPI.DescribeTableWithIndex)
+ [Consultar um índice secundário global](#GSIJavaDocumentAPI.QueryAnIndex)
+ [Exemplo: índices secundários globais que usam a API de documento do AWS SDK para Java](GSIJavaDocumentAPI.Example.md)

## Criar uma tabela com um índice secundário global
<a name="GSIJavaDocumentAPI.CreateTableWithIndex"></a>

Você pode criar índices secundários globais ao mesmo tempo em que cria uma tabela. Para fazer isso, use `CreateTable` e forneça suas especificações para um ou mais índices secundários globais. O exemplo de código Java a seguir cria uma tabela para armazenar informações sobre dados climáticos. A chave de partição é `Location` e a chave de classificação é `Date`. Um índice secundário global chamado `PrecipIndex` permite acesso rápido aos dados de precipitação de vários locais.

Veja a seguir as etapas necessárias para criar uma tabela com um índice secundário global usando a API de documentos do DynamoDB. 

1. Crie uma instância da classe `DynamoDB`.

1. Crie uma instância da classe `CreateTableRequest` para fornecer as informações solicitadas.

   Você deve fornecer o nome da tabela, sua chave primária e os valores de throughput provisionado. Para o índice secundário global, você deve fornecer o nome do índice, suas configurações de throughput provisionado, as definições de atributo da chave de classificação do índice, o esquema de chaves do índice e a projeção do atributo.

1. Chame o método `createTable`, fornecendo o objeto de solicitação como um parâmetro.

O exemplo de código Java a seguir demonstra as etapas anteriores. O código cria uma tabela (`WeatherData`) com um índice secundário global (`PrecipIndex`). A chave de partição do índice é `Date` e a chave de classificação é `Precipitation`. Todos os atributos da tabela estão projetados no índice. Os usuários podem consultar esse índice para obter dados climáticos de uma data específica, opcionalmente, classificar os dados por quantidade de precipitação. 

Como `Precipitation` não é um atributo de chave para a tabela, ele não é necessário. No entanto, os itens de `WeatherData` sem `Precipitation` não aparecem no `PrecipIndex`.

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

// Attribute definitions
ArrayList<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();

attributeDefinitions.add(new AttributeDefinition()
    .withAttributeName("Location")
    .withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition()
    .withAttributeName("Date")
    .withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition()
    .withAttributeName("Precipitation")
    .withAttributeType("N"));

// Table key schema
ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
tableKeySchema.add(new KeySchemaElement()
    .withAttributeName("Location")
    .withKeyType(KeyType.HASH));  //Partition key
tableKeySchema.add(new KeySchemaElement()
    .withAttributeName("Date")
    .withKeyType(KeyType.RANGE));  //Sort key

// PrecipIndex
GlobalSecondaryIndex precipIndex = new GlobalSecondaryIndex()
    .withIndexName("PrecipIndex")
    .withProvisionedThroughput(new ProvisionedThroughput()
        .withReadCapacityUnits((long) 10)
        .withWriteCapacityUnits((long) 1))
        .withProjection(new Projection().withProjectionType(ProjectionType.ALL));

ArrayList<KeySchemaElement> indexKeySchema = new ArrayList<KeySchemaElement>();

indexKeySchema.add(new KeySchemaElement()
    .withAttributeName("Date")
    .withKeyType(KeyType.HASH));  //Partition key
indexKeySchema.add(new KeySchemaElement()
    .withAttributeName("Precipitation")
    .withKeyType(KeyType.RANGE));  //Sort key

precipIndex.setKeySchema(indexKeySchema);

CreateTableRequest createTableRequest = new CreateTableRequest()
    .withTableName("WeatherData")
    .withProvisionedThroughput(new ProvisionedThroughput()
        .withReadCapacityUnits((long) 5)
        .withWriteCapacityUnits((long) 1))
    .withAttributeDefinitions(attributeDefinitions)
    .withKeySchema(tableKeySchema)
    .withGlobalSecondaryIndexes(precipIndex);

Table table = dynamoDB.createTable(createTableRequest);
System.out.println(table.getDescription());
```

Você deve aguardar até que o DynamoDB crie a tabela e defina o status dessa tabela como `ACTIVE`. Depois disso, você poderá começar a inserir itens de dados na tabela.

## Descrever uma tabela com um índice secundário global
<a name="GSIJavaDocumentAPI.DescribeTableWithIndex"></a>

Para obter mais informações sobre índices secundários globais em uma tabela, use `DescribeTable`. Para cada índice, você pode acessar seu nome, esquema de chaves e atributos projetados.

Veja a seguir as etapas necessárias para acessar informações de índice secundário global em uma tabela. 

1. Crie uma instância da classe `DynamoDB`.

1. Crie uma instância da classe `Table` para representar o índice com o qual você deseja trabalhar.

1. Chame o método `describe` no objeto `Table`.

O exemplo de código Java a seguir demonstra as etapas anteriores.

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

Table table = dynamoDB.getTable("WeatherData");
TableDescription tableDesc = table.describe();
    

Iterator<GlobalSecondaryIndexDescription> gsiIter = tableDesc.getGlobalSecondaryIndexes().iterator();
while (gsiIter.hasNext()) {
    GlobalSecondaryIndexDescription gsiDesc = gsiIter.next();
    System.out.println("Info for index "
         + gsiDesc.getIndexName() + ":");

    Iterator<KeySchemaElement> kseIter = gsiDesc.getKeySchema().iterator();
    while (kseIter.hasNext()) {
        KeySchemaElement kse = kseIter.next();
        System.out.printf("\t%s: %s\n", kse.getAttributeName(), kse.getKeyType());
    }
    Projection projection = gsiDesc.getProjection();
    System.out.println("\tThe projection type is: "
        + projection.getProjectionType());
    if (projection.getProjectionType().toString().equals("INCLUDE")) {
        System.out.println("\t\tThe non-key projected attributes are: "
            + projection.getNonKeyAttributes());
    }
}
```

## Consultar um índice secundário global
<a name="GSIJavaDocumentAPI.QueryAnIndex"></a>

Você pode usar `Query` em um índice secundário global de forma semelhante ao uso de `Query` em uma tabela. Você precisa especificar o nome do índice, os critérios de consulta da chave de partição e da chave de classificação (se houver) do índice, e os atributos que você deseja retornar. Neste exemplo, o índice é `PrecipIndex`, que tem uma chave de partição `Date` e uma chave de classificação `Precipitation`. A consulta de índice retorna todos os dados climáticos de uma data específica, na qual a precipitação é maior que zero.

Veja a seguir as etapas necessárias para consultar um índice secundário global usando a API de documentos do AWS SDK para Java. 

1. Crie uma instância da classe `DynamoDB`.

1. Crie uma instância da classe `Table` para representar o índice com o qual você deseja trabalhar.

1. Crie uma instância da classe `Index` para o índice que deseja consultar.

1. Chame o método `query` no objeto `Index`.

O nome do atributo `Date` é uma palavra reservada do DynamoDB. Portanto, use um nome de atributo de expressão como um espaço reservado na `KeyConditionExpression`.

O exemplo de código Java a seguir demonstra as etapas anteriores.

**Example**  

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
DynamoDB dynamoDB = new DynamoDB(client);

Table table = dynamoDB.getTable("WeatherData");
Index index = table.getIndex("PrecipIndex");

QuerySpec spec = new QuerySpec()
    .withKeyConditionExpression("#d = :v_date and Precipitation = :v_precip")
    .withNameMap(new NameMap()
        .with("#d", "Date"))
    .withValueMap(new ValueMap()
        .withString(":v_date","2013-08-10")
        .withNumber(":v_precip",0));

ItemCollection<QueryOutcome> items = index.query(spec);
Iterator<Item> iter = items.iterator(); 
while (iter.hasNext()) {
    System.out.println(iter.next().toJSONPretty());
}
```

# Exemplo: índices secundários globais que usam a API de documento do AWS SDK para Java
<a name="GSIJavaDocumentAPI.Example"></a>

O código Java de exemplo a seguir mostra como trabalhar com índices secundários globais. O exemplo cria uma tabela chamada `Issues`, que pode ser usada em um sistema de controle de bugs simples para desenvolvimento de software. A chave de partição é `IssueId` e a chave de classificação é `Title`. Há três índices secundários globais nessa tabela:
+ `CreateDateIndex`: a chave de partição é `CreateDate` e a chave de classificação é `IssueId`. Além das chaves da tabela, os atributos `Description` e `Status` são projetados no índice.
+ `TitleIndex`: a chave de partição é `Title` e a chave de classificação é `IssueId`. Nenhum outro atributo além das chaves da tabela são projetos no índice.
+ `DueDateIndex`: a chave de partição é `DueDate` e não há chave de classificação. Todos os atributos da tabela estão projetados no índice.

Depois que a tabela `Issues` é criada, o programa carrega a tabela com os dados que representam relatórios de bugs do software. Ele consulta os dados usando os índices secundários globais. Por fim, o programa exclui a tabela `Issues`.

Para obter instruções passo a passo sobre como testar o exemplo a seguir, consulte [Exemplos de código Java](CodeSamples.Java.md).

**Example**  

```
package com.amazonaws.codesamples.document;

import java.util.ArrayList;
import java.util.Iterator;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Index;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.ItemCollection;
import com.amazonaws.services.dynamodbv2.document.QueryOutcome;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.dynamodbv2.document.spec.QuerySpec;
import com.amazonaws.services.dynamodbv2.document.utils.ValueMap;
import com.amazonaws.services.dynamodbv2.model.AttributeDefinition;
import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
import com.amazonaws.services.dynamodbv2.model.GlobalSecondaryIndex;
import com.amazonaws.services.dynamodbv2.model.KeySchemaElement;
import com.amazonaws.services.dynamodbv2.model.KeyType;
import com.amazonaws.services.dynamodbv2.model.Projection;
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;

public class DocumentAPIGlobalSecondaryIndexExample {

    static AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
    static DynamoDB dynamoDB = new DynamoDB(client);

    public static String tableName = "Issues";

    public static void main(String[] args) throws Exception {

        createTable();
        loadData();

        queryIndex("CreateDateIndex");
        queryIndex("TitleIndex");
        queryIndex("DueDateIndex");

        deleteTable(tableName);

    }

    public static void createTable() {

        // Attribute definitions
        ArrayList<AttributeDefinition> attributeDefinitions = new ArrayList<AttributeDefinition>();

        attributeDefinitions.add(new AttributeDefinition().withAttributeName("IssueId").withAttributeType("S"));
        attributeDefinitions.add(new AttributeDefinition().withAttributeName("Title").withAttributeType("S"));
        attributeDefinitions.add(new AttributeDefinition().withAttributeName("CreateDate").withAttributeType("S"));
        attributeDefinitions.add(new AttributeDefinition().withAttributeName("DueDate").withAttributeType("S"));

        // Key schema for table
        ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
        tableKeySchema.add(new KeySchemaElement().withAttributeName("IssueId").withKeyType(KeyType.HASH)); // Partition
                                                                                                           // key
        tableKeySchema.add(new KeySchemaElement().withAttributeName("Title").withKeyType(KeyType.RANGE)); // Sort
                                                                                                          // key

        // Initial provisioned throughput settings for the indexes
        ProvisionedThroughput ptIndex = new ProvisionedThroughput().withReadCapacityUnits(1L)
                .withWriteCapacityUnits(1L);

        // CreateDateIndex
        GlobalSecondaryIndex createDateIndex = new GlobalSecondaryIndex().withIndexName("CreateDateIndex")
                .withProvisionedThroughput(ptIndex)
                .withKeySchema(new KeySchemaElement().withAttributeName("CreateDate").withKeyType(KeyType.HASH), // Partition
                                                                                                                 // key
                        new KeySchemaElement().withAttributeName("IssueId").withKeyType(KeyType.RANGE)) // Sort
                                                                                                        // key
                .withProjection(
                        new Projection().withProjectionType("INCLUDE").withNonKeyAttributes("Description", "Status"));

        // TitleIndex
        GlobalSecondaryIndex titleIndex = new GlobalSecondaryIndex().withIndexName("TitleIndex")
                .withProvisionedThroughput(ptIndex)
                .withKeySchema(new KeySchemaElement().withAttributeName("Title").withKeyType(KeyType.HASH), // Partition
                                                                                                            // key
                        new KeySchemaElement().withAttributeName("IssueId").withKeyType(KeyType.RANGE)) // Sort
                                                                                                        // key
                .withProjection(new Projection().withProjectionType("KEYS_ONLY"));

        // DueDateIndex
        GlobalSecondaryIndex dueDateIndex = new GlobalSecondaryIndex().withIndexName("DueDateIndex")
                .withProvisionedThroughput(ptIndex)
                .withKeySchema(new KeySchemaElement().withAttributeName("DueDate").withKeyType(KeyType.HASH)) // Partition
                                                                                                              // key
                .withProjection(new Projection().withProjectionType("ALL"));

        CreateTableRequest createTableRequest = new CreateTableRequest().withTableName(tableName)
                .withProvisionedThroughput(
                        new ProvisionedThroughput().withReadCapacityUnits((long) 1).withWriteCapacityUnits((long) 1))
                .withAttributeDefinitions(attributeDefinitions).withKeySchema(tableKeySchema)
                .withGlobalSecondaryIndexes(createDateIndex, titleIndex, dueDateIndex);

        System.out.println("Creating table " + tableName + "...");
        dynamoDB.createTable(createTableRequest);

        // Wait for table to become active
        System.out.println("Waiting for " + tableName + " to become ACTIVE...");
        try {
            Table table = dynamoDB.getTable(tableName);
            table.waitForActive();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void queryIndex(String indexName) {

        Table table = dynamoDB.getTable(tableName);

        System.out.println("\n***********************************************************\n");
        System.out.print("Querying index " + indexName + "...");

        Index index = table.getIndex(indexName);

        ItemCollection<QueryOutcome> items = null;

        QuerySpec querySpec = new QuerySpec();

        if (indexName == "CreateDateIndex") {
            System.out.println("Issues filed on 2013-11-01");
            querySpec.withKeyConditionExpression("CreateDate = :v_date and begins_with(IssueId, :v_issue)")
                    .withValueMap(new ValueMap().withString(":v_date", "2013-11-01").withString(":v_issue", "A-"));
            items = index.query(querySpec);
        } else if (indexName == "TitleIndex") {
            System.out.println("Compilation errors");
            querySpec.withKeyConditionExpression("Title = :v_title and begins_with(IssueId, :v_issue)")
                    .withValueMap(
                            new ValueMap().withString(":v_title", "Compilation error").withString(":v_issue", "A-"));
            items = index.query(querySpec);
        } else if (indexName == "DueDateIndex") {
            System.out.println("Items that are due on 2013-11-30");
            querySpec.withKeyConditionExpression("DueDate = :v_date")
                    .withValueMap(new ValueMap().withString(":v_date", "2013-11-30"));
            items = index.query(querySpec);
        } else {
            System.out.println("\nNo valid index name provided");
            return;
        }

        Iterator<Item> iterator = items.iterator();

        System.out.println("Query: printing results...");

        while (iterator.hasNext()) {
            System.out.println(iterator.next().toJSONPretty());
        }

    }

    public static void deleteTable(String tableName) {

        System.out.println("Deleting table " + tableName + "...");

        Table table = dynamoDB.getTable(tableName);
        table.delete();

        // Wait for table to be deleted
        System.out.println("Waiting for " + tableName + " to be deleted...");
        try {
            table.waitForDelete();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void loadData() {

        System.out.println("Loading data into table " + tableName + "...");

        // IssueId, Title,
        // Description,
        // CreateDate, LastUpdateDate, DueDate,
        // Priority, Status

        putItem("A-101", "Compilation error", "Can't compile Project X - bad version number. What does this mean?",
                "2013-11-01", "2013-11-02", "2013-11-10", 1, "Assigned");

        putItem("A-102", "Can't read data file", "The main data file is missing, or the permissions are incorrect",
                "2013-11-01", "2013-11-04", "2013-11-30", 2, "In progress");

        putItem("A-103", "Test failure", "Functional test of Project X produces errors", "2013-11-01", "2013-11-02",
                "2013-11-10", 1, "In progress");

        putItem("A-104", "Compilation error", "Variable 'messageCount' was not initialized.", "2013-11-15",
                "2013-11-16", "2013-11-30", 3, "Assigned");

        putItem("A-105", "Network issue", "Can't ping IP address 127.0.0.1. Please fix this.", "2013-11-15",
                "2013-11-16", "2013-11-19", 5, "Assigned");

    }

    public static void putItem(

            String issueId, String title, String description, String createDate, String lastUpdateDate, String dueDate,
            Integer priority, String status) {

        Table table = dynamoDB.getTable(tableName);

        Item item = new Item().withPrimaryKey("IssueId", issueId).withString("Title", title)
                .withString("Description", description).withString("CreateDate", createDate)
                .withString("LastUpdateDate", lastUpdateDate).withString("DueDate", dueDate)
                .withNumber("Priority", priority).withString("Status", status);

        table.putItem(item);
    }

}
```

# Como trabalhar com índices secundários globais: .NET
<a name="GSILowLevelDotNet"></a>

Você pode usar a API de baixo nível do AWS SDK para .NET para criar uma tabela do Amazon DynamoDB com um ou mais índices secundários globais na tabela e executar consultas usando os índices. Essas operações são mapeadas nas operações do DynamoDB correspondentes. Para obter mais informações, consulte a [Referência de API do Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/). 

Veja a seguir as etapas comuns para operações de tabela usando a API de baixo nível do .NET. 

1. Crie uma instância da classe `AmazonDynamoDBClient`.

1. Forneça os parâmetros obrigatórios e opcionais para a operação, criando os objetos de solicitação correspondentes.

   Por exemplo, crie um objeto `CreateTableRequest` para criar uma tabela e um objeto `QueryRequest` para consultar uma tabela ou um índice. 

1. Execute o método apropriado fornecido pelo cliente que você criou na etapa anterior. 

**Topics**
+ [Criar uma tabela com um índice secundário global](#GSILowLevelDotNet.CreateTableWithIndex)
+ [Descrever uma tabela com um índice secundário global](#GSILowLevelDotNet.DescribeTableWithIndex)
+ [Consultar um índice secundário global](#GSILowLevelDotNet.QueryAnIndex)
+ [Exemplo: índices secundários globais que usam a API de baixo nível do AWS SDK para .NET](GSILowLevelDotNet.Example.md)

## Criar uma tabela com um índice secundário global
<a name="GSILowLevelDotNet.CreateTableWithIndex"></a>

Você pode criar índices secundários globais ao mesmo tempo em que cria uma tabela. Para fazer isso, use `CreateTable` e forneça suas especificações para um ou mais índices secundários globais. O exemplo de código C\$1a seguir cria uma tabela para armazenar informações sobre dados climáticos. A chave de partição é `Location` e a chave de classificação é `Date`. Um índice secundário global chamado `PrecipIndex` permite acesso rápido aos dados de precipitação de vários locais.

Veja a seguir as etapas necessárias para criar uma tabela com um índice secundário global usando a API de baixo nível do .NET. 

1. Crie uma instância da classe `AmazonDynamoDBClient`.

1. Crie uma instância da classe `CreateTableRequest` para fornecer as informações solicitadas. 

   Você deve fornecer o nome da tabela, sua chave primária e os valores de throughput provisionado. Para o índice secundário global, você deve fornecer o nome do índice, suas configurações de throughput provisionado, as definições de atributo da chave de classificação do índice, o esquema de chaves do índice e a projeção do atributo.

1. Execute o método `CreateTable` fornecendo o objeto de solicitação como um parâmetro.

O exemplo de código C\$1 a seguir demonstra as etapas anteriores. O código cria uma tabela (`WeatherData`) com um índice secundário global (`PrecipIndex`). A chave de partição do índice é `Date` e a chave de classificação é `Precipitation`. Todos os atributos da tabela estão projetados no índice. Os usuários podem consultar esse índice para obter dados climáticos de uma data específica, opcionalmente, classificar os dados por quantidade de precipitação. 

Como `Precipitation` não é um atributo de chave para a tabela, ele não é necessário. No entanto, os itens de `WeatherData` sem `Precipitation` não aparecem no `PrecipIndex`.

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

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

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

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

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

precipIndex.KeySchema = indexKeySchema;

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

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

Você deve aguardar até que o DynamoDB crie a tabela e defina o status dessa tabela como `ACTIVE`. Depois disso, você poderá começar a inserir itens de dados na tabela.

## Descrever uma tabela com um índice secundário global
<a name="GSILowLevelDotNet.DescribeTableWithIndex"></a>

Para obter mais informações sobre índices secundários globais em uma tabela, use `DescribeTable`. Para cada índice, você pode acessar seu nome, esquema de chaves e atributos projetados.

Veja a seguir as etapas para acessar informações do índice secundário global para uma tabela usando a API de baixo nível do .NET. 

1. Crie uma instância da classe `AmazonDynamoDBClient`.

1. Execute o método `describeTable` fornecendo o objeto de solicitação como um parâmetro.

   Crie uma instância da classe `DescribeTableRequest` para fornecer as informações solicitadas. Você deve fornecer o nome da tabela.

O exemplo de código C\$1 a seguir demonstra as etapas anteriores.

**Example**  

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

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

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

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

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

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

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

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

## Consultar um índice secundário global
<a name="GSILowLevelDotNet.QueryAnIndex"></a>

Você pode usar `Query` em um índice secundário global de forma semelhante ao uso de `Query` em uma tabela. Você precisa especificar o nome do índice, os critérios de consulta da chave de partição e da chave de classificação (se houver) do índice, e os atributos que você deseja retornar. Neste exemplo, o índice é `PrecipIndex`, que tem uma chave de partição `Date` e uma chave de classificação `Precipitation`. A consulta de índice retorna todos os dados climáticos de uma data específica, na qual a precipitação é maior que zero.

Veja a seguir as etapas para consultar um índice secundário global usando a API de baixo nível do .NET. 

1. Crie uma instância da classe `AmazonDynamoDBClient`.

1. Crie uma instância da classe `QueryRequest` para fornecer as informações solicitadas.

1. Execute o método `query` fornecendo o objeto de solicitação como um parâmetro.

O nome do atributo `Date` é uma palavra reservada do DynamoDB. Portanto, use um nome de atributo de expressão como um espaço reservado na `KeyConditionExpression`.

O exemplo de código C\$1 a seguir demonstra as etapas anteriores.

**Example**  

```
client = new AmazonDynamoDBClient();

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

var result = client.Query(queryRequest);

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

         }
     Console.WriteLine();
}
```

# Exemplo: índices secundários globais que usam a API de baixo nível do AWS SDK para .NET
<a name="GSILowLevelDotNet.Example"></a>

O código de exemplo Java a seguir mostra como trabalhar com índices secundários globais. O exemplo cria uma tabela chamada `Issues`, que pode ser usada em um sistema de controle de bugs simples para desenvolvimento de software. A chave de partição é `IssueId` e a chave de classificação é `Title`. Há três índices secundários globais nessa tabela:
+ `CreateDateIndex`: a chave de partição é `CreateDate` e a chave de classificação é `IssueId`. Além das chaves da tabela, os atributos `Description` e `Status` são projetados no índice.
+ `TitleIndex`: a chave de partição é `Title` e a chave de classificação é `IssueId`. Nenhum outro atributo além das chaves da tabela são projetos no índice.
+ `DueDateIndex`: a chave de partição é `DueDate` e não há chave de classificação. Todos os atributos da tabela estão projetados no índice.

Depois que a tabela `Issues` é criada, o programa carrega a tabela com os dados que representam relatórios de bugs do software. Ele consulta os dados usando os índices secundários globais. Por fim, o programa exclui a tabela `Issues`.

Para obter instruções detalhadas sobre como testar o exemplo a seguir, consulte [Exemplos de código .NET](CodeSamples.DotNet.md).

**Example**  

```
using System;
using System.Collections.Generic;
using System.Linq;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.Model;
using Amazon.Runtime;
using Amazon.SecurityToken;

namespace com.amazonaws.codesamples
{
    class LowLevelGlobalSecondaryIndexExample
    {
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();
        public static String tableName = "Issues";

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

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

            DeleteTable(tableName);

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

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

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

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

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

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

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



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

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

            WaitUntilTableReady(tableName);
        }

        private static void LoadData()
        {
            Console.WriteLine("Loading data into table " + tableName + "...");

            // IssueId, Title,
            // Description,
            // CreateDate, LastUpdateDate, DueDate,
            // Priority, Status

            putItem("A-101", "Compilation error",
                "Can't compile Project X - bad version number. What does this mean?",
                "2013-11-01", "2013-11-02", "2013-11-10",
                1, "Assigned");

            putItem("A-102", "Can't read data file",
                "The main data file is missing, or the permissions are incorrect",
                "2013-11-01", "2013-11-04", "2013-11-30",
                2, "In progress");

            putItem("A-103", "Test failure",
                "Functional test of Project X produces errors",
                "2013-11-01", "2013-11-02", "2013-11-10",
                1, "In progress");

            putItem("A-104", "Compilation error",
                "Variable 'messageCount' was not initialized.",
                "2013-11-15", "2013-11-16", "2013-11-30",
                3, "Assigned");

            putItem("A-105", "Network issue",
                "Can't ping IP address 127.0.0.1. Please fix this.",
                "2013-11-15", "2013-11-16", "2013-11-19",
                5, "Assigned");
        }

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

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

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

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

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


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

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

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

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

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

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

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

            queryRequest.KeyConditionExpression = keyConditionExpression;
            queryRequest.ExpressionAttributeValues = expressionAttributeValues;

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

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

        private static void WaitUntilTableReady(string tableName)
        {
            string status = null;
            // Let us wait until table is created. Call DescribeTable.
            do
            {
                System.Threading.Thread.Sleep(5000); // Wait 5 seconds.
                try
                {
                    var res = client.DescribeTable(new DescribeTableRequest
                    {
                        TableName = tableName
                    });

                    Console.WriteLine("Table name: {0}, status: {1}",
                              res.Table.TableName,
                              res.Table.TableStatus);
                    status = res.Table.TableStatus;
                }
                catch (ResourceNotFoundException)
                {
                    // DescribeTable is eventually consistent. So you might
                    // get resource not found. So we handle the potential exception.
                }
            } while (status != "ACTIVE");
        }

        private static void WaitForTableToBeDeleted(string tableName)
        {
            bool tablePresent = true;

            while (tablePresent)
            {
                System.Threading.Thread.Sleep(5000); // Wait 5 seconds.
                try
                {
                    var res = client.DescribeTable(new DescribeTableRequest
                    {
                        TableName = tableName
                    });

                    Console.WriteLine("Table name: {0}, status: {1}",
                              res.Table.TableName,
                              res.Table.TableStatus);
                }
                catch (ResourceNotFoundException)
                {
                    tablePresent = false;
                }
            }
        }
    }
}
```

# Trabalhar com índices secundários globais no DynamoDB usando a AWS CLI
<a name="GCICli"></a>

Você pode usar a AWS CLI para criar uma tabela do Amazon DynamoDB com um ou mais índices secundários globais na tabela e executar consultas usando os índices.

**Topics**
+ [Criar uma tabela com um índice secundário global](#GCICli.CreateTableWithIndex)
+ [Adicionar um índice secundário global a uma tabela existente](#GCICli.CreateIndexAfterTable)
+ [Descrever uma tabela com um índice secundário global](#GCICli.DescribeTableWithIndex)
+ [Consultar um índice secundário global](#GCICli.QueryAnIndex)

## Criar uma tabela com um índice secundário global
<a name="GCICli.CreateTableWithIndex"></a>

Você pode criar índices secundários globais ao mesmo tempo que cria uma tabela. Para fazer isso, use o parâmetro `create-table` e forneça suas especificações para um ou mais índices secundários globais. O exemplo a seguir cria uma tabela chamada `GameScores` com um índice secundário global chamado `GameTitleIndex`. A tabela-base tem uma chave de partição `UserId` e uma chave de classificação `GameTitle`, permitindo que você encontre a melhor pontuação de um usuário individual para um jogo específico de forma eficiente, enquanto o GSI tem uma chave de partição `GameTitle` e uma chave de classificação `TopScore`, permitindo que você encontre rapidamente a pontuação mais alta geral para um jogo específico.

```
aws dynamodb create-table \
    --table-name GameScores \
    --attribute-definitions AttributeName=UserId,AttributeType=S \
                            AttributeName=GameTitle,AttributeType=S \
                            AttributeName=TopScore,AttributeType=N  \
    --key-schema AttributeName=UserId,KeyType=HASH \
                 AttributeName=GameTitle,KeyType=RANGE \
    --provisioned-throughput ReadCapacityUnits=10,WriteCapacityUnits=5 \
    --global-secondary-indexes \
        "[
            {
                \"IndexName\": \"GameTitleIndex\",
                \"KeySchema\": [{\"AttributeName\":\"GameTitle\",\"KeyType\":\"HASH\"},
                                {\"AttributeName\":\"TopScore\",\"KeyType\":\"RANGE\"}],
                \"Projection\":{
                    \"ProjectionType\":\"INCLUDE\",
                    \"NonKeyAttributes\":[\"UserId\"]
                },
                \"ProvisionedThroughput\": {
                    \"ReadCapacityUnits\": 10,
                    \"WriteCapacityUnits\": 5
                }
            }
        ]"
```

Você deve aguardar até que o DynamoDB crie a tabela e defina o status dessa tabela como `ACTIVE`. Depois disso, você poderá começar a inserir itens de dados na tabela. Você pode usar [describe-table](https://docs.aws.amazon.com/cli/latest/reference/dynamodb/describe-table.html) para determinar o status da criação da tabela.

## Adicionar um índice secundário global a uma tabela existente
<a name="GCICli.CreateIndexAfterTable"></a>

Os índices secundários globais também podem ser adicionados ou modificados após a criação da tabela. Para fazer isso, use o parâmetro `update-table` e forneça suas especificações para um ou mais índices secundários globais. O exemplo a seguir usa o mesmo esquema do exemplo anterior, mas pressupõe que a tabela já foi criada e que adicionaremos o GSI mais tarde.

```
aws dynamodb update-table \
    --table-name GameScores \
    --attribute-definitions AttributeName=TopScore,AttributeType=N  \
    --global-secondary-index-updates \
        "[
            {
                \"Create\": {
                    \"IndexName\": \"GameTitleIndex\",
                    \"KeySchema\": [{\"AttributeName\":\"GameTitle\",\"KeyType\":\"HASH\"},
                                    {\"AttributeName\":\"TopScore\",\"KeyType\":\"RANGE\"}],
                    \"Projection\":{
                        \"ProjectionType\":\"INCLUDE\",
                        \"NonKeyAttributes\":[\"UserId\"]
                    }
                }
            }
        ]"
```

## Descrever uma tabela com um índice secundário global
<a name="GCICli.DescribeTableWithIndex"></a>

Para obter mais informações sobre índices secundários globais em uma tabela, use o parâmetro `describe-table`. Para cada índice, você pode acessar seu nome, esquema de chaves e atributos projetados.

```
aws dynamodb describe-table --table-name GameScores
```

## Consultar um índice secundário global
<a name="GCICli.QueryAnIndex"></a>

Você pode usar a operação `query` em um índice secundário global de forma semelhante ao uso de `query` em uma tabela. Você deve especificar o nome do índice, os critérios de consulta da chave de classificação do índice e os atributos que deseja retornar. Neste exemplo, o índice é `GameTitleIndex` e a chave de classificação do índice é `GameTitle`.

Os únicos atributos retornados são aqueles que foram projetados no índice. É possível modificar essa consulta para selecionar atributos não chave também, mas isso exigiria atividades de busca de tabela que são relativamente caras. Para obter mais informações sobre buscas de tabela, consulte [Projeções de atributo](GSI.md#GSI.Projections).

```
aws dynamodb query --table-name GameScores\
    --index-name GameTitleIndex \
    --key-condition-expression "GameTitle = :v_game" \
    --expression-attribute-values '{":v_game":{"S":"Alien Adventure"} }'
```