Commit 7d25b5ce authored by Bryan Kaperick's avatar Bryan Kaperick

allDocs method is updated to allow an options.revision_limit parameter which...

allDocs method is updated to allow an options.revision_limit parameter which allows queries on older revisions of documents.
parent 038c7a51
...@@ -26,119 +26,22 @@ ...@@ -26,119 +26,22 @@
type: "query", type: "query",
sub_storage: spec.sub_storage sub_storage: spec.sub_storage
}); });
this._lastseen = undefined;
this.param = true;
} }
BryanStorage.prototype.get = function (id_in, revision) { BryanStorage.prototype.get = function (id_in, steps) {
// Default behavior, get() returns the most recent revision if (steps === undefined) {
if (revision === undefined) { steps = 0;
revision = {
steps: 0,
path: "absolute"
};
}
// Default type of traversal is absolute:
// "absolute" -- step backward in chronological order of changes to document
// "consistent" -- step backward in chronological order of only edits the
// most recent version is based on. Other branches of edits are ignored
if (revision.path === undefined) {
revision.path = "absolute";
}
if (revision.steps === undefined) {
revision.steps = 0;
}
if (revision.steps === 0) {
revision.path = "absolute";
} }
// Query to get the last edit made to this document // Query to get the last edit made to this document
var storage = this, var substorage = this._sub_storage,
substorage = this._sub_storage,
options = { options = {
query: "doc_id: " + id_in, query: "doc_id: " + id_in,
sort_on: [["timestamp", "descending"]] sort_on: [["timestamp", "descending"]],
limit: [steps, 1]
}; };
if (revision.path === "leaves") {
return substorage.allDocs({
query: "leaf: true",
sort_on: [["timestamp", "descending"]]
})
.push(function (results) {
var promises = results.data.rows.map(function (res) {
return substorage.get(res.id);
});
return RSVP.all(promises);
})
.push(function (documents) {
var not_leaves = {},
leaves = [],
ind,
doc_data,
fix_leaves = [];
function update_leaf_state(data) {
var new_data = data;
new_data.leaf = false;
return substorage.put(data.timestamp, new_data);
}
// Loop through documents and push only leaf versions to leaves array
for (ind = 0; ind < documents.length; ind += 1) {
doc_data = documents[ind];
not_leaves[doc_data.lastseen] = true;
if (not_leaves[doc_data.timestamp] === true) {
fix_leaves.push(update_leaf_state);
} else {
if (doc_data.op === "put") {
// XXX: For cheaper evaluation, break out of the loop once
// leaves.length == revision.steps, and only fix the leaves in
// fix_leaves at that point.
// However, since we already spent time to retrieve all leaves,
// it may be better to go ahead and clean up all mislabelled
// leaves right now, so the next call to get.leaves is cheaper
leaves.push(doc_data.doc);
// revision.steps is guaranteed to be >= 1 in this branch
//
if (leaves.length - 1 === revision.steps) {
storage._lastseen = doc_data.timestamp;
}
}
}
}
// Fix all mislabelled leaves and then return the array of leaves
return RSVP.all(fix_leaves)
//XXX: Not sure why I can't use a .push here instead of .then
.then(function () {
if (leaves.length - 1 >= revision.steps) {
return leaves[revision.steps];
}
throw new jIO.util.jIOError(
"bryanstorage: there are fewer than " +
revision.steps + " leaf revisions for '" + id_in + "'",
404
);
});
});
}
// In "absolute" path, .get returns the revision.steps-most-recent revision
if (revision.path === "absolute") {
options.limit = [revision.steps, 1];
// In "consistent path, .get returns the most recent revision and looks
// deeper into history with the result's .lastseen attribute
} else if (revision.path === "consistent") {
options.limit = [0, 1];
}
return substorage.allDocs(options) return substorage.allDocs(options)
.push(function (results) { .push(function (results) {
if (results.data.rows.length > 0) { if (results.data.rows.length > 0) {
...@@ -151,62 +54,13 @@ ...@@ -151,62 +54,13 @@
}) })
.push(function (result) { .push(function (result) {
// Function used to chain together substorage.get's for "consistent"
// traversal
function recurse_get(result) {
if (result.lastseen === undefined) {
throw new jIO.util.jIOError(
"bryanstorage: cannot find object '" +
id_in +
"' (end of history)",
404
);
}
return substorage.get(result.lastseen);
}
// If last edit was a remove, throw a 'not found' error
if (result.op === "remove" && revision.path === "absolute") {
throw new jIO.util.jIOError(
"bryanstorage: cannot find object '" + id_in + "' (removed)",
404
);
}
if (result.op === "put") { if (result.op === "put") {
return result.doc;
// The query for "absolute" traversal returns exactly the document
// requested
if (revision.path === "absolute" || revision.steps === 0) {
storage._lastseen = result.timestamp;
return result.doc;
}
if (revision.path === "consistent") {
// Chain together promises to access history of document
var promise = substorage.get(result.lastseen);
while (revision.steps > 1) {
promise = promise.push(recurse_get);
revision.steps -= 1;
}
// Once at desired depth, update storage._lastseen and return doc
return promise.push(function (result) {
storage._lastseen = result.timestamp;
if (result.op === "remove") {
throw new jIO.util.jIOError(
"bryanstorage: cannot find object '" +
result.doc_id +
"' (removed)",
404
);
}
return result.doc;
});
}
} }
throw new jIO.util.jIOError(
"bryanstorage: cannot find object '" + id_in + "' (removed)",
404
);
}); });
}; };
...@@ -221,12 +75,9 @@ ...@@ -221,12 +75,9 @@
timestamp: timestamp, timestamp: timestamp,
doc_id: id, doc_id: id,
doc: data, doc: data,
op: "put", op: "put"
lastseen: this._lastseen,
leaf: true
}; };
this._lastseen = timestamp; this._lastseen = timestamp;
//console.log(metadata.doc.k, timestamp, metadata.lastseen);
return this._sub_storage.put(timestamp, metadata); return this._sub_storage.put(timestamp, metadata);
}; };
...@@ -236,12 +87,8 @@ ...@@ -236,12 +87,8 @@
// XXX: remove this attribute once query can sort_on id // XXX: remove this attribute once query can sort_on id
timestamp: timestamp, timestamp: timestamp,
doc_id: id, doc_id: id,
op: "remove", op: "remove"
lastseen: this._lastseen,
leaf: true
}; };
this._lastseen = timestamp;
//console.log("removed", timestamp, metadata.lastseen);
return this._sub_storage.put(timestamp, metadata); return this._sub_storage.put(timestamp, metadata);
}; };
...@@ -297,7 +144,6 @@ ...@@ -297,7 +144,6 @@
}); });
}; };
// Not implemented for IndexedDB
BryanStorage.prototype.repair = function () { BryanStorage.prototype.repair = function () {
return this._sub_storage.repair.apply(this._sub_storage, arguments); return this._sub_storage.repair.apply(this._sub_storage, arguments);
}; };
...@@ -306,13 +152,22 @@ ...@@ -306,13 +152,22 @@
}; };
BryanStorage.prototype.buildQuery = function (options) { BryanStorage.prototype.buildQuery = function (options) {
if (options === undefined) {
options = {};
}
if (options.sort_on === undefined) { if (options.sort_on === undefined) {
options.sort_on = []; options.sort_on = [];
} }
options.sort_on.push(["timestamp", "descending"]);
if (options.limit === undefined) { if (options.limit === undefined) {
options.limit = [0, -1]; options.limit = [0, -1];
} }
options.sort_on.push(["timestamp", "descending"]);
// Default behavior is to return only the latest revision of each document
if (options.revision_limit === undefined) {
options.revision_limit = [0, 1];
}
var meta_options = { var meta_options = {
// XXX: I don't believe it's currently possible to query on sub-attributes // XXX: I don't believe it's currently possible to query on sub-attributes
// so for now, we just use the inputted query, which obviously will fail // so for now, we just use the inputted query, which obviously will fail
...@@ -327,36 +182,52 @@ ...@@ -327,36 +182,52 @@
first_doc = options.limit[0]; first_doc = options.limit[0];
return this._sub_storage.allDocs(meta_options) return this._sub_storage.allDocs(meta_options)
// Get all documents found in query
.push(function (results) { .push(function (results) {
var promises = results.data.rows.map(function (data) { var promises = results.data.rows.map(function (data) {
return substorage.get(data.id); return substorage.get(data.id);
}); });
return RSVP.all(promises); return RSVP.all(promises);
}) })
.push(function (results_array) { .push(function (results_array) {
var clean_data = [], var clean_data = [],
ind, ind,
seen_docs = [], seen_docs = {},
current_doc, current_doc,
counter = 0; counter = 0;
// Default behavior is to not limit the number of documents returned
if (max_num_docs === -1) { if (max_num_docs === -1) {
max_num_docs = results_array.length; max_num_docs = results_array.length;
} }
for (ind = 0; ind < results_array.length; ind += 1) { for (ind = 0; ind < results_array.length; ind += 1) {
current_doc = results_array[ind]; current_doc = results_array[ind];
// Initialize count of revisions
if (!seen_docs.hasOwnProperty(current_doc.doc_id)) {
seen_docs[current_doc.doc_id] = 0;
}
// If the latest version of this document has not yet been // If the latest version of this document has not yet been
// included in query result // included in query result
if (seen_docs[current_doc.doc_id] !== true) { if (options.revision_limit[0] <= seen_docs[current_doc.doc_id] &&
seen_docs[current_doc.doc_id] < options.revision_limit[0] +
options.revision_limit[1]) {
// If the latest edit was a put operation, add it to query // If the latest edit was a put operation, add it to query
// results // results
if (current_doc.op === "put") { if (current_doc.op === "put") {
if (counter >= first_doc) { if (counter >= first_doc) {
// Note the rev attribute added to the output data.
// This guarantees that `this.get(id, rev) === doc`
clean_data.push({ clean_data.push({
doc: {}, doc: current_doc.doc,
value: {}, value: {},
id: current_doc.doc_id id: current_doc.doc_id,
rev: seen_docs[current_doc.doc_id]
}); });
if (clean_data.length === max_num_docs) { if (clean_data.length === max_num_docs) {
return clean_data; return clean_data;
...@@ -364,9 +235,9 @@ ...@@ -364,9 +235,9 @@
} }
counter += 1; counter += 1;
} }
// Mark document as read so no older edits are considered
seen_docs[current_doc.doc_id] = true;
} }
// Keep track of how many times this doc_id has been seen
seen_docs[current_doc.doc_id] += 1;
} }
// In passing results back to allDocs, formatting of query is handled // In passing results back to allDocs, formatting of query is handled
return clean_data; return clean_data;
......
...@@ -83,9 +83,7 @@ ...@@ -83,9 +83,7 @@
title: "foo0" title: "foo0"
}, },
timestamp: results.timestamp, timestamp: results.timestamp,
op: "put", op: "put"
lastseen: undefined,
leaf: true
}, "The first item in the log is pushing bar's title to 'foo0'"); }, "The first item in the log is pushing bar's title to 'foo0'");
return jio.remove("bar"); return jio.remove("bar");
}) })
...@@ -119,9 +117,7 @@ ...@@ -119,9 +117,7 @@
deepEqual(result, { deepEqual(result, {
doc_id: "bar", doc_id: "bar",
timestamp: result.timestamp, timestamp: result.timestamp,
op: "remove", op: "remove"
lastseen: result.lastseen,
leaf: true
}); });
}) })
.fail(function (error) { .fail(function (error) {
...@@ -291,7 +287,7 @@ ...@@ -291,7 +287,7 @@
test("Testing proper retrieval of older revisions of documents", test("Testing proper retrieval of older revisions of documents",
function () { function () {
stop(); stop();
expect(8); expect(12);
// create storage of type "bryan" with memory as substorage // create storage of type "bryan" with memory as substorage
var jio = jIO.createJIO({ var jio = jIO.createJIO({
...@@ -303,13 +299,47 @@ ...@@ -303,13 +299,47 @@
} }
} }
}); });
jio.get("doc")
jio.put("doc", { .push(function () {
"k0": "v0" ok(false, "This query should have thrown a 404 error");
}) },
function (error) {
deepEqual(error.status_code,
404,
"Document does not exist yet.");
})
.push(function () {
return jio.put("doc", {
"k0": "v0"
});
})
.push(function () {
return jio.get("doc", 0);
})
.push(function (result) {
deepEqual(result, {
"k0": "v0"
});
})
.push(function () { .push(function () {
return jio.put("doc", {"k1": "v1"}); return jio.put("doc", {"k1": "v1"});
}) })
.push(function () {
return jio.get("doc", 0);
})
.push(function (result) {
deepEqual(result, {
"k1": "v1"
});
})
.push(function () {
return jio.get("doc", 1);
})
.push(function (result) {
deepEqual(result, {
"k0": "v0"
});
})
.push(function () { .push(function () {
return jio.put("doc", {"k2": "v2"}); return jio.put("doc", {"k2": "v2"});
}) })
...@@ -329,19 +359,19 @@ ...@@ -329,19 +359,19 @@
deepEqual(result, deepEqual(result,
{"k4": "v4"}, {"k4": "v4"},
"By default, .get returns latest revision"); "By default, .get returns latest revision");
return jio.get("doc", {steps: 0}); return jio.get("doc", 0);
}) })
.push(function (result) { .push(function (result) {
deepEqual(result, deepEqual(result,
{"k4": "v4"}, {"k4": "v4"},
".get returns latest revision with second input = 0"); ".get returns latest revision with second input = 0");
return jio.get("doc", {steps: 1}); return jio.get("doc", 1);
}) })
.push(function (result) { .push(function (result) {
deepEqual(result, deepEqual(result,
{"k3": "v3"}, {"k3": "v3"},
"Walk back one revision with second input = 1"); "Walk back one revision with second input = 1");
return jio.get("doc", {steps: 2}); return jio.get("doc", 2);
}) })
.push(function () { .push(function () {
ok(false, "This query should have thrown a 404 error"); ok(false, "This query should have thrown a 404 error");
...@@ -350,25 +380,25 @@ ...@@ -350,25 +380,25 @@
deepEqual(error.status_code, deepEqual(error.status_code,
404, 404,
"Current state of document is 'removed'."); "Current state of document is 'removed'.");
return jio.get("doc", {steps: 3}); return jio.get("doc", 3);
}) })
.push(function (result) { .push(function (result) {
deepEqual(result, deepEqual(result,
{"k2": "v2"}, {"k2": "v2"},
"Walk back three revisions with second input = 3"); "Walk back three revisions with second input = 3");
return jio.get("doc", {steps: 4}); return jio.get("doc", 4);
}) })
.push(function (result) { .push(function (result) {
deepEqual(result, deepEqual(result,
{"k1": "v1"}, {"k1": "v1"},
"Walk back four revisions with second input = 4"); "Walk back four revisions with second input = 4");
return jio.get("doc", {steps: 5}); return jio.get("doc", 5);
}) })
.push(function (result) { .push(function (result) {
deepEqual(result, deepEqual(result,
{"k0": "v0"}, {"k0": "v0"},
"Walk back five revisions with second input = 5"); "Walk back five revisions with second input = 5");
return jio.get("doc", {steps: 6}); return jio.get("doc", 6);
}) })
.push(function () { .push(function () {
ok(false, "This query should have thrown a 404 error"); ok(false, "This query should have thrown a 404 error");
...@@ -386,38 +416,18 @@ ...@@ -386,38 +416,18 @@
}); });
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// Accessing older revisions with multiple users // Accessing older revisions with two users
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
module("bryanStorage.accessing_older_revisions_multiple_users"); module("bryanStorage.querying_old_revisions");
test("Testing retrieval of older revisions of documents with multiple users", test("Testing retrieval of older revisions via allDocs calls",
function () { function () {
stop(); stop();
expect(51); expect(47);
// create storage of type "bryan" with memory as substorage // create storage of type "bryan" with memory as substorage
var dbname = "multi_user_db" + Date.now(), var dbname = "db-" + Date.now(),
jio1 = jIO.createJIO({ jio = jIO.createJIO({
type: "bryan",
sub_storage: {
type: "uuid",
sub_storage: {
type: "indexeddb",
database: dbname
}
}
}),
jio2 = jIO.createJIO({
type: "bryan",
sub_storage: {
type: "uuid",
sub_storage: {
type: "indexeddb",
database: dbname
}
}
}),
jio3 = jIO.createJIO({
type: "bryan", type: "bryan",
sub_storage: { sub_storage: {
type: "uuid", type: "uuid",
...@@ -427,696 +437,239 @@ ...@@ -427,696 +437,239 @@
} }
} }
}); });
jio.put("doc", {
jio1.put("doc", { "k": "v0"
"k": "v0.1"
}) })
.push(function () { .push(function () {
return jio2.get("doc"); return jio.put("doc", {
}) "k": "v1"
.push(function () {
return jio3.get("doc");
})
.push(function () {
return jio2.put("doc", {
"k": "v0.1.2"
}); });
}) })
.push(function () { .push(function () {
return jio3.put("doc", { return jio.put("doc", {
"k": "v0.1.3" "k": "v2"
});
})
/**
.push(function () {
return jio2.put("doc", {
"k": "v0.1.2.2"
});
})
**/
.push(function () {
return jio2.remove("doc");
})
.push(function () {
return jio3.put("doc", {
"k": "v0.1.3.3"
});
})
.push(function () {
return jio1.get("doc");
})
.push(function () {
return jio1.put("doc", {
"k": "v0.1.3.3.1"
}); });
}) })
.push(function () { .push(function () {
return jio2.put("doc", { return jio.put("doc", {
"k": "v0.1.2.2.2" "k": "v3"
}); });
}) })
.push(function () { .push(function () {
return jio3.put("doc", { return jio.allDocs({
"k": "v0.1.3.3.3" query: "",
}); revision_limit: [0, 1]
})
.push(function () {
return jio1.get("doc");
})
// jio2 has a different version than 1 & 3 as its latest revision
/**
.push(function () {
return jio2.get("doc");
})
**/
.push(function () {
return jio3.get("doc");
})
// Test all lastseens are the same
.push(function () {
// These are all undefined outside the storage definition, so these
// tests are meaningless
//equal(jio1._lastseen, jio2._lastseen, "All users see same version");
//equal(jio1._lastseen, jio3._lastseen, "All users see same version");
//
// Test consistent history of user 1
//
return jio1.get("doc", {
path: "consistent",
steps: 0
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.3.3.3"
}, "Get of depth 0 returns latest version"
);
return jio1.get("doc", {
path: "consistent",
steps: 1
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.3.3"
}, "Get of consistent depth 1 returns correct version"
);
return jio1.get("doc", {
path: "consistent",
steps: 2
}); });
}) })
.push(function (results) {
.push(function (result) { deepEqual(results.data.rows.length,
deepEqual(result, { 1,
"k": "v0.1.3" "Only one query returned with options.revision_limit == [0,1]");
}, "Get of consistent depth 2 returns correct version" return jio.get(results.data.rows[0].id);
);
return jio1.get("doc", {
path: "consistent",
steps: 3
});
}) })
.push(function (result) { .push(function (result) {
deepEqual(result, { deepEqual(result, {
"k": "v0.1" "k": "v3"
}, "Get of consistent depth 3 returns correct version"
);
return jio1.get("doc", {
path: "consistent",
steps: 4
}); });
}) })
.push(function () { .push(function () {
ok(false, "This query should have thrown a 404 error"); return jio.allDocs({
}, query: "",
function (error) { revision_limit: [1, 1]
deepEqual(error.status_code,
404,
"There are only 3 previous states of this document: " + error);
})
.push(function () {
//
// Test consistent history of user 2 (Is the same as 1 & 3 even though
// User 2 has not explicitly called .get since the latest changes
// were made)
//
return jio2.get("doc", {
path: "consistent",
steps: 0
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.3.3.3"
}, "Get of depth 0 returns latest version"
);
return jio2.get("doc", {
path: "consistent",
steps: 1
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.3.3"
}, "Get of depth 0 returns latest version"
);
return jio2.get("doc", {
path: "consistent",
steps: 2
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.3"
}, "Get of consistent depth 2 returns correct version"
);
return jio2.get("doc", {
path: "consistent",
steps: 3
}); });
}) })
.push(function (results) {
.push(function (result) { deepEqual(results.data.rows.length,
deepEqual(result, { 1,
"k": "v0.1" "Only one query returned with options.revision_limit == [1,1]");
}, "Get of consistent depth 3 returns correct version" deepEqual(results.data.rows[0].doc, {
); "k": "v2"
return jio2.get("doc", {
path: "consistent",
steps: 4
}); });
}) return jio.allDocs({
.push(function () { query: "",
ok(false, "This query should have thrown a 404 error"); revision_limit: [2, 1]
},
function (error) {
deepEqual(error.status_code,
404,
"There are only 3 previous states of this document: " + error);
})
.push(function () {
//
// Test consistent history of user 3 (Should be same as user 1)
//
return jio3.get("doc", {
path: "consistent",
steps: 0
}); });
}) })
.push(function (result) { .push(function (results) {
deepEqual(result, { deepEqual(results.data.rows.length,
"k": "v0.1.3.3.3" 1,
}, "User 2 consistent history is same as user 1" "Only one query returned with options.revision_limit == [2,1]");
); deepEqual(results.data.rows[0].doc, {
return jio3.get("doc", { "k": "v1"
path: "consistent",
steps: 1
}); });
}) return jio.allDocs({
.push(function (result) { query: "",
deepEqual(result, { revision_limit: [3, 1]
"k": "v0.1.3.3"
}, "User 2 consistent history is same as user 1"
);
return jio3.get("doc", {
path: "consistent",
steps: 2
}); });
}) })
.push(function (result) { .push(function (results) {
deepEqual(result, { deepEqual(results.data.rows.length,
"k": "v0.1.3" 1,
}, "User 2 consistent history is same as user 1" "Only one query returned with options.revision_limit == [3,1]");
); deepEqual(results.data.rows[0].doc, {
return jio3.get("doc", { "k": "v0"
path: "consistent",
steps: 3
}); });
}) return jio.allDocs({
.push(function (result) { query: "",
deepEqual(result, { revision_limit: [4, 1]
"k": "v0.1"
}, "User 2 consistent history is same as user 1"
);
return jio3.get("doc", {
path: "consistent",
steps: 4
}); });
}) })
.push(function () { .push(function (results) {
ok(false, "This query should have thrown a 404 error"); equal(results.data.rows.length, 0, "Past all previous revisions");
},
function (error) {
deepEqual(error.status_code,
404,
"There are only 3 previous states of this document");
})
// Reset jio3._lastseen to be at v0.1.3.3.3
.push(function () {
return jio3.get("doc");
}) })
//
// Test absolute history of user 1
//
.push(function () { .push(function () {
return jio1.get("doc", { return jio.allDocs({
path: "absolute", revision_limit: [0, 2]
steps: 0
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.3.3.3"
}, "Get of absolute depth 0 returns latest version"
);
return jio1.get("doc", {
path: "absolute",
steps: 1
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.2.2.2"
}, "Get of absolute depth 1 returns correct version"
);
return jio1.get("doc", {
path: "absolute",
steps: 2
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.3.3.1"
}, "Get of absolute depth 2 returns correct version"
);
return jio1.get("doc", {
path: "absolute",
steps: 3
}); });
}) })
.push(function (result) { .push(function (results) {
deepEqual(result, { equal(results.data.rows.length, 2);
"k": "v0.1.3.3" deepEqual(results.data.rows[0].doc, {
}, "Get of absolute depth 3 returns correct version" "k": "v3"
); }, "Only retrieve two most recent revions");
return jio1.get("doc", { deepEqual(results.data.rows[1].doc, {
path: "absolute", "k": "v2"
steps: 4
}); });
}) })
.push(function () { .push(function () {
ok(false, "This query should have thrown a 404 error"); return jio.remove("doc");
},
function (error) {
deepEqual(error.status_code,
404,
"Document has been removed at this point");
return jio1.get("doc", {
path: "absolute",
steps: 5
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.3"
}, "Get of absolute depth 5 returns correct version"
);
return jio1.get("doc", {
path: "absolute",
steps: 6
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.2"
}, "Get of absolute depth 6 returns correct version"
);
return jio1.get("doc", {
path: "absolute",
steps: 7
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1"
});
return jio1.get("doc", {
path: "absolute",
steps: 8
});
}) })
.push(function () { .push(function () {
ok(false, "This query should have thrown a 404 error"); return jio.allDocs({
}, query: "",
function (error) { revision_limit: [0, 1]
deepEqual(error.status_code,
404,
"There are only 3 previous states of this document");
})
//
// Test absolute history of user 2
//
.push(function () {
return jio2.get("doc", {
path: "absolute",
steps: 0
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.3.3.3"
});
return jio2.get("doc", {
path: "absolute",
steps: 1
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.2.2.2"
});
return jio2.get("doc", {
path: "absolute",
steps: 2
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.3.3.1"
});
return jio2.get("doc", {
path: "absolute",
steps: 3
}); });
}) })
.push(function (result) { .push(function (results) {
deepEqual(result, { equal(results.data.rows.length, 0,
"k": "v0.1.3.3" "Query does not return removed doc");
});
return jio2.get("doc", {
path: "absolute",
steps: 4
});
}) })
.push(function () { .push(function () {
ok(false, "This query should have thrown a 404 error"); return jio.allDocs({
}, query: "",
function (error) { revision_limit: [1, 3]
deepEqual(error.status_code,
404,
"Document has been removed at this point");
return jio2.get("doc", {
path: "absolute",
steps: 5
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.3"
});
return jio2.get("doc", {
path: "absolute",
steps: 6
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.2"
});
return jio2.get("doc", {
path: "absolute",
steps: 7
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1"
});
return jio2.get("doc", {
path: "absolute",
steps: 8
}); });
}) })
.push(function () { .push(function (results) {
ok(false, "This query should have thrown a 404 error"); equal(results.data.rows.length, 3);
}, deepEqual(results.data.rows[0].doc, {
function (error) { "k": "v3"
deepEqual(error.status_code, }, "1st, 2nd, and 3rd versions removed from current are retrieved");
404, deepEqual(results.data.rows[1].doc, {
"There are only 3 previous states of this document"); "k": "v2"
}) }, "1st, 2nd, and 3rd versions removed from current are retrieved");
deepEqual(results.data.rows[2].doc, {
// "k": "v1"
// Tests on checking out an older revision and making a new edit branch }, "1st, 2nd, and 3rd versions removed from current are retrieved");
//
.push(function () {
return jio1.get("doc", {
path: "absolute",
steps: 1
});
}) })
.push(function () { .push(function () {
return jio1.put("doc", { return jio.put("doc2", {
"k": "v0.1.2.2.2.1" "k2": "w0"
}); });
}) })
.push(function () { .push(function () {
return jio1.get("doc", { return jio.allDocs({
path: "consistent", revision_limit: [1, 3]
steps: 1
}); });
}) })
.push(function (result) { .push(function (results) {
deepEqual(result, { equal(results.data.rows.length, 3);
"k": "v0.1.2.2.2" deepEqual(results.data.rows[0].doc, {
}, "The new document is added to the correct edit branch" "k": "v3"
); }, "Does not retrieve new document outside queried revision range");
return jio1.get("doc", { deepEqual(results.data.rows[1].doc, {
path: "consistent", "k": "v2"
steps: 2 }, "Does not retrieve new document outside queried revision range");
}); deepEqual(results.data.rows[2].doc, {
"k": "v1"
}, "Does not retrieve new document outside queried revision range");
}) })
.push(function () { .push(function () {
ok(false, "This query should have thrown a 404 error"); return jio.allDocs({
}, revision_limit: [0, 2]
function (error) {
deepEqual(error.status_code,
404,
"This document was removed at this time");
return jio1.get("doc", {
path: "consistent",
steps: 3
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.2"
}, "The new document is added to the correct edit branch"
);
return jio1.get("doc", {
path: "consistent",
steps: 4
}); });
}) })
.push(function (result) { .push(function (results) {
deepEqual(result, { equal(results.data.rows.length, 2);
"k": "v0.1" deepEqual(results.data.rows[0].doc, {
}, "This edit branch also leads back to the original version" "k2": "w0"
); }, "Retrieves all documents with versions in queried revision range");
return jio1.get("doc", { deepEqual(results.data.rows[1].doc, {
path: "consistent", "k": "v3"
steps: 5 }, "Retrieves all documents with versions in queried revision range");
});
}) })
.push(function () { .push(function () {
ok(false, "This query should have thrown a 404 error"); return jio.put("doc2", {
}, "k2": "w1"
function (error) {
deepEqual(error.status_code,
404,
"There are no revisions before the original document");
})
.push(function () {
return jio3.put("doc", {
"k": "v0.1.3.3.3.3"
}); });
}) })
// All three users have the same latest revision
.push(function () { .push(function () {
return jio1.get("doc"); return jio.allDocs();
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.3.3.3.3"
}, "User one accesses latest revision correctly"
);
return jio2.get("doc");
}) })
.push(function (result) { .push(function (results) {
deepEqual(result, { equal(results.data.rows.length, 1,
"k": "v0.1.3.3.3.3" "There is only one non-removed doc");
}, "User two accesses latest revision correctly" equal(results.data.rows[0].rev, 0);
); deepEqual(results.data.rows[0].doc, {
return jio3.get("doc"); "k2": "w1"
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.3.3.3.3"
}, "User three accesses latest revision correctly"
);
return jio2.get("doc", {
path: "consistent",
steps: 1
}); });
}) })
.push(function (result) {
deepEqual(result, {
"k": "v0.1.3.3.3"
}, "User 2 accesses the 1st edit in consistent traversal."
);
})
//
// Testing .getting on leaf nodes
//
.push(function () { .push(function () {
return jio1.get("doc", { return jio.remove("doc2");
path: "leaves"
});
}) })
.push(function (result) { .push(function () {
deepEqual(result, { return jio.allDocs({
"k": "v0.1.3.3.3.3" revision_limit: [0, 4]
}, "First result is the most-recently-added leaf"
);
return jio2.get("doc", {
path: "leaves",
steps: 1
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.2.2.2.1"
}, "Second result is the 2nd most-recently-added leaf"
);
return jio3.get("doc", {
path: "leaves",
steps: 2,
db: "jio3"
}); });
}) })
.push(function (result) { .push(function (results) {
deepEqual(result, { equal(results.data.rows.length, 5);
"k": "v0.1.3.3.1" equal(results.data.rows[0].rev, 1, "Rev parameter is correct");
}, "Third result is the 3rd most-recently-added leaf" equal(results.data.rows[1].rev, 2, "Rev parameter is correct");
); equal(results.data.rows[2].rev, 1, "Rev parameter is correct");
equal(results.data.rows[3].rev, 2, "Rev parameter is correct");
// equal(results.data.rows[4].rev, 3, "Rev parameter is correct");
// Editing document revisions stemming from the latest leaf nodes seen deepEqual(results.data.rows[0].doc, {
// "k2": "w1"
return jio1.put("doc", {
"k": "v0.1.3.3.3.3.1"
}); });
}) deepEqual(results.data.rows[1].doc, {
.push(function () { "k2": "w0"
return jio3.remove("doc"); // removing v0.1.3.3.1
})
// Check that jio1 sees latest non-removed revision
.push(function () {
return jio1.get("doc");
})
.push(function () {
ok(false, "This query should have thrown a 404 error");
},
function (error) {
deepEqual(error.status_code,
404,
"The most recent edit was a remove, so throw error");
})
.push(function () {
// jio2 lastseen should point to "v0.1.2.2.2.1"
return jio2.put("doc", {
"k": "v0.1.2.2.2.1.2"
}); });
}) deepEqual(results.data.rows[2].doc, {
.push(function () { "k": "v3"
return jio1.get("doc", {
path: "leaves",
steps: 0
}); });
}) deepEqual(results.data.rows[3].doc, {
.push(function (result) { "k": "v2"
deepEqual(result, {
"k": "v0.1.2.2.2.1.2"
}, "Accessing the first leaf node at this time"
);
return jio1.get("doc", {
path: "leaves",
steps: 1
}); });
}) deepEqual(results.data.rows[4].doc, {
.push(function (result) { "k": "v1"
deepEqual(result, {
"k": "v0.1.3.3.3.3.1"
}, "Accessing the second leaf node at this time"
);
return jio1.get("doc", {
path: "leaves",
steps: 2
}); });
}) })
.push(function () { .push(function () {
ok(false, "This query should have thrown a 404 error"); return jio.allDocs({
}, limit: [1, 4],
function (error) { revision_limit: [0, 4]
deepEqual(error.status_code,
404,
"There are only two non-removed leaves");
// jio1 should still have lastseen at v0.1.3.3.3.3.1
return jio1.put("doc", {
"k": "v0.1.3.3.3.3.1.1"
});
})
.push(function () {
return jio1.get("doc", {
path: "consistent",
steps: 1
});
})
.push(function (result) {
deepEqual(result, {
"k": "v0.1.3.3.3.3.1"
}, "If a .get fails, that should not reset ._lastseen parameter"
);
return jio1.get("doc", {
path: "consistent",
steps: 2
}); });
}) })
.push(function (result) { .push(function (results) {
deepEqual(result, { equal(results.data.rows.length, 4,
"k": "v0.1.3.3.3.3" "Correct number of results with options.limit set");
}, "History of 0.1.2.2.2 has been constructed correctly."); equal(results.data.rows[0].rev, 2, "Rev parameter is correct");
equal(results.data.rows[1].rev, 1, "Rev parameter is correct");
equal(results.data.rows[2].rev, 2, "Rev parameter is correct");
equal(results.data.rows[3].rev, 3, "Rev parameter is correct");
deepEqual(results.data.rows[0].doc, {
"k2": "w0"
}, "Correct results with options.limit set");
deepEqual(results.data.rows[1].doc, {
"k": "v3"
}, "Correct results with options.limit set");
deepEqual(results.data.rows[2].doc, {
"k": "v2"
}, "Correct results with options.limit set");
deepEqual(results.data.rows[3].doc, {
"k": "v1"
}, "Correct results with options.limit set");
}) })
.fail(function (error) { .fail(function (error) {
//console.log(error); //console.log(error);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment