IndexedDB ile çalışma

Bu kılavuz, IndexedDB API ile ilgili temel bilgileri içerir. Jake Archibald'ın IndexedDB Promified kitaplığını kullanıyoruz. Bu kitaplık, IndexedDB API'ye çok benzer ancak daha kısa söz dizimi için await oluşturabileceğiniz vaatlerden yararlanıyor. Bu, hem API'yi basitleştirir hem de yapısını korur.

IndexedDB nedir?

IndexedDB, kullanıcının tarayıcısında hemen hemen her şeyin depolanmasına olanak tanıyan büyük ölçekli bir NoSQL depolama sistemidir. IndexedDB, olağan arama, alma ve yerleştirme işlemlerine ek olarak işlemleri de destekler ve büyük miktarlarda yapılandırılmış veri depolamak için uygundur.

Her IndexedDB veritabanı bir origin için benzersizdir (genellikle site alanı veya alt alan adı). Diğer bir deyişle, başka herhangi bir kaynaktan erişilemez. Veri depolama sınırları genellikle büyüktür (varsa) ancak farklı tarayıcılar, sınırları ve veri çıkarma işlemlerini farklı şekilde yönetir. Daha fazla bilgi için Daha fazla bilgi bölümüne bakın.

IndexedDB terimleri

Veritabanı
En yüksek IndexedDB seviyesi. Nesne depolarını içerir. Bunlar da kalıcı olmasını istediğiniz verileri içerir. İstediğiniz adlarla birden fazla veritabanı oluşturabilirsiniz.
Nesne deposu
İlişkisel veritabanlarındaki tablolara benzer şekilde, verileri depolamak için ayrı bir paket. Genellikle, depoladığınız her veri türü (JavaScript veri türü için değil) için bir nesne deposu vardır. Veritabanı tablolarından farklı olarak, bir mağazadaki JavaScript veri türlerinin tutarlı olması gerekmez. Örneğin, bir uygulamada üç kişi hakkında bilgi içeren people nesne deposu varsa bu kişilerin yaş özellikleri 53, 'twenty-five' ve unknown olabilir.
Dizin
Verilerin başka bir nesne deposundaki (referans nesne deposu olarak adlandırılır) ayrı bir özelliğe göre düzenlenmesini sağlayan bir tür nesne deposudur. Dizin, nesne deposundaki kayıtları bu özellik tarafından almak için kullanılır. Örneğin, kişileri depoluyorsanız daha sonra adlarına, yaşlarına veya en sevdikleri hayvana göre getirmek isteyebilirsiniz.
İşlem
Veritabanıyla etkileşim.
İşlem
Veritabanı bütünlüğünü sağlayan bir işlemin veya işlem grubunun etrafındaki sarmalayıcı. Bir işlemdeki işlemlerden biri başarısız olursa hiçbiri uygulanmaz ve veritabanı, işlem başlamadan önceki durumuna döner. IndexedDB'deki tüm okuma veya yazma işlemleri bir işlemin parçası olmalıdır. Bu sayede, veritabanında aynı anda işlem yapan diğer iş parçacıklarıyla çakışma riski olmadan atomik okuma-değiştirme-yazma işlemleri gerçekleştirilebilir.
İmleç
Bir veritabanında birden fazla kayıt üzerinde yineleme mekanizması.

IndexedDB desteğinin olup olmadığını kontrol etme

IndexedDB neredeyse evrensel olarak desteklenmektedir. Ancak, eski tarayıcılarla çalışıyorsanız her ihtimale karşı özellik algılama desteğini algılamak kötü bir fikir değildir. En kolay yol, window nesnesini kontrol etmektir:

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();

Veritabanı nasıl açılır?

IndexedDB ile seçtiğiniz herhangi bir adla birden çok veritabanı oluşturabilirsiniz. Açmaya çalıştığınızda mevcut bir veritabanı yoksa otomatik olarak oluşturulur. Bir veritabanını açmak için idb kitaplığındaki openDB() yöntemini kullanın:

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();

Bu yöntem, veritabanı nesnesine çözümlenen bir söz döndürür. openDB() yöntemini kullanırken veritabanını ayarlamak için ad, sürüm numarası ve etkinlik nesnesi sağlayın.

