| 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, results, resultsIdx, 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 mergeDoc(currentDoc, results, resultsIdx, revLimit, newEdits, wasDelete, writeDoc, docWritten) { |
| // 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, results, resultsIdx, newEdits, wasDelete, writeDoc, docWritten); |
| } |
| |
| function mergeOrUpdateDoc(id, fetchedDocs, currentDoc, results, resultsIdx, revLimit, newEdits, |
| wasDelete, writeDoc, docWritten) { |
| if (fetchedDocs.has(id)) { |
| updateDoc(revLimit, fetchedDocs.get(id), currentDoc, results, |
| resultsIdx, docWritten, writeDoc, newEdits); |
| } else { |
| mergeDoc(currentDoc, results, resultsIdx, revLimit, newEdits, wasDelete, writeDoc, docWritten); |
| } |
| } |
| |
| function processsMultipleDocsWithSameId(id, docs, fetchedDocs, results, revLimit, newEdits, |
| wasDelete, writeDoc, 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]; |
| |
| mergeOrUpdateDoc(id, fetchedDocs, currentDoc, results, resultsIdx, revLimit, newEdits, |
| wasDelete, writeDoc, docWritten); |
| } |
| nextDoc(); |
| } |
| |
| function afterLocalPutOrRemove(i, results, checkAllDocsDone) { |
| return function (err, res) { |
| results[i] = err || res; |
| checkAllDocsDone(); |
| }; |
| } |
| |
| function processDocs(revLimit, docInfos, api, fetchedDocs, tx, results, |
| writeDoc, opts, callback) { |
| |
| // 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 && callback) { |
| callback(); |
| } |
| } |
| |
| for (var i = 0, len = docInfos.length; i < len ; i++) { |
| var currentDocInfo = docInfos[i]; |
| if (currentDocInfo._id && isLocalId(currentDocInfo._id)) { |
| var fun = currentDocInfo._deleted ? '_removeLocal' : '_putLocal'; |
| api[fun](currentDocInfo, {ctx: tx}, afterLocalPutOrRemove(i, results, checkAllDocsDone)); |
| } else { |
| var currentDocId = currentDocInfo.metadata.id; |
| var currentKey = '$' + currentDocId; |
| var currentValue = idsToDocs[currentKey]; |
| if (currentValue) { |
| docsToDo--; // duplicate |
| currentValue.push([currentDocInfo, i]); |
| } else { |
| idsToDocs[currentKey] = [[currentDocInfo, 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 id = thisKey.substring(1); // cut off initial '$' |
| var docs = idsToDocs[thisKey]; |
| if (docs.length === 1) { // fast case |
| var thisDoc = docs[0]; |
| mergeOrUpdateDoc(id, fetchedDocs, thisDoc[0], results, thisDoc[1], revLimit, newEdits, |
| wasDelete, writeDoc, checkAllDocsDone); |
| } else { // slow case |
| processsMultipleDocsWithSameId(id, docs, fetchedDocs, results, revLimit, newEdits, |
| wasDelete, writeDoc, checkAllDocsDone); |
| } |
| } |
| } |
| } |
| |
| export default processDocs; |