Skip to content

Instantly share code, notes, and snippets.

@redgeoff
Created July 29, 2014 12:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save redgeoff/b3b91ec2f46f29551090 to your computer and use it in GitHub Desktop.
Save redgeoff/b3b91ec2f46f29551090 to your computer and use it in GitHub Desktop.
Partial updates using pouch and the every-doc-is-a-delta pattern
<html>
<body>
<script src="//cdnjs.cloudflare.com/ajax/libs/pouchdb/2.2.3/pouchdb.min.js"></script>
<script>
function merge(obj1, obj2) {
for (var i in obj2) {
obj1[i] = obj2[i];
}
}
function first(obj) {
for (var i in obj) {
return obj[i];
}
}
// ---
function all(db) {
return new Promise(function(fulfill, reject) {
var docs = {};
db.allDocs({include_docs: true}, function(err, doc) {
// sort by createdAt as cannot guarantee that order preserved by pouch/couch
doc.rows.sort(function(a, b) {
return a.doc.$createdAt > b.doc.$createdAt;
});
doc.rows.forEach(function(el, i) {
if (!el.doc.$id) { // first delta for doc?
el.doc.$id = el.doc._id;
}
if (docs[el.doc.$id]) { // exists?
merge(docs[el.doc.$id], el.doc);
} else {
docs[el.doc.$id] = el.doc;
}
if (i == doc.rows.length - 1) { // last element?
fulfill(docs);
}
});
});
});
}
function put(object, db) {
object.$createdAt = (new Date()).toJSON();
return new Promise(function(fulfill, reject) {
db.post(object).then(function(object) {
fulfill(object);
});
});
}
function cleanup() {
var docs = {};
db.allDocs({include_docs: true}, function(err, doc) {
// reverse sort by createdAt
doc.rows.sort(function(a, b) {
return a.doc.$createdAt < b.doc.$createdAt;
});
doc.rows.forEach(function(el, i) {
db.get(el.doc._id).then(function(object) {
if (!el.doc.$id) { // first delta for doc?
el.doc.$id = el.doc._id;
}
if (docs[el.doc.$id]) { // exists?
var undefined = false;
for (var i in el.doc) {
if (typeof docs[el.doc.$id][i] === 'undefined') {
undefined = true;
break;
}
}
if (undefined) {
merge(docs[el.doc.$id], el.doc);
} else { // duplicate update, remove
db.remove(object);
}
} else {
docs[el.doc.$id] = el.doc;
}
});
});
});
}
function purge() {
db.allDocs({include_docs: true}, function(err, doc) {
doc.rows.forEach(function(el, i) {
db.get(el.doc._id).then(function(object) {
db.remove(object);
});
});
});
}
// ---
var task = null,
db = new PouchDB('tasks'),
remoteCouch = 'http://user:password@127.0.0.1:5984/tasks';
function onCreate(object) {
db.get(object.id).then(function(object) {
if (!task) {
task = {};
}
merge(task, object);
updateUi();
});
}
db.info(function(err, info) {
db
.changes({
since: info.update_seq,
live: true
})
.on('create', onCreate);
});
var opts = {live: true};
db.replicate.to(remoteCouch, opts);
db.replicate.from(remoteCouch, opts);
function save(partialTask) {
if (task) { // exists?
partialTask.$id = task.$id;
partialTask._rev = task.rev;
}
put(partialTask, db).then(function(response) {
if (!task) {
task = partialTask;
task.$id = response.id; // associate next update with the current id
}
merge(task, partialTask);
task._rev = response.rev;
});
}
function saveTitle() {
save({ title: document.getElementById('title').value });
}
function savePriority() {
save({ priority: document.getElementById('priority').value });
}
function updateUi() {
document.getElementById('title').value = task.title;
document.getElementById('priority').value = task.priority;
}
all(db).then(function(objects) {
// done loading all objects
task = first(objects);
updateUi();
});
</script>
<select id="title" onchange="saveTitle()">
<option>Take out trash</option><option>Clean dishes</option><option>Dust</option>
</select>
<select id="priority" onchange="savePriority()">
<option>High</option><option>Medium</option><option>Low</option>
</select>
<hr/>
<button onclick="cleanup()">Cleanup</button> <button onclick="purge()">Purge</button>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment