| import { createError, IDB_ERROR } from 'pouchdb-errors'; |
| import { collectConflicts } from 'pouchdb-merge'; |
| |
| import { |
| ATTACH_STORE, |
| BY_SEQ_STORE, |
| DOC_STORE |
| } from './constants'; |
| |
| import { |
| decodeDoc, |
| decodeMetadata, |
| fetchAttachmentsIfNecessary, |
| postProcessAttachments, |
| openTransactionSafely |
| } from './utils'; |
| |
| function createKeyRange(start, end, inclusiveEnd, key, descending) { |
| try { |
| if (start && end) { |
| if (descending) { |
| return IDBKeyRange.bound(end, start, !inclusiveEnd, false); |
| } else { |
| return IDBKeyRange.bound(start, end, false, !inclusiveEnd); |
| } |
| } else if (start) { |
| if (descending) { |
| return IDBKeyRange.upperBound(start); |
| } else { |
| return IDBKeyRange.lowerBound(start); |
| } |
| } else if (end) { |
| if (descending) { |
| return IDBKeyRange.lowerBound(end, !inclusiveEnd); |
| } else { |
| return IDBKeyRange.upperBound(end, !inclusiveEnd); |
| } |
| } else if (key) { |
| return IDBKeyRange.only(key); |
| } |
| } catch (e) { |
| return {error: e}; |
| } |
| return null; |
| } |
| |
| function handleKeyRangeError(api, opts, err, callback) { |
| if (err.name === "DataError" && err.code === 0) { |
| // data error, start is less than end |
| return callback(null, { |
| total_rows: api._meta.docCount, |
| offset: opts.skip, |
| rows: [] |
| }); |
| } |
| callback(createError(IDB_ERROR, err.name, err.message)); |
| } |
| |
| function idbAllDocs(opts, api, idb, callback) { |
| |
| function allDocsQuery(opts, callback) { |
| var start = 'startkey' in opts ? opts.startkey : false; |
| var end = 'endkey' in opts ? opts.endkey : false; |
| var key = 'key' in opts ? opts.key : false; |
| var skip = opts.skip || 0; |
| var limit = typeof opts.limit === 'number' ? opts.limit : -1; |
| var inclusiveEnd = opts.inclusive_end !== false; |
| var descending = 'descending' in opts && opts.descending ? 'prev' : null; |
| |
| var keyRange = createKeyRange(start, end, inclusiveEnd, key, descending); |
| if (keyRange && keyRange.error) { |
| return handleKeyRangeError(api, opts, keyRange.error, callback); |
| } |
| |
| var stores = [DOC_STORE, BY_SEQ_STORE]; |
| |
| if (opts.attachments) { |
| stores.push(ATTACH_STORE); |
| } |
| var txnResult = openTransactionSafely(idb, stores, 'readonly'); |
| if (txnResult.error) { |
| return callback(txnResult.error); |
| } |
| var txn = txnResult.txn; |
| var docStore = txn.objectStore(DOC_STORE); |
| var seqStore = txn.objectStore(BY_SEQ_STORE); |
| var cursor = descending ? |
| docStore.openCursor(keyRange, descending) : |
| docStore.openCursor(keyRange); |
| var docIdRevIndex = seqStore.index('_doc_id_rev'); |
| var results = []; |
| var docCount = 0; |
| |
| // if the user specifies include_docs=true, then we don't |
| // want to block the main cursor while we're fetching the doc |
| function fetchDocAsynchronously(metadata, row, winningRev) { |
| var key = metadata.id + "::" + winningRev; |
| docIdRevIndex.get(key).onsuccess = function onGetDoc(e) { |
| row.doc = decodeDoc(e.target.result); |
| if (opts.conflicts) { |
| var conflicts = collectConflicts(metadata); |
| if (conflicts.length) { |
| row.doc._conflicts = conflicts; |
| } |
| } |
| fetchAttachmentsIfNecessary(row.doc, opts, txn); |
| }; |
| } |
| |
| function allDocsInner(cursor, winningRev, metadata) { |
| var row = { |
| id: metadata.id, |
| key: metadata.id, |
| value: { |
| rev: winningRev |
| } |
| }; |
| var deleted = metadata.deleted; |
| if (opts.deleted === 'ok') { |
| results.push(row); |
| // deleted docs are okay with "keys" requests |
| if (deleted) { |
| row.value.deleted = true; |
| row.doc = null; |
| } else if (opts.include_docs) { |
| fetchDocAsynchronously(metadata, row, winningRev); |
| } |
| } else if (!deleted && skip-- <= 0) { |
| results.push(row); |
| if (opts.include_docs) { |
| fetchDocAsynchronously(metadata, row, winningRev); |
| } |
| if (--limit === 0) { |
| return; |
| } |
| } |
| cursor.continue(); |
| } |
| |
| function onGetCursor(e) { |
| docCount = api._meta.docCount; // do this within the txn for consistency |
| var cursor = e.target.result; |
| if (!cursor) { |
| return; |
| } |
| var metadata = decodeMetadata(cursor.value); |
| var winningRev = metadata.winningRev; |
| |
| allDocsInner(cursor, winningRev, metadata); |
| } |
| |
| function onResultsReady() { |
| callback(null, { |
| total_rows: docCount, |
| offset: opts.skip, |
| rows: results |
| }); |
| } |
| |
| function onTxnComplete() { |
| if (opts.attachments) { |
| postProcessAttachments(results, opts.binary).then(onResultsReady); |
| } else { |
| onResultsReady(); |
| } |
| } |
| |
| txn.oncomplete = onTxnComplete; |
| cursor.onsuccess = onGetCursor; |
| } |
| |
| function allDocs(opts, callback) { |
| |
| if (opts.limit === 0) { |
| return callback(null, { |
| total_rows: api._meta.docCount, |
| offset: opts.skip, |
| rows: [] |
| }); |
| } |
| allDocsQuery(opts, callback); |
| } |
| |
| allDocs(opts, callback); |
| } |
| |
| export default idbAllDocs; |