Aşağıda, bağlam içinde openDB() yöntemi bir örneği verilmiştir:

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();

IndexedDB destek kontrolünü anonim işlevin en üstüne yerleştirin. Tarayıcı, IndexedDB'yi desteklemiyorsa bu dosya işlevden çıkar. İşlev devam edebilirse 'test-db1' adlı veritabanını açmak için openDB() yöntemini çağırır. Bu örnekte, isteğe bağlı etkinlikler nesnesi, işleri basitleştirmek için dışarıda bırakıldı, ancak IndexedDB ile anlamlı bir iş yapmak için bunu belirtmeniz gerekiyor.

Nesne depolarıyla nasıl çalışılır?

Bir IndexedDB veritabanı, her biri anahtar için bir sütun ve bu anahtarla ilişkili veriler için başka bir sütuna sahip bir veya daha fazla nesne deposu içerir.

Nesne depoları oluşturma

İyi yapılandırılmış bir IndexedDB veritabanı, korunması gereken her veri türü için bir nesne deposuna sahip olmalıdır. Örneğin, kullanıcı profillerini ve notlarını kalıcı olarak tutan bir site, person nesneleri içeren bir people nesne deposuna ve note nesneleri içeren bir notes nesne deposuna sahip olabilir.

Veritabanı bütünlüğünü sağlamak için yalnızca bir openDB() çağrısında etkinlik nesnesindeki nesne depolarını oluşturabilir veya kaldırabilirsiniz. Etkinlikler nesnesi, nesne depoları oluşturmanıza olanak tanıyan bir upgrade() yöntemi sunar. Nesne deposunu oluşturmak için upgrade() yöntemi içinde createObjectStore() yöntemini çağırın:

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();

Bu yöntem, nesne deposunun adını ve nesne deposu için çeşitli özellikler tanımlamanıza olanak tanıyan isteğe bağlı bir yapılandırma nesnesini alır.

Aşağıda, createObjectStore() öğesinin nasıl kullanılacağına dair bir örnek verilmiştir:

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();

Bu örnekte, nesne deposunu oluşturmak için openDB() yöntemine bir etkinlik nesnesi geçirilir ve daha önce olduğu gibi nesne deposunu oluşturma işlemi, etkinlik nesnesinin upgrade() yöntemiyle gerçekleştirilir. Bununla birlikte, zaten var olan bir nesne deposu oluşturmaya çalıştığınızda tarayıcı hata verdiğinden, createObjectStore() yöntemini, nesne deposunun var olup olmadığını kontrol eden bir if ifadesi için sarmalamanızı öneririz. if bloğunun içinde 'firstOS' adlı bir nesne deposu oluşturmak için createObjectStore() çağrısı yapın.

Birincil anahtarları tanımlama

Nesne depolarını tanımlarken, verilerin mağazada birincil anahtar kullanılarak benzersiz şekilde nasıl tanımlanacağını tanımlayabilirsiniz. Anahtar yolu tanımlayarak veya anahtar oluşturucu kullanarak birincil anahtar tanımlayabilirsiniz.

Anahtar yolu, her zaman var olan ve benzersiz bir değer içeren bir özelliktir. Örneğin, people nesne deposu söz konusu olduğunda anahtar yolu olarak e-posta adresini seçebilirsiniz:

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();

Bu örnek, 'people' adında bir nesne deposu oluşturur ve keyPath seçeneğinde birincil anahtar olarak email özelliğini atar.

autoIncrement gibi bir anahtar oluşturma aracı da kullanabilirsiniz. Anahtar oluşturucu, nesne deposuna eklenen her nesne için benzersiz bir değer oluşturur. Varsayılan olarak, bir anahtar belirtmezseniz IndexedDB bir anahtar oluşturur ve bunu verilerden ayrı olarak depolar.

Aşağıdaki örnekte 'notes' adlı bir nesne deposu oluşturulur ve birincil anahtarı otomatik olarak artan bir sayı olarak atanacak şekilde ayarlanır:

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();

Aşağıdaki örnek, önceki örneğe benzemekle birlikte bu kez otomatik artış değeri, 'id' adlı bir mülke açık bir şekilde atanmıştır.

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();

