Utilizza IndexedDB

Questa guida illustra le nozioni di base API IndexedDB. Utilizziamo il libro di Jake Archibald Promesso di Database indicizzato , che è molto simile all'API IndexedDB, ma utilizza le promesse, che puoi usare await per una sintassi più concisa. Ciò semplifica l'API mantenendone la struttura.

IndexedDB è un sistema di archiviazione NoSQL su larga scala che consente l'archiviazione di su qualsiasi elemento nel browser dell'utente. Oltre alle solite operazioni di ricerca, get e mettere azioni, IndexedDB supporta anche le transazioni ed è adatto per per archiviare grandi quantità di dati strutturati.

Ogni database IndexedDB è univoco per un'origine (in genere il dominio o il sottodominio del sito), nel senso che non è accessibile o accessibile da qualsiasi altra origine. I limiti di archiviazione dei dati di solito sono grandi, se presenti, ma browser diversi gestiscono i limiti e l'eliminazione dei dati in modo diverso. Consulta la sezione Per approfondire ulteriori informazioni.

Database
Il livello più alto di IndexedDB. Contiene gli archivi di oggetti, che a loro volta contengono i dati che vuoi mantenere. Puoi creare più database con con il nome che preferisci.
Archivio di oggetti
Un singolo bucket per l'archiviazione dei dati, simile alle tabelle nei database relazionali. In genere, esiste un archivio di oggetti per ogni tipo (non i dati JavaScript) dei dati che stai archiviando. A differenza delle tabelle di database, i dati JavaScript tipi di dati in un negozio non è necessario che siano coerenti. Ad esempio, se un'app ha un archivio di oggetti people contenente informazioni su tre persone, ovvero Le proprietà per l'età delle persone potrebbero essere 53, 'twenty-five' e unknown.
Indice
Un tipo di archivio di oggetti per organizzare i dati in un altro archivio di oggetti (denominato archiviazione di oggetti di riferimento) in base a una singola proprietà dei dati. L'indice viene utilizzato per recuperare i record nell'archivio oggetti da questa proprietà. Ad esempio, se stai memorizzano le persone, potresti recuperarle in un secondo momento in base a nome, età l'animale preferito.
Operazione
Un'interazione con il database.
Transazione
Un wrapper attorno a un'operazione o un gruppo di operazioni che garantisce che il database dei dati. Se una delle azioni in una transazione non va a buon fine, nessuna verrà applicata e il database torna allo stato in cui si trovava prima della transazione è iniziato. Tutte le operazioni di lettura o scrittura in IndexedDB devono essere parte di una transazione. Ciò consente operazioni atomiche di lettura, modifica e scrittura senza il rischio di conflitti con altri thread che operano sul database contemporaneamente.
Cursore
Un meccanismo per eseguire l'iterazione su più record di un database.

Come verificare il supporto di IndexedDB

IndexedDB è quasi universalmente supportato. Tuttavia, se utilizzi browser meno recenti, non è una cattiva idea il rilevamento delle funzionalità in caso di necessità. Il modo più semplice è controllare 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();

Come aprire un database

Con IndexedDB, puoi creare più database con il nome che preferisci. Se un database non esiste quando provi ad aprirlo, creato automaticamente. Per aprire un database, usa il metodo openDB() dalla libreria 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();

Questo metodo restituisce una promessa che si risolve in un oggetto di database. Quando utilizzi il openDB(), fornisci un nome, un numero di versione e un oggetto eventi da impostare per configurare il database.

Ecco un esempio del metodo openDB() nel contesto:

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

Posiziona il controllo per il supporto IndexedDB nella parte superiore della funzione anonima. Questo esce dalla funzione se il browser non supporta IndexedDB. Se la funzione può continua, chiama il metodo openDB() per aprire un database denominato 'test-db1'. In questo esempio, l'oggetto eventi facoltativo è stato tralasciato per mantenere gli elementi ma devi specificarlo per svolgere operazioni significative con IndexedDB.

Come utilizzare gli archivi di oggetti

Un database IndexedDB contiene uno o più archivi di oggetti, ciascuno con un per una chiave e un'altra colonna per i dati associati a quella chiave.

Crea archivi di oggetti

