

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

# Indeks sekunder lokal
<a name="LSI"></a>

Beberapa aplikasi hanya perlu mengkueri data menggunakan kunci primer tabel dasar. Namun, mungkin ada situasi ketika kunci urutan alternatif akan berguna. Untuk memberi aplikasi Anda pilihan kunci urutan, Anda dapat membuat satu atau beberapa indeks sekunder lokal pada tabel Amazon DynamoDB dan mengeluarkan permintaan `Query` atau `Scan` terhadap indeks ini.

**Topics**
+ [Skenario: Menggunakan Indeks Sekunder Lokal](#LSI.Scenario)
+ [Proyeksi atribut](#LSI.Projections)
+ [Membuat Indeks Sekunder Lokal](#LSI.Creating)
+ [Membaca data dari Indeks Sekunder Lokal](#LSI.Reading)
+ [Penulisan item dan Indeks Sekunder Lokal](#LSI.Writes)
+ [Pertimbangan throughput yang disediakan untuk Indeks Sekunder Lokal](#LSI.ThroughputConsiderations)
+ [Pertimbangan penyimpanan untuk Indeks Sekunder Lokal](#LSI.StorageConsiderations)
+ [Kumpulan item dalam Indeks Sekunder Lokal](#LSI.ItemCollections)
+ [Menggunakan Indeks Sekunder Lokal: Java](LSIJavaDocumentAPI.md)
+ [Menggunakan Indeks Sekunder Lokal: .NET](LSILowLevelDotNet.md)
+ [Bekerja dengan Indeks Sekunder Lokal di DynamoDB AWS CLI](LCICli.md)

## Skenario: Menggunakan Indeks Sekunder Lokal
<a name="LSI.Scenario"></a>

Sebagai contoh, perhatikan `Thread` tabel. Tabel ini berguna untuk aplikasi seperti [forum diskusi AWS](https://forums.aws.amazon.com/). Diagram berikut menunjukkan bagaimana item dalam tabel akan diatur. (Tidak semua atribut ditampilkan.)

![\[Tabel utas yang berisi daftar nama forum, subjek, waktu posting terakhir, dan jumlah balasan.\]](http://docs.aws.amazon.com/id_id/amazondynamodb/latest/developerguide/images/LSI_01.png)


DynamoDB menyimpan semua item dengan nilai kunci partisi yang sama secara berkelanjutan. Dalam contoh ini, dengan `ForumName` tertentu, operasi `Query` dapat segera menemukan semua utas untuk forum itu. Dalam grup item dengan nilai kunci partisi yang sama, item diurutkan berdasarkan nilai kunci urutan. Jika kunci urutan (`Subject`) juga disediakan dalam kueri, DynamoDB dapat mempersempit hasil yang dikembalikan—misalnya, mengembalikan semua utas di forum "S3" yang memiliki `Subject` yang diawali dengan huruf "a".

Beberapa permintaan mungkin memerlukan pola akses data yang lebih kompleks. Misalnya:
+ Utas forum mana yang paling banyak dilihat dan mendapatkan balasan?
+ Utas mana di forum tertentu yang memiliki jumlah pesan terbanyak?
+ Berapa banyak utas yang diposting di forum tertentu dalam jangka waktu tertentu?

Untuk menjawab pertanyaan-pertanyaan ini, tindakan `Query` tidak akan cukup. Sebaliknya, Anda harus `Scan` seluruh tabel. Untuk tabel dengan jutaan item, ini akan menghabiskan banyak throughput baca yang disediakan dan membutuhkan waktu lama untuk selesai.

Namun, Anda dapat menentukan satu atau beberapa indeks sekunder lokal pada atribut non-kunci, seperti `Replies` atau `LastPostDateTime`.

*Indeks sekunder lokal* mempertahankan kunci urutan alternatif untuk nilai kunci partisi yang diberikan. Indeks sekunder lokal juga berisi salinan beberapa atau semua atribut dari tabel dasarnya. Anda akan menentukan atribut yang diproyeksikan ke indeks sekunder lokal saat membuat tabel. Data dalam indeks sekunder lokal diatur oleh kunci partisi yang sama dengan tabel dasar, tetapi dengan kunci urutan yang berbeda. Ini memungkinkan Anda mengakses item data secara efisien di seluruh dimensi yang berbeda ini. Untuk fleksibilitas kueri atau pemindaian yang lebih baik, Anda dapat membuat hingga lima indeks sekunder lokal per tabel. 

Misalkan sebuah aplikasi perlu menemukan semua utas yang telah diposting dalam tiga bulan terakhir di forum tertentu. Tanpa indeks sekunder lokal, aplikasi harus `Scan` seluruh tabel `Thread` dan membuang postingan apa pun yang tidak berada dalam periode waktu yang ditentukan. Dengan indeks sekunder lokal, operasi `Query` dapat menggunakan `LastPostDateTime` sebagai kunci urutan dan menemukan data dengan cepat.

Diagram berikut menunjukkan indeks sekunder lokal bernama `LastPostIndex`. Perhatikan bahwa kunci partisi sama dengan tabel `Thread`, tetapi kunci urutannya adalah `LastPostDateTime`.

![\[LastPostIndex tabel yang berisi daftar nama forum, subjek, dan waktu posting terakhir.\]](http://docs.aws.amazon.com/id_id/amazondynamodb/latest/developerguide/images/LSI_02.png)


Setiap indeks sekunder lokal harus memenuhi syarat berikut:
+ Kunci partisi sama dengan tabel dasarnya.
+ Kunci urutan terdiri dari satu atribut skalar.
+ Kunci urutan tabel dasar diproyeksikan ke dalam indeks, tempat kunci tersebut berfungsi sebagai atribut non-kunci.

Dalam contoh ini, kunci partisi adalah `ForumName` dan kunci urutan indeks sekunder lokal adalah `LastPostDateTime`. Selain itu, nilai kunci urutan dari tabel dasar (dalam contoh ini, `Subject`) diproyeksikan ke dalam indeks, tetapi bukan merupakan bagian dari kunci indeks. Jika aplikasi membutuhkan daftar yang didasarkan pada `ForumName` dan `LastPostDateTime`, aplikasi dapat mengeluarkan permintaan `Query` terhadap `LastPostIndex`. Hasil kueri diurutkan menurut `LastPostDateTime`, dan dapat dikembalikan dalam urutan menaik atau menurun. Kueri juga dapat menerapkan syarat kunci, seperti hanya mengembalikan item yang memiliki `LastPostDateTime` dalam rentang waktu tertentu.

Setiap indeks sekunder lokal otomatis berisi kunci partisi dan kunci urutan dari tabel dasarnya; Anda secara opsional dapat memproyeksikan atribut non-kunci ke dalam indeks. Saat Anda mengkueri indeks, DynamoDB dapat mengambil atribut yang diproyeksikan ini secara efisien. Saat Anda mengkueri indeks sekunder lokal, kueri juga dapat mengambil atribut yang *tidak* diproyeksikan ke dalam indeks. DynamoDB otomatis mengambil atribut ini dari tabel dasar, tetapi dengan latensi lebih besar dan dengan biaya throughput yang disediakan lebih tinggi.

Untuk indeks sekunder lokal apa pun, Anda dapat menyimpan hingga 10 GB data per nilai kunci partisi yang berbeda. Angka ini mencakup semua item dalam tabel dasar, ditambah semua item dalam indeks, yang memiliki nilai kunci partisi yang sama. Untuk informasi selengkapnya, lihat [Kumpulan item dalam Indeks Sekunder Lokal](#LSI.ItemCollections).

## Proyeksi atribut
<a name="LSI.Projections"></a>

Dengan `LastPostIndex`, sebuah aplikasi dapat menggunakan `ForumName` dan `LastPostDateTime` sebagai kriteria kueri. Namun, untuk mengambil atribut tambahan apa pun, DynamoDB harus melakukan operasi baca tambahan terhadap tabel `Thread`. Pembacaan tambahan ini dikenal sebagai *pengambilan*, dan pembacaan tersebut dapat meningkatkan jumlah total throughput yang disediakan yang diperlukan untuk kueri.

Misalkan Anda ingin mengisi halaman web dengan daftar semua utas di “S3" dan jumlah balasan untuk setiap utas, diurutkan berdasarkan balasan terakhir yang date/time dimulai dengan balasan terbaru. Untuk mengisi daftar ini, Anda memerlukan atribut berikut:
+ `Subject`
+ `Replies`
+ `LastPostDateTime`

Cara paling efisien untuk mengkueri data ini dan untuk menghindari operasi pengambilan adalah dengan memproyeksikan atribut `Replies` dari tabel ke dalam indeks sekunder lokal, seperti yang ditunjukkan dalam diagram ini.

![\[LastPostIndex tabel yang berisi daftar nama forum, waktu posting terakhir, subjek, dan balasan.\]](http://docs.aws.amazon.com/id_id/amazondynamodb/latest/developerguide/images/LSI_03.png)




*Proyeksi* adalah kumpulan atribut yang disalin dari tabel ke indeks sekunder. Kunci partisi dan kunci urutan tabel selalu diproyeksikan ke dalam indeks; Anda dapat memproyeksikan atribut lain untuk mendukung persyaratan kueri aplikasi Anda. Saat Anda mengkueri indeks, Amazon DynamoDB dapat mengakses atribut apa pun dalam proyeksi seolah-olah atribut tersebut berada dalam tabelnya sendiri.

Saat membuat indeks sekunder, Anda perlu menentukan atribut yang akan diproyeksikan ke dalam indeks. DynamoDB menyediakan tiga opsi berbeda untuk ini:
+ *KEYS\$1ONLY* – Setiap item dalam indeks hanya terdiri dari kunci partisi tabel dan nilai kunci urutan, ditambah nilai kunci indeks. Opsi `KEYS_ONLY` menghasilkan indeks sekunder sekecil mungkin.
+ *INCLUDE* – Selain atribut yang dijelaskan dalam `KEYS_ONLY`, indeks sekunder akan menyertakan atribut non-kunci lain yang Anda tentukan.
+ *ALL* – Indeks sekunder menyertakan semua atribut dari tabel sumber. Karena semua data tabel diduplikasi dalam indeks, proyeksi `ALL` menghasilkan indeks sekunder sebesar yang mungkin.

Pada diagram sebelumnya, atribut non-kunci `Replies` diproyeksikan ke dalam `LastPostIndex`. Aplikasi dapat meminta `LastPostIndex` sebagai ganti dari tabel `Thread` lengkap untuk mengisi halaman web dengan `Subject`, `Replies`, dan `LastPostDateTime`. Jika ada atribut non-kunci lain yang diminta, DynamoDB perlu mengambil atribut tersebut dari tabel `Thread`. 

Dari sudut pandang aplikasi, pengambilan atribut tambahan dari tabel dasar dilakukan secara otomatis dan transparan, jadi tidak perlu menulis ulang logika aplikasi apa pun. Namun, pengambilan tersebut dapat sangat mengurangi keuntungan performa menggunakan indeks sekunder lokal.

Saat memilih atribut untuk diproyeksikan ke dalam indeks sekunder lokal, Anda harus mempertimbangkan tradeoff antara biaya throughput yang disediakan dan biaya penyimpanan:
+ Jika Anda hanya perlu mengakses beberapa atribut dengan latensi serendah mungkin, pertimbangkan untuk hanya memproyeksikan atribut tersebut ke dalam indeks sekunder lokal. Semakin kecil indeks, semakin sedikit biaya penyimpanannya, dan semakin sedikit pula biaya tulis Anda. Jika ada atribut yang terkadang perlu Anda ambil, biaya untuk throughput yang disediakan mungkin lebih besar daripada biaya jangka panjang untuk menyimpan atribut tersebut.
+ Jika aplikasi Anda sering mengakses beberapa atribut non-kunci, sebaiknya Anda memproyeksikan atribut tersebut ke dalam indeks sekunder lokal. Biaya penyimpanan tambahan untuk indeks sekunder lokal mengimbangi biaya melakukan pemindaian tabel yang sering dilakukan.
+ Jika sering mengakses sebagian besar atribut non-kunci, Anda dapat memproyeksikan atribut ini—atau bahkan seluruh tabel dasar— ke dalam indeks sekunder lokal. Ini memberi Anda fleksibilitas maksimum dan penggunaan persediaan throughput terendah, karena pengambilan tidak diperlukan. Namun, biaya penyimpanan Anda akan meningkat, atau bahkan dua kali lipat jika Anda memproyeksikan semua atribut.
+ Jika aplikasi Anda jarang mengkueri tabel, tetapi harus melakukan banyak penulisan atau pembaruan terhadap data dalam tabel, pertimbangkan untuk memproyeksikan *KEYS\$1ONLY*. Indeks sekunder lokal akan berukuran minimal, tetapi akan tetap tersedia jika diperlukan untuk aktivitas kueri. 

## Membuat Indeks Sekunder Lokal
<a name="LSI.Creating"></a>

Untuk membuat satu atau beberapa indeks sekunder lokal pada tabel, gunakan parameter `LocalSecondaryIndexes` operasi `CreateTable`. Indeks sekunder lokal pada tabel dibuat saat tabel dibuat. Saat Anda menghapus tabel, indeks sekunder lokal apa pun di tabel itu juga akan dihapus.

Anda harus menentukan satu atribut non-kunci untuk berfungsi sebagai kunci urutan indeks sekunder lokal. Atribut yang Anda pilih harus berupa skalar `String`, `Number`, atau `Binary`. Jenis skalar, jenis dokumen, dan jenis kumpulan lain tidak diperbolehkan. Untuk daftar lengkap jenis data, lihat [Jenis Data](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes).

**penting**  
Untuk tabel dengan indeks sekunder lokal, ada batas ukuran 10 GB per nilai kunci partisi. Tabel dengan indeks sekunder lokal dapat menyimpan berapa pun jumlah item, asalkan ukuran total untuk nilai kunci satu partisi tidak melebihi 10 GB. Untuk informasi selengkapnya, lihat [Batas ukuran kumpulan item](#LSI.ItemCollections.SizeLimit).

Anda dapat memproyeksikan atribut jenis data apa pun ke dalam indeks sekunder lokal. Ini termasuk skalar, dokumen, dan kumpulan. Untuk daftar lengkap jenis data, lihat [Jenis Data](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes).

## Membaca data dari Indeks Sekunder Lokal
<a name="LSI.Reading"></a>

Anda dapat mengambil item dari indeks sekunder lokal menggunakan operasi `Query` dan `Scan`. Operasi `GetItem` dan `BatchGetItem` tidak dapat digunakan pada indeks sekunder lokal.

### Mengkueri Indeks Sekunder Lokal
<a name="LSI.Querying"></a>

Dalam tabel DynamoDB, nilai kunci partisi gabungan dan nilai kunci urutan untuk setiap item harus unik. Namun, dalam indeks sekunder lokal, nilai kunci urutan tidak perlu unik untuk nilai kunci partisi yang diberikan. Jika ada beberapa item dalam indeks sekunder lokal yang memiliki nilai kunci urutan yang sama, operasi `Query` mengembalikan semua item yang memiliki nilai kunci partisi yang sama. Sebagai respons, item yang cocok tidak dikembalikan dalam urutan tertentu.

Anda dapat mengkueri indeks sekunder lokal menggunakan bacaan akhir konsisten atau bacaan sangat konsisten. Untuk menentukan jenis konsistensi yang Anda inginkan, gunakan parameter `ConsistentRead` operasi `Query`. Bacaan sangat konsisten dari indeks sekunder lokal selalu mengembalikan nilai terbaru yang diperbarui. Jika kueri perlu mengambil atribut tambahan dari tabel dasar, atribut tersebut akan konsisten dengan indeks.

**Example**  
Pertimbangkan data berikut yang dikembalikan dari `Query` yang meminta data dari utas diskusi di forum tertentu.  

```
{
    "TableName": "Thread",
    "IndexName": "LastPostIndex",
    "ConsistentRead": false,
    "ProjectionExpression": "Subject, LastPostDateTime, Replies, Tags",
    "KeyConditionExpression": 
        "ForumName = :v_forum and LastPostDateTime between :v_start and :v_end",
    "ExpressionAttributeValues": {
        ":v_start": {"S": "2015-08-31T00:00:00.000Z"},
        ":v_end": {"S": "2015-11-31T00:00:00.000Z"},
        ":v_forum": {"S": "EC2"}
    }
}
```
Dalam kueri ini:  
+ DynamoDB mengakses `LastPostIndex`, menggunakan kunci partisi `ForumName` untuk menemukan item indeks untuk "EC2". Semua item indeks dengan kunci ini disimpan berdekatan satu sama lain untuk pengambilan cepat.
+ Dalam forum ini, DynamoDB menggunakan indeks untuk mencari kunci yang cocok dengan syarat `LastPostDateTime` yang ditentukan.
+ Karena atribut `Replies` diproyeksikan ke dalam indeks, DynamoDB dapat mengambil atribut ini tanpa menggunakan throughput tambahan yang disediakan.
+ Atribut `Tags` tidak diproyeksikan ke dalam indeks, jadi DynamoDB harus mengakses tabel `Thread` dan mengambil atribut ini.
+ Hasilnya ditampilkan, diurutkan berdasarkan `LastPostDateTime`. Entri indeks diurutkan menurut nilai kunci partisi dan kemudian menurut nilai kunci urutan, dan `Query` mengembalikannya sesuai urutan penyimpanannya. (Anda dapat menggunakan parameter `ScanIndexForward` untuk mengembalikan hasil dalam urutan menurun.)
Karena atribut `Tags` tidak diproyeksikan ke indeks sekunder lokal, DynamoDB harus menggunakan unit kapasitas baca tambahan untuk mengambil atribut ini dari tabel dasar. Jika sering menjalankan kueri ini, Anda harus memproyeksikan `Tags` ke `LastPostIndex` untuk menghindari pengambilan dari tabel dasar. Namun, jika Anda hanya perlu mengakses `Tags` sesekali, biaya penyimpanan tambahan untuk memproyeksikan `Tags` ke dalam indeks mungkin tidak bermanfaat.

### Memindai Indeks Sekunder Lokal
<a name="LSI.Scanning"></a>

Anda dapat menggunakan `Scan` untuk mengambil semua data dari indeks sekunder lokal. Anda harus memberikan nama tabel dasar dan nama indeks dalam permintaan. Dengan `Scan`, DynamoDB membaca semua data dalam indeks dan mengembalikannya ke aplikasi. Anda juga dapat meminta agar hanya sebagian data yang dikembalikan, dan data yang tersisa harus dibuang. Untuk melakukannya, gunakan parameter `FilterExpression` API `Scan`. Untuk informasi selengkapnya, lihat [Ekspresi filter untuk pemindaian](Scan.md#Scan.FilterExpression).

## Penulisan item dan Indeks Sekunder Lokal
<a name="LSI.Writes"></a>

DynamoDB otomatis membuat semua indeks sekunder lokal tersinkronisasi dengan tabel dasar masing-masing. Aplikasi tidak pernah menulis langsung ke indeks. Namun, Anda harus memahami implikasi dari cara DynamoDB mempertahankan indeks ini.

Saat membuat indeks sekunder lokal, Anda menentukan atribut untuk dijadikan sebagai kunci urutan untuk indeks. Anda juga menentukan jenis data untuk atribut tersebut. Artinya, setiap kali Anda menulis item ke tabel dasar, jika item tersebut mendefinisikan atribut kunci indeks, jenisnya harus cocok dengan jenis data skema kunci indeks. Dalam kasus `LastPostIndex`, kunci urutan `LastPostDateTime` dalam indeks didefinisikan sebagai jenis data `String`. Jika Anda mencoba menambahkan item ke tabel `Thread` dan menentukan jenis data yang berbeda untuk `LastPostDateTime` (seperti `Number`), DynamoDB mengembalikan `ValidationException` karena jenis data tidak cocok.

Tidak ada persyaratan untuk one-to-one hubungan antara item dalam tabel dasar dan item dalam indeks sekunder lokal. Bahkan, perilaku ini dapat menguntungkan untuk banyak aplikasi. 

Tabel dengan banyak indeks sekunder lokal menimbulkan biaya lebih tinggi untuk aktivitas tulis dibandingkan tabel dengan lebih sedikit indeks. Untuk informasi selengkapnya, lihat [Pertimbangan throughput yang disediakan untuk Indeks Sekunder Lokal](#LSI.ThroughputConsiderations).

**penting**  
Untuk tabel dengan indeks sekunder lokal, ada batas ukuran 10 GB per nilai kunci partisi. Tabel dengan indeks sekunder lokal dapat menyimpan berapa pun jumlah item, asalkan ukuran total untuk nilai kunci satu partisi tidak melebihi 10 GB. Untuk informasi selengkapnya, lihat [Batas ukuran kumpulan item](#LSI.ItemCollections.SizeLimit).

## Pertimbangan throughput yang disediakan untuk Indeks Sekunder Lokal
<a name="LSI.ThroughputConsiderations"></a>

Saat membuat tabel di DynamoDB, Anda menyediakan unit kapasitas baca dan tulis untuk beban kerja tabel yang diharapkan. Beban kerja tersebut mencakup aktivitas baca dan tulis pada indeks sekunder lokal tabel.

Untuk melihat tarif saat ini untuk kapasitas throughput yang disediakan, lihat [Harga Amazon DynamoDB](https://aws.amazon.com/dynamodb/pricing).

### Unit kapasitas baca
<a name="LSI.ThroughputConsiderations.Reads"></a>

Saat Anda mengkueri indeks sekunder lokal, jumlah unit kapasitas baca yang digunakan bergantung pada cara data diakses.

Seperti kueri tabel, kueri indeks dapat menggunakan bacaan akhir konsisten atau bacaan sangat konsisten, tergantung pada nilai `ConsistentRead`. Satu bacaan sangat konsisten menghabiskan satu unit kapasitas baca; bacaan akhir konsisten hanya menghabiskan setengahnya. Jadi, dengan memilih bacaan akhir konsisten, Anda dapat mengurangi biaya unit kapasitas baca Anda.

Untuk kueri indeks yang hanya meminta kunci indeks dan atribut yang diproyeksikan, DynamoDB menghitung aktivitas baca yang disediakan dengan cara yang sama seperti kueri terhadap tabel. Satu-satunya hal yang membedakan adalah penghitungan didasarkan pada ukuran entri indeks, bukan ukuran item dalam tabel dasar. Jumlah unit kapasitas baca adalah jumlah dari semua ukuran atribut yang diproyeksikan di semua item yang dikembalikan; hasilnya kemudian dibulatkan ke batas 4 KB berikutnya. Untuk informasi selengkapnya tentang cara DynamoDB menghitung penggunaan throughput yang disediakan, lihat [DynamoDB menyediakan mode kapasitas](provisioned-capacity-mode.md).

Untuk kueri indeks yang membaca atribut yang tidak diproyeksikan ke indeks sekunder lokal, DynamoDB perlu mengambil atribut tersebut dari tabel dasar, selain membaca atribut yang diproyeksikan dari indeks. Pengambilan ini terjadi saat Anda menyertakan atribut yang tidak diproyeksikan dalam parameter `Select` atau `ProjectionExpression` operasi `Query`. Pengambilan menyebabkan latensi tambahan dalam respons kueri, dan juga menimbulkan biaya throughput yang disediakan lebih tinggi: Selain pembacaan dari indeks sekunder lokal yang dijelaskan sebelumnya, Anda dikenakan biaya untuk unit kapasitas baca untuk setiap item tabel dasar yang diambil. Biaya ini untuk pembacaan setiap item dari tabel, bukan hanya atribut yang diminta.

Ukuran maksimum hasil yang dikembalikan oleh operasi `Query` adalah 1 MB. Ini termasuk ukuran semua nama dan nilai atribut di semua item yang dikembalikan. Namun, jika Kueri terhadap indeks sekunder lokal menyebabkan DynamoDB mengambil atribut item dari tabel dasar, ukuran maksimum data dalam hasil mungkin lebih rendah. Dalam hal ini, ukuran hasil adalah jumlah dari:
+ Ukuran item yang cocok dalam indeks, dibulatkan ke atas hingga 4 KB berikutnya.
+ Ukuran setiap item yang cocok di tabel dasar, dengan setiap item dibulatkan satu per satu ke 4 KB berikutnya.

Menggunakan rumus ini, ukuran maksimum hasil yang dikembalikan oleh operasi Kueri masih 1 MB.

Misalnya, pertimbangkan tabel tempat ukuran setiap item adalah 300 byte. Ada indeks sekunder lokal di tabel itu, tetapi hanya 200 byte dari setiap item yang diproyeksikan ke dalam indeks. Sekarang anggaplah Anda `Query` indeks ini, bahwa kueri memerlukan pengambilan tabel untuk setiap item, dan kueri mengembalikan 4 item. DynamoDB merangkum berikut ini:
+ Ukuran item yang cocok dalam indeks: 200 byte × 4 item = 800 byte; ini kemudian dibulatkan menjadi 4 KB.
+ Ukuran setiap item yang cocok di tabel dasar: (300 byte, dibulatkan hingga 4 KB) × 4 item = 16 KB.

Oleh karena itu, ukuran total data dalam hasil adalah 20 KB.

### Unit kapasitas tulis
<a name="LSI.ThroughputConsiderations.Writes"></a>

Saat item dalam tabel ditambahkan, diperbarui, atau dihapus, memperbarui indeks sekunder lokal akan menggunakan unit kapasitas tulis yang disediakan untuk tabel tersebut. Total biaya throughput yang disediakan untuk penulisan adalah jumlah unit kapasitas tulis yang digunakan dengan menulis ke tabel dan yang digunakan dengan memperbarui indeks sekunder lokal.

Biaya tulis item ke indeks sekunder lokal tergantung pada beberapa faktor:
+ Jika Anda menulis item baru ke tabel yang menentukan atribut yang diindeks, atau Anda memperbarui item yang ada untuk menentukan atribut terindeks yang sebelumnya tidak ditentukan, satu operasi tulis diperlukan untuk memasukkan item ke dalam indeks.
+ Jika pembaruan pada tabel mengubah nilai atribut kunci yang diindeks (dari A menjadi B), diperlukan dua penulisan: satu untuk menghapus item sebelumnya dari indeks dan satu lagi untuk memasukkan item baru ke dalam indeks.  
+ Jika suatu item ada dalam indeks, tetapi penulisan ke tabel menyebabkan atribut yang diindeks terhapus, satu penulisan diperlukan untuk menghapus proyeksi item lama dari indeks.
+ Jika item tidak ada dalam indeks sebelum atau setelah item diperbarui, tidak ada biaya penulisan tambahan untuk indeks tersebut.

Semua faktor ini mengasumsikan bahwa ukuran setiap item dalam indeks kurang dari atau sama dengan ukuran item 1 KB untuk menghitung unit kapasitas tulis. Entri indeks yang lebih besar memerlukan unit kapasitas tulis tambahan. Anda dapat meminimalkan biaya penulisan dengan mempertimbangkan atribut yang perlu dikembalikan oleh kueri dan hanya memproyeksikan atribut tersebut ke dalam indeks.

## Pertimbangan penyimpanan untuk Indeks Sekunder Lokal
<a name="LSI.StorageConsiderations"></a>

Saat aplikasi menulis item ke tabel, DynamoDB otomatis menyalin subset atribut yang benar ke indeks sekunder lokal tempat atribut tersebut akan muncul. AWS Akun Anda dikenakan biaya untuk penyimpanan item di tabel dasar dan juga untuk penyimpanan atribut dalam indeks sekunder lokal apa pun di tabel itu.

Jumlah ruang yang digunakan oleh item indeks adalah jumlah dari berikut ini:
+ Ukuran kunci primer tabel dasar dalam byte (kunci partisi dan kunci urutan)
+ Ukuran atribut kunci indeks dalam byte
+ Ukuran atribut yang diproyeksikan dalam byte (jika ada)
+ 100 byte dari overhead per item indeks

Untuk memperkirakan kebutuhan penyimpanan indeks sekunder lokal, Anda dapat memperkirakan ukuran rata-rata item dalam indeks lalu mengalikannya dengan jumlah item di index.

Jika tabel berisi item dengan atribut tertentu tidak ditentukan, tetapi atribut tersebut didefinisikan sebagai kunci urutan indeks, maka DynamoDB tidak menulis data apa pun untuk item tersebut ke indeks. 

## Kumpulan item dalam Indeks Sekunder Lokal
<a name="LSI.ItemCollections"></a>

**catatan**  
Bagian ini hanya berkaitan dengan tabel yang memiliki indeks sekunder lokal.

Di DynamoDB, *kumpulan item* adalah grup item yang memiliki nilai kunci partisi yang sama dalam tabel dan semua indeks sekunder lokalnya. Dalam contoh yang digunakan di seluruh bagian ini, kunci partisi untuk tabel `Thread` adalah `ForumName`, dan kunci partisi untuk `LastPostIndex` juga `ForumName`. Semua item tabel dan indeks dengan `ForumName` yang sama adalah bagian dari kumpulan item yang sama. Misalnya, di tabel `Thread` dan indeks sekunder lokal `LastPostIndex`, terdapat kumpulan item untuk forum `EC2` dan kumpulan item yang berbeda untuk forum `RDS`.

Diagram berikut menunjukkan kumpulan item untuk forum `S3`.

![\[Koleksi item DynamoDB dengan tabel dan item Indeks Sekunder Lokal yang memiliki nilai kunci partisi yang sama. S3\]](http://docs.aws.amazon.com/id_id/amazondynamodb/latest/developerguide/images/LSI_04.png)


Dalam diagram ini, kumpulan item terdiri dari semua item di `Thread` dan `LastPostIndex` dengan nilai kunci partisi `ForumName`-nya adalah "S3". Jika terdapat indeks sekunder lokal lainnya di tabel, item apa pun dalam indeks tersebut dengan `ForumName` yang sama dengan "S3" juga akan menjadi bagian dari kumpulan item.

Anda dapat menggunakan salah satu operasi berikut di DynamoDB untuk menampilkan informasi tentang kumpulan item:
+ `BatchWriteItem`
+ `DeleteItem`
+ `PutItem`
+ `UpdateItem`
+ `TransactWriteItems`

Masing-masing operasi ini mendukung parameter `ReturnItemCollectionMetrics`. Jika mengatur parameter ini ke `SIZE`, Anda dapat melihat informasi tentang ukuran setiap kumpulan item dalam indeks.

**Example**  
Berikut ini adalah contoh dari output operasi `UpdateItem` pada tabel `Thread`, dengan `ReturnItemCollectionMetrics` diatur ke `SIZE`. Item yang diperbarui memiliki nilai `ForumName` "EC2", jadi outputnya mencakup informasi tentang kumpulan item tersebut.  

```
{
    ItemCollectionMetrics: {
        ItemCollectionKey: {
            ForumName: "EC2"
        },
        SizeEstimateRangeGB: [0.0, 1.0]
    }
}
```
Objek `SizeEstimateRangeGB` menunjukkan bahwa ukuran kumpulan item ini antara 0 dan 1 GB. DynamoDB memperbarui perkiraan ukuran ini secara berkala, sehingga jumlahnya mungkin berbeda saat item diubah lagi.

### Batas ukuran kumpulan item
<a name="LSI.ItemCollections.SizeLimit"></a>

Ukuran maksimum kumpulan item apa pun untuk tabel yang memiliki satu atau beberapa indeks sekunder lokal adalah 10 GB. Ini tidak berlaku untuk kumpulan item dalam tabel tanpa indeks sekunder lokal, dan juga tidak berlaku untuk kumpulan item dalam indeks sekunder global. Hanya tabel yang memiliki satu atau beberapa indeks sekunder lokal yang terpengaruh.

Jika koleksi item melebihi batas 10 GB, DynamoDB dapat mengembalikan`ItemCollectionSizeLimitExceededException`, dan Anda mungkin tidak dapat menambahkan lebih banyak item ke koleksi item atau menambah ukuran item yang ada dalam koleksi item. (Operasi baca dan tulis yang mengecilkan ukuran kumpulan item masih diperbolehkan.) Anda masih dapat menambahkan item ke kumpulan item lain.

Untuk mengurangi ukuran kumpulan item, Anda dapat melakukan salah satu berikut ini:
+ Hapus item yang tidak diperlukan dengan nilai kunci partisi terkait. Jika Anda menghapus item ini dari tabel dasar, DynamoDB juga menghapus entri indeks yang memiliki nilai kunci partisi yang sama.
+ Perbarui item dengan menghapus atribut atau dengan mengurangi ukuran atribut. Jika atribut ini diproyeksikan ke indeks sekunder lokal, DynamoDB juga mengurangi ukuran entri indeks terkait.
+ Buat tabel baru dengan kunci partisi dan kunci urutan yang sama, lalu pindahkan item dari tabel lama ke tabel baru. Ini mungkin merupakan pendekatan yang tepat jika tabel memiliki data historis yang jarang diakses. Anda juga dapat mempertimbangkan untuk mengarsipkan data historis ini ke Amazon Simple Storage Service (Amazon S3).

Ketika ukuran total kumpulan item turun di bawah 10 GB, Anda dapat menambahkan item dengan nilai kunci partisi yang sama sekali lagi.

Sebagai praktik terbaik, sebaiknya Anda melengkapi aplikasi Anda untuk memantau ukuran kumpulan item Anda. Salah satu cara untuk melakukannya adalah dengan mengatur parameter `ReturnItemCollectionMetrics` ke `SIZE` setiap kali Anda menggunakan `BatchWriteItem`, `DeleteItem`, `PutItem`, atau `UpdateItem`. Aplikasi Anda akan memeriksa objek `ReturnItemCollectionMetrics` di output dan mencatat pesan kesalahan setiap kali kumpulan item melebihi batas yang ditentukan pengguna (8 GB, misalnya). Menetapkan batas kurang dari 10 GB akan memberi sistem peringatan dini sehingga Anda mengetahui bahwa kumpulan item mendekati batas, dan Anda dapat melakukan tindakan terhadap hal tersebut.

### Kumpulan dan partisi item
<a name="LSI.ItemCollections.OnePartition"></a>

Di tabel dengan satu atau beberapa indeks sekunder lokal, masing-masing kumpulan item disimpan dalam satu partisi. Ukuran total kumpulan item tersebut terbatas pada kemampuan partisi itu: 10 GB. Untuk aplikasi yang model datanya menyertakan kumpulan item yang ukurannya tidak dibatasi, atau jika Anda memperkirakan beberapa kumpulan item akan bertambah melebihi 10 GB di masa mendatang, Anda sebaiknya mempertimbangkan untuk menggunakan indeks sekunder global.

Anda harus merancang aplikasi Anda sehingga data tabel didistribusikan secara merata ke seluruh nilai kunci partisi yang berbeda. Untuk tabel dengan indeks sekunder lokal, aplikasi Anda tidak boleh membuat "hot spot" aktivitas baca dan tulis dalam satu kumpulan item pada satu partisi. 

# Menggunakan Indeks Sekunder Lokal: Java
<a name="LSIJavaDocumentAPI"></a>

Anda dapat menggunakan AWS SDK untuk Java Document API untuk membuat tabel Amazon DynamoDB dengan satu atau beberapa indeks sekunder lokal, menjelaskan indeks pada tabel, dan melakukan kueri menggunakan indeks.

Berikut ini adalah langkah-langkah umum untuk operasi tabel menggunakan AWS SDK untuk Java Document API.

1. Buat instans dari kelas `DynamoDB`.

1. Berikan parameter wajib dan opsional untuk operasi dengan membuat objek permintaan yang sesuai. 

1. Panggil metode sesuai yang ditentukan oleh klien yang Anda buat pada langkah sebelumnya. 

**Topics**
+ [Membuat tabel dengan Indeks Sekunder Lokal](#LSIJavaDocumentAPI.CreateTableWithIndex)
+ [Mendeskripsikan tabel dengan Indeks Sekunder Lokal](#LSIJavaDocumentAPI.DescribeTableWithIndex)
+ [Mengkueri Indeks Sekunder Lokal](#LSIJavaDocumentAPI.QueryAnIndex)
+ [Contoh: Indeks Sekunder Lokal menggunakan API dokumen Java](LSIJavaDocumentAPI.Example.md)

## Membuat tabel dengan Indeks Sekunder Lokal
<a name="LSIJavaDocumentAPI.CreateTableWithIndex"></a>

Indeks sekunder lokal harus dibuat pada saat Anda membuat tabel. Untuk melakukannya, gunakan metode `createTable` dan berikan spesifikasi Anda untuk satu atau beberapa indeks sekunder lokal. Contoh kode Java berikut membuat tabel untuk menyimpan informasi tentang lagu dalam koleksi musik. Kunci partisinya adalah `Artist` dan kunci urutannya adalah `SongTitle`. Indeks sekunder, `AlbumTitleIndex`, memfasilitasi kueri berdasarkan judul album. 

Berikut adalah langkah-langkah untuk membuat tabel dengan indeks sekunder lokal, menggunakan API dokumen DynamoDB. 

1. Buat instans dari kelas `DynamoDB`.

1. Buat instans dari kelas `CreateTableRequest` untuk memberikan informasi permintaan. 

   Anda harus memberikan nama tabel, kunci primernya, dan nilai throughput yang ditentukan. Untuk indeks sekunder lokal, Anda harus memberikan nama indeks, nama dan jenis data untuk kunci urutan indeks, skema kunci untuk indeks, dan proyeksi atribut.

1. Panggil metode `createTable` dengan menentukan objek permintaan sebagai parameter.

Contoh kode Java berikut mendemonstrasikan langkah sebelumnya. Kode ini membuat tabel (`Music`) dengan indeks sekunder pada atribut `AlbumTitle`. Kunci urutan dan kunci partisi tabel, ditambah kunci urutan indeks, adalah satu-satunya atribut yang diproyeksikan ke dalam indeks.

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

String tableName = "Music";

CreateTableRequest createTableRequest = new CreateTableRequest().withTableName(tableName);

//ProvisionedThroughput
createTableRequest.setProvisionedThroughput(new ProvisionedThroughput().withReadCapacityUnits((long)5).withWriteCapacityUnits((long)5));

//AttributeDefinitions
ArrayList<AttributeDefinition> attributeDefinitions= new ArrayList<AttributeDefinition>();
attributeDefinitions.add(new AttributeDefinition().withAttributeName("Artist").withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition().withAttributeName("SongTitle").withAttributeType("S"));
attributeDefinitions.add(new AttributeDefinition().withAttributeName("AlbumTitle").withAttributeType("S"));

createTableRequest.setAttributeDefinitions(attributeDefinitions);

//KeySchema
ArrayList<KeySchemaElement> tableKeySchema = new ArrayList<KeySchemaElement>();
tableKeySchema.add(new KeySchemaElement().withAttributeName("Artist").withKeyType(KeyType.HASH));  //Partition key
tableKeySchema.add(new KeySchemaElement().withAttributeName("SongTitle").withKeyType(KeyType.RANGE));  //Sort key

createTableRequest.setKeySchema(tableKeySchema);

ArrayList<KeySchemaElement> indexKeySchema = new ArrayList<KeySchemaElement>();
indexKeySchema.add(new KeySchemaElement().withAttributeName("Artist").withKeyType(KeyType.HASH));  //Partition key
indexKeySchema.add(new KeySchemaElement().withAttributeName("AlbumTitle").withKeyType(KeyType.RANGE));  //Sort key

Projection projection = new Projection().withProjectionType(ProjectionType.INCLUDE);
ArrayList<String> nonKeyAttributes = new ArrayList<String>();
nonKeyAttributes.add("Genre");
nonKeyAttributes.add("Year");
projection.setNonKeyAttributes(nonKeyAttributes);

LocalSecondaryIndex localSecondaryIndex = new LocalSecondaryIndex()
    .withIndexName("AlbumTitleIndex").withKeySchema(indexKeySchema).withProjection(projection);

ArrayList<LocalSecondaryIndex> localSecondaryIndexes = new ArrayList<LocalSecondaryIndex>();
localSecondaryIndexes.add(localSecondaryIndex);
createTableRequest.setLocalSecondaryIndexes(localSecondaryIndexes);

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

Anda harus menunggu hingga DynamoDB membuat tabel dan menetapkan status tabel menjadi `ACTIVE`. Setelah itu, Anda bisa mulai memasukkan item data ke dalam tabel.

## Mendeskripsikan tabel dengan Indeks Sekunder Lokal
<a name="LSIJavaDocumentAPI.DescribeTableWithIndex"></a>

Untuk mendapatkan informasi tentang indeks sekunder lokal pada tabel, gunakan metode `describeTable`. Untuk setiap indeks, Anda dapat mengakses namanya, skema kunci, dan atribut yang diproyeksikan.

Berikut ini adalah langkah-langkah untuk mengakses informasi indeks sekunder lokal suatu tabel menggunakan API Dokumen AWS SDK untuk Java .

1. Buat instans dari kelas `DynamoDB`.

1. Buat instans dari kelas `Table`. Anda harus memberikan nama tabel.

1. Panggil metode `describeTable` pada objek `Table`.

Contoh kode Java berikut mendemonstrasikan langkah sebelumnya.

**Example**  

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

String tableName = "Music";

Table table = dynamoDB.getTable(tableName);

TableDescription tableDescription = table.describe();

List<LocalSecondaryIndexDescription> localSecondaryIndexes 
    = tableDescription.getLocalSecondaryIndexes();

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

Iterator<LocalSecondaryIndexDescription> lsiIter = localSecondaryIndexes.iterator();
while (lsiIter.hasNext()) {

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

## Mengkueri Indeks Sekunder Lokal
<a name="LSIJavaDocumentAPI.QueryAnIndex"></a>

Anda dapat menggunakan operasi `Query` pada indeks sekunder lokal dengan cara yang hampir sama seperti Anda `Query` tabel. Anda harus menentukan nama indeks, kriteria kueri untuk kunci urutan indeks, dan atribut yang ingin Anda kembalikan. Dalam contoh ini, indeks adalah `AlbumTitleIndex` dan kunci urutan indeks adalah `AlbumTitle`. 

Satu-satunya atribut yang dikembalikan adalah atribut yang telah diproyeksikan ke dalam indeks. Anda dapat memodifikasi kueri ini untuk memilih atribut non-kunci juga, tetapi ini akan memerlukan aktivitas pengambilan tabel yang relatif mahal. Untuk informasi selengkapnya tentang pengambilan tabel, lihat [Proyeksi atribut](LSI.md#LSI.Projections).

Berikut ini adalah langkah-langkah untuk query indeks sekunder lokal menggunakan AWS SDK untuk Java Document API. 

1. Buat instans dari kelas `DynamoDB`.

1. Buat instans dari kelas `Table`. Anda harus memberikan nama tabel.

1. Buat instans dari kelas `Index`. Anda harus memberikan nama indeks.

1. Panggil metode `query` dari kelas `Index`.

Contoh kode Java berikut mendemonstrasikan langkah sebelumnya.

**Example**  

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

String tableName = "Music";

Table table = dynamoDB.getTable(tableName);
Index index = table.getIndex("AlbumTitleIndex");

QuerySpec spec = new QuerySpec()
    .withKeyConditionExpression("Artist = :v_artist and AlbumTitle = :v_title")
    .withValueMap(new ValueMap()
        .withString(":v_artist", "Acme Band")
        .withString(":v_title", "Songs About Life"));

ItemCollection<QueryOutcome> items = index.query(spec);

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

while (itemsIter.hasNext()) {
    Item item = itemsIter.next();
    System.out.println(item.toJSONPretty());
}
```

### Bacaan yang konsisten pada Indeks Sekunder Lokal
<a name="LSIJavaDocumentAPI.ConsistentReads"></a>

Tidak seperti indeks sekunder global, yang hanya mendukung pembacaan yang konsisten pada akhirnya, indeks sekunder lokal mendukung pembacaan yang konsisten dan sangat konsisten. Bacaan sangat konsisten dari indeks sekunder lokal selalu mengembalikan nilai terbaru yang diperbarui. Jika kueri perlu mengambil atribut tambahan dari tabel dasar, atribut yang diambil tersebut juga konsisten sehubungan dengan indeks.

Secara default, `Query` menggunakan pembacaan yang konsisten pada akhirnya. Untuk meminta pembacaan yang sangat konsisten, atur `ConsistentRead` ke `true` dalam`QuerySpec`. Contoh kueri berikut `AlbumTitleIndex` menggunakan pembacaan yang sangat konsisten:

**Example**  

```
QuerySpec spec = new QuerySpec()
    .withKeyConditionExpression("Artist = :v_artist and AlbumTitle = :v_title")
    .withValueMap(new ValueMap()
        .withString(":v_artist", "Acme Band")
        .withString(":v_title", "Songs About Life"))
    .withConsistentRead(true);
```

**catatan**  
Pembacaan yang sangat konsisten mengkonsumsi satu unit kapasitas baca per 4 KB data yang dikembalikan (dibulatkan ke atas), sedangkan pembacaan yang konsisten pada akhirnya mengkonsumsi setengahnya. Misalnya, pembacaan yang sangat konsisten yang mengembalikan 9 KB data mengkonsumsi 3 unit kapasitas baca (9 KB/ 4 KB = 2,25, dibulatkan ke 3), sedangkan kueri yang sama menggunakan pembacaan yang konsisten pada akhirnya mengkonsumsi 1,5 unit kapasitas baca. Jika aplikasi Anda dapat mentolerir membaca data yang mungkin sedikit basi, gunakan pembacaan yang konsisten pada akhirnya untuk mengurangi penggunaan kapasitas baca Anda. Untuk informasi selengkapnya, lihat [Unit kapasitas baca](LSI.md#LSI.ThroughputConsiderations.Reads).

# Contoh: Indeks Sekunder Lokal menggunakan API dokumen Java
<a name="LSIJavaDocumentAPI.Example"></a>

Contoh kode Java berikut menunjukkan cara menggunakan indeks sekunder lokal di Amazon DynamoDB. Contoh membuat tabel bernama `CustomerOrders` dengan kunci partisi `CustomerId` dan kunci urutan `OrderId`. Ada dua indeks sekunder lokal di tabel ini:
+ `OrderCreationDateIndex` — Kunci urutannya adalah `OrderCreationDate`, dan atribut berikut diproyeksikan ke dalam indeks:
  + `ProductCategory`
  + `ProductName`
  + `OrderStatus`
  + `ShipmentTrackingId`
+ `IsOpenIndex` — Kunci urutannya adalah `IsOpen`, dan semua atribut tabel diproyeksikan ke dalam indeks.

Setelah tabel `CustomerOrders` dibuat, program memuat tabel dengan data yang mewakili pesanan pelanggan. Kemudian mengkueri data menggunakan indeks sekunder lokal. Terakhir, program menghapus tabel `CustomerOrders`.

Untuk step-by-step instruksi untuk menguji sampel berikut, lihat[Contoh kode Java](CodeSamples.Java.md).

**Example**  

```
package com.example.dynamodb;

import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.waiters.DynamoDbWaiter;

import java.util.HashMap;
import java.util.Map;

public class DocumentAPILocalSecondaryIndexExample {

    static DynamoDbClient client = DynamoDbClient.create();
    public static String tableName = "CustomerOrders";

    public static void main(String[] args) {
        createTable();
        loadData();
        query(null);
        query("IsOpenIndex");
        query("OrderCreationDateIndex");
        deleteTable(tableName);
    }

    public static void createTable() {
        CreateTableRequest request = CreateTableRequest.builder()
            .tableName(tableName)
            .provisionedThroughput(ProvisionedThroughput.builder()
                .readCapacityUnits(1L)
                .writeCapacityUnits(1L)
                .build())
            .attributeDefinitions(
                AttributeDefinition.builder().attributeName("CustomerId").attributeType(ScalarAttributeType.S).build(),
                AttributeDefinition.builder().attributeName("OrderId").attributeType(ScalarAttributeType.N).build(),
                AttributeDefinition.builder().attributeName("OrderCreationDate").attributeType(ScalarAttributeType.N).build(),
                AttributeDefinition.builder().attributeName("IsOpen").attributeType(ScalarAttributeType.N).build())
            .keySchema(
                KeySchemaElement.builder().attributeName("CustomerId").keyType(KeyType.HASH).build(),
                KeySchemaElement.builder().attributeName("OrderId").keyType(KeyType.RANGE).build())
            .localSecondaryIndexes(
                LocalSecondaryIndex.builder()
                    .indexName("OrderCreationDateIndex")
                    .keySchema(
                        KeySchemaElement.builder().attributeName("CustomerId").keyType(KeyType.HASH).build(),
                        KeySchemaElement.builder().attributeName("OrderCreationDate").keyType(KeyType.RANGE).build())
                    .projection(Projection.builder()
                        .projectionType(ProjectionType.INCLUDE)
                        .nonKeyAttributes("ProductCategory", "ProductName")
                        .build())
                    .build(),
                LocalSecondaryIndex.builder()
                    .indexName("IsOpenIndex")
                    .keySchema(
                        KeySchemaElement.builder().attributeName("CustomerId").keyType(KeyType.HASH).build(),
                        KeySchemaElement.builder().attributeName("IsOpen").keyType(KeyType.RANGE).build())
                    .projection(Projection.builder()
                        .projectionType(ProjectionType.ALL)
                        .build())
                    .build())
            .build();

        System.out.println("Creating table " + tableName + "...");
        client.createTable(request);

        try (DynamoDbWaiter waiter = client.waiter()) {
            WaiterResponse<DescribeTableResponse> response = waiter.waitUntilTableExists(r -> r.tableName(tableName));
            response.matched().response().ifPresent(System.out::println);
        }
    }

    public static void query(String indexName) {
        System.out.println("\n***********************************************************\n");
        System.out.println("Querying table " + tableName + "...");

        if ("IsOpenIndex".equals(indexName)) {
            System.out.println("\nUsing index: '" + indexName + "': Bob's orders that are open.");
            System.out.println("Only a user-specified list of attributes are returned\n");

            Map<String, AttributeValue> values = new HashMap<>();
            values.put(":v_custid", AttributeValue.builder().s("bob@example.com").build());
            values.put(":v_isopen", AttributeValue.builder().n("1").build());

            QueryRequest request = QueryRequest.builder()
                .tableName(tableName)
                .indexName(indexName)
                .keyConditionExpression("CustomerId = :v_custid and IsOpen = :v_isopen")
                .expressionAttributeValues(values)
                .projectionExpression("OrderCreationDate, ProductCategory, ProductName, OrderStatus")
                .build();

            System.out.println("Query: printing results...");
            client.query(request).items().forEach(System.out::println);

        } else if ("OrderCreationDateIndex".equals(indexName)) {
            System.out.println("\nUsing index: '" + indexName + "': Bob's orders that were placed after 01/31/2015.");
            System.out.println("Only the projected attributes are returned\n");

            Map<String, AttributeValue> values = new HashMap<>();
            values.put(":v_custid", AttributeValue.builder().s("bob@example.com").build());
            values.put(":v_orddate", AttributeValue.builder().n("20150131").build());

            QueryRequest request = QueryRequest.builder()
                .tableName(tableName)
                .indexName(indexName)
                .keyConditionExpression("CustomerId = :v_custid and OrderCreationDate >= :v_orddate")
                .expressionAttributeValues(values)
                .select(Select.ALL_PROJECTED_ATTRIBUTES)
                .build();

            System.out.println("Query: printing results...");
            client.query(request).items().forEach(System.out::println);

        } else {
            System.out.println("\nNo index: All of Bob's orders, by OrderId:\n");

            Map<String, AttributeValue> values = new HashMap<>();
            values.put(":v_custid", AttributeValue.builder().s("bob@example.com").build());

            QueryRequest request = QueryRequest.builder()
                .tableName(tableName)
                .keyConditionExpression("CustomerId = :v_custid")
                .expressionAttributeValues(values)
                .build();

            System.out.println("Query: printing results...");
            client.query(request).items().forEach(System.out::println);
        }
    }

    public static void deleteTable(String tableName) {
        System.out.println("Deleting table " + tableName + "...");
        client.deleteTable(DeleteTableRequest.builder().tableName(tableName).build());

        try (DynamoDbWaiter waiter = client.waiter()) {
            waiter.waitUntilTableNotExists(r -> r.tableName(tableName));
        }
    }

    public static void loadData() {
        System.out.println("Loading data into table " + tableName + "...");

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("alice@example.com").build(),
            "OrderId", AttributeValue.builder().n("1").build(),
            "IsOpen", AttributeValue.builder().n("1").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150101").build(),
            "ProductCategory", AttributeValue.builder().s("Book").build(),
            "ProductName", AttributeValue.builder().s("The Great Outdoors").build(),
            "OrderStatus", AttributeValue.builder().s("PACKING ITEMS").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("alice@example.com").build(),
            "OrderId", AttributeValue.builder().n("2").build(),
            "IsOpen", AttributeValue.builder().n("1").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150221").build(),
            "ProductCategory", AttributeValue.builder().s("Bike").build(),
            "ProductName", AttributeValue.builder().s("Super Mountain").build(),
            "OrderStatus", AttributeValue.builder().s("ORDER RECEIVED").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("alice@example.com").build(),
            "OrderId", AttributeValue.builder().n("3").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150304").build(),
            "ProductCategory", AttributeValue.builder().s("Music").build(),
            "ProductName", AttributeValue.builder().s("A Quiet Interlude").build(),
            "OrderStatus", AttributeValue.builder().s("IN TRANSIT").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("176493").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("1").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150111").build(),
            "ProductCategory", AttributeValue.builder().s("Movie").build(),
            "ProductName", AttributeValue.builder().s("Calm Before The Storm").build(),
            "OrderStatus", AttributeValue.builder().s("SHIPPING DELAY").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("859323").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("2").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150124").build(),
            "ProductCategory", AttributeValue.builder().s("Music").build(),
            "ProductName", AttributeValue.builder().s("E-Z Listening").build(),
            "OrderStatus", AttributeValue.builder().s("DELIVERED").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("756943").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("3").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150221").build(),
            "ProductCategory", AttributeValue.builder().s("Music").build(),
            "ProductName", AttributeValue.builder().s("Symphony 9").build(),
            "OrderStatus", AttributeValue.builder().s("DELIVERED").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("645193").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("4").build(),
            "IsOpen", AttributeValue.builder().n("1").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150222").build(),
            "ProductCategory", AttributeValue.builder().s("Hardware").build(),
            "ProductName", AttributeValue.builder().s("Extra Heavy Hammer").build(),
            "OrderStatus", AttributeValue.builder().s("PACKING ITEMS").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("5").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150309").build(),
            "ProductCategory", AttributeValue.builder().s("Book").build(),
            "ProductName", AttributeValue.builder().s("How To Cook").build(),
            "OrderStatus", AttributeValue.builder().s("IN TRANSIT").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("440185").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("6").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150318").build(),
            "ProductCategory", AttributeValue.builder().s("Luggage").build(),
            "ProductName", AttributeValue.builder().s("Really Big Suitcase").build(),
            "OrderStatus", AttributeValue.builder().s("DELIVERED").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("893927").build()));

        putItem(Map.of(
            "CustomerId", AttributeValue.builder().s("bob@example.com").build(),
            "OrderId", AttributeValue.builder().n("7").build(),
            "OrderCreationDate", AttributeValue.builder().n("20150324").build(),
            "ProductCategory", AttributeValue.builder().s("Golf").build(),
            "ProductName", AttributeValue.builder().s("PGA Pro II").build(),
            "OrderStatus", AttributeValue.builder().s("OUT FOR DELIVERY").build(),
            "ShipmentTrackingId", AttributeValue.builder().s("383283").build()));
    }

    private static void putItem(Map<String, AttributeValue> item) {
        client.putItem(PutItemRequest.builder().tableName(tableName).item(item).build());
    }
}
```

# Menggunakan Indeks Sekunder Lokal: .NET
<a name="LSILowLevelDotNet"></a>

**Topics**
+ [Membuat tabel dengan Indeks Sekunder Lokal](#LSILowLevelDotNet.CreateTableWithIndex)
+ [Mendeskripsikan tabel dengan Indeks Sekunder Lokal](#LSILowLevelDotNet.DescribeTableWithIndex)
+ [Mengkueri Indeks Sekunder Lokal](#LSILowLevelDotNet.QueryAnIndex)
+ [Contoh: Indeks Sekunder Lokal menggunakan API tingkat AWS SDK untuk .NET rendah](LSILowLevelDotNet.Example.md)

Anda dapat menggunakan API AWS SDK untuk .NET tingkat rendah untuk membuat tabel Amazon DynamoDB dengan satu atau beberapa indeks sekunder lokal, menjelaskan indeks pada tabel, dan melakukan kueri menggunakan indeks. Operasi ini dipetakan ke tindakan DynamoDB API tingkat rendah yang sesuai. Untuk informasi selengkapnya, lihat [Contoh kode .NET](CodeSamples.DotNet.md). 

Berikut ini adalah langkah-langkah umum untuk operasi tabel menggunakan API tingkat rendah .NET. 

1. Buat instans dari kelas `AmazonDynamoDBClient`.

1. Berikan parameter wajib dan opsional untuk operasi dengan membuat objek permintaan yang sesuai.

   Misalnya, buat objek `CreateTableRequest` untuk membuat tabel dan objek `QueryRequest` untuk mengkueri tabel atau indeks. 

1. Jalankan metode sesuai yang ditentukan oleh klien yang Anda buat pada langkah sebelumnya. 

## Membuat tabel dengan Indeks Sekunder Lokal
<a name="LSILowLevelDotNet.CreateTableWithIndex"></a>

Indeks sekunder lokal harus dibuat pada saat Anda membuat tabel. Untuk melakukannya, gunakan `CreateTable` dan berikan spesifikasi Anda untuk satu atau beberapa indeks sekunder lokal. Contoh kode C\$1 berikut membuat tabel untuk menyimpan informasi tentang lagu dalam koleksi musik. Kunci partisinya adalah `Artist` dan kunci urutannya adalah `SongTitle`. Indeks sekunder, `AlbumTitleIndex`, memfasilitasi kueri berdasarkan judul album. 

Berikut ini adalah langkah-langkah untuk membuat tabel dengan indeks sekunder lokal, menggunakan API tingkat rendah .NET. 

1. Buat instans dari kelas `AmazonDynamoDBClient`.

1. Buat instans dari kelas `CreateTableRequest` untuk memberikan informasi permintaan. 

   Anda harus memberikan nama tabel, kunci primernya, dan nilai throughput yang ditentukan. Untuk indeks sekunder lokal, Anda harus memberikan nama indeks, nama dan jenis data kunci urutan indeks, skema kunci untuk indeks, dan proyeksi atribut.

1. Jalankan metode `CreateTable` dengan menentukan objek permintaan sebagai parameter.

Contoh kode \$1C berikut mendemonstrasikan langkah sebelumnya. Kode ini membuat tabel (`Music`) dengan indeks sekunder pada atribut `AlbumTitle`. Kunci urutan dan kunci partisi tabel, ditambah kunci urutan indeks, adalah satu-satunya atribut yang diproyeksikan ke dalam indeks.

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

CreateTableRequest createTableRequest = new CreateTableRequest()
{
    TableName = tableName
};

//ProvisionedThroughput
createTableRequest.ProvisionedThroughput = new ProvisionedThroughput()
{
    ReadCapacityUnits = (long)5,
    WriteCapacityUnits = (long)5
};

//AttributeDefinitions
List<AttributeDefinition> attributeDefinitions = new List<AttributeDefinition>();

attributeDefinitions.Add(new AttributeDefinition()
{
    AttributeName = "Artist",
    AttributeType = "S"
});

attributeDefinitions.Add(new AttributeDefinition()
 {
     AttributeName = "SongTitle",
     AttributeType = "S"
 });

attributeDefinitions.Add(new AttributeDefinition()
 {
     AttributeName = "AlbumTitle",
     AttributeType = "S"
 });

createTableRequest.AttributeDefinitions = attributeDefinitions;

//KeySchema
List<KeySchemaElement> tableKeySchema = new List<KeySchemaElement>();

tableKeySchema.Add(new KeySchemaElement() { AttributeName = "Artist", KeyType = "HASH" });  //Partition key
tableKeySchema.Add(new KeySchemaElement() { AttributeName = "SongTitle", KeyType = "RANGE" });  //Sort key

createTableRequest.KeySchema = tableKeySchema;

List<KeySchemaElement> indexKeySchema = new List<KeySchemaElement>();
indexKeySchema.Add(new KeySchemaElement() { AttributeName = "Artist", KeyType = "HASH" });  //Partition key
indexKeySchema.Add(new KeySchemaElement() { AttributeName = "AlbumTitle", KeyType = "RANGE" });  //Sort key

Projection projection = new Projection() { ProjectionType = "INCLUDE" };

List<string> nonKeyAttributes = new List<string>();
nonKeyAttributes.Add("Genre");
nonKeyAttributes.Add("Year");
projection.NonKeyAttributes = nonKeyAttributes;

LocalSecondaryIndex localSecondaryIndex = new LocalSecondaryIndex()
{
    IndexName = "AlbumTitleIndex",
    KeySchema = indexKeySchema,
    Projection = projection
};

List<LocalSecondaryIndex> localSecondaryIndexes = new List<LocalSecondaryIndex>();
localSecondaryIndexes.Add(localSecondaryIndex);
createTableRequest.LocalSecondaryIndexes = localSecondaryIndexes;

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

Anda harus menunggu hingga DynamoDB membuat tabel dan menetapkan status tabel menjadi `ACTIVE`. Setelah itu, Anda bisa mulai memasukkan item data ke dalam tabel.

## Mendeskripsikan tabel dengan Indeks Sekunder Lokal
<a name="LSILowLevelDotNet.DescribeTableWithIndex"></a>

Untuk mendapatkan informasi tentang indeks sekunder lokal pada tabel, gunakan API `DescribeTable`. Untuk setiap indeks, Anda dapat mengakses namanya, skema kunci, dan atribut yang diproyeksikan.

Berikut ini adalah langkah-langkah untuk mengakses informasi indeks sekunder lokal suatu tabel menggunakan API tingkat rendah .NET. 

1. Buat instans dari kelas `AmazonDynamoDBClient`.

1. Buat instans dari kelas `DescribeTableRequest` untuk memberikan informasi permintaan. Anda harus memberikan nama tabel.

1. Jalankan metode `describeTable` dengan menentukan objek permintaan sebagai parameter.

Contoh kode \$1C berikut mendemonstrasikan langkah sebelumnya.

**Example**  

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

DescribeTableResponse response = client.DescribeTable(new DescribeTableRequest() { TableName = tableName });
List<LocalSecondaryIndexDescription> localSecondaryIndexes =
    response.DescribeTableResult.Table.LocalSecondaryIndexes;

// This code snippet will work for multiple indexes, even though
// there is only one index in this example.
foreach (LocalSecondaryIndexDescription lsiDescription in localSecondaryIndexes)
{
    Console.WriteLine("Info for index " + lsiDescription.IndexName + ":");

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

    Projection projection = lsiDescription.Projection;

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

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

        foreach (String s in projection.NonKeyAttributes)
        {
            Console.WriteLine("\t\t" + s);
        }

    }
}
```

## Mengkueri Indeks Sekunder Lokal
<a name="LSILowLevelDotNet.QueryAnIndex"></a>

Anda dapat menggunakan `Query` pada indeks sekunder lokal, sama seperti Anda `Query` tabel. Anda harus menentukan nama indeks, kriteria kueri untuk kunci urutan indeks, dan atribut yang ingin Anda kembalikan. Dalam contoh ini, indeks adalah `AlbumTitleIndex`, dan kunci urutan indeks adalah `AlbumTitle`. 

Satu-satunya atribut yang dikembalikan adalah atribut yang telah diproyeksikan ke dalam indeks. Anda dapat memodifikasi kueri ini untuk memilih atribut non-kunci juga, tetapi ini akan memerlukan aktivitas pengambilan tabel yang relatif mahal. Untuk informasi selengkapnya tentang pengambilan tabel, lihat [Proyeksi atribut](LSI.md#LSI.Projections)

Berikut ini adalah langkah-langkah untuk mengkueri indeks sekunder lokal menggunakan API tingkat rendah .NET. 

1. Buat instans dari kelas `AmazonDynamoDBClient`.

1. Buat instans dari kelas `QueryRequest` untuk memberikan informasi permintaan.

1. Jalankan metode `query` dengan menentukan objek permintaan sebagai parameter.

Contoh kode \$1C berikut mendemonstrasikan langkah sebelumnya.

**Example**  

```
QueryRequest queryRequest = new QueryRequest
{
    TableName = "Music",
    IndexName = "AlbumTitleIndex",
    Select = "ALL_ATTRIBUTES",
    ScanIndexForward = true,
    KeyConditionExpression = "Artist = :v_artist and AlbumTitle = :v_title",
    ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
    {
        {":v_artist",new AttributeValue {S = "Acme Band"}},
        {":v_title",new AttributeValue {S = "Songs About Life"}}
    },
};

QueryResponse response = client.Query(queryRequest);

foreach (var attribs in response.Items)
{
    foreach (var attrib in attribs)
    {
        Console.WriteLine(attrib.Key + " ---> " + attrib.Value.S);
    }
    Console.WriteLine();
}
```

# Contoh: Indeks Sekunder Lokal menggunakan API tingkat AWS SDK untuk .NET rendah
<a name="LSILowLevelDotNet.Example"></a>

Contoh kode C\$1 berikut menunjukkan cara menggunakan indeks sekunder lokal di Amazon DynamoDB. Contoh membuat tabel bernama `CustomerOrders` dengan kunci partisi `CustomerId` dan kunci urutan `OrderId`. Ada dua indeks sekunder lokal di tabel ini:
+ `OrderCreationDateIndex` — Kunci urutannya adalah `OrderCreationDate`, dan atribut berikut diproyeksikan ke dalam indeks:
  + `ProductCategory`
  + `ProductName`
  + `OrderStatus`
  + `ShipmentTrackingId`
+ `IsOpenIndex` — Kunci urutannya adalah `IsOpen`, dan semua atribut tabel diproyeksikan ke dalam indeks.

Setelah tabel `CustomerOrders` dibuat, program memuat tabel dengan data yang mewakili pesanan pelanggan. Kemudian mengkueri data menggunakan indeks sekunder lokal. Terakhir, program menghapus tabel `CustomerOrders`.

Untuk step-by-step instruksi untuk menguji contoh berikut, lihat[Contoh kode .NET](CodeSamples.DotNet.md).

**Example**  

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

namespace com.amazonaws.codesamples
{
    class LowLevelLocalSecondaryIndexExample
    {
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();
        private static string tableName = "CustomerOrders";

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

                Query(null);
                Query("IsOpenIndex");
                Query("OrderCreationDateIndex");

                DeleteTable(tableName);

                Console.WriteLine("To continue, press Enter");
                Console.ReadLine();
            }
            catch (AmazonDynamoDBException e) { Console.WriteLine(e.Message); }
            catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
            catch (Exception e) { Console.WriteLine(e.Message); }
        }

        private static void CreateTable()
        {
            var createTableRequest =
                new CreateTableRequest()
                {
                    TableName = tableName,
                    ProvisionedThroughput =
                    new ProvisionedThroughput()
                    {
                        ReadCapacityUnits = (long)1,
                        WriteCapacityUnits = (long)1
                    }
                };

            var attributeDefinitions = new List<AttributeDefinition>()
        {
            // Attribute definitions for table primary key
            { new AttributeDefinition() {
                  AttributeName = "CustomerId", AttributeType = "S"
              } },
            { new AttributeDefinition() {
                  AttributeName = "OrderId", AttributeType = "N"
              } },
            // Attribute definitions for index primary key
            { new AttributeDefinition() {
                  AttributeName = "OrderCreationDate", AttributeType = "N"
              } },
            { new AttributeDefinition() {
                  AttributeName = "IsOpen", AttributeType = "N"
              }}
        };

            createTableRequest.AttributeDefinitions = attributeDefinitions;

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

            createTableRequest.KeySchema = tableKeySchema;

            var localSecondaryIndexes = new List<LocalSecondaryIndex>();

            // OrderCreationDateIndex
            LocalSecondaryIndex orderCreationDateIndex = new LocalSecondaryIndex()
            {
                IndexName = "OrderCreationDateIndex"
            };

            // Key schema for OrderCreationDateIndex
            var indexKeySchema = new List<KeySchemaElement>()
        {
            { new KeySchemaElement() {
                  AttributeName = "CustomerId", KeyType = "HASH"
              } },                                                    //Partition key
            { new KeySchemaElement() {
                  AttributeName = "OrderCreationDate", KeyType = "RANGE"
              } }                                                            //Sort key
        };

            orderCreationDateIndex.KeySchema = indexKeySchema;

            // Projection (with list of projected attributes) for
            // OrderCreationDateIndex
            var projection = new Projection()
            {
                ProjectionType = "INCLUDE"
            };

            var nonKeyAttributes = new List<string>()
        {
            "ProductCategory",
            "ProductName"
        };
            projection.NonKeyAttributes = nonKeyAttributes;

            orderCreationDateIndex.Projection = projection;

            localSecondaryIndexes.Add(orderCreationDateIndex);

            // IsOpenIndex
            LocalSecondaryIndex isOpenIndex
                = new LocalSecondaryIndex()
                {
                    IndexName = "IsOpenIndex"
                };

            // Key schema for IsOpenIndex
            indexKeySchema = new List<KeySchemaElement>()
        {
            { new KeySchemaElement() {
                  AttributeName = "CustomerId", KeyType = "HASH"
              }},                                                     //Partition key
            { new KeySchemaElement() {
                  AttributeName = "IsOpen", KeyType = "RANGE"
              }}                                                  //Sort key
        };

            // Projection (all attributes) for IsOpenIndex
            projection = new Projection()
            {
                ProjectionType = "ALL"
            };

            isOpenIndex.KeySchema = indexKeySchema;
            isOpenIndex.Projection = projection;

            localSecondaryIndexes.Add(isOpenIndex);

            // Add index definitions to CreateTable request
            createTableRequest.LocalSecondaryIndexes = localSecondaryIndexes;

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

        public static void Query(string indexName)
        {
            Console.WriteLine("\n***********************************************************\n");
            Console.WriteLine("Querying table " + tableName + "...");

            QueryRequest queryRequest = new QueryRequest()
            {
                TableName = tableName,
                ConsistentRead = true,
                ScanIndexForward = true,
                ReturnConsumedCapacity = "TOTAL"
            };


            String keyConditionExpression = "CustomerId = :v_customerId";
            Dictionary<string, AttributeValue> expressionAttributeValues = new Dictionary<string, AttributeValue> {
            {":v_customerId", new AttributeValue {
                 S = "bob@example.com"
             }}
        };


            if (indexName == "IsOpenIndex")
            {
                Console.WriteLine("\nUsing index: '" + indexName
                          + "': Bob's orders that are open.");
                Console.WriteLine("Only a user-specified list of attributes are returned\n");
                queryRequest.IndexName = indexName;

                keyConditionExpression += " and IsOpen = :v_isOpen";
                expressionAttributeValues.Add(":v_isOpen", new AttributeValue
                {
                    N = "1"
                });

                // ProjectionExpression
                queryRequest.ProjectionExpression = "OrderCreationDate, ProductCategory, ProductName, OrderStatus";
            }
            else if (indexName == "OrderCreationDateIndex")
            {
                Console.WriteLine("\nUsing index: '" + indexName
                          + "': Bob's orders that were placed after 01/31/2013.");
                Console.WriteLine("Only the projected attributes are returned\n");
                queryRequest.IndexName = indexName;

                keyConditionExpression += " and OrderCreationDate > :v_Date";
                expressionAttributeValues.Add(":v_Date", new AttributeValue
                {
                    N = "20130131"
                });

                // Select
                queryRequest.Select = "ALL_PROJECTED_ATTRIBUTES";
            }
            else
            {
                Console.WriteLine("\nNo index: All of Bob's orders, by OrderId:\n");
            }
            queryRequest.KeyConditionExpression = keyConditionExpression;
            queryRequest.ExpressionAttributeValues = expressionAttributeValues;

            var result = client.Query(queryRequest);
            var items = result.Items;
            foreach (var currentItem in items)
            {
                foreach (string attr in currentItem.Keys)
                {
                    if (attr == "OrderId" || attr == "IsOpen"
                        || attr == "OrderCreationDate")
                    {
                        Console.WriteLine(attr + "---> " + currentItem[attr].N);
                    }
                    else
                    {
                        Console.WriteLine(attr + "---> " + currentItem[attr].S);
                    }
                }
                Console.WriteLine();
            }
            Console.WriteLine("\nConsumed capacity: " + result.ConsumedCapacity.CapacityUnits + "\n");
        }

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

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

            Dictionary<string, AttributeValue> item = new Dictionary<string, AttributeValue>();

            item["CustomerId"] = new AttributeValue
            {
                S = "alice@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "1"
            };
            item["IsOpen"] = new AttributeValue
            {
                N = "1"
            };
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130101"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Book"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "The Great Outdoors"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "PACKING ITEMS"
            };
            /* no ShipmentTrackingId attribute */
            PutItemRequest putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "alice@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "2"
            };
            item["IsOpen"] = new AttributeValue
            {
                N = "1"
            };
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130221"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Bike"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Super Mountain"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "ORDER RECEIVED"
            };
            /* no ShipmentTrackingId attribute */
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "alice@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "3"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130304"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Music"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "A Quiet Interlude"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "IN TRANSIT"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "176493"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "1"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130111"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Movie"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Calm Before The Storm"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "SHIPPING DELAY"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "859323"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "2"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130124"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Music"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "E-Z Listening"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "DELIVERED"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "756943"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "3"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130221"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Music"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Symphony 9"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "DELIVERED"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "645193"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "4"
            };
            item["IsOpen"] = new AttributeValue
            {
                N = "1"
            };
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130222"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Hardware"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Extra Heavy Hammer"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "PACKING ITEMS"
            };
            /* no ShipmentTrackingId attribute */
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "5"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130309"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Book"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "How To Cook"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "IN TRANSIT"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "440185"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "6"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130318"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Luggage"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "Really Big Suitcase"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "DELIVERED"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "893927"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);

            item = new Dictionary<string, AttributeValue>();
            item["CustomerId"] = new AttributeValue
            {
                S = "bob@example.com"
            };
            item["OrderId"] = new AttributeValue
            {
                N = "7"
            };
            /* no IsOpen attribute */
            item["OrderCreationDate"] = new AttributeValue
            {
                N = "20130324"
            };
            item["ProductCategory"] = new AttributeValue
            {
                S = "Golf"
            };
            item["ProductName"] = new AttributeValue
            {
                S = "PGA Pro II"
            };
            item["OrderStatus"] = new AttributeValue
            {
                S = "OUT FOR DELIVERY"
            };
            item["ShipmentTrackingId"] = new AttributeValue
            {
                S = "383283"
            };
            putItemRequest = new PutItemRequest
            {
                TableName = tableName,
                Item = item,
                ReturnItemCollectionMetrics = "SIZE"
            };
            client.PutItem(putItemRequest);
        }

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

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

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

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

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

# Bekerja dengan Indeks Sekunder Lokal di DynamoDB AWS CLI
<a name="LCICli"></a>

Anda dapat menggunakan AWS CLI untuk membuat tabel Amazon DynamoDB dengan satu atau beberapa Indeks Sekunder Lokal, menjelaskan indeks pada tabel, dan melakukan kueri menggunakan indeks.

**Topics**
+ [Membuat tabel dengan Indeks Sekunder Lokal](#LCICli.CreateTableWithIndex)
+ [Mendeskripsikan tabel dengan Indeks Sekunder Lokal](#LCICli.DescribeTableWithIndex)
+ [Mengkueri Indeks Sekunder Lokal](#LCICli.QueryAnIndex)

## Membuat tabel dengan Indeks Sekunder Lokal
<a name="LCICli.CreateTableWithIndex"></a>

Indeks Sekunder Lokal harus dibuat pada saat Anda membuat tabel. Untuk melakukannya, gunakan parameter `create-table` dan berikan spesifikasi Anda untuk satu atau beberapa Indeks Sekunder Lokal. Contoh berikut membuat tabel (`Music`) untuk menyimpan informasi tentang lagu dalam koleksi musik. Kunci partisinya adalah `Artist` dan kunci urutannya adalah `SongTitle`. Indeks sekunder, `AlbumTitleIndex` pada atribut `AlbumTitle` memfasilitasi kueri berdasarkan judul album. 

```
aws dynamodb create-table \
    --table-name Music \
    --attribute-definitions AttributeName=Artist,AttributeType=S AttributeName=SongTitle,AttributeType=S \
        AttributeName=AlbumTitle,AttributeType=S  \
    --key-schema AttributeName=Artist,KeyType=HASH AttributeName=SongTitle,KeyType=RANGE \
    --provisioned-throughput \
        ReadCapacityUnits=10,WriteCapacityUnits=5 \
    --local-secondary-indexes \
        "[{\"IndexName\": \"AlbumTitleIndex\",
        \"KeySchema\":[{\"AttributeName\":\"Artist\",\"KeyType\":\"HASH\"},
                      {\"AttributeName\":\"AlbumTitle\",\"KeyType\":\"RANGE\"}],
        \"Projection\":{\"ProjectionType\":\"INCLUDE\",  \"NonKeyAttributes\":[\"Genre\", \"Year\"]}}]"
```

Anda harus menunggu hingga DynamoDB membuat tabel dan menetapkan status tabel menjadi `ACTIVE`. Setelah itu, Anda bisa mulai memasukkan item data ke dalam tabel. Anda dapat menggunakan [describe-table](https://docs.aws.amazon.com/cli/latest/reference/dynamodb/describe-table.html) untuk menentukan status pembuatan tabel. 

## Mendeskripsikan tabel dengan Indeks Sekunder Lokal
<a name="LCICli.DescribeTableWithIndex"></a>

Untuk mendapatkan informasi tentang indeks sekunder lokal pada tabel, gunakan parameter `describe-table`. Untuk setiap indeks, Anda dapat mengakses namanya, skema kunci, dan atribut yang diproyeksikan.

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

## Mengkueri Indeks Sekunder Lokal
<a name="LCICli.QueryAnIndex"></a>

Anda dapat menggunakan operasi `query` pada Indeks Sekunder Lokal dengan cara yang hampir sama seperti Anda `query` tabel. Anda harus menentukan nama indeks, kriteria kueri untuk kunci urutan indeks, dan atribut yang ingin Anda kembalikan. Dalam contoh ini, indeks adalah `AlbumTitleIndex` dan kunci urutan indeks adalah `AlbumTitle`. 

Satu-satunya atribut yang dikembalikan adalah atribut yang telah diproyeksikan ke dalam indeks. Anda dapat memodifikasi kueri ini untuk memilih atribut non-kunci juga, tetapi ini akan memerlukan aktivitas pengambilan tabel yang relatif mahal. Untuk informasi selengkapnya tentang pengambilan tabel, lihat [Proyeksi atribut](LSI.md#LSI.Projections).

```
aws dynamodb query \
    --table-name Music \
    --index-name AlbumTitleIndex \
    --key-condition-expression "Artist = :v_artist and AlbumTitle = :v_title" \
    --expression-attribute-values  '{":v_artist":{"S":"Acme Band"},":v_title":{"S":"Songs About Life"} }'
```