Anahtarı tanımlamak için kullanılacak yöntemin seçimi verilerinize bağlıdır. Verileriniz her zaman benzersiz olan bir özelliğe sahipse bu benzersizliği sağlamak için verileri keyPath yapabilirsiniz. Aksi takdirde, otomatik artış değeri kullanın.

Aşağıdaki kod, nesne depolarında birincil anahtarları tanımlamanın çeşitli yollarını gösteren üç nesne deposu oluşturur:

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();

Dizinleri tanımlama

Dizinler, belirtilen bir mülk tarafından referans nesne deposundan veri almak için kullanılan bir nesne deposu türüdür. Bir dizin, referans nesne deposunun içinde bulunur ve aynı verileri içerir, ancak referans deposunun birincil anahtarı yerine anahtar yolu olarak belirtilen özelliği kullanır. Dizinler, nesne depolarınızı oluştururken oluşturmanız gerekir ve verilerinizde benzersiz bir kısıtlama tanımlamak için kullanılabilir.

Dizin oluşturmak için bir nesne deposu örneğinde createIndex() yöntemini çağırın:

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();

Bu yöntem, bir dizin nesnesi oluşturur ve döndürür. Nesne deposunun örneğindeki createIndex() yöntemi, yeni dizinin adını ilk bağımsız değişken olarak alır. İkinci bağımsız değişken ise dizine eklemek istediğiniz verideki özelliği belirtir. Son bağımsız değişken, dizinin nasıl çalışacağını belirleyen iki seçenek tanımlamanızı sağlar: unique ve multiEntry. unique, true değerine ayarlanırsa dizin, tek bir anahtar için yinelenen değerlere izin vermez. Ardından multiEntry, dizine eklenen özellik bir dizi olduğunda createIndex() özelliğinin nasıl davranacağını belirler. true olarak ayarlanırsa createIndex(), her dizi öğesi için dizine bir giriş ekler. Aksi takdirde, diziyi içeren tek bir giriş ekler.

Aşağıda bir örnek verilmiştir:

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();

Bu örnekte, 'people' ve 'notes' nesne depolarının dizinleri vardır. Dizinleri oluşturmak için önce createObjectStore() sonucunu (nesne deposu nesnesi) bir değişkene atayın. Böylece değişkende createIndex() çağrısı yapabilirsiniz.

Verilerle çalışma

Bu bölümde, verilerin nasıl oluşturulacağı, okunacağı, güncelleneceği ve silineceği açıklanmaktadır. Bu işlemlerin hepsi eşzamansız olup IndexedDB API'nin istekleri kullandığı vaatler kullanılır. Bu, API'yi basitleştirir. İstek tarafından tetiklenen etkinlikleri dinlemek yerine, veritabanıyla etkileşim başlatmak için openDB() yönteminden döndürülen veritabanı nesnesinde .then() veya veritabanının oluşturulmasını await çağırabilirsiniz.

IndexedDB'deki tüm veri işlemleri bir işlem içinde gerçekleştirilir. Her işlem aşağıdaki biçimdedir:

  1. Veritabanı nesnesi alma.
  2. İşlemi veritabanında açın.
  3. İşlemde nesne deposu açın.
  4. Nesne deposunda işlem gerçekleştirin.

İşlem, bir işlemin veya işlem grubunun etrafındaki güvenli bir sarmalayıcı olarak düşünülebilir. Bir işlemdeki eylemlerden biri başarısız olursa tüm işlemler geri alınır. İşlemler, işlemi açtığınızda tanımladığınız bir veya daha fazla nesne deposuna özeldir. Salt okunur veya okunup yazılabilirler. Bu, işlem içindeki işlemlerin verileri okuyup okumadığını veya veritabanında bir değişiklik yapıp yapmadığını belirtir.

Veri oluşturma

Veri oluşturmak için veritabanı örneğinde add() yöntemini çağırın ve eklemek istediğiniz verileri iletin. add() yönteminin ilk bağımsız değişkeni, verileri eklemek istediğiniz nesne deposudur. İkinci bağımsız değişken ise eklemek istediğiniz alanları ve ilişkili verileri içeren bir nesnedir. Tek bir veri satırının eklendiği en basit örneği aşağıda görebilirsiniz:

import {openDB} from 'idb';