Un database IndexedDB ben strutturato deve avere un archivio di oggetti per ogni tipo di dati che devono essere persistenti. Ad esempio, un sito che rende persistenti gli utenti i profili e le note potrebbero avere un archivio di oggetti people contenente person e un archivio di oggetti notes contenente note oggetti.

Per garantire l'integrità del database, puoi creare o rimuovere archivi di oggetti solo nella in una chiamata openDB(). L'oggetto eventi espone un elemento upgrade() che consente di creare archivi di oggetti. Chiama il createObjectStore() all'interno del metodo upgrade() per creare l'archivio di oggetti:

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

Questo metodo prende il nome dell'archivio di oggetti e una configurazione facoltativa che consente di definire varie proprietà dell'archivio oggetti.

Di seguito è riportato un esempio di come utilizzare 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();

In questo esempio, un oggetto event viene passato al metodo openDB() per creare l'archivio di oggetti e, come prima, la creazione dell'archivio di oggetti termina nel metodo upgrade() dell'oggetto evento. Tuttavia, poiché il browser genera un errore se provi a creare un archivio di oggetti già esistente, aggregare il metodo createObjectStore() in un'istruzione if che verifica se l'archivio di oggetti esiste. Dall'interno del blocco if, chiama createObjectStore() per creare un archivio di oggetti denominato 'firstOS'.

Come definire le chiavi primarie

Quando definisci gli archivi di oggetti, puoi definire in che modo i dati vengono identificati in modo univoco utilizzando una chiave primaria. Puoi definire una chiave primaria definendo una chiave o usando un generatore di chiavi.

Un percorso chiave è una proprietà che esiste sempre e contiene un valore univoco. Per Ad esempio, nel caso di un archivio di oggetti people, potresti scegliere l'indirizzo email come percorso della chiave:

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

In questo esempio viene creato un archivio di oggetti denominato 'people' e viene assegnato il valore email come chiave primaria nell'opzione keyPath.

Puoi anche usare un generatore di chiavi come autoIncrement. Il generatore di chiavi crea un valore univoco per ogni oggetto aggiunto all'archivio oggetti. Per impostazione predefinita, Se non specifichi una chiave, IndexedDB crea una chiave e la archivia separatamente. a partire dai dati.

L'esempio seguente crea un archivio di oggetti denominato 'notes' e imposta il valore chiave primaria da assegnare automaticamente come numero con incremento automatico:

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

L'esempio seguente è simile a quello precedente, ma questa volta il valore a incremento automatico è assegnato esplicitamente a una proprietà denominata '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();

La scelta del metodo da utilizzare per definire la chiave dipende dai dati. Se le tue dati hanno una proprietà sempre univoca, puoi impostare keyPath per per applicare questa univocità. In caso contrario, utilizza un valore a incremento automatico.

Il seguente codice crea tre archivi di oggetti che mostrano i vari modi di definizione delle chiavi primarie negli archivi di oggetti:

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

Come definire gli indici

Gli indici sono un tipo di archivio di oggetti utilizzato per recuperare dati dal riferimento di archiviazione di oggetti in base a una proprietà specificata. Un indice risiede all'interno dell'oggetto di riferimento Archivia e contiene gli stessi dati, ma utilizza la proprietà specificata come della chiave anziché la chiave primaria dell'archivio di riferimento. Gli indici devono essere creati quando crei i tuoi archivi di oggetti e può essere utilizzato per definire un vincolo univoco i tuoi dati.

Per creare un indice, chiama createIndex() su un'istanza dell'archivio di oggetti:

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

Questo metodo crea e restituisce un oggetto indice. Il metodo createIndex() attivato l'istanza dell'archivio di oggetti assume il nome del nuovo indice come primo e il secondo argomento si riferisce alla proprietà sui dati che vuoi indice. L'argomento finale consente di definire due opzioni che determinano il modo in cui nell'indice: unique e multiEntry. Se il criterio unique viene impostato su true, il valore non consente valori duplicati per una singola chiave. Poi, multiEntry determina il comportamento di createIndex() quando la proprietà indicizzata è un array. Se è impostato su true, createIndex() aggiunge una voce all'indice per ogni array . In caso contrario, aggiunge una singola voce contenente l'array.

