

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

# Utilizzo degli indici secondari globali in DynamoDB
<a name="GSI"></a>

Alcune applicazioni devono poter eseguire molti tipi di query, usando un'ampia gamma di attributi diversi come criteri di query. Per supportare questi requisiti, è possibile creare uno o più *indici secondari globali* ed emettere richieste `Query` rispetto a questi indici in Amazon DynamoDB.

**Topics**
+ [Scenario: Utilizzo di un indice secondario globale](#GSI.scenario)
+ [Proiezioni di attributi](#GSI.Projections)
+ [Schema chiave con più attributi](#GSI.MultiAttributeKeys)
+ [Lettura di dati da un indice secondario globale](#GSI.Reading)
+ [Sincronizzazione dei dati tra tabelle e indici secondari globali](#GSI.Writes)
+ [Classi di tabella con indici secondari globali](#GSI.tableclasses)
+ [Considerazioni sulla velocità di trasmissione effettiva assegnata per indici secondari globali](#GSI.ThroughputConsiderations)
+ [Considerazioni sullo storage per indici secondari globali](#GSI.StorageConsiderations)
+ [Modelli di progettazione](GSI.DesignPatterns.md)
+ [Gestione degli indici secondari globali in DynamoDB](GSI.OnlineOps.md)
+ [Rilevamento e correzione delle violazioni delle chiavi in DynamoDB](GSI.OnlineOps.ViolationDetection.md)
+ [Utilizzo di indici secondari globali: Java](GSIJavaDocumentAPI.md)
+ [Utilizzo di indici secondari globali: .NET](GSILowLevelDotNet.md)
+ [Utilizzo di indici secondari in DynamoDB con la AWS CLI](GCICli.md)

## Scenario: Utilizzo di un indice secondario globale
<a name="GSI.scenario"></a>

A titolo illustrativo, considera una tabella denominata `GameScores` che tiene traccia di utenti e punteggi per un'applicazione di gioco per dispositivi mobili. Ogni item in `GameScores` è identificato da una chiave di partizione (`UserId`) e una chiave di ordinamento (`GameTitle`). Il seguente diagramma mostra in che modo verrebbero organizzarti gli elementi nella tabella. Non tutti gli attributi vengono visualizzati.

![\[GameScores tabella contenente un elenco di ID utente, titolo, punteggio, data e vittorie/sconfitte.\]](http://docs.aws.amazon.com/it_it/amazondynamodb/latest/developerguide/images/GSI_01.png)


Supponi ora di voler scrivere un'applicazione di tipo classifica per visualizzare i punteggi più alti per ogni gioco. Una query che specifica gli attributi chiave (`UserId` e `GameTitle`) sarebbe molto efficiente. Tuttavia, se l'applicazione deve recuperare i dati da `GameScores` solo in base a `GameTitle`, occorre utilizzare un'operazione `Scan`. Con l'aggiunta di altri item alla tabella, le scansioni di tutti i dati rallenterebbero e diventerebbero meno pratiche, rendendo difficile rispondere a domande come le seguenti:
+ Qual è il punteggio più alto mai registrato per il gioco Meteor Blasters?
+ Quale utente ha il punteggio più alto per Galaxy Invaders?
+ Qual è il rapporto più elevato tra vittorie e sconfitte?

Per accelerare le query su attributi non chiave, è possibile creare un indice secondario globale. Un indice secondario globale contiene una selezione di attributi dalla tabella di base organizzati tramite una chiave primaria diversa da quella della tabella. La chiave dell'indice non deve avere alcuno degli attributi di chiave della tabella, né lo stesso schema della chiave di una tabella.

Ad esempio, è possibile creare un indice secondario globale denominato `GameTitleIndex` con una chiave di partizione `GameTitle` e una chiave di ordinamento `TopScore`. Gli attributi della chiave primaria della tabella di base vengono sempre proiettati in un indice, pertanto è presente anche l'attributo `UserId`. Il diagramma seguente mostra l'aspetto di un indice `GameTitleIndex`.

![\[GameTitleIndex tabella contenente un elenco di titoli, punteggi e ID utente.\]](http://docs.aws.amazon.com/it_it/amazondynamodb/latest/developerguide/images/GSI_02.png)


A questo punto, puoi eseguire una query su `GameTitleIndex` e ottenere facilmente i punteggi per Meteor Blasters. I risultati sono ordinati in base ai valori della chiave di ordinamento, ovvero `TopScore`. Se imposti il parametro `ScanIndexForward` su false, i risultati vengono restituiti in ordine decrescente e di conseguenza il punteggio più alto viene restituito per primo.

Ogni indice secondario globale deve avere una chiave di partizione e può avere anche una chiave di ordinamento facoltativa. Lo schema della chiave di indicizzazione può essere diverso dallo schema della tabella di base. È possibile avere una tabella con una chiave primaria semplice (chiave di partizione) e creare un indice secondario globale con una chiave primaria composita (chiave di partizione e chiave di ordinamento) o viceversa. Gli attributi della chiave dell'indice possono essere costituiti da qualsiasi attributo `String`, `Number` o `Binary` di primo livello della tabella di base. Altri tipi scalari, documento e set non sono consentiti.

Se vuoi, puoi proiettare altri attributi della tabella di base nell'indice. Quando si esegue una query sull'indice, DynamoDB può recuperare questi attributi proiettati in modo efficiente. Tuttavia, le query su un indice secondario globale non possono recuperare gli attributi dalla tabella di base. Ad esempio, se esegui una query `GameTitleIndex` come mostrato nel diagramma precedente, la query non è in grado di accedere ad alcun attributo non di chiave diverso da `TopScore` (se vengono automaticamente proiettati gli attributi della chiave `GameTitle` e `UserId`).

In una tabella DynamoDB ogni valore di chiave deve essere univoco. Tuttavia, i valori delle chiavi in un indice secondario globale non devono essere univoci. A titolo illustrativo, supponi che un gioco denominato Comet Quest sia particolarmente difficile, con molti nuovi utenti che provano ma non riescono a ottenere un punteggio maggiore di zero. Di seguito sono illustrati alcuni dati che possono rappresentare questa situazione.


****  

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

Quando questi dati vengono aggiunti alla tabella `GameScores`, vengono propagati da DynamoDB a `GameTitleIndex`. Se quindi eseguiamo una query sull'indice utilizzando Comet Quest per `GameTitle` e 0 per `TopScore`, vengono restituiti i dati seguenti.

![\[Tabella contenente un elenco di titoli, punteggi più alti e ID utente.\]](http://docs.aws.amazon.com/it_it/amazondynamodb/latest/developerguide/images/GSI_05.png)


Nella risposta vengono visualizzati solo gli elementi con i valori di chiave specificati. All'interno di questo set di dati, gli elementi non hanno un ordine specifico. 

Un indice secondario globale tiene traccia solo degli elementi di dati in cui esistono effettivamente i suoi attributi di chiave. Ad esempio, supponi di aver aggiunto un nuovo item alla tabella `GameScores`, ma di aver fornito solo gli attributi della chiave primaria obbligatori.


****  

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

Poiché non è stato specificato l'attributo `TopScore`, DynamoDB non propaga questo elemento a `GameTitleIndex`. Di conseguenza, se ha eseguito una query su `GameScores` per tutti gli elementi di Comet Quest, ottieni i quattro item seguenti:

![\[Tabella contenente un elenco di quattro titoli, punteggi più alti e ID utente.\]](http://docs.aws.amazon.com/it_it/amazondynamodb/latest/developerguide/images/GSI_04.png)


Una query simile su `GameTitleIndex` continua a restituire tre item anziché quattro. Il motivo è che l'item con `TopScore` inesistente non viene propagato nell'indice.

![\[Tabella contenente un elenco di tre titoli, punteggi più alti e ID utente.\]](http://docs.aws.amazon.com/it_it/amazondynamodb/latest/developerguide/images/GSI_05.png)


## Proiezioni di attributi
<a name="GSI.Projections"></a>

Una *proiezione* è l'insieme di attributi copiato da una tabella in un indice secondario. La chiave di partizione e la chiave di ordinamento della tabella vengono sempre proiettati nell'indice; è possibile proiettare altri attributi per supportare i requisiti di query dell'applicazione. Quando si esegue una query su un indice, Amazon DynamoDB può accedere a qualsiasi attributo nella proiezione come se tali attributi fossero in una propria tabella.

Quando si crea un indice secondario, è necessario specificare gli attributi che saranno proiettati nell'indice. DynamoDB fornisce tre diverse opzioni per questo:
+ *KEYS\$1ONLY*: ogni elemento dell'indice è costituito solo dalla chiave di partizione della tabella e dai valori della chiave di ordinamento, oltre ai valori della chiave di indice. L'opzione `KEYS_ONLY` si traduce nell'indice secondario più piccolo possibile.
+ *INCLUDE*: oltre agli attributi descritti in `KEYS_ONLY`, l'indice secondario includerà gli altri attributi non chiave che sono stati specificati.
+ *ALL*: l'indice secondario include tutti gli attributi della tabella di origine. Poiché tutti i dati della tabella sono duplicati nell'indice, una proiezione `ALL` restituisce il più grande indice secondario possibile.

Nel diagramma precedente, `GameTitleIndex` dispone di un solo attributo proiettato: `UserId`. Pertanto, sebbene un'applicazione possa determinare in maniera efficiente il valore `UserId` dei punteggi più alti per ogni partita utilizzando `GameTitle` e `TopScore` nelle query, non può determinare in maniera efficiente il rapporto più elevato tra vittorie e sconfitte per i punteggi più alti. A tale scopo, l'applicazione dovrebbe eseguire un'interrogazione aggiuntiva sulla tabella di base per recuperare le vittorie e le sconfitte di ciascuno dei migliori marcatori. Un modo più efficiente per supportare le query su questi dati consiste nel proiettare gli attributi dalla tabella di base nell'indice secondario globale, come mostrato in questo diagramma. 

![\[Rappresentazione della proiezione di attributi non chiave in un GSI per supportare l’esecuzione di query efficienti.\]](http://docs.aws.amazon.com/it_it/amazondynamodb/latest/developerguide/images/GSI_06.png)


Poiché gli attributi non di chiave `Wins` e `Losses` vengono proiettati nell'indice, un'applicazione può determinare il rapporto tra vittorie e sconfitte per qualsiasi gioco o per qualsiasi combinazione di gioco e ID utente.

Quando si scelgono gli attributi da proiettare in un indice secondario globale, è necessario considerare il compromesso tra i costi correlati alla velocità effettiva assegnata e i costi di archiviazione:
+ Se è necessario accedere a pochi attributi con la latenza più bassa possibile, considerare la possibilità di proiettare solo quegli attributi in un indice secondario globale. Più piccolo è l'indice, minore è il costo per memorizzarlo e minori sono i costi di scrittura.
+ Se l'applicazione accede frequentemente ad alcuni attributi non chiave, è necessario considerare di proiettare quegli attributi in un indice secondario globale. I costi di archiviazione aggiuntivi per l'indice secondario globale compensano il costo di esecuzione di scansioni frequenti delle tabelle.
+ Se è necessario accedere alla maggior parte degli attributi non chiave su base frequente, è possibile proiettare questi attributi, o anche l'intera tabella di base, in un indice secondario globale. Questo offre massima flessibilità. Tuttavia, i costi di storage aumenteranno o addirittura raddoppieranno.
+ Se l'applicazione esegue di rado le query sulla tabella, ma esegue molte scritture o aggiornamenti dei dati nella tabella, considera di proiettare `KEYS_ONLY`. L'indice secondario globale avrebbe dimensioni minime e sarebbe comunque disponibile se necessario per l'attività di query. 

## Schema chiave con più attributi
<a name="GSI.MultiAttributeKeys"></a>

Gli indici secondari globali supportano chiavi con più attributi, che consentono di comporre chiavi di partizione e ordinare le chiavi a partire da più attributi. Con le chiavi multiattributo, è possibile creare una chiave di partizione da un massimo di quattro attributi e una chiave di ordinamento da un massimo di quattro attributi, per un totale di un massimo di otto attributi per schema di chiavi.

Le chiavi multiattributo semplificano il modello di dati eliminando la necessità di concatenare manualmente gli attributi in chiavi sintetiche. Invece di creare stringhe composite, ad esempio`TOURNAMENT#WINTER2024#REGION#NA-EAST`, puoi utilizzare direttamente gli attributi naturali del tuo modello di dominio. DynamoDB gestisce automaticamente la logica delle chiavi composite, eseguendo l'hashing di più attributi chiave di partizione per la distribuzione dei dati e mantenendo l'ordinamento gerarchico tra più attributi delle chiavi di ordinamento.

Ad esempio, prendi in considerazione un sistema di tornei di gioco in cui desideri organizzare le partite per torneo e regione. Con le chiavi multiattributo, è possibile definire la chiave di partizione come due attributi separati: `tournamentId` e. `region` Allo stesso modo, è possibile definire la chiave di ordinamento utilizzando più attributi come `round``bracket`, e `matchId` creare una gerarchia naturale. Questo approccio mantiene i dati digitati e il codice pulito, senza manipolazione o analisi delle stringhe.

Quando si esegue una query su un indice secondario globale con chiavi multiattributo, è necessario specificare tutti gli attributi delle chiavi di partizione utilizzando condizioni di uguaglianza. Per gli attributi chiave di ordinamento, è possibile interrogarli left-to-right nell'ordine in cui sono definiti nello schema chiave. Ciò significa che è possibile interrogare solo il primo attributo chiave di ordinamento, i primi due attributi insieme o tutti gli attributi insieme, ma non è possibile saltare gli attributi intermedi. Condizioni di disuguaglianza come`>`, `<``BETWEEN`, o `begins_with()` devono essere l'ultima condizione della query.

Le chiavi con più attributi funzionano particolarmente bene quando si creano indici secondari globali su tabelle esistenti. È possibile utilizzare gli attributi già presenti nella tabella senza inserire le chiavi sintetiche tra i dati. Ciò semplifica l'aggiunta di nuovi modelli di query all'applicazione creando indici che riorganizzano i dati utilizzando diverse combinazioni di attributi.

Ogni attributo in una chiave con più attributi può avere il proprio tipo di dati: `String` (S), (N) o `Number` (B). `Binary` Quando scegliete i tipi di dati, tenete presente che `Number` gli attributi vengono ordinati numericamente senza richiedere il riempimento zero, mentre gli attributi vengono ordinati lessicograficamente. `String` Ad esempio, se utilizzi un `Number` tipo per un attributo score, i valori 5, 50, 500 e 1000 vengono ordinati in ordine numerico naturale. Gli stessi valori `String` di type verrebbero ordinati come «1000", «5", «50", «500" a meno che non vengano riempiti con zeri iniziali.

Quando progettate chiavi con più attributi, ordinate gli attributi dal più generale al più specifico. Per le chiavi di partizione, combinate gli attributi che vengono sempre interrogati insieme e che garantiscono una buona distribuzione dei dati. Per le chiavi di ordinamento, posiziona gli attributi più richiesti al primo posto nella gerarchia per massimizzare la flessibilità delle query. Questo ordinamento consente di eseguire query a qualsiasi livello di granularità che corrisponda ai modelli di accesso.

Vedi gli esempi di [Chiavi con più attributi](GSI.DesignPattern.MultiAttributeKeys.md) implementazione.

## Lettura di dati da un indice secondario globale
<a name="GSI.Reading"></a>

È possibile recuperare gli elementi da un indice secondario globale utilizzando le operazioni `Query` e `Scan`. Le operazioni `GetItem` e `BatchGetItem` non possono essere utilizzate su un indice secondario globale.

### Esecuzione di query su un indice secondario globale
<a name="GSI.Querying"></a>

È possibile utilizzare l'operazione `Query` per accedere a uno o più elementi in un indice secondario globale. La query deve specificare il nome della tabella di base e il nome dell'indice che si desidera utilizzare, gli attributi da restituire nei risultati della query e qualsiasi condizione di query da applicare. DynamoDB può restituire i risultati in ordine crescente o decrescente.

Considera i dati seguenti restituiti da un'operazione `Query` che richiede dati di gioco per un'applicazione di tipo classifica.

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

In questa query:
+ DynamoDB *GameTitleIndex*accede, utilizzando la chiave di partizione per individuare gli elementi *GameTitle*dell'indice per Meteor Blasters. Tutti gli elementi dell'indice con questa chiave sono memorizzati l'uno accanto all'altro per il recupero rapido.
+ All'interno di questo gioco, DynamoDB utilizza l'indice per accedere a tutti gli IDs utenti e ai punteggi più alti di questo gioco.
+ Vengono restituiti i risultati in base all'ordine decrescente, perché il parametro `ScanIndexForward` è impostato su false.

### Scansione di un indice secondario globale
<a name="GSI.Scanning"></a>

È possibile utilizzare l'operazione `Scan` per recuperare tutti i dati da un indice secondario globale. Devi fornire il nome della tabella di base e il nome dell'indice nella richiesta. Con un'operazione `Scan`, DynamoDB legge tutti i dati nell'indice e li restituisce all'applicazione. Inoltre puoi richiedere che vengano restituiti solo alcuni dati e che quelli rimanenti vengano eliminati. A questo scopo, usa il parametro `FilterExpression` dell'operazione `Scan`. Per ulteriori informazioni, consulta [Espressioni di filtro per la scansione](Scan.md#Scan.FilterExpression).

## Sincronizzazione dei dati tra tabelle e indici secondari globali
<a name="GSI.Writes"></a>

DynamoDB sincronizza automaticamente ogni indice secondario globale con la relativa tabella di base. Quando un'applicazione scrive o elimina elementi in una tabella, ogni indice secondario globale nella tabella viene aggiornato in modo asincrono, usando un modello a consistenza finale. Le applicazioni non scrivono mai direttamente in un indice. Tuttavia, è importante comprendere le implicazioni di come DynamoDB mantiene questi indici.

 Gli indici secondari globali ereditano la modalità di read/write capacità dalla tabella base. Per ulteriori informazioni, consulta [Considerazioni sul passaggio tra modalità di capacità in DynamoDB](bp-switching-capacity-modes.md). 

Quando si crea un indice secondario globale, viene specificato uno o più attributi della chiave di indice e i rispettivi tipi di dati. Questo significa che ogni volta che scrivi un item nella tabella di base, i tipi di dati per questi attributi devono corrispondere ai tipi di dati dello schema della chiave dell'indice. Nel caso di `GameTitleIndex`, la chiave di partizione `GameTitle` nell'indice è definita come un tipo di dati `String`. La chiave di ordinamento `TopScore` nell'indice è di tipo `Number`. Se si prova ad aggiungere un elemento alla tabella `GameScores` e si specifica un tipo di dati diverso per `GameTitle` o `TopScore`, DynamoDB restituisce un `ValidationException` perché il tipo di dati non corrisponde.

Quando inserisci o elimini item in una tabella, ogni indice secondario globale nella tabella viene aggiornato in base a un modello eventualmente consistente. Le modifiche apportate ai dati della tabella vengono propagate in ogni indice secondario globale entro una frazione di secondo in condizioni normali. Tuttavia, in alcuni improbabili scenari di errore, possono verificarsi ritardi di propagazione prolungati. Per questo motivo, le applicazioni devono poter prevedere e gestire le situazioni in cui una query su un indice secondario globale restituisce risultati non aggiornati.

Se si scrive un elemento in una tabella, non è necessario specificare gli attributi per la chiave di ordinamento di alcun indice secondario globale. Utilizzando `GameTitleIndex` come un esempio, non devi specificare un valore per l'attributo `TopScore` per scrivere un nuovo item nella tabella `GameScores`. In questo caso, DynamoDB non scrive alcun dato nell'indice per questo particolare elemento.

Una tabella con molti indici secondari globali comporta costi maggiori per l'attività di scrittura rispetto alle tabelle con meno indici. Per ulteriori informazioni, consulta [Considerazioni sulla velocità di trasmissione effettiva assegnata per indici secondari globali](#GSI.ThroughputConsiderations).

## Classi di tabella con indici secondari globali
<a name="GSI.tableclasses"></a>

Un indice secondario globale utilizzerà sempre la stessa classe di tabella della tabella di base. Ogni volta che viene aggiunto un nuovo indice secondario globale per una tabella, il nuovo indice utilizzerà la stessa classe di tabella della tabella di base. Quando la classe di tabella di una tabella viene aggiornata, vengono aggiornati anche tutti gli indici secondari globali associati.

## Considerazioni sulla velocità di trasmissione effettiva assegnata per indici secondari globali
<a name="GSI.ThroughputConsiderations"></a>

Quando si crea un indice secondario globale su una tabella con modalità assegnata, è necessario specificare le unità di capacità di lettura e scrittura per il carico di lavoro previsto sull'indice. Le impostazioni correlate alla velocità effettiva assegnata di un indice secondario globale sono separate da quelle della relativa tabella di base. Un'operazione `Query` su un indice secondario globale utilizza unità di capacità di lettura dell'indice e non della tabella di base. Quando inserisci, aggiorni o elimini item in una tabella, anche gli indici secondari globali nella tabella vengono aggiornati. Questi aggiornamenti dell'indice utilizzano unità di capacità in scrittura dell'indice, non della tabella.

Ad esempio, se si esegui un'operazione `Query` su un indice secondario globale e se ne supera la capacità di lettura assegnata, la richiesta sarà sottoposta a limitazione. Se si esegue un'attività di scrittura pesante sulla tabella, ma un indice secondario globale su quella tabella ha una capacità di scrittura insufficiente, l'attività di scrittura sulla tabella verrà limitata.

**Importante**  
 Per evitare il possibile throttling, la capacità in scrittura assegnata per un indice secondario globale deve essere maggiore o uguale alla capacità in scrittura della tabella di base poiché nuovi aggiornamenti scrivono nella tabella di base e nell'indice secondario globale. 

Per visualizzare le impostazioni di velocità effettiva assegnata per un indice secondario globale, utilizza l'operazione `DescribeTable`. Vengono restituite informazioni dettagliare relative a tutti gli indici secondari della tabella.

### Unità di capacità in lettura
<a name="GSI.ThroughputConsiderations.Reads"></a>

Gli indici secondari globali supportano letture consistenti finali, ognuna delle quali utilizza metà di un'unità di capacità in lettura. Questo significa che una singola query su un indice secondario globale può recuperare fino a 2 × 4 KB = 8 KB per ogni unità di capacità di lettura.

Per le query su un indice secondario globale, DynamoDB calcola l'attività di lettura assegnata come avviene per le query sulle tabelle. L'unica differenza è che il calcolo si basa sulle dimensioni delle voci dell'indice, piuttosto che sulla dimensione dell'item nella tabella di base. Il numero di unità di capacità in lettura è la somma di tutte le dimensioni degli attributi proiettati di tutti gli elementi restituiti. Il risultato viene arrotondato al limite di 4 KB successivo. Per ulteriori informazioni sul modo in cui DynamoDB calcola l'uso della velocità effettiva assegnata, consulta [Modalità con capacità allocata di DynamoDB](provisioned-capacity-mode.md).

La dimensione massima dei risultati restituiti da un'operazione `Query` è 1 MB. Sono incluse le dimensioni di tutti i nomi e i valori degli attributi in tutti gli elementi restituiti.

Ad esempio, si consideri un indice secondario globale in cui ogni elemento contiene 2.000 byte di dati. Si supponga ora di eseguire un'operazione `Query` su questo indice e che `KeyConditionExpression` della query restituisca otto elementi. La dimensione totale degli elementi corrispondenti è 2.000 byte 8 elementi = 16.000 byte. Questo risultato viene quindi arrotondato al limite da 4 KB più vicino. Poiché le query su un indice secondario globale sono a consistenza finale, il costo totale equivale a 0,5 × (16 KB / 4 KB) o 2 unità di capacità di lettura.

### Unità di capacità in scrittura
<a name="GSI.ThroughputConsiderations.Writes"></a>

Quando un elemento in una tabella viene aggiunto, aggiornato o eliminato e questa operazione interessa un indice secondario globale, l'indice utilizzerà le unità di capacità di scrittura assegnate per l'operazione. Il costo totale del throughput assegnato per una scrittura è la somma delle unità di capacità in scrittura utilizzate dalla scrittura nella tabella di base e quelle utilizzate dall'aggiornamento degli indici secondari globali. Se una scrittura in una tabella non richiede l'aggiornamento di un indice secondario globale, non viene utilizzata capacità di scrittura dell'indice.

Perché la scrittura in una tabella riesca, le impostazioni correlate al throughput assegnato per la tabella e tutti i suoi indici secondari globali devono avere capacità in scrittura sufficiente per la scrittura. In caso contrario, la scrittura nella tabella viene limitata. 

**Importante**  
Quando si crea un indice secondario globale (GSI), le operazioni di scrittura sulla tabella di base possono essere sottoposte a limitazione (della larghezza di banda della rete) se l’attività dei GSI risultante dalle scritture sulla tabella di base supera la capacità di scrittura allocata del GSI. Questa limitazione (della larghezza di banda della rete) influisce su tutte le operazioni di scrittura, dal processo di indicizzazione alla potenziale interruzione dei carichi di lavoro di produzione. Per ulteriori informazioni, consulta [Risoluzione dei problemi di limitazione (della larghezza di banda della rete) in Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TroubleshootingThrottling.html).

Il costo della scrittura di un elemento in un indice secondario globale dipende da diversi fattori:
+ Se scrivi un nuovo item nella tabella che definisce un attributo indicizzato o aggiorni un item esistente per definire un attributo indicizzato in precedenza non definito, è necessario eseguire un'operazione di scrittura per inserire l'item nell'indice.
+ Se un aggiornamento della tabella modifica il valore di un attributo chiave indicizzato (da A a B), sono necessarie due scritture, una per eliminare l'item precedente dall'indice e un'altra per inserire il nuovo item nell'indice.  
+ Se un item era presente nell'indice, ma una scrittura nella tabella ha causato la cancellazione dell'attributo indicizzato, è necessaria una scrittura per eliminare la proiezione dell'item precedente dall'indice.
+ Se un item non è presente nell'indice prima o dopo l'aggiornamento dell'item, non vi è alcun costo di scrittura aggiuntivo per l'indice.
+ Se un aggiornamento della tabella modifica solo il valore degli attributi proiettati nello schema della chiave dell'indice, ma non modifica il valore di alcun attributo di chiave indicizzato, è necessaria una scrittura per aggiornare i valori degli attributi proiettati nell'indice.

Tutti questi fattori presuppongono che la dimensione di ciascun elemento nell'indice sia minore o uguale alla dimensione dell'elemento di 1 KB per il calcolo delle unità di capacità di scrittura. Le voci di indice più grandi richiedono unità di capacità in scrittura aggiuntive. Puoi contenere al minimo i costi di scrittura considerando gli attributi che le query devono restituire e proiettando solo tali attributi nell'indice.

## Considerazioni sullo storage per indici secondari globali
<a name="GSI.StorageConsiderations"></a>

Quando un'applicazione scrive un elemento in una tabella, DynamoDB copia automaticamente il sottoinsieme di attributi corretto in qualsiasi indice secondario globale in cui devono essere presenti gli attributi. All' AWS account vengono addebitati i costi per la memorizzazione dell'articolo nella tabella di base e anche per la memorizzazione degli attributi in tutti gli indici secondari globali di tale tabella.

La quantità di spazio utilizzata da un item dell'indice è la somma di quanto segue:
+ La dimensione in byte della chiave primaria della tabella di base (chiave di partizione e chiave di ordinamento)
+ La dimensione in byte dell'attributo della chiave di indicizzazione
+ La dimensione in byte degli attributi proiettati (se presenti)
+ 100 byte di sovraccarico per elemento dell'indice

Per stimare i requisiti di archiviazione per un indice secondario globale, è possibile stimare la dimensione media di un elemento nell'indice e quindi moltiplicarla per il numero di elementi nella tabella di base che hanno attributi di chiave dell'indice secondario globale.

Se una tabella contiene un elemento in cui uno o più attributi particolari non sono definiti, ma tale attributo è definito come chiave di partizione dell'indice o chiave di ordinamento, DynamoDB non scrive alcun dato per quell'elemento nell'indice.

# Modelli di progettazione
<a name="GSI.DesignPatterns"></a>

I modelli di progettazione forniscono soluzioni comprovate alle sfide più comuni quando si lavora con indici secondari globali. Questi modelli consentono di creare applicazioni efficienti e scalabili mostrando come strutturare gli indici per casi d'uso specifici.

Ogni modello include una guida all'implementazione completa con esempi di codice, best practice e casi d'uso reali per aiutarvi ad applicare il pattern alle vostre applicazioni.

**Topics**
+ [Chiavi con più attributi](GSI.DesignPattern.MultiAttributeKeys.md)

# Schema di chiavi multiattributo
<a name="GSI.DesignPattern.MultiAttributeKeys"></a>

## Panoramica
<a name="GSI.DesignPattern.MultiAttributeKeys.Overview"></a>

Le chiavi multi-attributo consentono di creare chiavi di partizione e ordinamento del Global Secondary Index (GSI) composte da un massimo di quattro attributi ciascuna. Ciò riduce il codice lato client e semplifica la modellazione iniziale dei dati e l'aggiunta di nuovi modelli di accesso in un secondo momento.

Consideriamo uno scenario comune: per creare un GSI che interroghi gli elementi in base a più attributi gerarchici, tradizionalmente è necessario creare chiavi sintetiche concatenando i valori. Ad esempio, in un'app di gioco, per interrogare le partite dei tornei per torneo, regione e round, potresti creare una chiave di partizione GSI sintetica come TOURNAMENT\$1 WINTER2 024 \$1REGION \$1NA -EAST e una chiave di ordinamento sintetica come ROUND \$1SEMIFINALS \$1BRACKET \$1UPPER. Questo approccio funziona, ma richiede la concatenazione di stringhe durante la scrittura dei dati, l'analisi durante la lettura e il riempimento delle chiavi sintetiche su tutti gli elementi esistenti se si aggiunge il GSI a una tabella esistente. Ciò rende il codice più disordinato e difficile da mantenere la sicurezza dei tipi sui singoli componenti chiave.

Le chiavi multiattributo risolvono questo problema per. GSIs Definisci la tua chiave di partizione GSI utilizzando più attributi esistenti come TournamentID e region. DynamoDB gestisce automaticamente la logica delle chiavi composite, eseguendo l'hashing delle stesse per la distribuzione dei dati. Gli elementi vengono scritti utilizzando gli attributi naturali del modello di dominio e il GSI li indicizza automaticamente. Nessuna concatenazione, nessuna analisi, nessun riempimento. Il codice rimane pulito, i dati rimangono digitati e le query rimangono semplici. Questo approccio è particolarmente utile quando si hanno dati gerarchici con raggruppamenti di attributi naturali (come torneo → regione → round o organizzazione → dipartimento → squadra).

## Esempio di applicazione
<a name="GSI.DesignPattern.MultiAttributeKeys.ApplicationExample"></a>

Questa guida illustra la creazione di un sistema di tracciamento delle partite di torneo per una piattaforma di eSport. La piattaforma deve interrogare in modo efficiente le partite su più fronti: per torneo e regione per la gestione dei gironi, per giocatore per la cronologia delle partite e per data di programmazione.

## Modello di dati
<a name="GSI.DesignPattern.MultiAttributeKeys.DataModel"></a>

In questa procedura dettagliata, il sistema di tracciamento delle partite dei tornei supporta tre modelli di accesso principali, ognuno dei quali richiede una struttura chiave diversa:

**Schema di accesso 1:** cerca una partita specifica in base al relativo ID univoco
+ **Soluzione:** tabella di base con `matchId` chiave di partizione

**Schema di accesso 2:** interroga tutte le partite di un torneo e di una regione specifici, filtrandole facoltativamente per round, tabellone o partita
+ **Soluzione:** Indice secondario globale con chiave di partizione multiattributo (`tournamentId`\$1`region`) e chiave di ordinamento multiattributo (\$1) `round` `bracket` `matchId`
+ **Query di esempio:** «Tutte le partite WINTER2 024 nella regione NA-EST» o «Tutte le SEMIFINALI corrispondono nel girone SUPERIORE per 024/NA-EST» WINTER2

**Schema di accesso 3:** interroga la cronologia delle partite di un giocatore, filtrandola facoltativamente per intervallo di date o round del torneo
+ **Soluzione:** Indice secondario globale con chiave di partizione singola (`player1Id`) e chiave di ordinamento multiattributo (\$1) `matchDate` `round`
+ **Domande di esempio:** «Tutte le partite del giocatore 101" o «Le partite del giocatore 101 a gennaio 2024"

La differenza fondamentale tra gli approcci tradizionali e quelli con più attributi diventa evidente quando si esamina la struttura degli elementi:

**Approccio tradizionale all'indice secondario globale (chiavi concatenate):**

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

**Approccio all'indice secondario globale multiattributo (chiavi native):**

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

Con le chiavi multiattributo, gli elementi vengono scritti una sola volta con attributi di dominio naturali. DynamoDB li indicizza automaticamente su GSIs più pagine senza richiedere chiavi sintetiche concatenate.

**Schema della tabella di base:**
+ Chiave di partizione: `matchId` (1 attributo)

**Schema dell'indice secondario globale (TournamentRegionIndex con chiavi multiattributo):**
+ Chiave di partizione:`tournamentId`, `region` (2 attributi)
+ Chiave di ordinamento: `round``bracket`,, `matchId` (3 attributi)

**Schema dell'indice secondario globale (PlayerMatchHistoryIndex con chiavi multiattributo):**
+ Chiave di partizione: `player1Id` (1 attributo)
+ Chiave di ordinamento:`matchDate`, `round` (2 attributi)

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


| MatchID (PK) | ID del torneo | region | round | staffa | ID giocatore 1 | ID giocatore 2 | Data della partita | vincitore | punteggio | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| partita-001 | WINTER2024 | NA-EST | FINALE | CAMPIONATO | 101 | 103 | 2024-01-20 | 101 | 3-1 | 
| inquadrato-002 | WINTER2024 | NA-EST | SEMIFINALI | UPPER | 101 | 105 | 2024-01-18 | 101 | 3-2 | 
| inquadrato-003 | WINTER2024 | NA-EST | SEMIFINALI | UPPER | 103 | 107 | 2024-01-18 | 103 | 3-0 | 
| inquadrato-004 | WINTER2024 | NA-EST | QUARTI DI FINALE | UPPER | 101 | 109 | 2024-01-15 | 101 | 3-1 | 
| inquadrato-005 | WINTER2024 | NORD-OVEST | FINALE | CAMPIONATO | 102 | 104 | 2024-01-20 | 102 | 3-2 | 
| inquadrato-006 | WINTER2024 | NORD-OVEST | SEMIFINALI | UPPER | 102 | 106 | 2024-01-18 | 102 | 3-1 | 
| partita-007 | SPRING2024 | NA-EST | QUARTI DI FINALE | UPPER | 101 | 108 | 2024-03-15 | 101 | 3-0 | 
| inquadrato-008 | SPRING2024 | NA-EST | QUARTI DI FINALE | LOWER | 103 | 110 | 2024-03-15 | 103 | 3-2 | 

### GSI: TournamentRegionIndex (chiavi multiattributo)
<a name="GSI.DesignPattern.MultiAttributeKeys.TournamentRegionIndexTable"></a>


| ID torneo (PK) | regione (PK) | rotondo (SK) | staffa (SK) | MatchID (SK) | ID giocatore 1 | ID giocatore 2 | Data della partita | vincitore | punteggio | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| WINTER2024 | NA-EST | FINALE | CAMPIONATO | partita-001 | 101 | 103 | 2024-01-20 | 101 | 3-1 | 
| WINTER2024 | NA-EST | QUARTI DI FINALE | UPPER | match-004 | 101 | 109 | 2024-01-15 | 101 | 3-1 | 
| WINTER2024 | NA-EST | SEMIFINALI | UPPER | partita-002 | 101 | 105 | 2024-01-18 | 101 | 3-2 | 
| WINTER2024 | NA-EST | SEMIFINALI | UPPER | match-003 | 103 | 107 | 2024-01-18 | 103 | 3-0 | 
| WINTER2024 | NORD-OVEST | FINALE | CAMPIONATO | partita-005 | 102 | 104 | 2024-01-20 | 102 | 3-2 | 
| WINTER2024 | NORD-OVEST | SEMIFINALI | UPPER | partita-006 | 102 | 106 | 2024-01-18 | 102 | 3-1 | 
| SPRING2024 | NA-EST | QUARTI DI FINALE | LOWER | partita-008 | 103 | 110 | 2024-03-15 | 103 | 3-2 | 
| SPRING2024 | NA-EST | QUARTI DI FINALE | UPPER | match-007 | 101 | 108 | 2024-03-15 | 101 | 3-0 | 

### GSI: PlayerMatchHistoryIndex (chiavi multiattributo)
<a name="GSI.DesignPattern.MultiAttributeKeys.PlayerMatchHistoryIndexTable"></a>


| Player1ID (PK) | MatchDate (SK) | rotondo (SK) | ID del torneo | region | staffa | MatchID | ID giocatore 2 | vincitore | punteggio | 
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | 
| 101 | 2024-01-15 | QUARTI DI FINALE | WINTER2024 | NA-EST | UPPER | partita-004 | 109 | 101 | 3-1 | 
| 101 | 2024-01-18 | SEMIFINALI | WINTER2024 | NA-EST | UPPER | partita-002 | 105 | 101 | 3-2 | 
| 101 | 2024-01-20 | FINALE | WINTER2024 | NA-EST | CAMPIONATO | partita-001 | 103 | 101 | 3-1 | 
| 101 | 2024-03-15 | QUARTI DI FINALE | SPRING2024 | NA-EST | UPPER | partita-007 | 108 | 101 | 3-0 | 
| 102 | 2024-01-18 | SEMIFINALI | WINTER2024 | NORD-OVEST | UPPER | incontro 006 | 106 | 102 | 3-1 | 
| 102 | 2024-01-20 | FINALE | WINTER2024 | NORD-OVEST | CAMPIONATO | partita-005 | 104 | 102 | 3-2 | 
| 103 | 2024-01-18 | SEMIFINALI | WINTER2024 | NA-EST | UPPER | partita-003 | 107 | 103 | 3-0 | 
| 103 | 2024-03-15 | QUARTI DI FINALE | SPRING2024 | NA-EST | LOWER | partita-008 | 110 | 103 | 3-2 | 

## Prerequisiti
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites"></a>

Prima di iniziare, assicurati di disporre dei seguenti elementi:

### Account e autorizzazioni
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites.AWSAccount"></a>
+ Un AWS account attivo ([creane uno qui](https://aws.amazon.com/free/) se necessario)
+ Autorizzazioni IAM per le operazioni DynamoDB:
  + `dynamodb:CreateTable`
  + `dynamodb:DeleteTable`
  + `dynamodb:DescribeTable`
  + `dynamodb:PutItem`
  + `dynamodb:Query`
  + `dynamodb:BatchWriteItem`

**Nota**  
**Nota di sicurezza:** per l'uso in produzione, crea una policy IAM personalizzata con solo le autorizzazioni necessarie. Per questo tutorial, puoi utilizzare la policy AWS `AmazonDynamoDBFullAccessV2` gestita.

### Ambiente di sviluppo
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites.DevEnvironment"></a>
+ Node.js installato sul tuo computer
+ AWS credenziali configurate utilizzando uno di questi metodi:

**Opzione 1: AWS CLI**

```
aws configure
```

**Opzione 2: variabili di 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
```

### Installazione dei pacchetti richiesti
<a name="GSI.DesignPattern.MultiAttributeKeys.Prerequisites.InstallPackages"></a>

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

## Implementazione
<a name="GSI.DesignPattern.MultiAttributeKeys.Implementation"></a>

### Passaggio 1: creare una tabella GSIs utilizzando chiavi multiattributo
<a name="GSI.DesignPattern.MultiAttributeKeys.CreateTable"></a>

Crea una tabella con una struttura chiave di base semplice e GSIs che utilizzi chiavi multiattributo.

#### esempio di codice
<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");
```

**Principali decisioni di progettazione:**

**Tabella base:** la tabella base utilizza una semplice chiave di `matchId` partizione per le ricerche dirette, mantenendo la struttura della tabella di base semplice e GSIs fornendo al contempo modelli di interrogazione complessi.

**TournamentRegionIndex Indice secondario globale: l'indice** secondario `TournamentRegionIndex` globale utilizza `tournamentId` \$1 `region` come chiave di partizione multiattributo, creando un isolamento tra regione e torneo in cui i dati vengono distribuiti combinando l'hash di entrambi gli attributi, permettendo di eseguire query efficienti all'interno di un contesto specifico tra torneo e regione. La chiave di ordinamento multiattributo (`round`\$1 `bracket` \$1`matchId`) fornisce un ordinamento gerarchico che supporta le query a qualsiasi livello della gerarchia con un ordinamento naturale da generale (round) a specifico (match ID).

**PlayerMatchHistoryIndex Indice secondario globale:** il `PlayerMatchHistoryIndex` Global Secondary Index riorganizza i dati per giocatore utilizzandoli `player1Id` come chiave di partizione, abilitando le interrogazioni tra tornei per un giocatore specifico. La chiave di ordinamento multiattributo (`matchDate`\$1`round`) fornisce un ordine cronologico con la possibilità di filtrare per intervalli di date o turni di torneo specifici.

### Fase 2: Inserimento di dati con attributi nativi
<a name="GSI.DesignPattern.MultiAttributeKeys.InsertData"></a>

Aggiungi i dati delle partite del torneo utilizzando attributi naturali. Il GSI indicizzerà automaticamente questi attributi senza richiedere chiavi sintetiche.

#### esempio di codice
<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");
```

**Struttura dei dati spiegata:**

**Utilizzo degli attributi naturali:** ogni attributo rappresenta un vero concetto di torneo senza necessità di concatenazione o analisi di stringhe, e fornisce una mappatura diretta al modello di dominio.

Indicizzazione **automatica dell'indice secondario globale: indicizza** GSIs automaticamente gli elementi utilizzando gli attributi esistenti (`tournamentId`,,,, `matchId` for TournamentRegionIndex e `region` `round` `bracket` `player1Id``matchDate`, `round` for PlayerMatchHistoryIndex) senza richiedere chiavi concatenate sintetiche.

**Non è necessario il backfill:** quando aggiungi un nuovo indice secondario globale con chiavi multiattributo a una tabella esistente, DynamoDB indicizza automaticamente tutti gli elementi esistenti utilizzando i loro attributi naturali, senza bisogno di aggiornare gli elementi con chiavi sintetiche.

### Fase 3: Interroga l'indice secondario globale con tutti gli attributi delle chiavi di partizione TournamentRegionIndex
<a name="GSI.DesignPattern.MultiAttributeKeys.QueryAllPartitionKeys"></a>

Questo esempio interroga il TournamentRegionIndex Global Secondary Index che ha una chiave di partizione multiattributo (\$1). `tournamentId` `region` Tutti gli attributi delle chiavi di partizione devono essere specificati con condizioni di uguaglianza nelle query: non è possibile eseguire query `tournamentId` solo con o utilizzare operatori di disuguaglianza sugli attributi delle chiavi di partizione.

#### esempio di codice
<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`);
});
```

**Output previsto:**

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

**Interrogazioni non valide:**

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

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

**Prestazioni: le** chiavi di partizione con più attributi vengono codificate insieme, fornendo le stesse prestazioni di ricerca O (1) delle chiavi ad attributo singolo.

### Fase 4: Interroga le chiavi di ordinamento dell'indice secondario globale left-to-right
<a name="GSI.DesignPattern.MultiAttributeKeys.QuerySortKeysLeftToRight"></a>

Gli attributi delle chiavi di ordinamento devono essere interrogati left-to-right nell'ordine in cui sono definiti nell'indice secondario globale. Questo esempio dimostra l'esecuzione di interrogazioni TournamentRegionIndex a diversi livelli gerarchici: filtraggio per just`round`, per `round` \$1 `bracket` o per tutti e tre gli attributi chiave di ordinamento. Non è possibile saltare gli attributi al centro, ad esempio non è possibile eseguire una query per e mentre si salta. `round` `matchId` `bracket`

#### esempio di codice
<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`);
}
```

**Output previsto:**

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

**Left-to-right regole di interrogazione:** è necessario interrogare gli attributi in ordine da sinistra a destra, senza saltarne nessuno.

**Modelli validi:**
+ Solo primo attributo: `round = 'SEMIFINALS'`
+ Primi due attributi: `round = 'SEMIFINALS' AND bracket = 'UPPER'`
+ Tutti e tre gli attributi: `round = 'SEMIFINALS' AND bracket = 'UPPER' AND matchId = 'match-002'`

**Schemi non validi:**
+ Il primo attributo viene ignorato: `bracket = 'UPPER'` (salta il giro)
+ Interrogazione non corretta: `matchId = 'match-002' AND round = 'SEMIFINALS'`
+ Lasciare spazi vuoti: `round = 'SEMIFINALS' AND matchId = 'match-002'` (salta la parentesi)

**Nota**  
**Suggerimento di progettazione:** ordina gli attributi chiave di ordinamento dal più generale al più specifico per massimizzare la flessibilità delle query.

### Fase 5: Utilizza le condizioni di disuguaglianza nelle chiavi di ordinamento dell'Indice secondario globale
<a name="GSI.DesignPattern.MultiAttributeKeys.InequalityConditions"></a>

Le condizioni di disuguaglianza devono essere l'ultima condizione della ricerca. Questo esempio dimostra l'utilizzo degli operatori di confronto (`>=`,`BETWEEN`) e del prefisso matching (`begins_with()`) sugli attributi delle chiavi di ordinamento. Una volta utilizzato un operatore di disuguaglianza, non è possibile aggiungere ulteriori condizioni di chiave di ordinamento dopo di esso: la disuguaglianza deve essere l'ultima condizione nell'espressione della condizione chiave.

#### esempio di codice
<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`);
}
```

**Regole degli operatori di disuguaglianza:** è possibile utilizzare gli operatori di confronto (`>`,,,`<=`) `>=``<`, `BETWEEN` per le interrogazioni sugli intervalli e per la corrispondenza dei prefissi. `begins_with()` La disuguaglianza deve essere l'ultima condizione della query.

**Schemi validi:**
+ Condizioni di uguaglianza seguite dalla disuguaglianza: `round = 'SEMIFINALS' AND bracket = 'UPPER' AND matchId > 'match-001'`
+ Disuguaglianza sul primo attributo: `round BETWEEN 'QUARTERFINALS' AND 'SEMIFINALS'`
+ Corrispondenza del prefisso come condizione finale: `round = 'SEMIFINALS' AND begins_with(bracket, 'U')`

**Schemi non validi:**
+ Aggiungere condizioni dopo una disuguaglianza: `round > 'QUARTERFINALS' AND bracket = 'UPPER'`
+ Utilizzo di più disuguaglianze: `round > 'QUARTERFINALS' AND bracket > 'L'`

**Importante**  
`begins_with()`viene considerata una condizione di disuguaglianza, quindi non può essere seguita da alcuna condizione di chiave di ordinamento aggiuntiva.

### Fase 6: Interrogazione dell'indice secondario PlayerMatchHistoryIndex globale con una chiave di ordinamento multiattributo
<a name="GSI.DesignPattern.MultiAttributeKeys.QueryPlayerHistory"></a>

Questo esempio interroga il PlayerMatchHistoryIndex che ha una chiave di partizione singola (`player1Id`) e una chiave di ordinamento con più attributi (\$1). `matchDate` `round` Ciò consente l'analisi tra tornei interrogando tutte le partite di un giocatore specifico senza conoscere il torneo, IDs mentre la tabella base richiederebbe interrogazioni separate per combinazione torneo-regione.

#### esempio di codice
<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`);
```

## Variazioni del modello
<a name="GSI.DesignPattern.MultiAttributeKeys.PatternVariations"></a>

### Dati di serie temporali con chiavi multiattributo
<a name="GSI.DesignPattern.MultiAttributeKeys.TimeSeries"></a>

Ottimizzazione per le query su serie temporali con attributi temporali gerarchici

#### esempio di codice
<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
```

**Vantaggi:** la gerarchia temporale naturale (anno → mese → giorno → timestamp) consente una granularità delle query efficienti in qualsiasi momento senza analisi o manipolazione della data. L'indice secondario globale indicizza automaticamente tutte le letture utilizzando i loro attributi temporali naturali.

### Ordini di e-commerce con chiavi multiattributo
<a name="GSI.DesignPattern.MultiAttributeKeys.ECommerce"></a>

Tieni traccia degli ordini con più dimensioni

#### esempio di codice
<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
```

### Dati organizzativi gerarchici
<a name="GSI.DesignPattern.MultiAttributeKeys.Hierarchical"></a>

Gerarchie organizzative modello

#### esempio di codice
<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
```

### Chiavi sparse con più attributi
<a name="GSI.DesignPattern.MultiAttributeKeys.Sparse"></a>

Combina chiavi multiattributo per creare un GSI sparso

#### esempio di codice
<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
```

### Multi-tenancy SaaS
<a name="GSI.DesignPattern.MultiAttributeKeys.SaaS"></a>

Piattaforma SaaS multi-tenant con isolamento del cliente

#### esempio di codice
<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'
    }
}));
```

**Vantaggi:** interrogazioni efficienti nel contesto tra inquilino e cliente e organizzazione naturale dei dati.

### Transazioni finanziarie
<a name="GSI.DesignPattern.MultiAttributeKeys.Financial"></a>

Sistema bancario che monitora le transazioni del conto utilizzando GSIs

#### esempio di codice
<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'
    }
}));
```

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

L'esempio seguente illustra le chiavi con più attributi dalla configurazione alla pulizia:

### esempio di codice
<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);
```

**Scaffold di codice minimo**

### esempio di codice
<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' }
```

## Risorse aggiuntive
<a name="GSI.DesignPattern.MultiAttributeKeys.AdditionalResources"></a>
+ [Best practice per DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/best-practices.html)
+ [Lavorare con tabelle e dati](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithTables.html)
+ [Indici secondari globali](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html)
+ [Operazioni di interrogazione e scansione](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html)

# Gestione degli indici secondari globali in DynamoDB
<a name="GSI.OnlineOps"></a>

In questa sezione viene descritto come creare, modificare ed eliminare indici secondari globali in Amazon DynamoDB.

**Topics**
+ [Creazione di una tabella con indici secondari globali](#GSI.Creating)
+ [Descrizione degli indici secondari globali su una tabella](#GSI.Describing)
+ [Aggiunta di un indice secondario globale a una tabella esistente](#GSI.OnlineOps.Creating)
+ [Eliminazione di un indice secondario globale](#GSI.OnlineOps.Deleting)
+ [Modifica di un indice secondario globale durante la creazione](#GSI.OnlineOps.Creating.Modify)

## Creazione di una tabella con indici secondari globali
<a name="GSI.Creating"></a>

Per creare una tabella con uno o più indici secondari globali, utilizza l'operazione `CreateTable` con il parametro `GlobalSecondaryIndexes`. Per una massima flessibilità delle query, è possibile creare fino a 20 indici secondari globali (quota predefinita) per tabella. 

Devi specificare un attributo che funga da chiave di partizione dell'indice. Facoltativamente, puoi specificare un altro attributo per la chiave di ordinamento dell'indice. Non è necessario che questi attributi di chiave coincidano con quelli della tabella. Ad esempio, nella *GameScores*tabella (vedi[Utilizzo degli indici secondari globali in DynamoDB](GSI.md)), `TopScore` né lo `TopScoreDateTime` sono gli attributi chiave. È possibile creare un indice secondario globale con una chiave di partizione `TopScore` e una chiave di ordinamento `TopScoreDateTime`. Puoi utilizzare questo indice per determinare se esiste una correlazione tra punteggi elevati e il periodo del giorno in cui si svolge un gioco.

Ogni attributo di chiave dell'indice deve essere uno scalare di tipo `String`, `Number` o `Binary` (non può essere un tipo documento o set). È possibile proiettare gli attributi di qualsiasi tipo di dati in un indice secondario globale. Questo include scalari, documenti e set. Per l'elenco completo dei tipi di dati, consulta [Tipi di dati](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes).

Se utilizzi la modalità assegnata, devi specificare le impostazioni `ProvisionedThroughput` per l'indice, consistenti di `ReadCapacityUnits` e `WriteCapacityUnits`. Queste impostazioni di throughput assegnato sono distinte da quelle della tabella, ma il funzionamento è analogo. Per ulteriori informazioni, consulta [Considerazioni sulla velocità di trasmissione effettiva assegnata per indici secondari globali](GSI.md#GSI.ThroughputConsiderations).

 Gli indici secondari globali ereditano la modalità di read/write capacità dalla tabella di base. Per ulteriori informazioni, consulta [Considerazioni sul passaggio tra modalità di capacità in DynamoDB](bp-switching-capacity-modes.md). 

**Nota**  
 Quando si crea un nuovo GSI, può essere importante verificare se la chiave di partizione scelta sta producendo una distribuzione irregolare o ristretta di dati o traffico attraverso i valori chiave della partizione del nuovo indice. In questo caso, è possibile che si verifichino operazioni di backfill e scrittura contemporaneamente e limitando le scritture nella tabella di base. Il servizio adotta misure per ridurre al minimo il potenziale di questo scenario, ma non ha alcuna visione della forma dei dati dei clienti in relazione alla chiave di partizione dell'indice, alla proiezione scelta o alla scarsità della chiave primaria dell'indice.  
Se si sospetta che il nuovo indice secondario globale possa avere dati limitati o disallineati o distribuzione del traffico tra i valori chiave delle partizioni, considerare quanto segue prima di aggiungere nuovi indici alle tabelle di importanza operativa.  
Potrebbe essere più sicuro aggiungere l'indice in un momento in cui l’applicazione guida la quantità minima di traffico.
Valuta la possibilità di abilitare CloudWatch Contributor Insights sulla tabella e sugli indici di base. Questo ti darà informazioni preziose sulla distribuzione del traffico.
 Guarda `WriteThrottleEvents` e `OnlineIndexPercentageProgress` CloudWatch metriche `ThrottledRequests` durante tutto il processo. Adatta la capacità di scrittura assegnata in base alle esigenze per completare il backfill in un tempo ragionevole senza effetti di limitazione significativi sulle operazioni in corso. `OnlineIndexConsumedWriteCapacity`e `OnlineThrottleEvents` si prevede che mostrino 0 durante il riempimento dell'indice.
Preparatevi ad annullare la creazione dell'indice se doveste riscontrare un impatto operativo dovuto alla limitazione della scrittura.

## Descrizione degli indici secondari globali su una tabella
<a name="GSI.Describing"></a>

Per visualizzare lo stato di tutti gli indici secondari globali su una tabella, utilizza l'operazione `DescribeTable`. La parte `GlobalSecondaryIndexes` della risposta mostra tutti gli indici della tabella insieme allo stato corrente di ciascuno (`IndexStatus`).

`IndexStatus` per un indice secondario globale sarà uno dei seguenti:
+ `CREATING`: l'indice è in fase di creazione e non è ancora disponibile per l'uso.
+ `ACTIVE`: l'indice è pronto per l'uso e le applicazioni possono eseguire operazioni `Query` sull'indice.
+ `UPDATING`: vengono modificate le impostazioni di velocità effettiva assegnata dell'indice.
+ `DELETING`: l'indice è attualmente in fase di eliminazione e non può essere più utilizzato.

Quando DynamoDB ha terminato la creazione di un indice secondario globale, lo stato dell'indice cambia da `CREATING` a `ACTIVE`.

## Aggiunta di un indice secondario globale a una tabella esistente
<a name="GSI.OnlineOps.Creating"></a>

Per aggiungere un indice secondario globale a una tabella esistente, utilizza l'operazione `UpdateTable` con il parametro `GlobalSecondaryIndexUpdates`. Devi specificare quanto segue:
+ Un nome per l'indice. Il nome deve essere univoco tra tutti gli indici della tabella.
+ Lo schema delle chiavi dell'indice. Devi specificare un attributo per la chiave di partizione dell'indice; opzionalmente, puoi specificare un altro attributo per la chiave di ordinamento dell'indice. Non è necessario che questi attributi di chiave coincidano con quelli della tabella. I tipi di dati per ogni attributo dello schema devono essere scalari: `String`, `Number` o `Binary`.
+ Gli attributi da proiettare dalla tabella all'indice:
  + `KEYS_ONLY`: ogni elemento dell'indice è costituito solo dalla chiave di partizione della tabella e dai valori della chiave di ordinamento, oltre ai valori della chiave di indice. 
  + `INCLUDE`: oltre agli attributi descritti in `KEYS_ONLY`, l'indice secondario include gli altri attributi non chiave che sono stati specificati.
  + `ALL`: l'indice include tutti gli attributi della tabella di origine.
+ Le impostazioni di throughput assegnato dell'indice, consistenti di `ReadCapacityUnits` e `WriteCapacityUnits`. Queste impostazioni di throughput assegnato sono distinte da quelle della tabella.

È possibile creare un solo indice secondario globale per operazione `UpdateTable`.

### Fasi della creazione di un indice
<a name="GSI.OnlineOps.Creating.Phases"></a>

Quando si aggiunge un nuovo indice secondario locale a una tabella esistente, mentre l'indice viene creato la tabella continua a essere disponibile. Tuttavia, il nuovo indice non è disponibile per le operazioni Query finché il suo stato non cambia da `CREATING` ad `ACTIVE`.

**Nota**  
La creazione di un indice secondario globale non utilizza Application Auto Scaling. L’aumento della capacità di `MIN` Application Auto Scaling non diminuisce il tempo di creazione dell'indice secondario globale.

Dietro le quinte, DynamoDB crea l'indice in due fasi:

**Allocazione delle risorse**  
DynamoDB alloca le risorse di calcolo e archiviazione necessarie per la creazione dell'indice.  
Durante la fase di allocazione delle risorse, l'attributo `IndexStatus` è `CREATING` e l'attributo `Backfilling` è false. Utilizza l'operazione `DescribeTable` per recuperare lo stato di una tabella e di tutti i suoi indici secondari.  
Mentre l'indice si trova nella fase di allocazione delle risorse, non puoi eliminare l'indice o la sua tabella padre. Inoltre, non puoi modificare il throughput assegnato dell'indice o della tabella. Non puoi aggiungere o eliminare altri indici nella tabella. Tuttavia, puoi modificare il throughput assegnato di questi altri indici.

**Compilazione**  
Per ogni elemento nella tabella, DynamoDB determina quale set di attributi scrivere sull'indice in base alla relativa proiezione (`KEYS_ONLY`, `INCLUDE` o `ALL`). Procede quindi a scrivere questi attributi nell'indice. Durante la fase di backfill, DynamoDB tiene traccia degli elementi che vengono aggiunti, eliminati o aggiornati nella tabella. Anche gli attributi di questi item vengono aggiunti, eliminati o aggiornati nell'indice secondo il caso.  
Durante la fase di compilazione, l'attributo `IndexStatus` è impostato su `CREATING` e l'attributo `Backfilling` è true. Utilizza l'operazione `DescribeTable` per recuperare lo stato di una tabella e di tutti i suoi indici secondari.  
Mentre l'indice è in fase di compilazione, non puoi eliminare la tabella padre. Tuttavia, puoi comunque eliminare l'indice o modificare il throughput assegnato della tabella e di qualsiasi suoi indici secondari globali.  
Nella fase di compilazione, alcune scritture di item in violazione possono riuscire mentre altre vengono rifiutate. Dopo la compilazione, tutte le scritture negli elementi che violano lo schema delle chiavi del nuovo indice vengono rifiutate. È consigliabile eseguire lo strumento Rilevamento violazioni alla fine della fase di backfill per rilevare e risolvere le eventuali violazioni delle chiavi che possono essersi verificate. Per ulteriori informazioni, consulta [Rilevamento e correzione delle violazioni delle chiavi in DynamoDB](GSI.OnlineOps.ViolationDetection.md).

Mentre le fasi di allocazione delle risorse e compilazione sono in corso, l'indice si trova nello stato `CREATING`. Durante questo periodo, DynamoDB esegue operazioni di lettura sulla tabella. Le operazioni di lettura dalla tabella di base per popolare l'indice secondario globale non vengono addebitate.

Quando la creazione è terminata, lo stato dell'indice diventa `ACTIVE`. Non puoi eseguire operazioni di `Query` o `Scan` dell'indice finché è `ACTIVE`.

**Nota**  
In alcuni casi DynamoDB non è in grado di scrivere dati dalla tabella all'indice a causa di violazioni delle chiavi di indice. Ciò può verificarsi se:  
Il tipo di dati di un valore di attributo non corrisponde al tipo di dati di un tipo di dati dello schema della chiave dell'indice.
La dimensione di un attributo supera la lunghezza massima di un attributo della chiave dell'indice.
Un attributo della chiave dell'indice ha un valore di attributo String vuoto o Binary vuoto.
Le violazioni delle chiavi dell'indice non interferiscono con la creazione di un indice secondario. Tuttavia, quando l'indice diventa `ACTIVE`, le chiavi in violazione non sono presenti nell'indice.  
DynamoDB fornisce uno strumento autonomo per individuare e risolvere questi problemi. Per ulteriori informazioni, consulta [Rilevamento e correzione delle violazioni delle chiavi in DynamoDB](GSI.OnlineOps.ViolationDetection.md).

### Aggiunta di un indice secondario globale a una tabella di grandi dimensioni
<a name="GSI.OnlineOps.Creating.LargeTable"></a>

Il tempo necessario per creare un indice secondario globale dipende da diversi fattori, tra cui:
+ Le dimensioni della tabella
+ Il numero di item nella tabella idonei per essere inclusi nell'indice
+ Il numero di attributi proiettati nell'indice
+ L'attività di scrittura sulla tabella principale durante la creazione dell'indice

Se si aggiunge un indice secondario globale a una tabella molto grande, il processo di creazione potrebbe richiedere molto tempo. Per monitorare l'avanzamento e determinare se l'indice ha una capacità di scrittura sufficiente, consulta i seguenti CloudWatch parametri di Amazon:
+ `OnlineIndexPercentageProgress`

Per ulteriori informazioni sulle CloudWatch metriche relative a DynamoDB, vedere. [Parametri di DynamoDB](metrics-dimensions.md#dynamodb-metrics)

**Importante**  
Potrebbe essere necessario consentire l’inserimento di tabelle molto grandi prima di creare o aggiornare un indice secondario globale. Contatta l' AWS assistenza per inserire i tuoi tavoli nella lista dei preferiti.

Durante il backfill di un indice, DynamoDB utilizza la capacità interna del sistema per leggere dalla tabella. Ciò ha lo scopo di ridurre al minimo l'impatto della creazione dell'indice e di assicurare che la tabella non esaurisca la capacità di lettura.

## Eliminazione di un indice secondario globale
<a name="GSI.OnlineOps.Deleting"></a>

Se l'indice secondario globale non è più necessario, è possibile eliminarlo utilizzando l'operazione `UpdateTable`.

È possibile eliminare un solo indice secondario globale per operazione `UpdateTable`.

Mentre l'indice secondario globale viene eliminato, non vi sono effetti sull'attività di lettura o scrittura nella tabella padre. Durante l'eliminazione è comunque possibile modificare il throughput assegnato di altri indici.

**Nota**  
Quando elimini una tabella utilizzando l'operazione `DeleteTable`, vengono eliminati anche tutti gli indici secondari globali della tabella.
L'operazione di eliminazione dell'indice secondario globale non verrà addebitata sul tuo account.

## Modifica di un indice secondario globale durante la creazione
<a name="GSI.OnlineOps.Creating.Modify"></a>

Nel corso della creazione di un indice puoi utilizzare l'operazione `DescribeTable` per determinare in quale fase si trova. La descrizione dell'indice include un attributo booleano, `Backfilling`, che indica se DynamoDB sta attualmente caricando l'indice con elementi della tabella. Se `Backfilling` è true, la fase di allocazione delle risorse è terminata ed è in corso la compilazione dell'indice. 

Durante la fase di compilazione, puoi eliminare l'indice in corso di creazione. Durante questa fase, non puoi aggiungere o eliminare altri indici nella tabella.

**Nota**  
Per gli indici creati nell'ambito di un'operazione `CreateTable`, l'attributo `Backfilling` non compare nell'output di `DescribeTable`. Per ulteriori informazioni, consulta [Fasi della creazione di un indice](#GSI.OnlineOps.Creating.Phases).

# Rilevamento e correzione delle violazioni delle chiavi in DynamoDB
<a name="GSI.OnlineOps.ViolationDetection"></a>

Durante la fase di backfill della creazione dell'indice secondario globale, Amazon DynamoDB esamina ogni elemento nella tabella per determinare se è idoneo all'inclusione nell'indice. Alcuni elementi potrebbero non essere idonei perché causerebbero violazioni della chiave dell'indice. In questi casi, gli elementi rimangono nella tabella, ma l'indice non ha una voce corrispondente per tale elemento.

Una *violazione della chiave di indice* si verifica nelle seguenti situazioni:
+ È presente una mancata corrispondenza del tipo di dati tra un valore di attributo e il tipo di dati dello schema della chiave dell'indice. Si supponga, ad esempio, che uno degli elementi nella tabella `GameScores` aveva un valore `TopScore` di tipo `String`. Se è stato aggiunto un indice secondario globale con una chiave di partizione di `TopScore`, di tipo `Number`, l'elemento della tabella violerebbe la chiave di indice.
+ Il valore di un attributo supera la lunghezza massima di un attributo della chiave dell'indice. La lunghezza massima di una chiave di partizione è 2048 byte e la lunghezza massima di una chiave di ordinamento è 1024 byte. Se uno qualsiasi dei valori di attributo corrispondenti nella tabella supera questi limiti, l'elemento della tabella violerebbe la chiave di indice.

**Nota**  
Se un valore di attributo String o Binary è impostato per un attributo utilizzato come chiave di indice, il valore dell'attributo deve avere una lunghezza maggiore di zero; in caso contrario, l'elemento della tabella violerebbe la chiave di indice.  
Questo strumento non contrassegna questa violazione della chiave di indice, al momento.

Se si verifica una violazione della chiave di indice, la fase di backfill continua senza interruzioni. Tuttavia, gli elementi che violano non saranno inclusi nell'indice. Una volta completata la fase di backfill, tutte le scritture sugli elementi che violano lo schema delle chiavi del nuovo indice saranno rifiutate.

Per identificare e correggere i valori degli attributi in una tabella che violano una chiave di indice, utilizza lo strumento Rilevatore di violazioni. Per eseguire Rilevatore di violazioni, è necessario creare un file di configurazione che specifica il nome di una tabella da sottoporre a scansione, i nomi e i tipi di dati della chiave di partizione dell'indice secondario globale e della chiave di ordinamento e quali azioni intraprendere se vengono rilevate violazioni della chiave di indice. Rilevatore di violazioni può essere eseguito in una delle due diverse modalità:
+ **Modalità di rilevamento**: rileva le violazioni della chiave dell'indice. Utilizza la modalità di rilevamento per segnalare gli elementi della tabella che causerebbero violazioni chiave in un indice secondario globale. Facoltativamente, è possibile richiedere che questi elementi di tabella che violano vengano eliminati immediatamente quando vengono rilevati. L'output dalla modalità di rilevamento viene scritto in un file, che è possibile utilizzare per ulteriori analisi.
+ **Modalità di correzione**: correggere le violazioni della chiave di indice. In modalità di correzione, Rilevatore violazioni legge un file di input con lo stesso formato del file di output dalla modalità di rilevamento. La modalità di correzione legge i record dal file di input e, per ogni record, elimina o aggiorna gli elementi corrispondenti nella tabella. Se si sceglie di aggiornare gli elementi, è necessario modificare il file di input e impostare i valori appropriati per questi aggiornamenti.

## Download ed esecuzione di Rilevatore violazioni
<a name="GSI.OnlineOps.ViolationDetection.Running"></a>

Rilevatore violazioni è disponibile come file eseguibile Java Archive (`.jar`) e viene eseguito su computer Windows, macOS o Linux. Rilevatore violazioni richiede Java 1.7 (o versioni successive) e Apache Maven.
+ [Scarica il rilevatore di violazioni da GitHub](https://github.com/awslabs/dynamodb-online-index-violation-detector)

Seguire le istruzioni riportate nel file `README.md` per scaricare e installare Rilevatore violazioni tramite Maven.

Per avviare Rilevatore violazioni, passare alla directory dove è stato creato `ViolationDetector.java` ed immettere il comando seguente.

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

La riga di comando di Rilevatore violazioni accetta le seguenti opzioni:
+ `-h | --help`: stampa un riepilogo di utilizzo e le opzioni per Rilevatore violazioni.
+ `-p | --configFilePath` `value`: il nome completo di un file di configurazione di Rilevatore violazioni. Per ulteriori informazioni, consulta [File di configurazione di Rilevatore violazioni](#GSI.OnlineOps.ViolationDetection.ConfigFile).
+ `-t | --detect` `value`: rileva le violazioni della chiave di indice nella tabella e le scrive nel file di output di Rilevatore violazioni. Se il valore di questo parametro è impostato su `keep`, gli elementi con violazioni della chiave non vengono modificati. Se il valore è impostato su `delete`, gli elementi con violazioni della chiave vengono eliminati dalla tabella.
+ `-c | --correct` `value`: legge le violazioni della chiave di indice da un file di input ed esegue azioni correttive sugli elementi della tabella. Se il valore di questo parametro è impostato su `update`, gli elementi con violazioni della chiave vengono aggiornati con valori nuovi e che non violano. Se il valore è impostato su `delete`, gli elementi con violazioni della chiave vengono eliminati dalla tabella.

## File di configurazione di Rilevatore violazioni
<a name="GSI.OnlineOps.ViolationDetection.ConfigFile"></a>

Durante il runtime, lo strumento Rilevatore violazioni richiede un file di configurazione. I parametri di questo file determinano a quali risorse DynamoDB può accedere Rilevatore violazioni e la velocità effettiva assegnata che può utilizzare. Nella tabella seguente vengono descritti questi parametri.


****  

| Nome del parametro | Description | Obbligatorio? | 
| --- | --- | --- | 
|  `awsCredentialsFile`  |  Il nome completo di un file contenente le credenziali AWS . Il file delle credenziali deve avere il seguente formato: <pre>accessKey = access_key_id_goes_here<br />secretKey = secret_key_goes_here </pre>  |  Sì  | 
|  `dynamoDBRegion`  |  La AWS regione in cui risiede la tabella. Ad esempio: `us-west-2`.  |  Sì  | 
|  `tableName`  | Il nome della tabella DynamoDB da sottoporre a scansione. |  Sì  | 
|  `gsiHashKeyName`  |  Il nome della chiave della partizione dell'indice.  |  Sì  | 
|  `gsiHashKeyType`  |  Il tipo di dati della chiave di partizione dell'indice, `String`, `Number` o `Binary`: `S \| N \| B`  |  Sì  | 
|  `gsiRangeKeyName`  |  Il nome della chiave di ordinamento dell'indice. Non specificare questo parametro se l'indice dispone solo di una chiave primaria semplice (chiave di partizione).  |  No  | 
|  `gsiRangeKeyType`  |  Il tipo di dati della chiave di ordinamento dell'indice, `String`, `Number` o `Binary`: `S \| N \| B`  Non specificare questo parametro se l'indice dispone solo di una chiave primaria semplice (chiave di partizione).  |  No  | 
|  `recordDetails`  |  Indica se scrivere i dettagli completi delle violazioni della chiave di indice nel file di output. Se impostato su `true` (impostazione predefinita), vengono riportate tutte le informazioni sugli elementi che violano. Se impostato su `false`, viene riportato solo il numero di violazioni.  |  No  | 
|  `recordGsiValueInViolationRecord`  |  Indica se scrivere i valori delle chiavi di indice che violano nel file di output. Se impostato su `true` (impostazione predefinita), vengono riportati i valori chiave. Se impostato su `false` (impostazione predefinita), i valori chiave non vengono riportati.  |  No  | 
|  `detectionOutputPath`  |  Il percorso completo del file di output di Rilevatore violazioni. Questo parametro supporta la scrittura in una directory locale o in Amazon Simple Storage Service (Amazon S3). Di seguito vengono mostrati gli esempi: `detectionOutputPath = ``//local/path/filename.csv` `detectionOutputPath = ``s3://bucket/filename.csv` Le informazioni nel file di output vengono visualizzate in formato CSV (valori separati da virgola). Se non si imposta `detectionOutputPath`, il file di output è denominato `violation_detection.csv`e viene scritto nella directory di lavoro corrente.  |  No  | 
|  `numOfSegments`  | Il numero di segmenti di scansione parallela da utilizzare quando Rilevatore violazioni esegue la scansione della tabella. Il valore predefinito è 1, che indica che la tabella viene scansionata in modo sequenziale. Se il valore è 2 o superiore, Rilevatore violazioni divide la tabella in tanti segmenti logici e un numero uguale di thread di scansione. L'impostazione massima per `numOfSegments` è 4.096.Per le tabelle più grandi, una scansione parallela è generalmente più veloce di una scansione sequenziale. Inoltre, se la tabella è abbastanza grande da estendersi su più partizioni, una scansione parallela distribuisce l'attività di lettura in modo uniforme su più partizioni.Per ulteriori informazioni sulle scansioni parallele in DynamoDB, consulta [Scansione parallela](Scan.md#Scan.ParallelScan). |  No  | 
|  `numOfViolations`  |  Il limite superiore delle violazioni della chiave di indice da scrivere nel file di output. Se impostato su `-1` (impostazione predefinita), viene scansionata l'intera tabella. Se impostato su un numero intero positivo, Rilevatore violazioni si interrompe dopo il rilevamento di tale numero di violazioni.  |  No  | 
|  `numOfRecords`  |  Il numero di elementi nella tabella da sottoporre a scansione. Se impostato su -1 (impostazione predefinita), viene scansionata l'intera tabella. Se impostato su un numero intero positivo, Rilevatore violazioni si interrompe dopo la scansione di molti elementi nella tabella.  |  No  | 
|  `readWriteIOPSPercent`  |  Regola la percentuale di unità di capacità di lettura assegnata utilizzate durante la scansione della tabella. I valori validi sono compresi tra `1` e `100`. Il valore predefinito (`25`) significa che Rilevatore violazioni non consumerà più del 25% della velocità effettiva di lettura assegnata della tabella.  |  No  | 
|  `correctionInputPath`  |  Il percorso completo del file di output di correzione di Rilevatore violazioni. Se si esegue Rilevatore violazioni in modalità di correzione, il contenuto di questo file viene utilizzato per modificare o eliminare elementi di dati nella tabella che violano l'indice secondario globale. Il formato del file `correctionInputPath` è uguale a quello del file `detectionOutputPath`. Ciò consente di elaborare l'output dalla modalità di rilevamento come input in modalità di correzione.  |  No  | 
|  `correctionOutputPath`  |  Il percorso completo del file di output di correzione di Rilevatore violazioni. Questo file viene creato solo se ci sono errori di aggiornamento. Questo parametro supporta la scrittura in una directory locale o su Amazon S3. Di seguito vengono mostrati gli esempi: `correctionOutputPath = ``//local/path/filename.csv` `correctionOutputPath = ``s3://bucket/filename.csv` Le informazioni nel file di output vengono visualizzate in formato CSV. Se non si imposta `correctionOutputPath`, il file di output è denominato `violation_update_errors.csv`e viene scritto nella directory di lavoro corrente.  |  No  | 

## Rilevamento
<a name="GSI.OnlineOps.ViolationDetection.Detection"></a>

Per rilevare le violazioni della chiave di indice, utilizza Rilevatore violazioni con l'opzione `--detect` della riga di comando. Per mostrare come funziona questa opzione, si consideri la tabella `ProductCatalog`. Di seguito è riportato un elenco di elementi nella tabella. Sono visualizzati solo la chiave primaria (`Id`) e l'attributo `Price`.


****  

| ID (chiave primaria) | Prezzo | 
| --- | --- | 
| 101 |  5  | 
| 102 |  20  | 
| 103 | 200  | 
| 201 |  100  | 
| 202 |  200  | 
| 203 |  300  | 
| 204 |  400  | 
| 205 |  500  | 

Tutti i valori per `Price` sono di tipo `Number`. Tuttavia, poiché DynamoDB è senza schemi, è possibile aggiungere un elemento con un `Price` non numerico. Ad esempio, si supponga di aggiungere un altro elemento alla tabella `ProductCatalog`.


****  

| ID (chiave primaria) | Prezzo | 
| --- | --- | 
| 999 | "Hello" | 

La tabella ha ora un totale di nove elementi.

A questo punto aggiungere un nuovo indice secondario globale alla tabella: `PriceIndex`. La chiave primaria di questo indice è una chiave di partizione, `Price`, che è di tipo `Number`. Dopo che l'indice è stato creato, conterrà otto elementi, ma la tabella `ProductCatalog` ne ha nove. La ragione di questa discrepanza è che il valore `"Hello"` è di tipo `String`, ma `PriceIndex` ha una chiave primaria di tipo `Number`. Il valore `String` viola la chiave dell'indice secondario globale, quindi non è presente nell'indice.

Per utilizzare Rilevatore violazioni in questo scenario, è innanzitutto necessario creare un file di configurazione come il seguente.

```
# 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
```

Quindi, è possibile eseguire Rilevatore violazioni come nell'esempio seguente.

```
$  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 il parametro di configurazione `recordDetails` è impostato su `true`, Rilevatore violazioni scrive i dettagli di ogni violazione nel file di output, come nell'esempio seguente.

```
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,,
```

Il file di output è in formato CSV. La prima riga del file è un'intestazione, seguita da un record per elemento che viola la chiave di indice. I campi di questi record di violazione sono i seguenti:
+ **Chiave hash tabella**: il valore della chiave di partizione dell'elemento nella tabella.
+ **Chiave di intervallo tabella**: il valore della chiave di ordinamento dell'elemento nella tabella.
+ **Valore chiave hash GSI**: il valore della chiave di partizione dell'indice secondario globale.
+ **Tipo di violazione della chiave hash GSI**: `Type Violation` o `Size Violation`.
+ **Descrizione della violazione della chiave hash GSI**: la causa della violazione.
+ **Valore di aggiornamento della chiave hash GSI (PER UTENTE)**: in modalità di correzione, un nuovo valore fornito dall'utente per l'attributo.
+ **Valore chiave hash GSI**: il valore della chiave di ordinamento dell'indice secondario globale.
+ **Tipo di violazione della chiave di intervallo GSI**: `Type Violation` o `Size Violation`.
+ **Descrizione della violazione della chiave di intervallo GSI**: la causa della violazione.
+ **Valore di aggiornamento della chiave di intervallo GSI (PER UTENTE)**: in modalità di correzione, un nuovo valore fornito dall'utente per l'attributo.
+ **Elimina attributo vuoto durante l'aggiornamento (S/N)**: in modalità di correzione, determina se eliminare (S) o mantenere (N) l'elemento di violazione nella tabella, ma solo se uno dei seguenti campi è vuoto:
  + `GSI Hash Key Update Value(FOR USER)`
  + `GSI Range Key Update Value(FOR USER)`

  Se uno di questi campi non è vuoto, allora `Delete Blank Attribute When Updating(Y/N)` non ha effetto.

**Nota**  
Il formato dell'output può variare a seconda del file di configurazione e delle opzioni della riga di comando. Ad esempio, se la tabella ha una chiave primaria semplice (senza una chiave di ordinamento), nell'output non saranno presenti campi della chiave di ordinamento.  
I record di violazione nel file potrebbero non essere ordinati.

## Correzione
<a name="GSI.OnlineOps.ViolationDetection.Correction"></a>

Per correggere le violazioni della chiave di indice, utilizza Rilevatore violazioni con l'opzione `--correct` della riga di comando. In modalità di correzione, Rilevatore violazioni legge il file di input specificato dal parametro `correctionInputPath`. Il file ha lo stesso formato del file `detectionOutputPath`, in modo da poter utilizzare l'output del rilevamento come input per la correzione.

Rilevatore violazioni fornisce due modi diversi per correggere le violazioni della chiave di indice:
+ **Elimina le violazioni**: eliminare gli elementi della tabella che hanno valori di attributi che violano.
+ **Aggiorna violazioni**: aggiornare gli elementi della tabella, sostituendo gli attributi che violano con valori che non violano.

In entrambi i casi, è possibile utilizzare il file di output dalla modalità di rilevamento come input per la modalità di correzione.

Continuando con l'esempio `ProductCatalog`, si supponga di voler eliminare l'elemento che viola dalla tabella. A tale scopo, utilizza la seguente riga di comando.

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

A questo punto, verrà richiesto di confermare se desideri eliminare gli elementi che violano.

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

Ora sia `ProductCatalog` che `PriceIndex` hanno lo stesso numero di elementi.

# Utilizzo di indici secondari globali: Java
<a name="GSIJavaDocumentAPI"></a>

Puoi utilizzare l'API AWS SDK per Java Document per creare una tabella Amazon DynamoDB con uno o più indici secondari globali, descrivere gli indici sulla tabella ed eseguire query utilizzando gli indici. 

Di seguito sono riportate le fasi comuni per le operazioni di tabella. 

1. Creare un'istanza della classe `DynamoDB`.

1. Fornisci i parametri obbligatori e facoltativi per l'operazione creando gli oggetti di richiesta corrispondenti. 

1. Chiama il metodo appropriato fornito dal client creato nella fase precedente. 

**Topics**
+ [Creazione di una tabella con un indice secondario globale](#GSIJavaDocumentAPI.CreateTableWithIndex)
+ [Descrizione di una tabella con un indice secondario globale](#GSIJavaDocumentAPI.DescribeTableWithIndex)
+ [Esecuzione di query su un indice secondario globale](#GSIJavaDocumentAPI.QueryAnIndex)
+ [Esempio: indici secondari globali che utilizzano l'API del documento AWS SDK per Java](GSIJavaDocumentAPI.Example.md)

## Creazione di una tabella con un indice secondario globale
<a name="GSIJavaDocumentAPI.CreateTableWithIndex"></a>

Puoi creare indici secondari globali al momento della creazione di una tabella. A tale scopo, utilizza `CreateTable` e fornisci le specifiche per uno o più indici secondari globali. Il seguente esempio di codice Java crea una tabella per contenere le informazioni sui dati meteo. La chiave di partizione è `Location` e la chiave di ordinamento è `Date`. Un indice secondario globale denominato `PrecipIndex` consente di accedere rapidamente ai dati delle precipitazioni di vari luoghi.

Di seguito sono riportate le fasi per creare una tabella con un indice secondario globale, utilizzando l'API documento di DynamoDB. 

1. Creare un'istanza della classe `DynamoDB`.

1. Crea un'istanza della classe `CreateTableRequest` per fornire le informazioni della richiesta.

   È necessario fornire il nome della tabella, la sua chiave primaria e i valori del throughput assegnato. Per l'indice secondario globale, è necessario fornire il nome dell'indice, le impostazioni di velocità effettiva assegnata, le definizioni degli attributi per la chiave di ordinamento dell'indice, lo schema della chiave per l'indice e la proiezione degli attributi.

1. Chiama il metodo `createTable` fornendo l'oggetto richiesta come parametro.

Il seguente esempio di codice Java mostra le fasi precedenti. Il codice crea una tabella (`WeatherData`) con un indice secondario globale (`PrecipIndex`). La chiave di partizione dell'indice è `Date` e la sua chiave di ordinamento è `Precipitation`. Tutti gli attributi della tabella vengono proiettati nell'indice. Gli utenti possono eseguire query su questo indice per ottenere dati sul meteo di una data specifica, ordinando facoltativamente i dati per quantità di precipitazioni. 

Poiché `Precipitation` non è un attributo chiave per la tabella, non è obbligatorio. Tuttavia, item `WeatherData` senza `Precipitation` non vengono visualizzati in `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());
```

È necessario attendere fino a quando DynamoDB crea la tabella e imposta lo stato su `ACTIVE`. Dopodiché, puoi iniziare a inserire item di dati nella tabella.

## Descrizione di una tabella con un indice secondario globale
<a name="GSIJavaDocumentAPI.DescribeTableWithIndex"></a>

Per ottenere informazioni sugli indici secondari globali in una tabella, utilizza `DescribeTable`. Puoi accedere al nome, allo schema della chiave e agli attributi proiettati di ciascun indice.

Di seguito sono riportate le fasi per accedere alle informazioni dell'indice secondario globale su una tabella. 

1. Creare un'istanza della classe `DynamoDB`.

1. Crea un'istanza della classe `Table` per rappresentare l'indice con cui intendi lavorare.

1. Chiama il metodo `describe` per l'oggetto `Table`.

Il seguente esempio di codice Java mostra le fasi precedenti.

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

## Esecuzione di query su un indice secondario globale
<a name="GSIJavaDocumentAPI.QueryAnIndex"></a>

È possibile utilizzare l'operazione `Query` su un indice secondario globale quasi nello stesso modo in cui si esegue una `Query` su una tabella. È necessario specificare il nome dell'indice, i criteri di query per la chiave di partizione e di ordinamento dell'indice (se presente) e gli attributi da restituire. In questo esempio, l'indice è `PrecipIndex`, avente una chiave di partizione `Date` e una chiave di ordinamento `Precipitation`. La query di indice restituisce tutti i dati sul meteo di una data specifica in cui le precipitazioni sono maggiori di zero.

Di seguito sono riportati i passaggi per interrogare un indice secondario globale utilizzando l'API Document. AWS SDK per Java 

1. Creare un'istanza della classe `DynamoDB`.

1. Crea un'istanza della classe `Table` per rappresentare l'indice con cui intendi lavorare.

1. Crea un'istanza della classe `Index` per l'indice su cui intendi eseguire una query.

1. Chiama il metodo `query` per l'oggetto `Index`.

Il nome di attributo `Date` è una parola riservata in DynamoDB. Pertanto, devi utilizzare un nome di attributo di espressione come segnaposto in `KeyConditionExpression`.

Il seguente esempio di codice Java mostra le fasi precedenti.

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

# Esempio: indici secondari globali che utilizzano l'API del documento AWS SDK per Java
<a name="GSIJavaDocumentAPI.Example"></a>

Il seguente esempio di codice Java mostra come utilizzare gli indici secondari globali. L'esempio crea una tabella denominata `Issues`, che può essere utilizzata in un semplice sistema di tracciamento di bug per lo sviluppo di software. La chiave di partizione è `IssueId` e la chiave di ordinamento è `Title`. In questa tabella esistono tre indici secondari globali:
+ `CreateDateIndex`: la chiave di partizione è `CreateDate` e la chiave di ordinamento è `IssueId`. Oltre alle chiavi di tabella, vengono proiettati nell'indice gli attributi `Description` e `Status`.
+ `TitleIndex`: la chiave di partizione è `Title` e la chiave di ordinamento è `IssueId`. Non vengono proiettati nell'indice attributi diversi dalle chiavi di tabella.
+ `DueDateIndex`: la chiave di partizione è `DueDate`; non è presente una chiave di ordinamento. Tutti gli attributi della tabella vengono proiettati nell'indice.

Una volta creata la tabella `Issues`, il programma carica la tabella con i dati che rappresentano i report sui bug del software. Quindi esegue una query sui dati utilizzando gli indici secondari globali. Infine, il programma elimina la tabella `Issues`.

Per step-by-step istruzioni su come testare l'esempio seguente, vedere. [Esempi di codice 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);
    }

}
```

# Utilizzo di indici secondari globali: .NET
<a name="GSILowLevelDotNet"></a>

Puoi utilizzare l'API di AWS SDK per .NET basso livello per creare una tabella Amazon DynamoDB con uno o più indici secondari globali, descrivere gli indici sulla tabella ed eseguire query utilizzando gli indici. Queste operazioni vengono mappate alle operazioni DynamoDB corrispondenti. Per ulteriori informazioni, consulta la [Documentazione di riferimento delle API di Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/). 

Di seguito sono riportate le operazioni comuni per le operazioni delle tabelle che utilizzano l'API di basso livello .NET. 

1. Creare un'istanza della classe `AmazonDynamoDBClient`.

1. Fornisci i parametri obbligatori e facoltativi per l'operazione creando gli oggetti di richiesta corrispondenti.

   Ad esempio, creare un oggetto `CreateTableRequest` per creare una tabella e un oggetto `QueryRequest` per eseguire una query su una tabella o un indice. 

1. Eseguire il metodo appropriato fornito dal client creato nella fase precedente. 

**Topics**
+ [Creazione di una tabella con un indice secondario globale](#GSILowLevelDotNet.CreateTableWithIndex)
+ [Descrizione di una tabella con un indice secondario globale](#GSILowLevelDotNet.DescribeTableWithIndex)
+ [Esecuzione di query su un indice secondario globale](#GSILowLevelDotNet.QueryAnIndex)
+ [Esempio AWS SDK per .NET : indici secondari globali che utilizzano l'API di basso livello](GSILowLevelDotNet.Example.md)

## Creazione di una tabella con un indice secondario globale
<a name="GSILowLevelDotNet.CreateTableWithIndex"></a>

Puoi creare indici secondari globali al momento della creazione di una tabella. A tale scopo, utilizza `CreateTable` e fornisci le specifiche per uno o più indici secondari globali. Il seguente esempio di codice C\$1 crea una tabella per contenere le informazioni sui dati meteo. La chiave di partizione è `Location` e la chiave di ordinamento è `Date`. Un indice secondario globale denominato `PrecipIndex` consente di accedere rapidamente ai dati delle precipitazioni di vari luoghi.

Di seguito sono riportate le fasi per creare una tabella con un indice secondario globale, utilizzando l'API di basso livello di DynamoDB. 

1. Creare un'istanza della classe `AmazonDynamoDBClient`.

1. Crea un'istanza della classe `CreateTableRequest` per fornire le informazioni della richiesta. 

   È necessario fornire il nome della tabella, la sua chiave primaria e i valori del throughput assegnato. Per l'indice secondario globale, è necessario fornire il nome dell'indice, le impostazioni di velocità effettiva assegnata, le definizioni degli attributi per la chiave di ordinamento dell'indice, lo schema della chiave per l'indice e la proiezione degli attributi.

1. Eseguire il metodo `CreateTable` fornendo l'oggetto della richiesta come parametro.

Il seguente esempio di codice C\$1 mostra le fasi precedenti. Il codice crea una tabella (`WeatherData`) con un indice secondario globale (`PrecipIndex`). La chiave di partizione dell'indice è `Date` e la sua chiave di ordinamento è `Precipitation`. Tutti gli attributi della tabella vengono proiettati nell'indice. Gli utenti possono eseguire query su questo indice per ottenere dati sul meteo di una data specifica, ordinando facoltativamente i dati per quantità di precipitazioni. 

Poiché `Precipitation` non è un attributo chiave per la tabella, non è obbligatorio. Tuttavia, item `WeatherData` senza `Precipitation` non vengono visualizzati in `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);
```

È necessario attendere fino a quando DynamoDB crea la tabella e imposta lo stato su `ACTIVE`. Dopodiché, puoi iniziare a inserire item di dati nella tabella.

## Descrizione di una tabella con un indice secondario globale
<a name="GSILowLevelDotNet.DescribeTableWithIndex"></a>

Per ottenere informazioni sugli indici secondari globali in una tabella, utilizza `DescribeTable`. Puoi accedere al nome, allo schema della chiave e agli attributi proiettati di ciascun indice.

Di seguito sono riportate le fasi per accedere alle informazioni dell'indice secondario globale per una tabella utilizzando l'API di basso livello .NET. 

1. Creare un'istanza della classe `AmazonDynamoDBClient`.

1. Eseguire il metodo `describeTable` fornendo l'oggetto della richiesta come parametro.

   Crea un'istanza della classe `DescribeTableRequest` per fornire le informazioni della richiesta. Devi specificare il nome della tabella.

Il seguente esempio di codice C\$1 mostra le fasi precedenti.

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

## Esecuzione di query su un indice secondario globale
<a name="GSILowLevelDotNet.QueryAnIndex"></a>

È possibile utilizzare l'operazione `Query` su un indice secondario globale quasi nello stesso modo in cui si esegue una `Query` su una tabella. È necessario specificare il nome dell'indice, i criteri di query per la chiave di partizione e di ordinamento dell'indice (se presente) e gli attributi da restituire. In questo esempio, l'indice è `PrecipIndex`, avente una chiave di partizione `Date` e una chiave di ordinamento `Precipitation`. La query di indice restituisce tutti i dati sul meteo di una data specifica in cui le precipitazioni sono maggiori di zero.

Di seguito sono riportate le fasi per eseguire una query su un indice secondario globale utilizzando l'API di basso livello .NET. 

1. Creare un'istanza della classe `AmazonDynamoDBClient`.

1. Crea un'istanza della classe `QueryRequest` per fornire le informazioni della richiesta.

1. Eseguire il metodo `query` fornendo l'oggetto della richiesta come parametro.

Il nome di attributo `Date` è una parola riservata in DynamoDB. Pertanto, devi utilizzare un nome di attributo di espressione come segnaposto in `KeyConditionExpression`.

Il seguente esempio di codice C\$1 mostra le fasi precedenti.

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

# Esempio AWS SDK per .NET : indici secondari globali che utilizzano l'API di basso livello
<a name="GSILowLevelDotNet.Example"></a>

Il seguente esempio di codice C\$1 mostra come utilizzare gli indici secondari globali. L'esempio crea una tabella denominata `Issues`, che può essere utilizzata in un semplice sistema di tracciamento di bug per lo sviluppo di software. La chiave di partizione è `IssueId` e la chiave di ordinamento è `Title`. In questa tabella esistono tre indici secondari globali:
+ `CreateDateIndex`: la chiave di partizione è `CreateDate` e la chiave di ordinamento è `IssueId`. Oltre alle chiavi di tabella, vengono proiettati nell'indice gli attributi `Description` e `Status`.
+ `TitleIndex`: la chiave di partizione è `Title` e la chiave di ordinamento è `IssueId`. Non vengono proiettati nell'indice attributi diversi dalle chiavi di tabella.
+ `DueDateIndex`: la chiave di partizione è `DueDate`; non è presente una chiave di ordinamento. Tutti gli attributi della tabella vengono proiettati nell'indice.

Una volta creata la tabella `Issues`, il programma carica la tabella con i dati che rappresentano i report sui bug del software. Quindi esegue una query sui dati utilizzando gli indici secondari globali. Infine, il programma elimina la tabella `Issues`.

Per step-by-step istruzioni su come testare il seguente esempio, vedere. [Esempi di codice .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;
                }
            }
        }
    }
}
```

# Utilizzo di indici secondari in DynamoDB con la AWS CLI
<a name="GCICli"></a>

È possibile utilizzare la AWS CLI per creare una tabella Amazon DynamoDB con uno o più indici secondari globali, descrivere gli indici sulla tabella ed eseguire query utilizzando gli indici.

**Topics**
+ [Creazione di una tabella con un indice secondario globale](#GCICli.CreateTableWithIndex)
+ [Aggiunta di un indice secondario globale a una tabella esistente](#GCICli.CreateIndexAfterTable)
+ [Descrizione di una tabella con un indice secondario globale](#GCICli.DescribeTableWithIndex)
+ [Esecuzione di query su un indice secondario globale](#GCICli.QueryAnIndex)

## Creazione di una tabella con un indice secondario globale
<a name="GCICli.CreateTableWithIndex"></a>

Gli indici secondari globali possono essere creati al momento della creazione di una tabella. A tale scopo, utilizza il parametro `create-table` e fornire le specifiche per uno o più indici secondari globali. Nell'esempio seguente viene creata una tabella denominata `GameScores` con un indice secondario globale denominato `GameTitleIndex`. La tabella di base ha una chiave di partizione di `UserId` e una chiave di ordinamento di `GameTitle`, permettendo di trovare il miglior punteggio di un singolo utente per un gioco specifico in modo efficiente, mentre il GSI ha una chiave di partizione di `GameTitle` e una chiave di ordinamento di `TopScore`, permettendo di trovare rapidamente il punteggio più alto complessivo per un determinato gioco.

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

È necessario attendere fino a quando DynamoDB crea la tabella e imposta lo stato su `ACTIVE`. Dopodiché, puoi iniziare a inserire item di dati nella tabella. È possibile utilizzare [describe-table](https://docs.aws.amazon.com/cli/latest/reference/dynamodb/describe-table.html) per determinare lo stato di avanzamento della creazione della tabella.

## Aggiunta di un indice secondario globale a una tabella esistente
<a name="GCICli.CreateIndexAfterTable"></a>

Gli indici secondari globali possono essere aggiunti o modificati anche dopo la creazione della tabella. A tale scopo, utilizza il parametro `update-table` e fornire le specifiche per uno o più indici secondari globali. Nell'esempio seguente viene utilizzato lo stesso schema dell'esempio precedente, ma si presuppone che la tabella sia già stata creata e che la GSI verrà aggiunta in seguito.

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

## Descrizione di una tabella con un indice secondario globale
<a name="GCICli.DescribeTableWithIndex"></a>

Per ottenere informazioni sugli indici secondari globali in una tabella, utilizza il parametro `describe-table`. Puoi accedere al nome, allo schema della chiave e agli attributi proiettati di ciascun indice.

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

## Esecuzione di query su un indice secondario globale
<a name="GCICli.QueryAnIndex"></a>

È possibile utilizzare l'operazione `query` su un indice secondario globale nello stesso modo in cui si esegue una `query` su una tabella. È necessario specificare il nome dell'indice, i criteri della query per la chiave di ordinamento dell'indice e gli attributi da restituire. In questo esempio, l'indice è `GameTitleIndex` e la chiave di ordinamento dell'indice è `GameTitle`.

Gli unici attributi restituiti sono quelli proiettati nell'indice. È possibile modificare questa query per selezionare anche attributi non chiave, ma ciò richiederebbe un'attività di recupero della tabella relativamente costosa. Per ulteriori informazioni sul recupero delle tabelle, consulta [Proiezioni di attributi](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"} }'
```