Panduan ini membahas dasar-dasar
IndexedDB API.
Kami menggunakan library IndexedDB Promised Jake Archibald, yang sangat mirip dengan IndexedDB API, tetapi menggunakan promise, yang dapat Anda await untuk sintaksis yang lebih ringkas. Hal ini menyederhanakan API sekaligus
mempertahankan strukturnya.
Apa itu IndexedDB?
IndexedDB adalah sistem penyimpanan NoSQL berskala besar yang memungkinkan penyimpanan hampir semua hal di browser pengguna. Selain tindakan penelusuran, pengambilan, dan penempatan yang biasa, IndexedDB juga mendukung transaksi, dan sangat cocok untuk menyimpan data terstruktur dalam jumlah besar.
Setiap database IndexedDB bersifat unik untuk sebuah origin (biasanya domain atau subdomain situs), yang berarti database tersebut tidak dapat mengakses atau diakses oleh origin lain. Batas penyimpanan data-nya biasanya besar, jika ada, tetapi browser yang berbeda menangani batas dan penghapusan data secara berbeda. Lihat bagian Bacaan lebih lanjut untuk mengetahui informasi selengkapnya.
Istilah IndexedDB
- Database
- Tingkat tertinggi IndexedDB. Database ini berisi penyimpanan objek, yang pada gilirannya berisi data yang ingin Anda pertahankan. Anda dapat membuat beberapa database dengan nama apa pun yang Anda pilih.
- Penyimpanan objek
- Bucket individual untuk menyimpan data, mirip dengan tabel dalam database relasional.
Biasanya, ada satu penyimpanan objek untuk setiap jenis (bukan jenis data JavaScript) data yang Anda simpan. Tidak seperti dalam tabel database, jenis data JavaScript dalam penyimpanan tidak harus konsisten. Misalnya, jika aplikasi
memiliki penyimpanan objek
peopleyang berisi informasi tentang tiga orang, properti usia orang tersebut dapat berupa53,'twenty-five', danunknown. - Indeks
- Jenis penyimpanan objek untuk mengatur data di penyimpanan objek lain (disebut penyimpanan objek referensi) berdasarkan properti individual data. Indeks digunakan untuk mengambil data di penyimpanan objek berdasarkan properti ini. Misalnya, jika Anda menyimpan data orang, Anda mungkin ingin mengambilnya nanti berdasarkan nama, usia, atau hewan favorit mereka.
- Operasi
- Interaksi dengan database.
- Transaksi
- Wrapper di sekitar operasi atau grup operasi yang memastikan integritas database. Jika salah satu tindakan dalam transaksi gagal, tidak ada tindakan yang diterapkan dan database akan kembali ke status sebelum transaksi dimulai. Semua operasi baca atau tulis di IndexedDB harus menjadi bagian dari transaksi. Hal ini memungkinkan operasi baca-ubah-tulis atomik tanpa risiko konflik dengan thread lain yang bertindak pada database secara bersamaan.
- Kursor
- Mekanisme untuk melakukan iterasi pada beberapa data dalam database.
Cara memeriksa dukungan IndexedDB
IndexedDB hampir didukung secara universal.
Namun, jika Anda menggunakan browser lama, sebaiknya
deteksi fitur dukungan untuk berjaga-jaga. Cara termudah adalah dengan memeriksa objek window:
function indexedDBStuff () {
// Check for IndexedDB support:
if (!('indexedDB' in window)) {
// Can't use IndexedDB
console.log("This browser doesn't support IndexedDB");
return;
} else {
// Do IndexedDB stuff here:
// ...
}
}
// Run IndexedDB code:
indexedDBStuff();
Cara membuka database
Dengan IndexedDB, Anda dapat membuat beberapa database dengan nama apa pun yang Anda pilih. Jika
database tidak ada saat Anda mencoba membukanya, database akan dibuat secara otomatis.
Untuk membuka database, gunakan metode openDB() dari library idb:
import {openDB} from 'idb';
async function useDB () {
// Returns a promise, which makes `idb` usable with async-await.
const dbPromise = await openDB('example-database', version, events);
}
useDB();
Metode ini menampilkan promise yang di-resolve ke objek database. Saat menggunakan metode openDB(), berikan nama, nomor versi, dan objek peristiwa untuk menyiapkan database.
Berikut adalah contoh metode openDB() dalam konteks:
import {openDB} from 'idb';
async function useDB () {
// Opens the first version of the 'test-db1' database.
// If the database does not exist, it will be created.
const dbPromise = await openDB('test-db1', 1);
}
useDB();
Tempatkan pemeriksaan dukungan IndexedDB di bagian atas fungsi anonim. Tindakan ini
akan menghentikan fungsi jika browser tidak mendukung IndexedDB. Jika fungsi dapat
dilanjutkan, fungsi tersebut akan memanggil metode openDB() untuk membuka database bernama 'test-db1'.
Dalam contoh ini, objek peristiwa opsional telah dihilangkan agar tetap
sederhana, tetapi Anda harus menentukannya untuk melakukan pekerjaan yang bermakna dengan IndexedDB.
Cara menggunakan penyimpanan objek
Database IndexedDB berisi satu atau beberapa penyimpanan objek, yang masing-masing memiliki kolom untuk kunci, dan kolom lain untuk data yang terkait dengan kunci tersebut.
Membuat penyimpanan objek
Database IndexedDB yang terstruktur dengan baik harus memiliki satu penyimpanan objek untuk setiap jenis
data yang perlu dipertahankan. Misalnya, situs yang menyimpan profil dan catatan pengguna mungkin memiliki penyimpanan objek people yang berisi objek person, dan penyimpanan objek notes yang berisi objek note.
Untuk memastikan integritas database, Anda hanya dapat membuat atau menghapus penyimpanan objek di
objek peristiwa dalam panggilan openDB(). Objek peristiwa mengekspos metode upgrade()
yang memungkinkan Anda membuat penyimpanan objek. Panggil metode
createObjectStore()
di dalam metode upgrade() untuk membuat penyimpanan objek:
import {openDB} from 'idb';
async function createStoreInDB () {
const dbPromise = await openDB('example-database', 1, {
upgrade (db) {
// Creates an object store:
db.createObjectStore('storeName', options);
}
});
}
createStoreInDB();
Metode ini menggunakan nama penyimpanan objek dan objek konfigurasi opsional yang memungkinkan Anda menentukan berbagai properti untuk penyimpanan objek.
Berikut adalah contoh cara menggunakan createObjectStore():
import {openDB} from 'idb';
async function createStoreInDB () {
const dbPromise = await openDB('test-db1', 1, {
upgrade (db) {
console.log('Creating a new object store...');
// Checks if the object store exists:
if (!db.objectStoreNames.contains('people')) {
// If the object store does not exist, create it:
db.createObjectStore('people');
}
}
});
}
createStoreInDB();
Dalam contoh ini, objek peristiwa diteruskan ke metode openDB() untuk membuat
penyimpanan objek, dan seperti sebelumnya, pekerjaan membuat penyimpanan objek dilakukan
dalam metode upgrade() objek peristiwa. Namun, karena browser menampilkan
error jika Anda mencoba membuat penyimpanan objek yang sudah ada, sebaiknya
gabungkan metode createObjectStore() dalam pernyataan if yang memeriksa
apakah penyimpanan objek ada. Di dalam blok if, panggil
createObjectStore() untuk membuat penyimpanan objek bernama 'firstOS'.
Cara menentukan kunci utama
Saat menentukan penyimpanan objek, Anda dapat menentukan cara data diidentifikasi secara unik di penyimpanan menggunakan kunci utama. Anda dapat menentukan kunci utama dengan menentukan jalur kunci atau menggunakan generator kunci.
Jalur kunci adalah properti yang selalu ada dan berisi nilai unik. Misalnya, dalam kasus penyimpanan objek people, Anda dapat memilih alamat email sebagai jalur kunci:
import {openDB} from 'idb';
async function createStoreInDB () {
const dbPromise = await openDB('test-db2', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('people')) {
db.createObjectStore('people', { keyPath: 'email' });
}
}
});
}
createStoreInDB();
Contoh ini membuat penyimpanan objek bernama 'people' dan menetapkan properti email
sebagai kunci utama dalam opsi keyPath.
Anda juga dapat menggunakan generator kunci seperti autoIncrement. Generator kunci
membuat nilai unik untuk setiap objek yang ditambahkan ke penyimpanan objek. Secara default,
jika Anda tidak menentukan kunci, IndexedDB akan membuat kunci dan menyimpannya secara terpisah
dari data.
Contoh berikut membuat penyimpanan objek bernama 'notes' dan menetapkan kunci utama yang akan ditetapkan secara otomatis sebagai angka yang bertambah otomatis:
import {openDB} from 'idb';
async function createStoreInDB () {
const dbPromise = await openDB('test-db2', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('notes')) {
db.createObjectStore('notes', { autoIncrement: true });
}
}
});
}
createStoreInDB();
Contoh berikut mirip dengan contoh sebelumnya, tetapi kali ini nilai penambahan otomatis ditetapkan secara eksplisit ke properti bernama 'id'.
import {openDB} from 'idb';
async function createStoreInDB () {
const dbPromise = await openDB('test-db2', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('logs')) {
db.createObjectStore('logs', { keyPath: 'id', autoIncrement: true });
}
}
});
}
createStoreInDB();
Pemilihan metode yang akan digunakan untuk menentukan kunci bergantung pada data Anda. Jika data Anda memiliki properti yang selalu unik, Anda dapat menjadikannya keyPath untuk menerapkan keunikan ini. Jika tidak, gunakan nilai penambahan otomatis.
Kode berikut membuat tiga penyimpanan objek yang menunjukkan berbagai cara menentukan kunci utama di penyimpanan objek:
import {openDB} from 'idb';
async function createStoresInDB () {
const dbPromise = await openDB('test-db2', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('people')) {
db.createObjectStore('people', { keyPath: 'email' });
}
if (!db.objectStoreNames.contains('notes')) {
db.createObjectStore('notes', { autoIncrement: true });
}
if (!db.objectStoreNames.contains('logs')) {
db.createObjectStore('logs', { keyPath: 'id', autoIncrement: true });
}
}
});
}
createStoresInDB();
Cara menentukan indeks
Indeks adalah jenis penyimpanan objek yang digunakan untuk mengambil data dari penyimpanan objek referensi berdasarkan properti yang ditentukan. Indeks berada di dalam penyimpanan objek referensi dan berisi data yang sama, tetapi menggunakan properti yang ditentukan sebagai jalur kuncinya, bukan kunci utama penyimpanan referensi. Indeks harus dibuat saat Anda membuat penyimpanan objek, dan dapat digunakan untuk menentukan batasan unik pada data Anda.
Untuk membuat indeks, panggil metode createIndex()
pada instance penyimpanan objek:
import {openDB} from 'idb';
async function createIndexInStore() {
const dbPromise = await openDB('storeName', 1, {
upgrade (db) {
const objectStore = db.createObjectStore('storeName');
objectStore.createIndex('indexName', 'property', options);
}
});
}
createIndexInStore();
Metode ini membuat dan menampilkan objek indeks. Metode createIndex() pada instance penyimpanan objek menggunakan nama indeks baru sebagai argumen pertama, dan argumen kedua mengacu pada properti pada data yang ingin Anda indeks. Argumen terakhir memungkinkan Anda menentukan dua opsi yang menentukan cara
indeks beroperasi: unique dan multiEntry. Jika unique ditetapkan ke true, indeks tidak mengizinkan nilai duplikat untuk satu kunci. Selanjutnya, multiEntry
menentukan perilaku createIndex() saat properti yang diindeks adalah array. Jika
disetel ke true, createIndex() akan menambahkan entri dalam indeks untuk setiap elemen
array. Jika tidak, metode ini akan menambahkan satu entri yang berisi array.
Berikut contohnya:
import {openDB} from 'idb';
async function createIndexesInStores () {
const dbPromise = await openDB('test-db3', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('people')) {
const peopleObjectStore = db.createObjectStore('people', { keyPath: 'email' });
peopleObjectStore.createIndex('gender', 'gender', { unique: false });
peopleObjectStore.createIndex('ssn', 'ssn', { unique: true });
}
if (!db.objectStoreNames.contains('notes')) {
const notesObjectStore = db.createObjectStore('notes', { autoIncrement: true });
notesObjectStore.createIndex('title', 'title', { unique: false });
}
if (!db.objectStoreNames.contains('logs')) {
const logsObjectStore = db.createObjectStore('logs', { keyPath: 'id', autoIncrement: true });
}
}
});
}
createIndexesInStores();
Dalam contoh ini, penyimpanan objek 'people' dan 'notes' memiliki indeks. Untuk membuat indeks, tetapkan terlebih dahulu hasil createObjectStore() (objek penyimpanan objek) ke variabel sehingga Anda dapat memanggil createIndex() di dalamnya.
Cara menggunakan data
Bagian ini menjelaskan cara membuat, membaca, mengupdate, dan menghapus data. Semua operasi ini bersifat asinkron, menggunakan promise, sedangkan IndexedDB API menggunakan permintaan. Hal ini menyederhanakan API. Daripada memproses peristiwa yang dipicu oleh permintaan, Anda dapat memanggil .then() pada objek database yang ditampilkan dari metode openDB() untuk memulai interaksi dengan database, atau await pembuatannya.
Semua operasi data di IndexedDB dilakukan di dalam transaksi. Setiap operasi memiliki bentuk berikut:
- Mendapatkan objek database.
- Membuka transaksi di database.
- Buka penyimpanan objek pada transaksi.
- Melakukan operasi pada penyimpanan objek.
Transaksi dapat dianggap sebagai wrapper yang aman untuk sebuah operasi atau sekelompok operasi. Jika salah satu tindakan dalam transaksi gagal, semua tindakan akan di-roll back. Transaksi bersifat spesifik untuk satu atau beberapa penyimpanan objek, yang Anda tentukan saat membuka transaksi. Volume ini dapat bersifat hanya baca atau baca dan tulis. Hal ini menandakan apakah operasi di dalam transaksi membaca data atau membuat perubahan pada database.
Membuat data
Untuk membuat data, panggil metode add()
pada instance database dan teruskan data yang ingin Anda tambahkan. Argumen pertama metode add()
adalah penyimpanan objek tempat Anda ingin menambahkan data, dan
argumen kedua adalah objek yang berisi kolom dan data terkait yang ingin Anda
tambahkan. Berikut adalah contoh paling sederhana, dengan satu baris data ditambahkan:
import {openDB} from 'idb';
async function addItemToStore () {
const db = await openDB('example-database', 1);
await db.add('storeName', {
field: 'data'
});
}
addItemToStore();
Setiap panggilan add() terjadi dalam transaksi, jadi meskipun promise berhasil diselesaikan, hal ini tidak berarti operasi tersebut berhasil. Untuk memastikan
operasi penambahan telah dilakukan, Anda perlu memeriksa apakah seluruh
transaksi telah selesai menggunakan metode transaction.done(). Ini adalah promise yang di-resolve saat transaksi selesai, dan ditolak jika transaksi mengalami error. Anda harus melakukan pemeriksaan ini untuk semua operasi "tulis",
karena ini adalah satu-satunya cara untuk mengetahui bahwa perubahan pada database benar-benar
terjadi.
Kode berikut menunjukkan penggunaan metode add() di dalam transaksi:
import {openDB} from 'idb';
async function addItemsToStore () {
const db = await openDB('test-db4', 1, {
upgrade (db) {
if (!db.objectStoreNames.contains('foods')) {
db.createObjectStore('foods', { keyPath: 'name' });
}
}
});
// Create a transaction on the 'foods' store in read/write mode:
const tx = db.transaction('foods', 'readwrite');
// Add multiple items to the 'foods' store in a single transaction:
await Promise.all([
tx.store.add({
name: 'Sandwich',
price: 4.99,
description: 'A very tasty sandwich!',
created: new Date().getTime(),
}),
tx.store.add({
name: 'Eggs',
price: 2.99,
description: 'Some nice eggs you can cook up!',
created: new Date().getTime(),
}),
tx.done
]);
}
addItemsToStore();
Setelah membuka database (dan membuat penyimpanan objek jika diperlukan), Anda harus
membuka transaksi dengan memanggil metode transaction() di dalamnya. Metode ini
mengambil argumen untuk penyimpanan yang ingin Anda transaksikan, serta modenya.
Dalam hal ini, kita ingin menulis ke penyimpanan, jadi contoh ini
menentukan 'readwrite'.
Langkah berikutnya adalah mulai menambahkan item ke toko sebagai bagian dari transaksi.
Dalam contoh sebelumnya, kita menangani tiga operasi di penyimpanan 'foods'
yang masing-masing menampilkan promise:
- Menambahkan data untuk sandwich yang lezat.
- Menambahkan data untuk beberapa telur.
- Menandakan bahwa transaksi telah selesai (
tx.done).
Karena semua tindakan ini berbasis promise, kita harus menunggu semuanya selesai. Meneruskan promise ini ke
Promise.all
adalah cara yang bagus dan ergonomis untuk melakukannya. Promise.all menerima array promise dan selesai saat semua promise yang diteruskan kepadanya telah diselesaikan.
Untuk dua data yang ditambahkan, antarmuka store instance transaksi
memanggil add() dan meneruskan data ke sana. Anda dapat await panggilan Promise.all
sehingga panggilan tersebut selesai saat transaksi selesai.
Membaca data
Untuk membaca data, panggil metode get()
pada instance database yang Anda ambil menggunakan metode openDB().
get() mengambil nama penyimpanan dan nilai kunci utama objek yang ingin Anda ambil. Berikut contoh dasarnya:
import {openDB} from 'idb';
async function getItemFromStore () {
const db = await openDB('example-database', 1);
// Get a value from the object store by its primary key value:
const value = await db.get('storeName', 'unique-primary-key-value');
}
getItemFromStore();
Seperti halnya add(), metode get() menampilkan promise, sehingga Anda dapat melakukan await jika
mau, atau menggunakan callback .then() promise.
Contoh berikut menggunakan metode get() pada penyimpanan objek 'foods' database 'test-db4' untuk mendapatkan satu baris berdasarkan kunci utama 'name':
import {openDB} from 'idb';
async function getItemFromStore () {
const db = await openDB('test-db4', 1);
const value = await db.get('foods', 'Sandwich');
console.dir(value);
}
getItemFromStore();
Mengambil satu baris dari database cukup mudah: buka database dan tentukan penyimpanan objek dan nilai kunci utama baris yang ingin Anda ambil datanya. Karena metode get() menampilkan promise, Anda dapat
await-nya.
Memperbarui data
Untuk memperbarui data, panggil metode put()
pada penyimpanan objek. Metode put() mirip dengan metode add()
dan juga dapat digunakan sebagai pengganti add() untuk membuat data. Berikut adalah contoh dasar penggunaan put() untuk memperbarui baris di penyimpanan objek berdasarkan nilai kunci utamanya:
import {openDB} from 'idb';
async function updateItemInStore () {
const db = await openDB('example-database', 1);
// Update a value from in an object store with an inline key:
await db.put('storeName', { inlineKeyName: 'newValue' });
// Update a value from in an object store with an out-of-line key.
// In this case, the out-of-line key value is 1, which is the
// auto-incremented value.
await db.put('otherStoreName', { field: 'value' }, 1);
}
updateItemInStore();
Seperti metode lainnya, metode ini menampilkan promise. Anda juga dapat menggunakan put() sebagai bagian dari transaksi. Berikut adalah contoh penggunaan toko 'foods' dari sebelumnya
yang memperbarui harga sandwich dan telur:
import {openDB} from 'idb';
async function updateItemsInStore () {
const db = await openDB('test-db4', 1);
// Create a transaction on the 'foods' store in read/write mode:
const tx = db.transaction('foods', 'readwrite');
// Update multiple items in the 'foods' store in a single transaction:
await Promise.all([
tx.store.put({
name: 'Sandwich',
price: 5.99,
description: 'A MORE tasty sandwich!',
updated: new Date().getTime() // This creates a new field
}),
tx.store.put({
name: 'Eggs',
price: 3.99,
description: 'Some even NICER eggs you can cook up!',
updated: new Date().getTime() // This creates a new field
}),
tx.done
]);
}
updateItemsInStore();
Cara item diperbarui bergantung pada cara Anda menetapkan kunci. Jika Anda menetapkan keyPath,
setiap baris di penyimpanan objek akan dikaitkan dengan kunci inline. Contoh sebelumnya
mengupdate baris berdasarkan kunci ini, dan saat Anda mengupdate baris dalam
situasi ini, Anda harus menentukan kunci tersebut untuk mengupdate item yang sesuai di
penyimpanan objek. Anda juga dapat membuat kunci out-of-line dengan menetapkan
autoIncrement sebagai kunci utama.
Menghapus data
Untuk menghapus data, panggil metode delete()
pada penyimpanan objek:
import {openDB} from 'idb';
async function deleteItemFromStore () {
const db = await openDB('example-database', 1);
// Delete a value
await db.delete('storeName', 'primary-key-value');
}
deleteItemFromStore();
Seperti add() dan put(), Anda dapat menggunakannya sebagai bagian dari transaksi:
import {openDB} from 'idb';
async function deleteItemsFromStore () {
const db = await openDB('test-db4', 1);
// Create a transaction on the 'foods' store in read/write mode:
const tx = db.transaction('foods', 'readwrite');
// Delete multiple items from the 'foods' store in a single transaction:
await Promise.all([
tx.store.delete('Sandwich'),
tx.store.delete('Eggs'),
tx.done
]);
}
deleteItemsFromStore();
Struktur interaksi database sama dengan operasi lainnya. Jangan lupa untuk memeriksa apakah seluruh transaksi telah selesai dengan
menyertakan metode tx.done dalam array yang Anda teruskan ke Promise.all.
Mendapatkan semua data
Sejauh ini, Anda hanya mengambil objek dari penyimpanan satu per satu. Anda juga dapat
mengambil semua data, atau subset, dari penyimpanan objek atau indeks menggunakan
metode getAll() atau kursor.
Metode getAll()
Cara termudah untuk mengambil semua data penyimpanan objek adalah dengan memanggil getAll()
pada penyimpanan objek atau indeks, seperti ini:
import {openDB} from 'idb';
async function getAllItemsFromStore () {
const db = await openDB('test-db4', 1);
// Get all values from the designated object store:
const allValues = await db.getAll('storeName');
console.dir(allValues);
}
getAllItemsFromStore();
Metode ini menampilkan semua objek di penyimpanan objek, tanpa batasan apa pun. Ini adalah cara paling langsung untuk mendapatkan semua nilai dari penyimpanan objek, tetapi juga yang paling tidak fleksibel.
import {openDB} from 'idb';
async function getAllItemsFromStore () {
const db = await openDB('test-db4', 1);
// Get all values from the designated object store:
const allValues = await db.getAll('foods');
console.dir(allValues);
}
getAllItemsFromStore();
Contoh ini memanggil getAll() di penyimpanan objek 'foods'. Tindakan ini akan menampilkan semua
objek dari 'foods', yang diurutkan berdasarkan kunci utama.
Cara menggunakan kursor
Kursor adalah cara yang lebih fleksibel untuk mengambil beberapa objek. Kursor memilih setiap objek dalam penyimpanan objek atau indeks satu per satu, sehingga Anda dapat melakukan sesuatu dengan data saat dipilih. Kursor, seperti operasi database lainnya, bekerja dalam transaksi.
Untuk membuat kursor, panggil openCursor()
pada penyimpanan objek sebagai bagian dari transaksi. Dengan menggunakan penyimpanan 'foods' dari contoh sebelumnya, berikut cara memajukan kursor melalui semua baris data di penyimpanan objek:
import {openDB} from 'idb';
async function getAllItemsFromStoreWithCursor () {
const db = await openDB('test-db4', 1);
const tx = await db.transaction('foods', 'readonly');
// Open a cursor on the designated object store:
let cursor = await tx.store.openCursor();
// Iterate on the cursor, row by row:
while (cursor) {
// Show the data in the row at the current cursor position:
console.log(cursor.key, cursor.value);
// Advance the cursor to the next row:
cursor = await cursor.continue();
}
}
getAllItemsFromStoreWithCursor();
Transaksi dalam kasus ini dibuka dalam mode 'readonly', dan metode openCursor-nya dipanggil. Dalam loop while berikutnya, baris pada posisi kursor saat ini dapat dibaca properti key dan value-nya, dan Anda dapat mengoperasikan nilai tersebut dengan cara apa pun yang paling sesuai untuk aplikasi Anda. Setelah siap, Anda dapat memanggil metode continue() objek cursor untuk beralih ke baris berikutnya, dan loop while akan berakhir saat kursor mencapai akhir set data.
Menggunakan kursor dengan rentang dan indeks
Indeks memungkinkan Anda mengambil data di penyimpanan objek berdasarkan properti selain kunci utama. Anda dapat membuat indeks pada properti apa pun, yang menjadi keyPath
untuk indeks, menentukan rentang pada properti tersebut, dan mendapatkan data dalam
rentang menggunakan getAll() atau kursor.
Tentukan rentang Anda menggunakan objek IDBKeyRange dan salah satu metode berikut:
upperBound().lowerBound().bound()(yang merupakan keduanya).only().includes().
Metode upperBound() dan lowerBound() menentukan batas atas dan bawah rentang.
IDBKeyRange.lowerBound(indexKey);
Atau:
IDBKeyRange.upperBound(indexKey);
Masing-masing menggunakan satu argumen: nilai keyPath indeks untuk item yang ingin Anda tentukan sebagai batas atas atau bawah.
Metode bound() menentukan batas atas dan bawah:
IDBKeyRange.bound(lowerIndexKey, upperIndexKey);
Rentang untuk fungsi ini bersifat inklusif secara default, yang berarti rentang tersebut mencakup
data yang ditentukan sebagai batas rentang. Untuk mengecualikan nilai tersebut,
tentukan rentang sebagai eksklusif dengan meneruskan true sebagai argumen kedua untuk
lowerBound() atau upperBound(), atau sebagai argumen ketiga dan keempat dari
bound(), masing-masing untuk batas bawah dan atas.
Contoh berikutnya menggunakan indeks pada properti 'price' di penyimpanan objek 'foods'. Toko kini juga memiliki formulir yang dilampirkan dengan dua input untuk
batas atas dan bawah rentang. Gunakan kode berikut untuk menemukan makanan dengan harga di antara batas tersebut:
import {openDB} from 'idb';
async function searchItems (lower, upper) {
if (!lower === '' && upper === '') {
return;
}
let range;
if (lower !== '' && upper !== '') {
range = IDBKeyRange.bound(lower, upper);
} else if (lower === '') {
range = IDBKeyRange.upperBound(upper);
} else {
range = IDBKeyRange.lowerBound(lower);
}
const db = await openDB('test-db4', 1);
const tx = await db.transaction('foods', 'readonly');
const index = tx.store.index('price');
// Open a cursor on the designated object store:
let cursor = await index.openCursor(range);
if (!cursor) {
return;
}
// Iterate on the cursor, row by row:
while (cursor) {
// Show the data in the row at the current cursor position:
console.log(cursor.key, cursor.value);
// Advance the cursor to the next row:
cursor = await cursor.continue();
}
}
// Get items priced between one and four dollars:
searchItems(1.00, 4.00);
Kode contoh pertama-tama mendapatkan nilai untuk batas dan memeriksa apakah batas tersebut ada. Blok kode berikutnya menentukan metode yang akan digunakan untuk membatasi rentang
berdasarkan nilai. Dalam interaksi database, buka penyimpanan objek pada
transaksi seperti biasa, lalu buka indeks 'price' di penyimpanan objek. Indeks 'price' memungkinkan Anda menelusuri item berdasarkan harga.
Kode tersebut kemudian membuka kursor pada indeks dan meneruskan rentang. Kursor
menampilkan promise yang mewakili objek pertama dalam rentang, atau undefined jika
tidak ada data dalam rentang. Metode cursor.continue() menampilkan kursor yang mewakili objek berikutnya, dan melanjutkan loop hingga Anda mencapai akhir rentang.
Pembuatan versi database
Saat memanggil metode openDB(), Anda dapat menentukan nomor versi database
dalam parameter kedua. Dalam semua contoh di panduan ini, versi telah
ditetapkan ke 1, tetapi database dapat diupgrade ke versi baru jika Anda perlu
memodifikasinya dengan cara tertentu. Jika versi yang ditentukan lebih besar dari versi database yang ada, callback upgrade dalam objek peristiwa akan dieksekusi, sehingga Anda dapat menambahkan penyimpanan objek dan indeks baru ke database.
Objek db dalam callback upgrade memiliki properti oldVersion khusus,
yang menunjukkan nomor versi database yang dapat diakses browser.
Anda dapat meneruskan nomor versi ini ke dalam pernyataan switch untuk mengeksekusi blok kode di dalam callback upgrade berdasarkan nomor versi database yang ada. Berikut contohnya:
import {openDB} from 'idb';
const db = await openDB('example-database', 2, {
upgrade (db, oldVersion) {
switch (oldVersion) {
case 0:
// Create first object store:
db.createObjectStore('store', { keyPath: 'name' });
case 1:
// Get the original object store, and create an index on it:
const tx = await db.transaction('store', 'readwrite');
tx.store.createIndex('name', 'name');
}
}
});
Contoh ini menetapkan versi terbaru database ke 2. Saat kode ini
pertama kali dijalankan, database belum ada di browser, sehingga oldVersion
adalah 0, dan pernyataan switch dimulai di case 0. Dalam contoh ini, tindakan ini akan menambahkan penyimpanan objek 'store' ke database.
Poin penting: Dalam pernyataan switch, biasanya ada break setelah setiap blok case, tetapi hal ini sengaja tidak digunakan di sini. Dengan cara ini, jika database yang ada tertinggal beberapa versi, atau jika tidak ada, kode akan melanjutkan ke seluruh blok case lainnya hingga diperbarui. Jadi, dalam contoh ini,
browser terus mengeksekusi melalui case 1, membuat indeks name di
penyimpanan objek store.
Untuk membuat indeks 'description' di penyimpanan objek 'store', perbarui
nomor versi dan tambahkan blok case baru sebagai berikut:
import {openDB} from 'idb';
const db = await openDB('example-database', 3, {
upgrade (db, oldVersion) {
switch (oldVersion) {
case 0:
// Create first object store:
db.createObjectStore('store', { keyPath: 'name' });
case 1:
// Get the original object store, and create an index on it:
const tx = await db.transaction('store', 'readwrite');
tx.store.createIndex('name', 'name');
case 2:
const tx = await db.transaction('store', 'readwrite');
tx.store.createIndex('description', 'description');
}
}
});
Jika database yang Anda buat di contoh sebelumnya masih ada di browser,
saat ini dijalankan, oldVersion adalah 2. Browser akan melewati case 0 dan
case 1, serta mengeksekusi kode di case 2, yang membuat indeks
description. Setelah itu, browser memiliki database di versi 3 yang berisi penyimpanan objek store
dengan indeks name dan description.
Bacaan lebih lanjut
Referensi berikut memberikan informasi dan konteks lebih lanjut untuk menggunakan IndexedDB.
Dokumentasi IndexedDB
- Repositori GitHub
idb - Menggunakan IndexedDB
- Konsep Dasar di Balik IndexedDB
- Spesifikasi Indexed Database API 3.0