async function addItemToStore () {
  const db = await openDB('example-database', 1);

  await db.add('storeName', {
    field: 'data'
  });
}

addItemToStore();

Her add() çağrısı bir işlem içinde gerçekleşir. Bu nedenle, taahhüt başarıyla sonuçlanmış olsa bile bu, işlemin başarılı olduğu anlamına gelmez. Ekleme işleminin yürütüldüğünden emin olmak için transaction.done() yöntemini kullanarak tüm işlemin tamamlanıp tamamlanmadığını kontrol etmeniz gerekir. Bu, işlem tamamlandığında çözümlenen ve işlem hataları varsa reddeden bir taahhüttür. Veritabanındaki değişikliklerin gerçekten olup olmadığını anlamanın tek yolu bu olduğundan tüm "yazma" işlemleri için bu kontrolü gerçekleştirmeniz gerekir.

Aşağıdaki kod, bir işlem içinde add() yönteminin kullanımını göstermektedir:

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();

Veritabanını açtıktan (ve gerekirse nesne deposu oluşturduktan) sonra bir işlem üzerinde transaction() yöntemini çağırarak açmanız gerekir. Bu yöntemde, işlem yapmak istediğiniz mağaza ve mod için bir bağımsız değişken uygulanır. Bu örnekte, mağazaya yazmak istediğimiz için bu örnekte 'readwrite' belirtilmiş.

Sonraki adım, işlemin bir parçası olarak mağazaya öğe eklemeye başlamaktır. Yukarıdaki örnekte, 'foods' mağazasında her biri vaat sağlayan üç işlem gerçekleştiriyoruz:

  1. Lezzetli bir sandviçle ilgili kayıt ekleniyor.
  2. Biraz yumurta için kayıt ekleniyor.
  3. İşlemin tamamlandığına dair sinyal (tx.done).

Tüm bu işlemler vaatlere dayalı olduğundan hepsinin tamamlanmasını beklememiz gerekiyor. Bu sözleri Promise.all adlı iş ortağına vermek, bunu yapmanın güzel ve ergonomik bir yoludur. Promise.all, kendisine iletilen tüm sözler yerine getirildiğinde bir dizi sözü ve bitişi kabul eder.

Eklenen iki kayıt için işlem örneğinin store arayüzü, add() yöntemini çağırır ve verileri buna iletir. Promise.all çağrısını, işlem tamamlandığında tamamlanması için await.

Verileri okuma

Verileri okumak için openDB() yöntemini kullanarak aldığınız veritabanı örneğinde get() yöntemini çağırın. get(), deponun adını ve geri almak istediğiniz nesnenin birincil anahtar değerini alır. Aşağıda temel bir örnek verilmiştir:

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();

add()'de olduğu gibi get() yöntemi de bir vaadi döndürür. Böylece isterseniz bu yöntemi await veya vadeli .then() geri çağırma yöntemini kullanabilirsiniz.

Aşağıdaki örnekte, 'name' birincil anahtarına göre tek bir satır almak için 'test-db4' veritabanının 'foods' nesne deposundaki get() yöntemi kullanılmaktadır:

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();

Veritabanından tek bir satır almak oldukça basit bir işlemdir: Veritabanını açın ve veri almak istediğiniz satırın nesne deposunu ve birincil anahtar değerini belirtin. get() yöntemi bir söz döndürdüğünden await.

Verileri güncelle

Verileri güncellemek için nesne deposunda put() yöntemini çağırın. put() yöntemi, add() yöntemine benzer ve veri oluşturmak için add() yerine de kullanılabilir. Bir nesne deposundaki satırı birincil anahtar değerine göre güncellemek için put() kullanmanın temel bir örneğini burada bulabilirsiniz:

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();

Diğer yöntemler gibi bu yöntem de umut verir. Bir işlemin parçası olarak da put() kullanabilirsiniz. Sandviç ve yumurtanın fiyatını güncelleyen, önceki bir 'foods' mağazasının kullanıldığı bir örneği aşağıda bulabilirsiniz:

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();

