blob: 3f97cd0f4bc97d2a86e8ca53069cda5d72a4ffb2 [file] [log] [blame]
import pick from './pick';
// Most browsers throttle concurrent requests at 6, so it's silly
// to shim _bulk_get by trying to launch potentially hundreds of requests
// and then letting the majority time out. We can handle this ourselves.
var MAX_NUM_CONCURRENT_REQUESTS = 6;
function identityFunction(x) {
return x;
}
function formatResultForOpenRevsGet(result) {
return [{
ok: result
}];
}
// shim for P/CouchDB adapters that don't directly implement _bulk_get
function bulkGet(db, opts, callback) {
var requests = opts.docs;
// consolidate into one request per doc if possible
var requestsById = new Map();
requests.forEach(function (request) {
if (requestsById.has(request.id)) {
requestsById.get(request.id).push(request);
} else {
requestsById.set(request.id, [request]);
}
});
var numDocs = requestsById.size;
var numDone = 0;
var perDocResults = new Array(numDocs);
function collapseResultsAndFinish() {
var results = [];
perDocResults.forEach(function (res) {
res.docs.forEach(function (info) {
results.push({
id: res.id,
docs: [info]
});
});
});
callback(null, {results: results});
}
function checkDone() {
if (++numDone === numDocs) {
collapseResultsAndFinish();
}
}
function gotResult(docIndex, id, docs) {
perDocResults[docIndex] = {id: id, docs: docs};
checkDone();
}
var allRequests = [];
requestsById.forEach(function (value, key) {
allRequests.push(key);
});
var i = 0;
function nextBatch() {
if (i >= allRequests.length) {
return;
}
var upTo = Math.min(i + MAX_NUM_CONCURRENT_REQUESTS, allRequests.length);
var batch = allRequests.slice(i, upTo);
processBatch(batch, i);
i += batch.length;
}
function processBatch(batch, offset) {
batch.forEach(function (docId, j) {
var docIdx = offset + j;
var docRequests = requestsById.get(docId);
// just use the first request as the "template"
// TODO: The _bulk_get API allows for more subtle use cases than this,
// but for now it is unlikely that there will be a mix of different
// "atts_since" or "attachments" in the same request, since it's just
// replicate.js that is using this for the moment.
// Also, atts_since is aspirational, since we don't support it yet.
var docOpts = pick(docRequests[0], ['atts_since', 'attachments']);
docOpts.open_revs = docRequests.map(function (request) {
// rev is optional, open_revs disallowed
return request.rev;
});
// remove falsey / undefined revisions
docOpts.open_revs = docOpts.open_revs.filter(identityFunction);
var formatResult = identityFunction;
if (docOpts.open_revs.length === 0) {
delete docOpts.open_revs;
// when fetching only the "winning" leaf,
// transform the result so it looks like an open_revs
// request
formatResult = formatResultForOpenRevsGet;
}
// globally-supplied options
['revs', 'attachments', 'binary', 'ajax', 'latest'].forEach(function (param) {
if (param in opts) {
docOpts[param] = opts[param];
}
});
db.get(docId, docOpts, function (err, res) {
var result;
/* istanbul ignore if */
if (err) {
result = [{error: err}];
} else {
result = formatResult(res);
}
gotResult(docIdx, docId, result);
nextBatch();
});
});
}
nextBatch();
}
export default bulkGet;