Skip to content

Instantly share code, notes, and snippets.

@apage43
Created September 13, 2010 20:35
Show Gist options
  • Save apage43/577988 to your computer and use it in GitHub Desktop.
Save apage43/577988 to your computer and use it in GitHub Desktop.
//Changes filter: "conflicts": "function(doc, req) { if(doc._conflicts) { return true; } else { return false; }}"
var dbase = 'focus';
var ddoc = 'app';
var sys = require('sys'),
couchdb = require('node-couchdb/lib/couchdb'),
client = couchdb.createClient(5984, 'crate.im'),
db = client.db(dbase),
changes = db.changesStream({filter: ddoc + '/conflicts'});
var default_merge = function(conflicts, prevdoc)
{
if(!prevdoc) {
return false;
}
delete prevdoc._rev;
var newdoc =JSON.parse(JSON.stringify(prevdoc));
var updated = {};
for(var i in conflicts)
{
sys.puts(' Conflict ' + i + ' ' + JSON.stringify(conflicts[i]));
for(var k in conflicts[i])
{
delete conflicts[i]._rev;
if(!newdoc[k])
{
//Key doesn't exist in previous rev.
newdoc[k] = conflicts[i][k];
} else {
if(!prevdoc[k]) {
//Conflicting docs both added the same new field.
//not handling this.
sys.puts("Both added field " + k);
return false;
} else {
//Key is in new doc and previous doc.
if(JSON.stringify(conflicts[i][k]) != JSON.stringify(prevdoc[k])) {
//We have a new version of this key.
if(!updated[k]) {
sys.puts("Field " + k + ": " + JSON.stringify(prevdoc[k]) + " -> " + JSON.stringify(conflicts[i][k]));
//We are the sole updater so far.
newdoc[k] = conflicts[i][k];
updated[k] = true;
} else {
//Conflicting docs both updated the same field, not handling this.
sys.puts("Both updated field " + k + "\n");
return false;
}
}
}
}
}
}
return newdoc;
}
db.getDoc('_design/' + ddoc, function (er, ddoc) {
if(er) throw new Error("Error fetching design doc");
var merge = default_merge;
if(ddoc.merge_func)
{
sys.puts("Custom merge function: " + ddoc.merge_func);
merge = eval(ddoc.merge_func);
}
changes.on('data', function (change) {
sys.print(JSON.stringify(change)+'\n');
db.request({method: 'get', path: '/' + change.id, query: {conflicts: true, revs: true}}, function(er, doc)
{
sys.print('Processing conflict...\n');
if(doc._conflicts) {
db.request({method: 'get', path: '/'+change.id, query: {
rev: (doc._revisions.start - 1) + '-' + doc._revisions.ids[1]
}}, function(er, previous) {
var conflicts = [];
sys.print(' Previous rev: ' + JSON.stringify(previous)+'\n');
for(var rev in doc._conflicts) {
db.request({method: 'get', path:'/' + change.id, query: {rev: doc._conflicts[rev]}}, function(er, cdoc) {
if(er) throw new Error(JSON.stringify(er));
conflicts.push(cdoc);
if(conflicts.length == doc._conflicts.length) {
var current = JSON.parse(JSON.stringify(doc));
delete current._revisions;
delete current._conflicts;
conflicts.push(current);
var result = merge(conflicts, previous);
if(result)
{
sys.puts(' Merge result: ' + JSON.stringify(result));
result._rev = doc._rev;
db.bulkDocs({docs: [result].concat(doc._conflicts.map(function(x) {
return {_id: doc._id, _rev: x, _deleted: true};
}))}, function(er) {
if(er) sys.puts("Commit of merge failed: " + JSON.stringify(er));
sys.puts("Merge successfully saved.");
});
} else {
sys.puts("Could not resolve conflict automatically.");
}
};
});
}
});
}
});
});
});
sys.print('Mergeist - couchdb conflicts automerger\n');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment