Praca z IndexedDB

Ten przewodnik zawiera podstawowe informacje Interfejs API IndexedDB. Mamy film Jake'a Archibalda Obiekt IndexedDB jest obiecujący która jest bardzo podobna do interfejsu IndexedDB API, ale korzysta z obietnic, które możesz użyć funkcji await, aby uzyskać bardziej zwięzłą składnię. Upraszcza to interfejs API, ale na utrzymanie jego struktury.

Co to jest IndexedDB?

IndexedDB to wielkoskalowy system przechowywania danych NoSQL, który pozwala przechowywać do wszystkiego. Oprócz zwykłego wyszukiwania, otrzymasz oraz działania, IndexedDB obsługuje też transakcje. przechowywania dużych ilości uporządkowanych danych.

Każda baza danych IndexedDB jest unikalna dla źródła (zwykle jest to domena witryny lub subdomena), co oznacza, że witryna nie ma dostępu do witryny lub nie można uzyskać do niej dostępu. z jakiegokolwiek innego źródła. limity przechowywania danych, są zwykle duże, o ile w ogóle istnieją, ale różne przeglądarki obsługują ograniczenia i usuwania danych w inny sposób. W sekcji Więcej informacji znajdziesz znajdziesz więcej informacji.

Terminy dotyczące IndexedDB

Baza danych
Najwyższy poziom IndexedDB. Zawiera magazyny obiektów, które z kolei zawierają dane, które chcesz zachować. Możesz utworzyć wiele baz danych za pomocą dowolne nazwy.
Składnica obiektów
Pojedynczy zasobnik do przechowywania danych, podobny do tabel w relacyjnych bazach danych. Zwykle dla każdego typu jest 1 magazyn obiektów (nie dane JavaScriptu) typ danych, które przechowujesz. W przeciwieństwie do tabel bazy danych dane JavaScript typów danych w sklepie nie muszą być spójne. Jeśli na przykład aplikacja zawiera magazyn obiektów people z informacjami o 3 osobach, właściwościami wieku użytkowników mogą być 53, 'twenty-five' i unknown.
Indeks
Rodzaj magazynu obiektów do porządkowania danych w innym magazynie obiektów odwołania do magazynu obiektów) według pojedynczej właściwości danych. Używany jest indeks. aby pobrać rekordy z magazynu obiektów przez tę właściwość. Jeśli na przykład jesteś zapisując dane osób, możesz pobrać je później według imienia i nazwiska, wieku lub ulubione zwierzę.
Operacja
Interakcja z bazą danych.
Transakcja
Kod wokół operacji lub grupy operacji, który zapewnia bazę danych uczciwości. Jeśli jedno z działań w ramach transakcji się nie powiedzie, żadne z nich nie zostanie została zastosowana, a baza danych wróci do stanu sprzed transakcji rozpoczęły się. Wszystkie operacje odczytu i zapisu w IndexedDB muszą być częścią transakcji. Umożliwia to niepodzielne operacje odczytu, modyfikacji i zapisu bez ryzyka konfliktów dzięki czemu inne wątki korzystają z bazy danych w tym samym czasie.
Kursor
Mechanizm iteracji wielu rekordów w bazie danych.

Jak sprawdzić obsługę IndexedDB

Technologia IndexedDB jest prawie powszechnie obsługiwana. Jednak w przypadku pracy ze starszymi przeglądarkami nie jest dobrym pomysłem na wszelki wypadek. Najłatwiej to sprawdzić window obiekt:

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

Jak otworzyć bazę danych

Dzięki IndexedDB możesz tworzyć wiele baz danych o dowolnych nazwach. Jeśli Przy próbie otwarcia bazy danych nie istnieje, utworzona automatycznie. Aby otworzyć bazę danych, użyj metody openDB() z biblioteki 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();

Ta metoda zwraca obietnicę zwracającą się do obiektu bazy danych. Jeśli korzystasz z metody openDB(), podaj nazwę, numer wersji i obiekt zdarzeń do ustawienia do uruchomienia bazy danych.

Oto przykład metody openDB() w kontekście:

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

Umieść zaznaczenie obsługi IndexedDB na górze funkcji anonimowej. Ten kończy funkcję, jeśli przeglądarka nie obsługuje IndexedDB. Jeśli funkcja może kontynuuje, wywołuje metodę openDB(), aby otworzyć bazę danych o nazwie 'test-db1'. W tym przykładzie opcjonalny obiekt zdarzeń został pominięty, aby zachować jest prosta, ale trzeba ją określić, aby wykonywać dowolne czynności związane z bazą IndexedDB.

Korzystanie z magazynów obiektów

