| import clone from '../deps/clone'; |
| import Promise from '../deps/promise'; |
| |
| function isGenOne(rev) { |
| return /^1-/.test(rev); |
| } |
| |
| function createBulkGetOpts(diffs) { |
| var requests = []; |
| Object.keys(diffs).forEach(function (id) { |
| var missingRevs = diffs[id].missing; |
| missingRevs.forEach(function (missingRev) { |
| requests.push({ |
| id: id, |
| rev: missingRev |
| }); |
| }); |
| }); |
| |
| return { |
| docs: requests, |
| revs: true, |
| attachments: true, |
| binary: true |
| }; |
| } |
| |
| // |
| // Fetch all the documents from the src as described in the "diffs", |
| // which is a mapping of docs IDs to revisions. If the state ever |
| // changes to "cancelled", then the returned promise will be rejected. |
| // Else it will be resolved with a list of fetched documents. |
| // |
| function getDocs(src, diffs, state) { |
| diffs = clone(diffs); // we do not need to modify this |
| |
| var resultDocs = []; |
| |
| function getAllDocs() { |
| |
| var bulkGetOpts = createBulkGetOpts(diffs); |
| |
| if (!bulkGetOpts.docs.length) { // optimization: skip empty requests |
| return; |
| } |
| |
| return src.bulkGet(bulkGetOpts).then(function (bulkGetResponse) { |
| /* istanbul ignore if */ |
| if (state.cancelled) { |
| throw new Error('cancelled'); |
| } |
| bulkGetResponse.results.forEach(function (bulkGetInfo) { |
| bulkGetInfo.docs.forEach(function (doc) { |
| if (doc.ok) { |
| resultDocs.push(doc.ok); |
| } |
| }); |
| }); |
| }); |
| } |
| |
| function hasAttachments(doc) { |
| return doc._attachments && Object.keys(doc._attachments).length > 0; |
| } |
| |
| function fetchRevisionOneDocs(ids) { |
| // Optimization: fetch gen-1 docs and attachments in |
| // a single request using _all_docs |
| return src.allDocs({ |
| keys: ids, |
| include_docs: true |
| }).then(function (res) { |
| if (state.cancelled) { |
| throw new Error('cancelled'); |
| } |
| res.rows.forEach(function (row) { |
| if (row.deleted || !row.doc || !isGenOne(row.value.rev) || |
| hasAttachments(row.doc)) { |
| // if any of these conditions apply, we need to fetch using get() |
| return; |
| } |
| |
| // the doc we got back from allDocs() is sufficient |
| resultDocs.push(row.doc); |
| delete diffs[row.id]; |
| }); |
| }); |
| } |
| |
| function getRevisionOneDocs() { |
| // filter out the generation 1 docs and get them |
| // leaving the non-generation one docs to be got otherwise |
| var ids = Object.keys(diffs).filter(function (id) { |
| var missing = diffs[id].missing; |
| return missing.length === 1 && isGenOne(missing[0]); |
| }); |
| if (ids.length > 0) { |
| return fetchRevisionOneDocs(ids); |
| } |
| } |
| |
| function returnDocs() { |
| return resultDocs; |
| } |
| |
| return Promise.resolve() |
| .then(getRevisionOneDocs) |
| .then(getAllDocs) |
| .then(returnDocs); |
| } |
| |
| export default getDocs; |