| // 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, startKey, 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 |
| range = IDBKeyRange.lowerBound(keysBatch[keysBatch.length - 1], true); |
| keysBatch = null; |
| valuesBatch = null; |
| objectStore.getAll(range, batchSize).onsuccess = onGetAll; |
| objectStore.getAllKeys(range, 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); |
| } |
| |
| var range = (startKey && !descending) ? IDBKeyRange.lowerBound(startKey, true) : null; |
| |
| if (useGetAll) { |
| pseudoCursor = {"continue": continuePseudoCursor}; |
| objectStore.getAll(range, batchSize).onsuccess = onGetAll; |
| objectStore.getAllKeys(range, batchSize).onsuccess = onGetAllKeys; |
| } else if (descending) { |
| objectStore.openCursor(range, 'prev').onsuccess = onCursor; |
| } else { |
| objectStore.openCursor(range).onsuccess = onCursor; |
| } |
| } |
| |
| export default runBatchedCursor; |