Baza danych IndexedDB zawiera co najmniej jeden magazyn obiektów, z których każdy ma kolumnę z kluczem, a drugą kolumnę z danymi powiązanymi z tym kluczem.

Tworzenie magazynów obiektów

Dobrze uporządkowana baza danych IndexedDB powinna mieć po 1 magazynie obiektów dla każdego typu które trzeba utrwalić. Na przykład w witrynie, która utrzymuje użytkowników profile i notatki mogą mieć magazyn obiektów people zawierający obiekt person i magazyn obiektów notes z note obiektami.

Aby zapewnić integralność bazy danych, możesz tworzyć i usuwać magazyny obiektów tylko w zdarzenia w wywołaniu openDB(). Obiekt zdarzeń ujawnia: upgrade() która pozwala tworzyć magazyny obiektów. Wywołaj funkcję createObjectStore() wewnątrz metody upgrade(), aby utworzyć magazyn obiektów:

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

Ta metoda przyjmuje nazwę magazynu obiektów i opcjonalną konfigurację , który pozwala zdefiniować różne właściwości magazynu obiektów.

Oto przykład użycia atrybutu 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();

W tym przykładzie obiekt zdarzeń jest przekazywany do metody openDB() w celu utworzenia w magazynie obiektów i tak jak wcześniej, utworzenie magazynu obiektów jest zakończone w metodzie upgrade() obiektu zdarzenia. Ponieważ jednak przeglądarka przesyła jeśli próbujesz utworzyć magazyn obiektów, który już istnieje, zalecamy można umieścić metodę createObjectStore() w instrukcji if, która sprawdza, sprawdzić, czy magazyn obiektów istnieje. W obrębie bloku if wywołaj createObjectStore(), aby utworzyć magazyn obiektów o nazwie 'firstOS'.

Jak zdefiniować klucze podstawowe

Definiując magazyny obiektów, możesz określić sposób jednoznacznego identyfikowania danych w w sklepie za pomocą klucza podstawowego. Klucz podstawowy możesz zdefiniować przez zdefiniowanie lub za pomocą generatora kluczy.

Ścieżka klucza to usługa, która zawsze istnieje i zawiera unikalną wartość. Dla: w przypadku magazynu obiektów people możesz wybrać adres e-mail, jako ścieżki klucza:

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

Ten przykład tworzy magazyn obiektów o nazwie 'people' i przypisuje email jako klucza podstawowego w opcji keyPath.

Możesz też użyć generatora kluczy, takiego jak autoIncrement. Generator kluczy tworzy unikalną wartość dla każdego obiektu dodanego do magazynu obiektów. Domyślnie Jeśli nie określisz klucza, IndexedDB utworzy klucz i będzie go przechowywać oddzielnie danych.

Poniższy przykład tworzy magazyn obiektów o nazwie 'notes' i ustawia klucz podstawowy zostanie przypisany automatycznie jako numer automatycznie zwiększający wartość:

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

Poniższy przykład jest podobny do poprzedniego, ale tym razem wartość automatycznego przyrostu jest jawnie przypisana do właściwości o nazwie '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();

Wybór metody definiowania klucza zależy od Twoich danych. Jeśli ma właściwość, która jest zawsze unikalna, możesz ustawić keyPath egzekwować tę wyjątkowość. W przeciwnym razie użyj wartości automatycznego przyrostu.

Poniższy kod tworzy 3 magazyny obiektów demonstrujące różne sposoby definiując klucze podstawowe w magazynach obiektów:

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

Jak zdefiniować indeksy

Indeksy to rodzaj magazynu obiektów używany do pobierania danych z odwołania magazyn obiektów według określonej właściwości. Indeks znajduje się wewnątrz obiektu referencyjnego przechowuje i zawiera te same dane, ale używa wskazanej właściwości jako ścieżki klucza zamiast klucza podstawowego magazynu odwołań. Indeksy należy tworzyć, gdy Twoich magazynów obiektów. Może służyć do zdefiniowania unikalnego ograniczenia danych.

Aby utworzyć indeks, wywołaj metodę createIndex(). w instancji magazynu obiektów:

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

Ta metoda tworzy i zwraca obiekt indeksu. Metoda createIndex() włączona instancja magazynu obiektów przyjmuje nazwę nowego indeksu jako pierwszy a drugi argument odnosi się do właściwości na danych, które chcesz indeksu. Ostatni argument pozwala zdefiniować dwie opcje, które określają sposób indeks to: unique i multiEntry. Jeśli unique ma wartość true, makro Indeks nie zezwala na zduplikowane wartości jednego klucza. Dalej: multiEntry określa działanie elementu createIndex(), gdy zindeksowana właściwość jest tablicą. Jeśli ma wartość true, createIndex() dodaje wpis w indeksie dla każdej tablicy . W przeciwnym razie doda jeden wpis zawierający tablicę.

