blob: 1d5b57497ce6759176ef6caa8539fb20133cb58c [file]
import { createError, MISSING_DOC, REV_CONFLICT } from 'pouchdb-errors';
import updateDoc from './updateDoc';
import {
isDeleted,
isLocalId,
merge,
winningRev as calculateWinningRev
} from 'pouchdb-merge';
function rootIsMissing(docInfo) {
return docInfo.metadata.rev_tree[0].ids[1].status === 'missing';
}
function insertDoc(docInfo, resultsIdx, results, newEdits, wasDelete, writeDoc, callback) {
// Cant insert new deleted documents
var winningRev = calculateWinningRev(docInfo.metadata);
var deleted = isDeleted(docInfo.metadata, winningRev);
if (wasDelete && deleted) {
results[resultsIdx] = createError(MISSING_DOC, 'deleted');
return callback();
}
// 4712 - detect whether a new document was inserted with a _rev
var inConflict = newEdits && rootIsMissing(docInfo);
if (inConflict) {
var err = createError(REV_CONFLICT);
results[resultsIdx] = err;
return callback();
}
var delta = deleted ? 0 : 1;
writeDoc(docInfo, winningRev, deleted, deleted, false,
delta, resultsIdx, callback);
}
function doWriteDocs(fetchedDocs, thisId, docs, results,
revLimit, writeDoc, newEdits, wasDelete, checkAllDocsDone) {
var numDone = 0;
function docWritten() {
if (++numDone < docs.length) {
nextDoc();
} else {
checkAllDocsDone();
}
}
function nextDoc() {
var value = docs[numDone];
var currentDoc = value[0];
var resultsIdx = value[1];
if (fetchedDocs.has(thisId)) {
updateDoc(revLimit, fetchedDocs.get(thisId), currentDoc, results,
resultsIdx, docWritten, writeDoc, newEdits);
} else {
// Ensure stemming applies to new writes as well
var merged = merge([], currentDoc.metadata.rev_tree[0], revLimit);
currentDoc.metadata.rev_tree = merged.tree;
currentDoc.stemmedRevs = merged.stemmedRevs || [];
insertDoc(currentDoc, resultsIdx, results, newEdits, wasDelete, writeDoc, docWritten);
}
}
nextDoc();
}
function processDocs(revLimit, docInfos, api, fetchedDocs, tx, results,
writeDoc, opts, overallCallback) {
// Default to 1000 locally
revLimit = revLimit || 1000;
var newEdits = opts.new_edits;
var wasDelete = 'was_delete' in opts;
var idsToDocs = {};
var docsDone = 0;
var docsToDo = docInfos.length;
function checkAllDocsDone() {
if (++docsDone === docsToDo && overallCallback) {
overallCallback();
}
}
for (var i = 0, len = docInfos.length; i < len; i++) {
var currentDoc = docInfos[i];
if (currentDoc._id && isLocalId(currentDoc._id)) {
var fun = currentDoc._deleted ? '_removeLocal' : '_putLocal';
api[fun](currentDoc, {ctx: tx}, function (err, res) {
results[i] = err || res;
checkAllDocsDone();
});
continue;
}
var id = currentDoc.metadata.id;
var key = '$' + id;
var currentValue = idsToDocs[key];
if (currentValue) {
docsToDo--; // duplicate
currentValue.push([currentDoc, i]);
} else {
idsToDocs[key] = [[currentDoc, i]];
}
}
// in the case of new_edits, the user can provide multiple docs
// with the same id. these need to be processed sequentially
for (var thisKey in idsToDocs) {
if (idsToDocs.hasOwnProperty(thisKey)) {
var thisId = thisKey.substring(1); // remove leading '$'
var docs = idsToDocs[thisKey];
doWriteDocs(fetchedDocs, thisId, docs, results, revLimit, writeDoc,
newEdits, wasDelete, checkAllDocsDone);
}
}
}
export default processDocs;