| 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; |