| '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(); |
| } |
| }; |
| |
| } |