Signatur
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
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ü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);
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`);
// Wichtig · Fallstricke
- Kein
value: Der zurückgegebene Cursor ist einIDBCursor, keinIDBCursorWithValue. Deshalb gibt es keincursor.value, undcursor.update()sowiecursor.delete()stehen nicht zur Verfügung. Zum Ändern oder Löschen nimmst dustore.put()/store.delete(cursor.primaryKey)oderopenCursor(). - Transaktions-Lebensdauer: Die Transaktion wird automatisch abgeschlossen, sobald keine Anfragen mehr offen sind. Wenn du zwischen zwei
continue()-Aufrufenawaitauf fremde Promises (z. B.fetch) verwendest, ist die Transaktion danach inaktiv, und es wird einTransactionInactiveErrorausgelöst. - Ausnahmen:
InvalidStateError, wenn der Store gelöscht wurde.TransactionInactiveError, wenn die Transaktion nicht mehr aktiv ist.DataErrorbei einem ungültigen Schlüssel oder Bereich. Bei einem ungültigendirection-Wert wird einTypeErrorausgelö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.