Oto przykład:

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

W tym przykładzie magazyny obiektów 'people' i 'notes' mają indeksy. Do utwórz indeksy, najpierw przypisz wynik funkcji createObjectStore() (obiekt Store obiektu) do zmiennej, aby można było wywołać z niej funkcję createIndex().

Jak pracować z danymi

Z tej sekcji dowiesz się, jak tworzyć, odczytywać, aktualizować i usuwać dane. Te asynchroniczne operacje, z użyciem obietnic, w których interfejs IndexedDB API żądań. Upraszcza to interfejs API. Zamiast nasłuchiwać zdarzeń wywoływanych przez możesz wywołać funkcję .then() na obiekcie bazy danych zwróconym przez openDB(), aby rozpocząć interakcje z bazą danych, lub await jej proces tworzenia.

Wszystkie operacje na danych w IndexedDB są wykonywane w ramach transakcji. Każdy ma taki format:

  1. Pobranie obiektu bazy danych.
  2. Otwórz transakcję w bazie danych.
  3. Otwórz magazyn obiektów w transakcji.
  4. Wykonaj operację na magazynie obiektów.

Transakcję można traktować jako bezpieczny kod wokół operacji lub grupy operacji. Jeśli jedno z działań w ramach transakcji się nie powiedzie, wszystkie czynności są wycofane. Transakcje są specyficzne dla co najmniej 1 magazynu obiektów, które są określane przy otwarciu transakcji. Mogą być dostępne tylko do odczytu lub tylko do odczytu. i zapis. Wskazuje, czy operacje w ramach transakcji odczytują lub wprowadzić zmiany w bazie danych.

Utwórz dane

Aby utworzyć dane, wywołaj metodę add() w instancji bazy danych i prześlij dane, które chcesz dodać. add() pierwszym argumentem jest magazyn obiektów, do którego chcesz dodać dane, a metoda jest to obiekt zawierający pola i powiązane dane, które mają dodać. Oto najprostszy przykład, w którym dodajesz jeden wiersz danych:

import {openDB} from 'idb';

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

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

addItemToStore();

Każde wywołanie add() jest realizowane w ramach transakcji, nawet jeśli obietnica zostanie zrealizowana nie musi to oznaczać, że operacja się udała. Aby upewnić się, operacji dodawania, musisz sprawdzić, czy cała transakcja została ukończona przy użyciu metody transaction.done(). To jest obietnicę rozwiązania problemu w chwili zrealizowania transakcji i odrzucanie, jeśli błędów transakcji. Musisz przeprowadzić tę kontrolę dla wszystkich żądań zapisu operacji, bo jest to jedyny sposób, aby się dowiedzieć, jakie zmiany w bazie danych miało miejsce.

Ten kod pokazuje użycie metody add() w transakcji:

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

Po otwarciu bazy danych (i w razie potrzeby utworzenie magazynu obiektów) aby otworzyć transakcję, wywołując w niej metodę transaction(). Ta metoda pobiera argument dla sklepu, w którym chcesz dokonać transakcji, oraz tryb. W tym przypadku chcemy napisać do sklepu, więc ten przykład określa 'readwrite'.

Następnym krokiem jest rozpoczęcie dodawania produktów do sklepu w ramach transakcji. W poprzednim przykładzie mamy do czynienia z 3 operacjami na 'foods' które zwracają obietnicę:

  1. Dodaję rekord pysznej kanapki.
  2. Dodaję rekord niektórych jajek.
  3. Informacja, że transakcja została zrealizowana (tx.done).

Wszystkie te działania opierają się na obietnicach, musimy więc poczekać, aż aby je dokończyć. Spełnienie tych obietnic Promise.all to przyjemny, ergonomiczny sposób. Promise.all akceptuje tablicę obiecuje i kończy, gdy wszystkie obietnice jej partnera zostaną spełnione.

W przypadku 2 dodawanych rekordów interfejs store instancji transakcji wywołuje metodę add() i przekazuje do niej dane. Możesz await wykonać połączenie typu Promise.all aby sfinalizować transakcję po jej sfinalizowaniu.

Odczytywanie danych

Aby odczytać dane, wywołaj metodę get() w instancji bazy danych pobieranej przy użyciu metody openDB(). get() przyjmuje nazwę magazynu i wartość klucza podstawowego obiektu, który które chcesz pobrać. Oto podstawowy przykład:

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

