Start · Sprachen · JavaScript · Referenz · IDBCursor

IDBCursor

Schnittstelle

Repräsentiert einen Cursor, mit dem man in IndexedDB schrittweise über Datensätze eines Object Stores oder Index iteriert.

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

Signatur

interface IDBCursor

Beschreibung

IDBCursor ist das Werkzeug der IndexedDB API, um mehrere Datensätze nacheinander zu durchlaufen, ohne alle auf einmal in den Speicher zu laden. Einen Cursor erhält man nicht per Konstruktor, sondern über IDBObjectStore.openCursor(), IDBObjectStore.openKeyCursor(), IDBIndex.openCursor() oder IDBIndex.openKeyCursor(). Das zugehörige IDBRequest feuert für jeden Datensatz ein success-Event; in request.result steht dann der Cursor – oder null, wenn das Ende erreicht ist.

Wichtige Eigenschaften (alle schreibgeschützt):

  • source – der IDBObjectStore oder IDBIndex, über den iteriert wird.
  • direction – Laufrichtung: "next", "nextunique", "prev" oder "prevunique".
  • key – der Schlüssel des aktuellen Datensatzes (bei Index-Cursors der Index-Schlüssel).
  • primaryKey – der Primärschlüssel des aktuellen Datensatzes.
  • request – das IDBRequest, mit dem der Cursor geöffnet wurde.

Wichtige Methoden:

  • continue(key?) – springt zum nächsten Datensatz bzw. zum ersten mit Schlüssel ≥ key (bei "prev": ≤).
  • continuePrimaryKey(key, primaryKey) – springt bei Index-Cursors zu einer bestimmten Kombination aus Index- und Primärschlüssel (ideal für Paginierung).
  • advance(count) – überspringt count Datensätze.
  • update(value) – ersetzt den aktuellen Datensatz (nur in readwrite-Transaktionen); liefert ein IDBRequest.
  • delete() – löscht den aktuellen Datensatz (nur readwrite); liefert ein IDBRequest.

Der Wert des Datensatzes selbst steht nur in der Unterklasse IDBCursorWithValue unter value zur Verfügung – diese liefert openCursor(). openKeyCursor() liefert dagegen ein reines IDBCursor ohne Wert, was bei reinen Schlüssel-Abfragen schneller und speichersparender ist.

Parameter

Name Typ Default Beschreibung
$key any (gültiger IndexedDB-Schlüssel) Für continue(key): Zielschlüssel, zu dem der Cursor springen soll. Ohne Angabe geht es zum nächsten Datensatz.
$primaryKey any (gültiger IndexedDB-Schlüssel) Für continuePrimaryKey(key, primaryKey): Primärschlüssel, ab dem innerhalb gleicher Index-Schlüssel weitergelesen wird. Nur bei Index-Cursors mit Richtung next/prev.
$count number Für advance(count): positive Ganzzahl – Anzahl der zu überspringenden Datensätze. 0 wirft einen TypeError.
$value any Für update(value): neuer Wert des aktuellen Datensatzes. Bei In-line-Keys darf der Schlüssel nicht verändert werden.

Rückgabewert

Typ
IDBCursor | IDBCursorWithValue | null
Beschreibung
Kein direkter Rückgabewert eines Konstruktors: Der Cursor steht in request.result des success-Events von openCursor()/openKeyCursor(), am Ende der Iteration ist er null. continue(), advance() und continuePrimaryKey() geben undefined zurück; update() und delete() liefern ein IDBRequest.

Beispiele

Alle Datensätze eines Object Stores durchlaufen

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

openReq.onupgradeneeded = () => {
  const db = openReq.result;
  const store = db.createObjectStore('produkte', { keyPath: 'id' });
  store.add({ id: 1, name: 'Tastatur', preis: 49 });
  store.add({ id: 2, name: 'Maus', preis: 25 });
};

openReq.onsuccess = () => {
  const db = openReq.result;
  const store = db.transaction('produkte', 'readonly').objectStore('produkte');
  const cursorReq = store.openCursor();

  cursorReq.onsuccess = () => {
    const cursor = cursorReq.result;
    if (cursor) {
      console.log(cursor.key, cursor.value.name);
      cursor.continue(); // nächster Datensatz -> erneutes success-Event
    } else {
      console.log('Fertig');
    }
  };
};
1 Tastatur 2 Maus Fertig

Datensätze per Cursor ändern und löschen

const preiseAnpassen = (db) => {
  const tx = db.transaction('produkte', 'readwrite');
  const req = tx.objectStore('produkte').openCursor();

  req.onsuccess = () => {
    const cursor = req.result;
    if (!cursor) return;

    const produkt = cursor.value;
    if (produkt.preis < 30) {
      cursor.delete(); // Billigartikel entfernen
    } else {
      cursor.update({ ...produkt, preis: produkt.preis * 1.1 });
    }
    cursor.continue();
  };

  tx.oncomplete = () => console.log('Preise aktualisiert');
};
Preise aktualisiert

Rückwärts über einen Index mit Schlüsselbereich (nur Schlüssel)

// Voraussetzung: Index 'nachPreis' auf keyPath 'preis'
const teuersteProdukte = (db) => new Promise((resolve, reject) => {
  const index = db.transaction('produkte').objectStore('produkte').index('nachPreis');
  const range = IDBKeyRange.lowerBound(40);
  const req = index.openKeyCursor(range, 'prev');
  const ids = [];

  req.onsuccess = () => {
    const cursor = req.result;
    if (cursor && ids.length < 3) {
      ids.push(cursor.primaryKey); // key = Preis, primaryKey = id
      cursor.continue();
    } else {
      resolve(ids);
    }
  };
  req.onerror = () => reject(req.error);
});

// const ids = await teuersteProdukte(db);
[1]

Paginierung mit advance()

const seiteLaden = (db, seite, proSeite = 10) => new Promise((resolve) => {
  const req = db.transaction('produkte').objectStore('produkte').openCursor();
  const ergebnis = [];
  let übersprungen = seite === 0;

  req.onsuccess = () => {
    const cursor = req.result;
    if (!cursor) return resolve(ergebnis);
    if (!übersprungen) {
      übersprungen = true;
      cursor.advance(seite * proSeite); // Anfang der Seite ansteuern
      return;
    }
    ergebnis.push(cursor.value);
    if (ergebnis.length < proSeite) cursor.continue();
    else resolve(ergebnis);
  };
});