IndexedDB - Cursors
IndexedDB - Iterating with Cursors
Section titled “IndexedDB - Iterating with Cursors”While methods like get() and getAll() are great for retrieving specific records or a full dataset, they can be inefficient if the dataset is very large, as getAll() loads every record into memory. Cursors provide a powerful, memory-efficient way to iterate through records in an object store or index one by one.
A cursor is like a pointer that moves through your records. You open a cursor, process the record at the cursor’s current position, and then tell it to advance to the next one.
Modern Alternatives: getAll() and getAllKeys()
Section titled “Modern Alternatives: getAll() and getAllKeys()”Before diving into cursors, always consider if a simpler method fits your needs. For moderately sized datasets, getAll() is often sufficient and leads to much simpler code.
// Get all objects from the storeconst allRecords = await store.getAll();
// Get all keys from the storeconst allKeys = await store.getAllKeys();Use cursors when you need to:
- Iterate over a very large number of records without high memory usage.
- Process, update, or delete records one by one based on a certain logic.
- Implement complex queries that cannot be achieved with
getAll()and a key range alone.
Using openCursor()
Section titled “Using openCursor()”You create a cursor using the openCursor() method on an object store or an index. The method is asynchronous and returns an IDBRequest object.
Syntax
Section titled “Syntax”objectStore.openCursor(range, direction);- range (optional): An
IDBKeyRangeto limit the iteration to specific keys. If null, it iterates over the entire store. - direction (optional): A string specifying the iteration direction. Can be
'next'(default),'prev','nextunique', or `‘prevunique’.
Example: Iterating Through Records
Section titled “Example: Iterating Through Records”Here is a complete HTML example showing how to use a cursor to read all records from an object store.
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>IndexedDB Cursors</title></head><body> <h1>Reading data with a cursor. Check the console.</h1> <ul id="user-list"></ul>
<script> const DB_NAME = "UserDB"; const STORE_NAME = "users"; const userList = document.getElementById('user-list');
// --- Database Setup (omitted for brevity, assume it's set up) --- async function setupAndPopulate() { return new Promise((resolve, reject) => { const request = indexedDB.open(DB_NAME, 1); request.onupgradeneeded = e => { const db = e.target.result; const store = db.createObjectStore(STORE_NAME, { keyPath: 'id' }); store.createIndex('by_name', 'name', { unique: false }); // Populate with sample data store.put({ id: 1, name: 'Alice', role: 'Admin' }); store.put({ id: 2, name: 'Bob', role: 'Editor' }); store.put({ id: 3, name: 'Charlie', role: 'Viewer' }); store.put({ id: 4, name: 'Diana', role: 'Editor' }); }; request.onsuccess = e => resolve(e.target.result); request.onerror = e => reject(e.target.error); }); }
async function iterateWithCursor(db) { return new Promise((resolve, reject) => { const transaction = db.transaction(STORE_NAME, 'readonly'); const store = transaction.objectStore(STORE_NAME); const request = store.openCursor();
request.onsuccess = event => { const cursor = event.target.result; if (cursor) { // We have a record const user = cursor.value; console.log(`Key: ${cursor.key}, Value:`, user);
// Display it in the UI const li = document.createElement('li'); li.textContent = `ID: ${user.id}, Name: ${user.name}, Role: ${user.role}`; userList.appendChild(li);
// Move to the next record cursor.continue(); } else { // No more records console.log('Cursor finished.'); resolve(); } };
request.onerror = event => { reject(event.target.error); }; }); }
async function main() { // Clear previous DB for a clean run indexedDB.deleteDatabase(DB_NAME).onsuccess = async () => { const db = await setupAndPopulate(); await iterateWithCursor(db); db.close(); } }
main(); </script></body></html>Advanced Cursor Operations
Section titled “Advanced Cursor Operations”- Updating Records: Inside a
'readwrite'transaction, you can callcursor.update(newValue)to modify the record at the cursor’s current position. - Deleting Records: Similarly, in a
'readwrite'transaction,cursor.delete()will remove the current record.
For example, to update all ‘Editor’ roles to ‘Contributor’:
const request = store.openCursor();request.onsuccess = event => { const cursor = event.target.result; if (cursor) { if (cursor.value.role === 'Editor') { const updatedUser = { ...cursor.value, role: 'Contributor' }; cursor.update(updatedUser); } cursor.continue(); }};