Tak jak add(), metoda get() zwraca obietnicę, więc możesz ją await, jeśli wolisz, lub skorzystaj z wywołania zwrotnego .then() obietnicy.

Poniższy przykład pokazuje użycie metody get() w bazie danych 'test-db4' 'foods' – magazyn obiektów, aby uzyskać pojedynczy wiersz według klucza podstawowego '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();

Pobieranie jednego wiersza z bazy danych jest dość proste: otwórz bazy danych i określ magazyn obiektów oraz wartość klucza podstawowego dla wiersza, z których chcesz uzyskać dane. Metoda get() zwraca obietnicę, więc możesz await.

Zaktualizuj dane

Aby zaktualizować dane, wywołaj put() w magazynie obiektów. Metoda put() jest podobna do metody add() , a także może być używany zamiast add() do tworzenia danych. Oto podstawowy przykład użycia funkcji put() do aktualizacji wiersza w magazynie obiektów o jej wartość klucza podstawowego:

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

Podobnie jak inne metody, ta metoda zwraca obietnicę. Możesz też użyć put() jako która stanowi część transakcji. Oto przykład z wykorzystaniem sklepu 'foods' z wcześniejszego okresu która aktualizuje cenę kanapki i jajek:

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

Sposób aktualizowania elementów zależy od tego, jak skonfigurujesz klucz. Jeśli ustawisz keyPath, każdy wiersz w magazynie obiektów jest powiązany z kluczem wbudowanym. Poprzednie aktualizuje wiersze na podstawie tego klucza, a gdy zaktualizujesz wiersze w tym kluczu, musisz podać ten klucz, aby zaktualizować odpowiedni element w obiektami pamięci masowej. Możesz też utworzyć klucz zewnętrzny, ustawiając klucz autoIncrement jako klucz podstawowy.

Usuń dane

Aby usunąć dane, wywołaj delete() w magazynie obiektów:

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

Tak jak w przypadku usług add() i put(), w ramach transakcji możesz użyć tych informacji:

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

Struktura interakcji z bazą danych jest taka sama jak w drugim operacji. Pamiętaj, aby sprawdzić, czy cała transakcja została zrealizowana do łącznie z metodą tx.done w tablicy przekazywanej do funkcji Promise.all.

Pobieram wszystkie dane

Do tej pory obiekty ze sklepu zostały pobrane tylko pojedynczo. Możesz też pobierz wszystkie dane lub ich podzbiór z magazynu lub indeksu obiektów za pomocą za pomocą metody getAll() albo kursorów.

Metoda getAll()

Najprostszym sposobem na pobranie wszystkich danych magazynu obiektów jest wywołanie metody getAll() w magazynie obiektów lub indeksie w ten sposób:

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

Ta metoda zwraca wszystkie obiekty w magazynie obiektów bez ograniczeń . To najprostszy sposób pobierania wszystkich wartości z magazynu obiektów, ale też najmniej elastyczny.

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

W tym przykładzie wywołujemy funkcję getAll() w magazynie obiektów 'foods'. Powoduje to zwrócenie wszystkich obiekty z 'foods' w kolejności od klucza podstawowego.

Jak korzystać z kursorów

Kursory to bardziej elastyczny sposób na pobieranie wielu obiektów. Kursor wybiera każdy obiekt w magazynie obiektów lub indeksuj go jeden po drugim, dzięki czemu możesz z danymi po ich zaznaczeniu. Kursory, podobnie jak inne operacje na bazach danych, jak szacuje się ich liczba.

Aby utworzyć kursor, wywołaj openCursor(). w magazynie obiektów w ramach transakcji. Korzystam ze sklepu 'foods' z W poprzednich przykładach pokazujemy, jak przesunąć kursor po wszystkich wierszach danych w magazyn obiektów:

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

Transakcja w tym przypadku jest otwarta w trybie 'readonly', a jej Wywoływana jest metoda openCursor. W kolejnej pętli while wiersz w w przypadku bieżącej pozycji kursora można odczytać właściwości key i value oraz Możesz operować na tych wartościach w dowolny sposób, . Następnie możesz wywołać funkcję continue() obiektu cursor powoduje przejście do następnego wiersza, a pętla while kończy się, gdy kursor dotrze do końca zbioru danych.

Używanie kursorów z zakresami i indeksami

Indeksy umożliwiają pobieranie danych do magazynu obiektów przez właściwość inną niż klucz podstawowy. Indeks możesz utworzyć dla dowolnej usługi, która stanie się domeną keyPath dla indeksu, określ zakres tej właściwości i pobierz dane z za pomocą getAll() lub kursora.