Ecco un esempio:

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

In questo esempio, gli archivi di oggetti 'people' e 'notes' hanno indici. A crea gli indici, assegna prima il risultato di createObjectStore() (un oggetto store) a una variabile in modo da poter chiamare createIndex().

Come utilizzare i dati

Questa sezione descrive come creare, leggere, aggiornare ed eliminare dati. Questi le operazioni sono tutte asincrone utilizzando promesse in cui l'API IndexedDB utilizza richieste. Ciò semplifica l'API. Anziché ascoltare gli eventi attivati la richiesta, puoi chiamare .then() sull'oggetto di database restituito Metodo openDB() per avviare le interazioni con il database o await il suo per la creazione di contenuti.

Tutte le operazioni sui dati in IndexedDB vengono eseguite all'interno di una transazione. Ciascuna ha il seguente formato:

  1. Ottieni oggetto di database.
  2. Apri la transazione sul database.
  3. Apri archivio oggetti al momento della transazione.
  4. Eseguire un'operazione sull'archivio di oggetti.

Una transazione può essere considerata come un wrapper sicuro per un'operazione o un gruppo delle operazioni quotidiane. Se una delle azioni all'interno di una transazione non va a buon fine, tutte le il rollback di tutte le azioni. Le transazioni sono specifiche di uno o più archivi di oggetti, che definisci quando apri la transazione. Possono essere di sola lettura o lettura e scrivere. Indica se le operazioni all'interno della transazione o apportare una modifica al database.

Crea i dati

Per creare i dati, chiama add() sull'istanza di database e passare i dati che vuoi aggiungere. add() il primo argomento del metodo è l'archivio di oggetti a cui vuoi aggiungere i dati e il secondo argomento è un oggetto contenente i campi e i dati associati che vuoi da aggiungere. Ecco l'esempio più semplice, in cui viene aggiunta una singola riga di dati:

import {openDB} from 'idb';

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

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

addItemToStore();

Ogni chiamata a add() avviene all'interno di una transazione, quindi anche se la promessa si risolve non significa necessariamente che l'operazione sia andata a buon fine. Per assicurarti è stata eseguita l'operazione di aggiunta, devi verificare se transazione è stata completata utilizzando il metodo transaction.done(). Si tratta di un che si risolve quando la transazione viene completata da sola e rifiuta se errori di transazione. Devi eseguire questo controllo per tutte le operazioni di "scrittura" operazioni, perché è l'unico modo per sapere che le modifiche apportate al database sono state effettivamente è successo.

Il seguente codice mostra l'utilizzo del metodo add() in una transazione:

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

Una volta aperto il database (e creato un archivio di oggetti, se necessario), dovrai per aprire una transazione chiamando il metodo transaction(). Questo metodo richiede un argomento per il negozio su cui vuoi effettuare transazioni, nonché la modalità. In questo caso, vorremmo scrivere al negozio, in modo che questo esempio specifica 'readwrite'.

Il passaggio successivo consiste nell'iniziare ad aggiungere articoli al negozio come parte della transazione. Nell'esempio precedente, abbiamo a che fare con tre operazioni su 'foods' che ciascuno restituisca una promessa:

  1. aggiungo un record per un panino gustoso.
  2. Aggiunta di un record per alcune uova.
  3. Segnalazione del completamento della transazione (tx.done).

Poiché tutte queste azioni sono basate su promesse, dobbiamo aspettare per completarli. Il rispetto di queste promesse Promise.all è un buon modo ergonomico per ottenere questo risultato. Promise.all accetta un array di promesse e finisce una volta risolte tutte le promesse fatte al riguardo.

Per i due record da aggiungere, l'interfaccia store dell'istanza di transazione chiama add() e gli passa i dati. Puoi await la chiamata Promise.all in modo che finisca al termine della transazione.

Lettura di dati

Per leggere i dati, chiama get() sull'istanza di database recuperata utilizzando il metodo openDB(). get() prende il nome del negozio e il valore della chiave primaria dell'oggetto che da recuperare. Ecco un esempio di base:

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

Come nel caso di add(), il metodo get() restituisce una promessa, quindi puoi awaitrla se che preferisci oppure usa il callback .then() della promessa.

