Creating and Querying Indexes
Why indexes?
Section titled “Why indexes?”Every IndexedDB object store is keyed by its primary key (the keyPath or an auto-incremented id). That is efficient for lookups by primary key, but if you want to query by a different field — for example, find all contacts by their name — you need an index.
An index is a secondary sorted structure that maps a chosen field’s value back to the record’s primary key. Once created, you can call get() or getAll() on the index exactly as you would on the store itself.
Creating an index with createIndex()
Section titled “Creating an index with createIndex()”Indexes must be created (or deleted) inside the onupgradeneeded callback. You cannot add an index to an existing store outside of an upgrade transaction.
const request = indexedDB.open('mydb', 1);
request.onupgradeneeded = (event) => { const db = event.target.result;
// Create the object store const store = db.createObjectStore('contacts', { keyPath: 'id' });
// Create an index on the 'name' field store.createIndex('by_name', 'name', { unique: false });};createIndex(name, keyPath, options) parameters
Section titled “createIndex(name, keyPath, options) parameters”| Parameter | Purpose |
|---|---|
name | The index name you will use to retrieve it later with store.index(name) |
keyPath | The property path on each record to index (e.g. 'name', 'address.city') |
unique | If true, the store rejects records whose indexed value duplicates an existing one |
multiEntry | If true and the indexed property is an array, each array element gets its own entry in the index |
Opening the index and querying
Section titled “Opening the index and querying”After the upgrade, open the index from a normal read/write transaction with store.index('by_name'), then call get(value) for a single record or getAll(value) for all matching records.
const tx = db.transaction('contacts', 'readonly');const store = tx.objectStore('contacts');const nameIndex = store.index('by_name');
// Single matchconst getReq = nameIndex.get('Alice');getReq.onsuccess = () => console.log(getReq.result); // { id: 1, name: 'Alice', ... }
// All records (no filter → returns every record sorted by index key)const getAllReq = nameIndex.getAll();getAllReq.onsuccess = () => console.log(getAllReq.result);Passing a value to getAll(value) filters to records whose indexed field equals that value. Passing no argument (or undefined) returns every record in the store, ordered by the index key.
The unique flag
Section titled “The unique flag”// Enforce unique emailsstore.createIndex('by_email', 'email', { unique: true });With unique: true, any add() or put() that would create a duplicate indexed value throws a ConstraintError. Use this for fields like email addresses or usernames where duplicates are not allowed.
The multiEntry flag
Section titled “The multiEntry flag”// Record: { id: 1, tags: ['js', 'idb', 'storage'] }store.createIndex('by_tag', 'tags', { multiEntry: true });With multiEntry: true, an array-valued field produces one index entry per element. You can then call index.getAll('idb') and the record above will be returned even though the value of tags is an array.