Öğelerin nasıl güncelleneceği, anahtarı nasıl ayarladığınıza bağlıdır. Bir keyPath ayarlarsanız nesne deposundaki her satır bir satır içi anahtar ile ilişkilendirilir. Yukarıdaki örnek, satırları bu anahtara dayalı olarak günceller ve bu durumdaki satırları güncellediğinizde, nesne deposundaki uygun öğeyi güncellemek için bu anahtarı belirtmeniz gerekir. Ayrıca birincil anahtar olarak autoIncrement ayarlayarak satır dışı anahtar oluşturabilirsiniz.

Verileri sil

Verileri silmek için nesne deposunda delete() yöntemini çağırın:

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();

add() ve put() gibi bunu da bir işlemin parçası olarak kullanabilirsiniz:

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();

Veritabanı etkileşiminin yapısı, diğer işlemlerle aynıdır. Promise.all öğesine ilettiğiniz diziye tx.done yöntemini ekleyerek tüm işlemin tamamlanıp tamamlanmadığını kontrol etmeyi unutmayın.

Tüm verileri alma

Şu ana kadar mağazadan her defasında yalnızca bir nesne aldınız. Ayrıca, getAll() yöntemini veya imleçleri kullanarak bir nesne deposundan ya da dizinden tüm verileri veya bir alt kümeyi alabilirsiniz.

getAll() yöntemi

Bir nesne deposunun tüm verilerini almanın en basit yolu, nesne deposunda veya dizininde getAll() öğesini çağırmaktır. Örneğin:

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();

Bu yöntem, hiçbir sınırlama olmadan nesne deposundaki tüm nesneleri döndürür. Bu, bir nesne deposundan tüm değerleri almanın en doğrudan yoludur ancak aynı zamanda en az esnek olan yöntemdir.

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();

Bu örnek, 'foods' nesne deposunda getAll() işlevini çağırır. Bu işlem, 'foods' nesnesindeki tüm nesneleri birincil anahtara göre sıralanmış şekilde döndürür.

İmleçler nasıl kullanılır?

İmleçler, birden çok nesneyi almak için daha esnek bir yöntemdir. İmleç, nesne deposundaki veya dizine eklenen her bir nesneyi tek tek seçerek seçildiğinde verilerle bir şeyler yapabilmenizi sağlar. İmleçler, diğer veritabanı işlemleri gibi işlemlerde çalışır.

İmleç oluşturmak için bir işlemin parçası olarak nesne deposunda openCursor() çağrısı yapın. Önceki örneklerde verilen 'foods' deposu kullanılarak, bir nesne deposundaki tüm veri satırları arasında imlecin nasıl ilerleyileceği aşağıda açıklanmıştır:

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();

Bu durumda işlem, 'readonly' modunda açılır ve openCursor yöntemi çağrılır. Sonraki bir while döngüsünde, imlecin geçerli konumundaki satırda key ve value özellikleri okunabilir ve uygulamanız için en uygun olan şekilde bu değerler üzerinde işlem yapabilirsiniz. Hazır olduğunuzda, sonraki satıra gitmek için cursor nesnesinin continue() yöntemini çağırabilirsiniz. while döngüsü, imleç veri kümesinin sonuna ulaştığında sona erer.

İmleçleri aralık ve dizinlerle kullanma

Dizinler, bir nesne deposundaki verileri birincil anahtar dışındaki bir özelliğe göre getirmenize olanak tanır. Herhangi bir özellikte dizin oluşturabilir ve bu dizin için keyPath haline gelir, bu özellikte bir aralık belirtebilir ve getAll() veya imleç kullanarak aralık içindeki verileri alabilirsiniz.

IDBKeyRange nesnesini ve aşağıdaki yöntemlerin herhangi birini kullanarak aralığınızı tanımlayın:

upperBound() ve lowerBound() yöntemleri, aralığın üst ve alt sınırlarını belirtir.

IDBKeyRange.lowerBound(indexKey);

veya:

IDBKeyRange.upperBound(indexKey);

Her biri bir bağımsız değişken alır: üst veya alt sınır olarak belirtmek istediğiniz öğe için dizinin keyPath değeri.

bound() yöntemi hem üst hem de alt sınırı belirtir:

IDBKeyRange.bound(lowerIndexKey, upperIndexKey);