L'esempio seguente utilizza il metodo get() nel database 'test-db4' 'foods' archivio di oggetti per ottenere una singola riga in base alla chiave primaria '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();

Il recupero di una singola riga dal database è piuttosto semplice: l'apertura del database e specifica l'archivio oggetti e il valore della chiave primaria della riga da cui vuoi prendere i dati. Poiché il metodo get() restituisce una promessa, puoi await.

Aggiorna dati

Per aggiornare i dati, chiama il put() sull'archivio di oggetti. Il metodo put() è simile al metodo add() e può anche essere utilizzato al posto di add() per creare dati. Ecco un esempio di base dell'utilizzo di put() per aggiornare una riga in un archivio di oggetti in base al valore della chiave primaria:

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

Come altri metodi, questo metodo restituisce una promessa. Puoi usare anche put() come di una transazione. Ecco un esempio di utilizzo dello store 'foods' di un elenco precedente che aggiorna il prezzo del panino e delle uova:

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

Il modo in cui gli elementi vengono aggiornati dipende da come hai impostato una chiave. Se imposti un valore keyPath, Ogni riga nell'archivio oggetti è associata a una chiave in linea. Il precedente esempio aggiorna le righe in base a questa chiave e quando aggiorni le righe in ogni caso, dovrai specificare la chiave per aggiornare l'elemento appropriato nel un archivio di oggetti. Puoi anche creare una chiave fuori riga impostando una proprietà autoIncrement come chiave primaria.

Elimina i dati

Per eliminare i dati, chiama il delete() sull'archivio di oggetti:

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

Come nel caso di add() e put(), puoi utilizzare questa opzione nell'ambito di una transazione:

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

La struttura dell'interazione con il database è la stessa dell'altra operazioni. Ricordati di controllare che l'intera transazione sia stata completata entro il giorno incluso il metodo tx.done nell'array passato a Promise.all.

Recupero di tutti i dati in corso...

Finora hai recuperato solo oggetti dal datastore uno alla volta. Puoi anche recupera tutti i dati o un sottoinsieme da un archivio o un indice di oggetti utilizzando il metodo getAll() o i cursori.

Il metodo getAll()

Il modo più semplice per recuperare tutti i dati di un archivio di oggetti è chiamare getAll() sull'archivio o sull'indice di oggetti, in questo modo:

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

Questo metodo restituisce tutti gli oggetti presenti nell'archivio, senza vincoli di qualunque cosa. È il modo più diretto per ottenere tutti i valori da un archivio di oggetti, ma anche il meno flessibile.

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

Questo esempio chiama getAll() nell'archivio di oggetti 'foods'. Vengono restituiti tutti i valori gli oggetti di 'foods', ordinati in base alla chiave primaria.

Come usare i cursori

I cursori sono un modo più flessibile per recuperare più oggetti. Un cursore seleziona ogni oggetto in un archivio o indice di oggetti uno alla volta, permettendoti di fare con i dati quando selezionato. I cursori, come le altre operazioni di database, delle transazioni.

Per creare un cursore, richiama openCursor() sull'archivio di oggetti nell'ambito di una transazione. Utilizzo dello store 'foods' da esempi precedenti, ecco come avanzare un cursore su tutte le righe di dati di un archivio di oggetti:

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

In questo caso, la transazione viene aperta in modalità 'readonly' e le relative Viene chiamato il metodo openCursor. In un ciclo while successivo, la riga in corrispondenza alla posizione corrente del cursore possono essere lette le proprietà key e value, e puoi operare su questi valori nel modo più adatto alle tue esigenze dell'app. Quando è tutto pronto, puoi chiamare continue() dell'oggetto cursor per andare alla riga successiva e il loop while termina quando il cursore raggiunge la fine del set di dati.

Utilizzare cursori con intervalli e indici

Gli indici consentono di recuperare i dati in un archivio di oggetti in base a una proprietà diversa dalla e la chiave primaria. Puoi creare un indice su qualsiasi proprietà, che diventa keyPath per l'indice, specifica un intervallo su quella proprietà e recupera i dati all'interno intervallo utilizzando getAll() o un cursore.

Definisci l'intervallo utilizzando l'oggetto IDBKeyRange. e uno dei seguenti metodo:

I metodi upperBound() e lowerBound() specificano i limiti superiore e inferiore dell'intervallo.

IDBKeyRange.lowerBound(indexKey);

Oppure:

IDBKeyRange.upperBound(indexKey);

Ognuno di loro ha un argomento: il valore keyPath dell'indice per l'elemento che vuoi da specificare come limite superiore o inferiore.

Il metodo bound() specifica un limite massimo e uno inferiore:

IDBKeyRange.bound(lowerIndexKey, upperIndexKey);

L'intervallo per queste funzioni è inclusivo per impostazione predefinita, il che significa che include i dati specificati come limiti dell'intervallo. Per escludere questi valori, specifica l'intervallo come esclusivo passando true come secondo argomento per lowerBound() o upperBound() oppure come terzo e quarto argomento di bound(), rispettivamente, per il limite inferiore e quello superiore.

L'esempio seguente utilizza un indice nella proprietà 'price' nell'oggetto 'foods' . Allo store è ora allegato anche un modulo con due input per limiti superiore e inferiore dell'intervallo. Utilizza il seguente codice per trovare alimenti con prezzi compresi tra questi limiti:

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

Il codice di esempio ottiene prima i valori per i limiti e controlla se i limiti esistono. Il blocco di codice successivo stabilisce quale metodo utilizzare per limitare l'intervallo in base a questi valori. Nell'interazione con il database, apri l'archivio di oggetti nella come al solito, quindi apri l'indice 'price' nell'archivio oggetti. La L'indice 'price' ti consente di cercare gli articoli in base al prezzo.

Il codice apre un cursore sull'indice e lo passa nell'intervallo. Il cursore restituisce una promessa che rappresenta il primo oggetto nell'intervallo oppure undefined se non sono presenti dati nell'intervallo. Il metodo cursor.continue() restituisce un cursore che rappresenta l'oggetto successivo e continua nel loop finché non raggiungere la fine dell'intervallo.

Controllo delle versioni del database

Quando chiami il metodo openDB(), puoi specificare il numero di versione del database nel secondo parametro. In tutti gli esempi di questa guida, la versione è stata impostato su 1, ma puoi eseguire l'upgrade di un database a una nuova versione se devi modificarlo in qualche modo. Se la versione specificata è superiore a quella di il database esistente, viene eseguito il callback upgrade nell'oggetto evento, consentendoti di aggiungere nuovi archivi e indici di oggetti al database.

L'oggetto db nel callback upgrade ha una proprietà oldVersion speciale, che indica il numero di versione del database a cui il browser ha accesso. Puoi passare questo numero di versione in un'istruzione switch per eseguire blocchi di all'interno del callback upgrade in base alla versione del database esistente numero. Ecco un esempio:

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

Questo esempio imposta la versione più recente del database su 2. Quando il codice viene eseguita per la prima volta, il database non esiste ancora nel browser, quindi oldVersion è 0 e l'istruzione switch inizia da case 0. Nell'esempio, questo aggiunge al database un archivio di oggetti 'store'.

Punto chiave: nelle istruzioni switch, di solito c'è un break dopo ogni case blocco note, che però non viene usato deliberatamente in questo caso. In questo modo, se l'istanza del database è indietro di alcune versioni oppure, se non esiste, il codice continua attraverso il resto dei blocchi di case finché non viene aggiornato. Quindi, nell'esempio, il browser continua a essere eseguito tramite case 1, creando un indice name nella Archivio di oggetti store.

Per creare un indice 'description' nell'archivio di oggetti 'store', aggiorna il valore numero di versione e aggiungi un nuovo blocco case come segue:

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

Se il database creato nell'esempio precedente esiste ancora nel browser, quando viene eseguita, oldVersion è 2. Il browser ignora case 0 e case 1 ed esegue il codice in case 2, creando un description indice. Successivamente, il browser avrà un database nella versione 3 contenente un store un archivio di oggetti con indici name e description.

Per approfondire

Le seguenti risorse forniscono ulteriori informazioni e contesto per l'utilizzo di IndexedDB.

Documentazione IndexedDB

Limiti di archiviazione dei dati