Start · Sprachen · JavaScript · Referenz · IDBObjectStore.openKeyCursor()

IDBObjectStore.openKeyCursor()

Methode

Öffnet einen reinen Schlüssel-Cursor (<code>IDBCursor</code>) über einen Object Store, der nur Primärschlüssel liefert, ohne die gespeicherten Werte zu laden.

seit JavaScript Baseline: alle modernen Browser (seit 2020) Kategorie: webapi

Signatur

IDBObjectStore.prototype.openKeyCursor(query, direction)

Beschreibung

openKeyCursor() gibt ein IDBRequest zurück, dessen result nach jedem success-Ereignis auf einen IDBCursor (bzw. null am Ende) gesetzt wird. Anders als bei openCursor() ist der Cursor ein reiner Schlüssel-Cursor: Er besitzt die Eigenschaften key und primaryKey, aber keine value-Eigenschaft.

Das ist nützlich, wenn man nur Schlüssel braucht, etwa um IDs aufzulisten, Datensätze zu zählen oder nach Schlüsselbereichen zu filtern. Der Browser muss die (oft großen) Werte nicht deserialisieren, was Speicher und Zeit spart.

Mit einem optionalen IDBKeyRange (oder einem einzelnen Schlüssel) grenzt man die Ergebnismenge ein, mit direction legt man die Reihenfolge fest. Weiter geht es mit cursor.continue(), cursor.continuePrimaryKey() oder cursor.advance(n). Jeder Schritt löst erneut success am selben Request aus.

Wenn du nur alle Schlüssel auf einmal brauchst, reicht oft getAllKeys(). openKeyCursor() lohnt sich, wenn du schrittweise vorgehen, früh abbrechen oder sehr große Datenmengen ohne großes Array verarbeiten willst.

Parameter

Name Typ Default Beschreibung
$query IDBKeyRange | IDBValidKey | null null Ein Schlüssel oder IDBKeyRange, der die durchlaufenen Datensätze begrenzt. Ohne Angabe oder mit null werden alle Datensätze durchlaufen.
$direction "next" | "nextunique" | "prev" | "prevunique" "next" Die Reihenfolge: "next" aufsteigend, "prev" absteigend. Die unique-Varianten verhalten sich bei Object Stores genauso, weil Primärschlüssel ohnehin eindeutig sind.

Rückgabewert

Typ
IDBRequest<IDBCursor | null>
Beschreibung
Ein IDBRequest. Sein result ist bei jedem success-Ereignis der aktuelle IDBCursor bzw. null, wenn es keine weiteren Datensätze gibt.

Beispiele

Alle Primärschlüssel eines Stores auflisten

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

openReq.onupgradeneeded = () => {
  const store = openReq.result.createObjectStore('produkte', { keyPath: 'id' });
  store.put({ id: 3, name: 'Tastatur' });
  store.put({ id: 1, name: 'Maus' });
  store.put({ id: 2, name: 'Monitor' });
};

openReq.onsuccess = () => {
  const db = openReq.result;
  const store = db.transaction('produkte', 'readonly').objectStore('produkte');
  const keys = [];

  store.openKeyCursor().onsuccess = (event) => {
    const cursor = event.target.result;
    if (cursor) {
      keys.push(cursor.key); // kein cursor.value vorhanden!
      cursor.continue();
    } else {
      console.log('Schlüssel:', keys);
    }
  };
};
Schlüssel: [1, 2, 3]

Schlüsselbereich rückwärts durchlaufen

const listNewestIds = (db, minId) => new Promise((resolve, reject) => {
  const store = db.transaction('produkte').objectStore('produkte');
  const range = IDBKeyRange.lowerBound(minId);
  const req = store.openKeyCursor(range, 'prev');
  const ids = [];

  req.onsuccess = () => {
    const cursor = req.result;
    if (!cursor) return resolve(ids);
    ids.push(cursor.primaryKey);
    cursor.continue();
  };
  req.onerror = () => reject(req.error);
});

// Aufruf, z. B. innerhalb von openReq.onsuccess:
// const ids = await listNewestIds(db, 2);
// console.log(ids);
[3, 2]

Datensätze per Schlüssel-Cursor löschen, ohne Werte zu laden

const deleteOlderThan = (db, timestamp) => new Promise((resolve, reject) => {
  // Store 'logs' mit Zeitstempel als Primärschlüssel
  const tx = db.transaction('logs', 'readwrite');
  const store = tx.objectStore('logs');
  let count = 0;

  store.openKeyCursor(IDBKeyRange.upperBound(timestamp, true)).onsuccess = (e) => {
    const cursor = e.target.result;
    if (cursor) {
      store.delete(cursor.primaryKey); // Schlüssel-Cursor hat kein cursor.delete()
      count++;
      cursor.continue();
    }
  };

  tx.oncomplete = () => resolve(count);
  tx.onerror = () => reject(tx.error);
});

// const geloescht = await deleteOlderThan(db, Date.now() - 7 * 864e5);
// console.log(`${geloescht} Einträge gelöscht`);
z. B. "42 Einträge gelöscht"

// Wichtig · Fallstricke

  • Kein value: Der zurückgegebene Cursor ist ein IDBCursor, kein IDBCursorWithValue. Deshalb gibt es kein cursor.value, und cursor.update() sowie cursor.delete() stehen nicht zur Verfügung. Zum Ändern oder Löschen nimmst du store.put()/store.delete(cursor.primaryKey) oder openCursor().
  • Transaktions-Lebensdauer: Die Transaktion wird automatisch abgeschlossen, sobald keine Anfragen mehr offen sind. Wenn du zwischen zwei continue()-Aufrufen await auf fremde Promises (z. B. fetch) verwendest, ist die Transaktion danach inaktiv, und es wird ein TransactionInactiveError ausgelöst.
  • Ausnahmen: InvalidStateError, wenn der Store gelöscht wurde. TransactionInactiveError, wenn die Transaktion nicht mehr aktiv ist. DataError bei einem ungültigen Schlüssel oder Bereich. Bei einem ungültigen direction-Wert wird ein TypeError ausgelöst.
  • Kompatibilität: IDBIndex.openKeyCursor() gab es schon lange. IDBObjectStore.openKeyCursor() kam später hinzu (Safari erst ab Version 10.1) und ist heute in allen aktuellen Browsern und Web Workern verfügbar.
  • Tipp zur Leistung: Für kleine bis mittlere Datenmengen ist getAllKeys(query, count) meist einfacher und schneller, weil es nur eine einzige Anfrage braucht.