| // Abstraction over IDBCursor and getAll()/getAllKeys() that allows us to batch our operations |
| // while falling back to a normal IDBCursor operation on browsers that don't support getAll() or |
| // getAllKeys(). This allows for a much faster implementation than just straight-up cursors, because |
| // we're not processing each document one-at-a-time. |
| function runBatchedCursor(objectStore, keyRange, descending, batchSize, onBatch) { |
| |
| // Bail out of getAll()/getAllKeys() in the following cases: |
| // 1) either method is unsupported - we need both |
| // 2) batchSize is 1 (might as well use IDBCursor), or batchSize is -1 (i.e. batchSize unlimited, |
| // not really clear the user wants a batched approach where the entire DB is read into memory, |
| // perhaps they are filtering on a per-doc basis) |
| // 3) descending – no real way to do this via getAll()/getAllKeys() |
| |
| var useGetAll = typeof objectStore.getAll === 'function' && |
| typeof objectStore.getAllKeys === 'function' && |
| batchSize > 1 && !descending; |
| |
| var keysBatch; |
| var valuesBatch; |
| var pseudoCursor; |
| |
| function onGetAll(e) { |
| valuesBatch = e.target.result; |
| if (keysBatch) { |
| onBatch(keysBatch, valuesBatch, pseudoCursor); |
| } |
| } |
| |
| function onGetAllKeys(e) { |
| keysBatch = e.target.result; |
| if (valuesBatch) { |
| onBatch(keysBatch, valuesBatch, pseudoCursor); |
| } |
| } |
| |
| function continuePseudoCursor() { |
| if (!keysBatch.length) { // no more results |
| return onBatch(); |
| } |
| // fetch next batch, exclusive start |
| var lastKey = keysBatch[keysBatch.length - 1]; |
| var newKeyRange; |
| if (keyRange && keyRange.upper) { |
| try { |
| newKeyRange = IDBKeyRange.bound(lastKey, keyRange.upper, |
| true, keyRange.upperOpen); |
| } catch (e) { |
| if (e.name === "DataError" && e.code === 0) { |
| return onBatch(); // we're done, startkey and endkey are equal |
| } |
| } |
| } else { |
| newKeyRange = IDBKeyRange.lowerBound(lastKey, true); |
| } |
| keyRange = newKeyRange; |
| keysBatch = null; |
| valuesBatch = null; |
| objectStore.getAll(keyRange, batchSize).onsuccess = onGetAll; |
| objectStore.getAllKeys(keyRange, batchSize).onsuccess = onGetAllKeys; |
| } |
| |
| function onCursor(e) { |
| var cursor = e.target.result; |
| if (!cursor) { // done |
| return onBatch(); |
| } |
| // regular IDBCursor acts like a batch where batch size is always 1 |
| onBatch([cursor.key], [cursor.value], cursor); |
| } |
| |
| if (useGetAll) { |
| pseudoCursor = {"continue": continuePseudoCursor}; |
| objectStore.getAll(keyRange, batchSize).onsuccess = onGetAll; |
| objectStore.getAllKeys(keyRange, batchSize).onsuccess = onGetAllKeys; |
| } else if (descending) { |
| objectStore.openCursor(keyRange, 'prev').onsuccess = onCursor; |
| } else { |
| objectStore.openCursor(keyRange).onsuccess = onCursor; |
| } |
| } |
| |
| export default runBatchedCursor; |