Zdefiniuj zakres za pomocą obiektu IDBKeyRange. i dowolny z tych elementów metody:

Metody upperBound() i lowerBound() określają górne i dolne granice w zakresie.

IDBKeyRange.lowerBound(indexKey);

Lub:

IDBKeyRange.upperBound(indexKey);

Każdy z nich przyjmuje po 1 argumencie: wartość keyPath indeksu dla danego elementu który określa górny lub dolny limit.

Metoda bound() określa zarówno górny, jak i dolny limit:

IDBKeyRange.bound(lowerIndexKey, upperIndexKey);

Zakres tych funkcji jest domyślnie włącznie, co oznacza, że obejmuje m.in. danych określonych jako granice zakresu. Aby wykluczyć te wartości, określ zakres jako wyłączny, przekazując true jako drugi argument funkcji lowerBound() lub upperBound() albo jako trzeci i czwarty argument argumentu bound() odpowiednio dla dolnego i górnego limitu.

W następnym przykładzie użyto indeksu we właściwości 'price' w obiekcie 'foods'. sklepu. Do sklepu jest teraz dołączony formularz z 2 wprowadzanymi w nim polami górnej i dolnej granicy zakresu. Użyj następującego kodu, aby znaleźć produkty zawierające: ceny między tymi limitami:

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

Przykładowy kod najpierw pobiera wartości limitów i sprawdza, czy limity istnieje. Następny blok kodu określa metodę, której należy użyć do ograniczenia zakresu na podstawie wartości. Podczas interakcji z bazą danych otwórz magazyn obiektów w transakcji w zwykły sposób, a następnie otwórz indeks 'price' w magazynie obiektów. Indeks 'price' umożliwia wyszukiwanie elementów według ceny.

Następnie kod otwiera kursor w indeksie i przekazuje w zakresie. Kursor zwraca obietnicę reprezentującą pierwszy obiekt w zakresie lub undefined, jeśli w zakresie nie ma żadnych danych. Metoda cursor.continue() zwraca błąd kursora reprezentującego następny obiekt. Kontynuujemy pętlę do momentu, osiągnie koniec zakresu.

Obsługa wersji bazy danych

Gdy wywołujesz metodę openDB(), możesz podać numer wersji bazy danych w drugim parametrze. We wszystkich przykładach w tym przewodniku wersja została ma wartość 1, ale w razie potrzeby bazę danych można uaktualnić do nowej wersji w jakiś sposób go zmodyfikować. Jeśli podana wersja jest nowsza niż wersja w istniejącej bazie danych wykonywane jest wywołanie zwrotne upgrade w obiekcie zdarzenia, co pozwala dodawać do bazy danych nowe magazyny obiektów i indeksy.

Obiekt db w wywołaniu zwrotnym upgrade ma specjalną właściwość oldVersion, , który wskazuje numer wersji bazy danych, do której ma dostęp przeglądarka. Możesz przekazać ten numer wersji do instrukcji switch, aby wykonywać bloki kod w wywołaniu zwrotnym upgrade na podstawie istniejącej wersji bazy danych numer. Oto przykład:

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

Ten przykład ustawia najnowszą wersję bazy danych na 2. Gdy ten kod bazy danych nie ma jeszcze w przeglądarce, więc oldVersion to 0, a instrukcja switch zaczyna się od case 0. W tym przykładzie dodaje do bazy danych bazę obiektów 'store'.

Ważne: w wyrażeniach switch po każdym case zwykle występuje break ale celowo nie używa się go tutaj. Dzięki temu, jeśli występują jest o kilka wersji wstecz, a jeśli baza danych nie istnieje, kod będzie kontynuowany przez resztę case blokad, aż zostanie zaktualizowana. W tym przykładzie przeglądarka kontynuuje wykonywanie zadań przez interfejs case 1, tworząc indeks name w Magazyn obiektów store.

Aby utworzyć indeks 'description' w magazynie obiektów 'store', zaktualizuj wersji i dodaj nowy blok case w następujący sposób:

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

Jeśli baza danych utworzona w poprzednim przykładzie nadal istnieje w przeglądarce, po wykonaniu tego polecenia oldVersion to 2. Przeglądarka pomija case 0 i case 1 i uruchamia kod w elemencie case 2, co tworzy description indeksu. Następnie przeglądarka korzysta z bazy danych w wersji 3 zawierającej tabelę store magazyn obiektów z indeksami name i description.

Więcej informacji

Poniższe zasoby zawierają więcej informacji i zapewniają dodatkowe informacje o korzystaniu z IndexedDB.

Dokumentacja IndexedDB

Limity miejsca na dane