blob: 70683ea1e726a4014f21b427091e17fa59d86217 [file] [log] [blame]
'use strict';
import { createError, IDB_ERROR } from 'pouchdb-errors';
import { collectConflicts } from 'pouchdb-merge';
import { DOC_STORE, processAttachment } from './util';
function allDocsKeys(keys, docStore, allDocsInner) {
// It's not guaranted to be returned in right order
var valuesBatch = new Array(keys.length);
var count = 0;
keys.forEach(function (key, index) {
docStore.get(key).onsuccess = function (event) {
if (event.target.result) {
valuesBatch[index] = event.target.result;
} else {
valuesBatch[index] = {key: key, error: 'not_found'};
}
count++;
if (count === keys.length) {
valuesBatch.forEach(function (doc) {
allDocsInner(doc);
});
}
};
});
}
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(opts, metadata, err, callback) {
if (err.name === "DataError" && err.code === 0) {
// data error, start is less than end
var returnVal = {
total_rows: metadata.doc_count,
offset: opts.skip,
rows: []
};
/* istanbul ignore if */
if (opts.update_seq) {
returnVal.update_seq = metadata.seq;
}
return callback(null, returnVal);
}
callback(createError(IDB_ERROR, err.name, err.message));
}
export default function (idb, metadata, opts, callback) {
// TODO: Weird hack, I dont like it
if (opts.limit === 0) {
var returnVal = {
total_rows: metadata.doc_count,
offset: opts.skip,
rows: []
};
/* istanbul ignore if */
if (opts.update_seq) {
returnVal.update_seq = metadata.seq;
}
return callback(null, returnVal);
}
var results = [];
var processing = [];
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 keys = 'keys' in opts ? opts.keys : 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;
if (!keys) {
keyRange = createKeyRange(start, end, inclusiveEnd, key, descending);
if (keyRange && keyRange.error) {
return handleKeyRangeError(opts, metadata, keyRange.error, callback);
}
}
var txn = idb.transaction([DOC_STORE], 'readonly');
var docStore = txn.objectStore(DOC_STORE);
txn.oncomplete = onTxnComplete;
if (keys) {
return allDocsKeys(opts.keys, docStore, allDocsInner);
}
function include_doc(row, doc) {
row.doc = doc.data;
row.doc._id = doc.id;
row.doc._rev = doc.rev;
if (opts.conflicts) {
var conflicts = collectConflicts(doc);
if (conflicts.length) {
row.doc._conflicts = conflicts;
}
}
if (opts.attachments && doc.data._attachments) {
for (var name in doc.data._attachments) {
processing.push(processAttachment(name, doc, row.doc, opts.binary));
}
}
}
function allDocsInner(doc) {
if (doc.error && keys) {
// key was not found with "keys" requests
results.push(doc);
return true;
}
var row = {
id: doc.id,
key: doc.id,
value: {
rev: doc.rev
}
};
var deleted = doc.deleted;
if (deleted) {
if (keys) {
results.push(row);
row.value.deleted = true;
row.doc = null;
}
} else if (skip-- <= 0) {
results.push(row);
if (opts.include_docs) {
include_doc(row, doc);
}
if (--limit === 0) {
return false;
}
}
return true;
}
function onTxnComplete() {
Promise.all(processing).then(function () {
var returnVal = {
total_rows: metadata.doc_count,
offset: 0,
rows: results
};
/* istanbul ignore if */
if (opts.update_seq) {
returnVal.update_seq = metadata.seq;
}
callback(null, returnVal);
});
}
var cursor = descending ?
docStore.openCursor(keyRange, descending) :
docStore.openCursor(keyRange);
cursor.onsuccess = function (e) {
var doc = e.target.result && e.target.result.value;
// Happens if opts does not have limit,
// because cursor will end normally then,
// when all docs are retrieved.
// Would not be needed, if getAll() optimization was used like in #6059
if (!doc) { return; }
// Skip local docs
if (/^_local/.test(doc.id)) {
return e.target.result.continue();
}
var continueCursor = allDocsInner(doc);
if (continueCursor) {
e.target.result.continue();
}
};
}