Commit c403f6bf authored by Bryan Kaperick's avatar Bryan Kaperick

Get and allDocs can now be used to access older revisions without changes to...

Get and allDocs can now be used to access older revisions without changes to the API.  The approach to doing this with allDocs is very costly right now, but is being fixed for the next commit.
parent 7d25b5ce
...@@ -28,18 +28,15 @@ ...@@ -28,18 +28,15 @@
}); });
} }
BryanStorage.prototype.get = function (id_in, steps) { BryanStorage.prototype.get = function (id_in) {
if (steps === undefined) {
steps = 0;
}
// Query to get the last edit made to this document // Query to get the last edit made to this document
var substorage = this._sub_storage, var 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] limit: [0, 1]
}; };
return substorage.allDocs(options) return substorage.allDocs(options)
...@@ -52,7 +49,47 @@ ...@@ -52,7 +49,47 @@
404 404
); );
}) })
.push(function (result) {
if (result.op === "put") {
return result.doc;
}
throw new jIO.util.jIOError(
"bryanstorage: cannot find object '" + id_in + "' (removed)",
404
);
// If no documents returned in first query, check if the id is encoding
// revision information
}, function () {
var steps,
steps_loc = id_in.lastIndexOf("_-");
// If revision signature is not in id_in, than return 404, since id
// is not found
if (steps_loc === -1) {
throw new jIO.util.jIOError(
"bryanstorage: cannot find object '" + id_in + "'",
404
);
}
// If revision signature is found, query storage based on this
steps = Number(id_in.slice(steps_loc + 2));
id_in = id_in.slice(0, steps_loc);
options = {
query: "doc_id: " + id_in,
sort_on: [["timestamp", "descending"]],
limit: [steps, 1]
};
return substorage.allDocs(options)
.push(function (results) {
if (results.data.rows.length > 0) {
return substorage.get(results.data.rows[0].id);
}
throw new jIO.util.jIOError(
"bryanstorage: cannot find object '" + id_in + "'",
404
);
})
.push(function (result) { .push(function (result) {
if (result.op === "put") { if (result.op === "put") {
return result.doc; return result.doc;
...@@ -62,6 +99,7 @@ ...@@ -62,6 +99,7 @@
404 404
); );
}); });
});
}; };
BryanStorage.prototype.post = function (metadata) { BryanStorage.prototype.post = function (metadata) {
...@@ -77,7 +115,6 @@ ...@@ -77,7 +115,6 @@
doc: data, doc: data,
op: "put" op: "put"
}; };
this._lastseen = timestamp;
return this._sub_storage.put(timestamp, metadata); return this._sub_storage.put(timestamp, metadata);
}; };
...@@ -152,34 +189,49 @@ ...@@ -152,34 +189,49 @@
}; };
BryanStorage.prototype.buildQuery = function (options) { BryanStorage.prototype.buildQuery = function (options) {
if (options === undefined) { if (options === undefined) {
options = {}; options = {};
} }
if (options.sort_on === undefined) { if (options.query === undefined) {
options.sort_on = []; options.query = "";
}
options.sort_on.push(["timestamp", "descending"]);
if (options.limit === undefined) {
options.limit = [0, -1];
}
// Default behavior is to return only the latest revision of each document
if (options.revision_limit === undefined) {
options.revision_limit = [0, 1];
} }
options.query = jIO.QueryFactory.create(options.query);
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
// so for now, we just use the inputted query, which obviously will fail // sub-attributes so for now, we just use the inputted query, which
query: options.query, // obviously will fail
query: "",
// XXX: same here, we cannot sort correctly because we cannot access // XXX: same here, we cannot sort correctly because we cannot access
// attributes of doc // attributes of doc
sort_on: options.sort_on sort_on: [["timestamp", "descending"]]
}, },
substorage = this._sub_storage, substorage = this._sub_storage,
max_num_docs = options.limit[1],
first_doc = options.limit[0]; // Check if query involved _REVISION. If not, we will later place a
// (*) AND (_REVISION: =0) as the default handling of revisions
rev_query = false,
query_obj = options.query,
query_stack = [],
ind;
if (query_obj.hasOwnProperty("query_list")) {
query_stack.push(query_obj);
} else {
rev_query = (query_obj.key === "_REVISION");
}
while (query_stack.length > 0 && (!rev_query)) {
query_obj = query_stack.pop();
for (ind = 0; ind < query_obj.query_list.length; ind += 1) {
if (query_obj.query_list[ind].hasOwnProperty("query_list")) {
query_stack.push(query_obj.query_list[ind]);
} else if (query_obj.query_list[ind].key === "_REVISION") {
rev_query = true;
break;
}
}
}
return this._sub_storage.allDocs(meta_options) return this._sub_storage.allDocs(meta_options)
...@@ -191,56 +243,66 @@ ...@@ -191,56 +243,66 @@
return RSVP.all(promises); return RSVP.all(promises);
}) })
.push(function (results_array) { .push(function (results) {
var clean_data = [], // Label all documents with their current revision status
ind, var doc,
seen_docs = {}, revision_tracker = {},
current_doc, promises;
counter = 0; for (ind = 0; ind < results.length; ind += 1) {
doc = results[ind];
// Default behavior is to not limit the number of documents returned if (revision_tracker.hasOwnProperty(doc.doc_id)) {
if (max_num_docs === -1) { revision_tracker[doc.doc_id] += 1;
max_num_docs = results_array.length; } else {
revision_tracker[doc.doc_id] = 0;
} }
for (ind = 0; ind < results_array.length; ind += 1) { doc._REVISION = revision_tracker[doc.doc_id];
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 // There must be a faster way
// included in query result promises = results.map(function (data) {
if (options.revision_limit[0] <= seen_docs[current_doc.doc_id] && return substorage.put(data.timestamp, data);
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
// results
if (current_doc.op === "put") {
if (counter >= first_doc) {
// Note the rev attribute added to the output data.
// This guarantees that `this.get(id, rev) === doc`
clean_data.push({
doc: current_doc.doc,
value: {},
id: current_doc.doc_id,
rev: seen_docs[current_doc.doc_id]
}); });
if (clean_data.length === max_num_docs) { return RSVP.all(promises);
return clean_data; })
} .push(function () {
} var latest_rev_query;
counter += 1; latest_rev_query = jIO.QueryFactory.create(
"(_REVISION: >= 0) AND (NOT op: remove)"
);
if (rev_query) {
latest_rev_query.query_list[0] = options.query;
} else {
latest_rev_query.query_list[0] = jIO.QueryFactory.create(
"(_REVISION: =0)"
);
if (options.query.type === "simple" ||
options.query.type === "complex") {
latest_rev_query.query_list.push(options.query);
} }
} }
// Keep track of how many times this doc_id has been seen // Build a query for final push
seen_docs[current_doc.doc_id] += 1; options.query = latest_rev_query;
if (options.sort_on === undefined) {
options.sort_on = [];
} }
// In passing results back to allDocs, formatting of query is handled options.sort_on.push(["timestamp", "descending"]);
return clean_data; return substorage.allDocs(options);
})
.push(function (results) {
var promises = results.data.rows.map(function (data) {
return substorage.get(data.id);
});
return RSVP.all(promises);
})
.push(function (results) {
return results
.map(function (current_doc) {
return {
doc: current_doc.doc,
value: {},
id: current_doc.doc_id
};
});
}); });
}; };
......
...@@ -287,7 +287,7 @@ ...@@ -287,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(12); expect(18);
// 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({
...@@ -314,7 +314,7 @@ ...@@ -314,7 +314,7 @@
}); });
}) })
.push(function () { .push(function () {
return jio.get("doc", 0); return jio.get("doc_-0");
}) })
.push(function (result) { .push(function (result) {
deepEqual(result, { deepEqual(result, {
...@@ -325,7 +325,7 @@ ...@@ -325,7 +325,7 @@
return jio.put("doc", {"k1": "v1"}); return jio.put("doc", {"k1": "v1"});
}) })
.push(function () { .push(function () {
return jio.get("doc", 0); return jio.get("doc_-0");
}) })
.push(function (result) { .push(function (result) {
deepEqual(result, { deepEqual(result, {
...@@ -333,7 +333,7 @@ ...@@ -333,7 +333,7 @@
}); });
}) })
.push(function () { .push(function () {
return jio.get("doc", 1); return jio.get("doc_-1");
}) })
.push(function (result) { .push(function (result) {
deepEqual(result, { deepEqual(result, {
...@@ -359,19 +359,19 @@ ...@@ -359,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", 0); return jio.get("doc");
}) })
.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", 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", 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");
...@@ -380,25 +380,25 @@ ...@@ -380,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", 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", 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", 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", 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");
...@@ -408,6 +408,65 @@ ...@@ -408,6 +408,65 @@
404, 404,
"There are only 5 previous states of this document"); "There are only 5 previous states of this document");
}) })
// Adding documents with problematic doc_id's
.push(function () {
return jio.put("doc_-name", {
"key": "val0"
});
})
.push(function () {
return jio.put("document_-0", {
"key": "and val0"
});
})
.push(function () {
return jio.put("doc_-name", {
"key": "val1"
});
})
.push(function () {
return jio.get("doc_-name");
})
.push(function (result) {
deepEqual(result, {
"key": "val1"
});
return jio.get("doc_-name_-0");
})
.push(function (result) {
deepEqual(result, {
"key": "val1"
});
return jio.get("doc_-name_-1");
})
.push(function (result) {
deepEqual(result, {
"key": "val0"
});
return jio.get("document_-0");
})
.push(function (result) {
deepEqual(result, {
"key": "and val0"
});
return jio.get("document_-0_-0");
})
.push(function (result) {
deepEqual(result, {
"key": "and val0"
});
return jio.get("document_-0_-1");
})
.push(function () {
ok(false, "This query should have thrown a 404 error");
},
function (error) {
deepEqual(error.status_code,
404,
"Document does not have this many revisions.");
})
.fail(function (error) { .fail(function (error) {
//console.log(error); //console.log(error);
ok(false, error); ok(false, error);
...@@ -416,24 +475,22 @@ ...@@ -416,24 +475,22 @@
}); });
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// Accessing older revisions with two users // Querying older revisions
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
module("bryanStorage.querying_old_revisions"); module("bryanStorage.querying_old_revisions");
test("Testing retrieval of older revisions via allDocs calls", test("Testing retrieval of older revisions via allDocs calls",
function () { function () {
stop(); stop();
expect(47); expect(37);
// create storage of type "bryan" with memory as substorage // create storage of type "bryan" with memory as substorage
var dbname = "db-" + Date.now(), var jio = jIO.createJIO({
jio = jIO.createJIO({
type: "bryan", type: "bryan",
sub_storage: { sub_storage: {
type: "uuid", type: "uuid",
sub_storage: { sub_storage: {
type: "indexeddb", type: "memory"
database: dbname
} }
} }
}); });
...@@ -457,8 +514,7 @@ ...@@ -457,8 +514,7 @@
}) })
.push(function () { .push(function () {
return jio.allDocs({ return jio.allDocs({
query: "", query: "_REVISION : 0"
revision_limit: [0, 1]
}); });
}) })
.push(function (results) { .push(function (results) {
...@@ -474,8 +530,7 @@ ...@@ -474,8 +530,7 @@
}) })
.push(function () { .push(function () {
return jio.allDocs({ return jio.allDocs({
query: "", query: "_REVISION : =1"
revision_limit: [1, 1]
}); });
}) })
.push(function (results) { .push(function (results) {
...@@ -486,8 +541,7 @@ ...@@ -486,8 +541,7 @@
"k": "v2" "k": "v2"
}); });
return jio.allDocs({ return jio.allDocs({
query: "", query: "_REVISION : =2"
revision_limit: [2, 1]
}); });
}) })
.push(function (results) { .push(function (results) {
...@@ -498,8 +552,7 @@ ...@@ -498,8 +552,7 @@
"k": "v1" "k": "v1"
}); });
return jio.allDocs({ return jio.allDocs({
query: "", query: "_REVISION : =3"
revision_limit: [3, 1]
}); });
}) })
.push(function (results) { .push(function (results) {
...@@ -510,8 +563,7 @@ ...@@ -510,8 +563,7 @@
"k": "v0" "k": "v0"
}); });
return jio.allDocs({ return jio.allDocs({
query: "", query: "_REVISION : =4"
revision_limit: [4, 1]
}); });
}) })
.push(function (results) { .push(function (results) {
...@@ -519,7 +571,7 @@ ...@@ -519,7 +571,7 @@
}) })
.push(function () { .push(function () {
return jio.allDocs({ return jio.allDocs({
revision_limit: [0, 2] query: "_REVISION: <= 1"
}); });
}) })
.push(function (results) { .push(function (results) {
...@@ -536,7 +588,7 @@ ...@@ -536,7 +588,7 @@
}) })
.push(function () { .push(function () {
return jio.allDocs({ return jio.allDocs({
query: "", query: "NOT (_REVISION: >= 1)",
revision_limit: [0, 1] revision_limit: [0, 1]
}); });
}) })
...@@ -546,8 +598,7 @@ ...@@ -546,8 +598,7 @@
}) })
.push(function () { .push(function () {
return jio.allDocs({ return jio.allDocs({
query: "", query: "(_REVISION: >= 1) AND (_REVISION: <= 3)"
revision_limit: [1, 3]
}); });
}) })
.push(function (results) { .push(function (results) {
...@@ -569,7 +620,7 @@ ...@@ -569,7 +620,7 @@
}) })
.push(function () { .push(function () {
return jio.allDocs({ return jio.allDocs({
revision_limit: [1, 3] query: "(_REVISION: >0) AND (_REVISION: <= 3)"
}); });
}) })
.push(function (results) { .push(function (results) {
...@@ -586,7 +637,7 @@ ...@@ -586,7 +637,7 @@
}) })
.push(function () { .push(function () {
return jio.allDocs({ return jio.allDocs({
revision_limit: [0, 2] query: "(_REVISION: = 0) OR (_REVISION: = 1)"
}); });
}) })
.push(function (results) { .push(function (results) {
...@@ -609,7 +660,6 @@ ...@@ -609,7 +660,6 @@
.push(function (results) { .push(function (results) {
equal(results.data.rows.length, 1, equal(results.data.rows.length, 1,
"There is only one non-removed doc"); "There is only one non-removed doc");
equal(results.data.rows[0].rev, 0);
deepEqual(results.data.rows[0].doc, { deepEqual(results.data.rows[0].doc, {
"k2": "w1" "k2": "w1"
}); });
...@@ -619,16 +669,13 @@ ...@@ -619,16 +669,13 @@
}) })
.push(function () { .push(function () {
return jio.allDocs({ return jio.allDocs({
revision_limit: [0, 4] query:
"_REVISION: 0 OR _REVISION: 1 OR " +
"(_REVISION: >= 2 AND _REVISION: <= 3)"
}); });
}) })
.push(function (results) { .push(function (results) {
equal(results.data.rows.length, 5); equal(results.data.rows.length, 5);
equal(results.data.rows[0].rev, 1, "Rev parameter is correct");
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");
deepEqual(results.data.rows[0].doc, { deepEqual(results.data.rows[0].doc, {
"k2": "w1" "k2": "w1"
}); });
...@@ -647,17 +694,13 @@ ...@@ -647,17 +694,13 @@
}) })
.push(function () { .push(function () {
return jio.allDocs({ return jio.allDocs({
limit: [1, 4], query: "_REVISION: <= 3",
revision_limit: [0, 4] limit: [1, 4]
}); });
}) })
.push(function (results) { .push(function (results) {
equal(results.data.rows.length, 4, equal(results.data.rows.length, 4,
"Correct number of results with options.limit set"); "Correct number of results with optins.limit set");
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, { deepEqual(results.data.rows[0].doc, {
"k2": "w0" "k2": "w0"
}, "Correct results with options.limit set"); }, "Correct results with options.limit set");
......
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