blob: 8843c980ca8faf365c4163644fb1731e3ee9c7cf [file] [log] [blame]
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;