Start · Sprachen · JavaScript · Referenz · IDBIndex.openCursor()

IDBIndex.openCursor()

Methode

Öffnet asynchron einen <code>IDBCursorWithValue</code> über die Einträge eines Index, optional eingeschränkt auf einen Schlüssel oder <code>IDBKeyRange</code>, und liefert ein <code>IDBRequest</code>.

seit JavaScript Baseline: alle Browser (IndexedDB 2.0) Kategorie: webapi

Signatur

IDBIndex.prototype.openCursor(query, direction)

Beschreibung

openCursor() durchläuft die Datensätze eines Object Stores in der Reihenfolge eines Index, also sortiert nach dem Index-Schlüssel (z. B. Nachname, Datum, Kategorie) statt nach dem Primärschlüssel. Die Methode gibt sofort ein IDBRequest zurück; das eigentliche Öffnen läuft asynchron. Das success-Ereignis wird für jeden Schritt des Cursors erneut ausgelöst.

In request.result steht ein IDBCursorWithValue: cursor.key ist der Index-Schlüssel, cursor.primaryKey der Primärschlüssel des Datensatzes und cursor.value der vollständige Datensatz. Mit cursor.continue() geht es zum nächsten Eintrag, mit cursor.advance(n) überspringt man Einträge. Ist das Ende erreicht, ist request.result null.

Verwende openCursor(), wenn du große Datenmengen schrittweise verarbeiten, Einträge unterwegs ändern bzw. löschen (cursor.update(), cursor.delete() – nur in readwrite-Transaktionen) oder Paginierung und umgekehrte Sortierung umsetzen willst. Brauchst du nur die Schlüssel, ist openKeyCursor() sparsamer; brauchst du alle Werte auf einmal, ist getAll() einfacher.

Parameter

Name Typ Default Beschreibung
$query any | IDBKeyRange | null null (alle Einträge) Ein einzelner gültiger Index-Schlüssel oder ein IDBKeyRange, der den Bereich eingrenzt. null oder weggelassen bedeutet: alle Einträge.
$direction "next" | "nextunique" | "prev" | "prevunique" "next" Laufrichtung. "next" aufsteigend, "prev" absteigend. Die unique-Varianten liefern bei doppelten Index-Schlüsseln nur den ersten Datensatz (mit kleinstem Primärschlüssel) je Schlüssel.

Rückgabewert

Typ
IDBRequest<IDBCursorWithValue | null>
Beschreibung
Ein IDBRequest, dessen result bei jedem success-Ereignis der aktuelle IDBCursorWithValue ist oder null, wenn keine (weiteren) Einträge vorhanden sind.

Beispiele

Alle Kunden nach Nachname sortiert ausgeben

const openReq = indexedDB.open('shop', 1);

openReq.onupgradeneeded = () => {
  const db = openReq.result;
  const store = db.createObjectStore('kunden', { keyPath: 'id' });
  store.createIndex('nachname', 'nachname');
  store.add({ id: 1, vorname: 'Anna', nachname: 'Schulz' });
  store.add({ id: 2, vorname: 'Ben', nachname: 'Albers' });
  store.add({ id: 3, vorname: 'Clara', nachname: 'Meier' });
};

openReq.onsuccess = () => {
  const db = openReq.result;
  const index = db
    .transaction('kunden', 'readonly')
    .objectStore('kunden')
    .index('nachname');

  index.openCursor().onsuccess = (event) => {
    const cursor = event.target.result;
    if (!cursor) {
      console.log('Fertig');
      return;
    }
    console.log(`${cursor.key}: ${cursor.value.vorname} (id ${cursor.primaryKey})`);
    cursor.continue();
  };
};
Albers: Ben (id 2) Meier: Clara (id 3) Schulz: Anna (id 1) Fertig

Bereich einschränken und absteigend sortieren

// Annahme: db ist geöffnet, Store 'bestellungen' hat Index 'datum' (ISO-Strings)
const neuesteBestellungen = (db, ab) =>
  new Promise((resolve, reject) => {
    const index = db
      .transaction('bestellungen')
      .objectStore('bestellungen')
      .index('datum');
    const range = IDBKeyRange.lowerBound(ab);
    const ergebnis = [];

    const req = index.openCursor(range, 'prev');
    req.onsuccess = () => {
      const cursor = req.result;
      if (cursor && ergebnis.length < 10) {
        ergebnis.push(cursor.value);
        cursor.continue();
      } else {
        resolve(ergebnis);
      }
    };
    req.onerror = () => reject(req.error);
  });

const liste = await neuesteBestellungen(db, '2024-01-01');
console.log(liste.length);
bis zu 10 (neueste zuerst)

Einträge mit bestimmtem Index-Wert aktualisieren

// Alle Aufgaben mit status 'offen' als 'archiviert' markieren
const tx = db.transaction('aufgaben', 'readwrite');
const index = tx.objectStore('aufgaben').index('status');
let anzahl = 0;

index.openCursor('offen').onsuccess = (event) => {
  const cursor = event.target.result;
  if (!cursor) return;
  cursor.update({ ...cursor.value, status: 'archiviert' });
  anzahl++;
  cursor.continue();
};

tx.oncomplete = () => console.log(`${anzahl} Aufgaben archiviert`);
3 Aufgaben archiviert

Nur eindeutige Kategorien ermitteln

const kategorien = [];
const index = db.transaction('produkte').objectStore('produkte').index('kategorie');

index.openCursor(null, 'nextunique').onsuccess = (e) => {
  const cursor = e.target.result;
  if (cursor) {
    kategorien.push(cursor.key);
    cursor.continue();
  } else {
    console.log(kategorien);
  }
};
["Bücher", "Elektronik", "Garten"]

// Wichtig · Fallstricke

  • Transaktion läuft ab: Rufe cursor.continue() synchron im success-Handler auf. Ein await auf fetch() o. Ä. dazwischen lässt die Transaktion automatisch committen – der nächste Aufruf wirft dann TransactionInactiveError.
  • continue() darf pro success-Ereignis nur einmal aufgerufen werden, sonst folgt InvalidStateError.
  • Ein ungültiger Schlüssel oder Bereich als query (z. B. undefined im Bereich, Objekt, NaN) führt zu DataError; wurde der Index oder Store gelöscht, zu InvalidStateError.
  • cursor.update()/delete() erfordern eine readwrite-Transaktion, sonst ReadOnlyError.
  • Bei multiEntry-Indizes taucht ein Datensatz für jedes Array-Element einmal auf.
  • Für reine Schlüssel-Abfragen ist openKeyCursor() performanter, da value nicht deserialisiert wird.
  • Kompatibilität: in allen modernen Browsern und Web Workern verfügbar; im privaten Modus einiger älterer Browser (z. B. Firefox vor Version 115) war IndexedDB eingeschränkt.