Bu işlevlerin aralığı varsayılan olarak kapsayıcıdır. Yani, aralığın sınırları olarak belirtilen verileri içerir. Bu değerleri dışarıda bırakmak istiyorsanız aralığı, sırasıyla alt ve üst sınırlar için lowerBound() veya upperBound() için ikinci bağımsız değişken olarak true, üçüncü ve dördüncü bağımsız değişkenleri olarak bound() ileterek aralığı hariç olarak belirtin.

Sıradaki örnekte, 'foods' nesne deposundaki 'price' özelliğindeki bir dizin kullanılmaktadır. Mağazaya artık aralığın üst ve alt sınırları için iki girişin yer aldığı bir form da eklenmiş. Fiyatı bu sınırlar arasında olan yiyecekleri bulmak için aşağıdaki kodu kullanın:

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);

Örnek kod, önce sınır değerlerini alır ve sınırların mevcut olup olmadığını kontrol eder. Sonraki kod bloğu, değerlere göre aralığı sınırlamak için hangi yöntemin kullanılacağına karar verir. Veritabanı etkileşiminde, işlemdeki nesne deposunu her zamanki gibi açın, ardından nesne deposunda 'price' dizinini açın. 'price' dizini, öğeleri fiyata göre aramanızı sağlar.

Kod, daha sonra dizinde bir imleç açar ve aralık içinde geçiş yapar. İmleç, aralıktaki ilk nesneyi temsil eden bir söz veya aralıkta veri yoksa undefined döndürür. cursor.continue() yöntemi, sonraki nesneyi temsil eden bir imleç döndürür ve siz aralığın sonuna ulaşana kadar döngü boyunca devam eder.

Veritabanı sürümü oluşturma

openDB() yöntemini çağırdığınızda veritabanı sürüm numarasını ikinci parametrede belirtebilirsiniz. Bu kılavuzdaki tüm örneklerde sürüm 1 olarak ayarlanmıştır ancak bir veritabanını değiştirmeniz gerekirse veritabanı yeni bir sürüme yükseltilebilir. Belirtilen sürüm mevcut veritabanının sürümünden büyükse etkinlik nesnesindeki upgrade geri çağırması yürütülerek veritabanına yeni nesne depoları ve dizinler eklemenize olanak tanır.

upgrade geri çağırmasındaki db nesnesi, tarayıcının erişebildiği veritabanının sürüm numarasını gösteren özel bir oldVersion özelliğine sahiptir. Mevcut veritabanı sürüm numarasına göre upgrade geri çağırma içindeki kod bloklarını yürütmek için bu sürüm numarasını bir switch deyimine iletebilirsiniz. Aşağıda bir örnek verilmiştir:

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');
    }
  }
});

Bu örnekte, veritabanının en yeni sürümü 2 olarak ayarlanmaktadır. Bu kod ilk kez çalıştırıldığında, veritabanı henüz tarayıcıda mevcut değildir. Bu nedenle oldVersion, 0 olur ve switch ifadesi case 0 ile başlar. Bu örnekte, veritabanına bir 'store' nesne deposu eklenmiştir.

Önemli nokta: switch ifadelerinde, genellikle her case bloğundan sonra bir break olur, ancak bu özellik burada özellikle kullanılmaz. Böylece, mevcut veritabanı birkaç sürüm gerideyse veya böyle bir veritabanı yoksa kod, güncellenene kadar kalan case blokları boyunca devam eder. Dolayısıyla, örnekte tarayıcı case 1 üzerinden yürütmeye devam ederek store nesne deposunda bir name dizini oluşturur.

'store' nesne deposunda bir 'description' dizini oluşturmak için sürüm numarasını güncelleyin ve aşağıdaki gibi yeni bir case bloğu ekleyin:

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');
    }
  }
});

Önceki örnekte oluşturduğunuz veritabanı hâlâ tarayıcıda mevcutsa bu işlem yürütüldüğünde oldVersion, 2 olur. Tarayıcı case 0 ve case 1 komutlarını atlar ve kodu case 2 içinde yürüterek bir description dizini oluşturur. Sonrasında, tarayıcının sürüm 3'te name ve description dizinlerine sahip store nesne deposunu içeren bir veritabanı olur.

Daha fazla bilgi

Aşağıdaki kaynaklar, IndexedDB'nin kullanımı hakkında daha fazla bilgi ve bağlam sağlar.

IndexedDB Belgeleri

Veri depolama sınırları