Commit c6db8c50 authored by Bryan Kaperick's avatar Bryan Kaperick

Made changes to simplify buildQuery revision queries allowing any property names possible.

parent 4306f29f
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"use strict"; "use strict";
// Used to distinguish between operations done within the same millisecond // Used to distinguish between operations done within the same millisecond
var unique_timestamp = function (time) { function generateUniqueTimestamp(time) {
// XXX: replace this with UUIDStorage function call to S4() when it becomes // XXX: replace this with UUIDStorage function call to S4() when it becomes
// publicly accessible // publicly accessible
...@@ -13,15 +13,28 @@ ...@@ -13,15 +13,28 @@
//timestamp = Date.now().toString(); //timestamp = Date.now().toString();
timestamp = time.toString(); timestamp = time.toString();
return timestamp + "-" + uuid; return timestamp + "-" + uuid;
}, }
looks_like_timestamp = function (id) {
//1529928772623-02e6 function isTimestamp(id) {
//A timestamp is of the form //A timestamp is of the form
//"[13 digit number]-[4 numbers/lowercase letters]" //"[13 digit number]-[4 numbers/lowercase letters]"
var re = /^[0-9]{13}-[a-z0-9]{4}$/; var re = /^[0-9]{13}-[a-z0-9]{4}$/;
return re.test(id); return re.test(id);
}; }
function throwCantFindError(id) {
throw new jIO.util.jIOError(
"HistoryStorage: cannot find object '" + id + "'",
404
);
}
function throwRemovedError(id) {
throw new jIO.util.jIOError(
"HistoryStorage: cannot find object '" + id + "' (removed)",
404
);
}
/** /**
* The jIO HistoryStorage extension * The jIO HistoryStorage extension
...@@ -31,11 +44,28 @@ ...@@ -31,11 +44,28 @@
*/ */
function HistoryStorage(spec) { function HistoryStorage(spec) {
this._sub_storage = jIO.createJIO(spec.sub_storage); this._sub_storage = jIO.createJIO(spec.sub_storage);
this._timestamps = {};
} }
HistoryStorage.prototype.get = function (id_in) { HistoryStorage.prototype.get = function (id_in) {
if (isTimestamp(id_in)) {
// Try to treat id_in as a timestamp instead of a name
return this._sub_storage.get(id_in)
.push(function (result) {
if (result.op === "put") {
return result.doc;
}
throwCantFindError(id_in);
}, function (error) {
if (error.status_code === 404 &&
error instanceof jIO.util.jIOError) {
throwRemovedError(id_in);
}
throw error;
});
}
// 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,
...@@ -66,65 +96,23 @@ ...@@ -66,65 +96,23 @@
return substorage.get(results.data.rows[0].id) return substorage.get(results.data.rows[0].id)
.push(function (result) { .push(function (result) {
return result.doc; return result.doc;
}, function (error) {
if (error.status_code === 400 &&
error instanceof jIO.util.jIOError) {
throw new jIO.util.jIOError(
"HistoryStorage: cannot find object '" + id_in +
"'",
404
);
}
}); });
} }
throw new jIO.util.jIOError( throwRemovedError(id_in);
"HistoryStorage: cannot find object '" + id_in + "' (removed)",
404
);
} }
// Try again by treating id_in as a timestamp instead of a name throwCantFindError(id_in);
return substorage.get(id_in)
.push(function (result) {
if (result.op === "put") {
return result.doc;
}
throw new jIO.util.jIOError(
"HistoryStorage: cannot find object '" + id_in +
"' (removed)",
404
);
}, function (error) {
if (error.status_code === 400 &&
error instanceof jIO.util.jIOError) {
throw new jIO.util.jIOError(
"HistoryStorage: cannot find object '" + id_in + "'",
404
);
}
});
}); });
}; };
HistoryStorage.prototype.put = function (id, data) { HistoryStorage.prototype.put = function (id, data) {
if (data.hasOwnProperty("_timestamp")) {
throw new jIO.util.jIOError( if (isTimestamp(id)) {
"Document cannot have metadata attribute '_timestamp'",
422
);
}
if (data.hasOwnProperty("_doc_id")) {
throw new jIO.util.jIOError(
"Document cannot have metadata attribute '_doc_id'",
422
);
}
if (looks_like_timestamp(id)) {
throw new jIO.util.jIOError( throw new jIO.util.jIOError(
"Document cannot have id of the same form as a timestamp", "Document cannot have id of the same form as a timestamp",
422 422
); );
} }
var timestamp = unique_timestamp(Date.now()), var timestamp = generateUniqueTimestamp(Date.now()),
metadata = { metadata = {
// XXX: remove this attribute once query can sort_on id // XXX: remove this attribute once query can sort_on id
timestamp: timestamp, timestamp: timestamp,
...@@ -132,60 +120,102 @@ ...@@ -132,60 +120,102 @@
doc: data, doc: data,
op: "put" op: "put"
}; };
if (this._timestamps.hasOwnProperty(id)) {
this._timestamps[id].push(timestamp);
} else {
this._timestamps[id] = [timestamp];
}
return this._sub_storage.put(timestamp, metadata); return this._sub_storage.put(timestamp, metadata);
}; };
HistoryStorage.prototype.remove = function (id) { HistoryStorage.prototype.remove = function (id) {
var timestamp = unique_timestamp(Date.now() - 1), var timestamp = generateUniqueTimestamp(Date.now() - 1),
metadata = { metadata = {
// 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"
}; };
this._timestamps[id].push(timestamp);
return this._sub_storage.put(timestamp, metadata); return this._sub_storage.put(timestamp, metadata);
}; };
HistoryStorage.prototype.allAttachments = function (id) { HistoryStorage.prototype.allAttachments = function (id) {
// XXX: If instead you passed a timestamp in as `id`, we could retrieve all // XXX: allAttachments with timestamp:
// the attachments of the document at that point in time. Not sure if this // should return all non-removed attachments at this point in time
// would be useful.
var substorage = this._sub_storage, var substorage = this._sub_storage,
// Include id as value in query object for safety (as opposed to string query_obj,
// concatenation) query_removed_check,
query_obj = new ComplexQuery({ options,
operator: "AND", query_doc_id,
query_list: [ options_remcheck;
new SimpleQuery({key: "doc_id", value: id}),
new ComplexQuery({
operator: "OR",
query_list: [
new SimpleQuery({key: "op", value: "putAttachment"}),
new SimpleQuery({key: "op", value: "removeAttachment"})
]
})
]
}),
// Only query for attachment edits if (isTimestamp(id)) {
options = { query_doc_id = new SimpleQuery({
query: query_obj, operator: "<=",
sort_on: [["timestamp", "descending"]], key: "timestamp",
select_list: ["op", "timestamp", "name"] value: id
}; });
return this._sub_storage.allDocs(options) } else {
query_doc_id = new SimpleQuery({key: "doc_id", value: id});
}
query_removed_check = new ComplexQuery({
operator: "AND",
query_list: [
query_doc_id,
new ComplexQuery({
operator: "OR",
query_list: [
new SimpleQuery({key: "op", value: "put"}),
new SimpleQuery({key: "op", value: "remove"})
]
})
]
});
query_obj = new ComplexQuery({
operator: "AND",
query_list: [
query_doc_id,
new ComplexQuery({
operator: "OR",
query_list: [
new SimpleQuery({key: "op", value: "putAttachment"}),
new SimpleQuery({key: "op", value: "removeAttachment"})
]
})
]
});
options_remcheck = {
query: query_removed_check,
select_list: ["op", "timestamp"],
//limit: [0, 1],
sort_on: [["timestamp", "descending"]]
};
options = {
query: query_obj,
sort_on: [["timestamp", "descending"]],
select_list: ["op", "name"]
};
return this._sub_storage.allDocs(options_remcheck)
.push(function (results) {
if (results.data.total_rows > 0) {
if (results.data.rows[0].value.op === "remove") {
throwRemovedError(id);
}
} else {
throwCantFindError(id);
}
})
.push(function () {
return substorage.allDocs(options);
})
.push(function (results) { .push(function (results) {
var seen = {}, var seen = {},
attachments = [], attachments = [],
attachment_promises = [], attachment_promises = [],
ind, ind,
entry; entry;
// Only return attachments if:
// (it is the most recent revision) AND (it is a putAttachment)
attachments = results.data.rows.filter(function (docum) { attachments = results.data.rows.filter(function (docum) {
if (!seen.hasOwnProperty(docum.value.name)) { if (!seen.hasOwnProperty(docum.value.name)) {
var output = (docum.value.op === "putAttachment"); var output = (docum.value.op === "putAttachment");
...@@ -193,17 +223,19 @@ ...@@ -193,17 +223,19 @@
return output; return output;
} }
}); });
// Assembles object of attachment_name: attachment_object
for (ind = 0; ind < attachments.length; ind += 1) { for (ind = 0; ind < attachments.length; ind += 1) {
entry = attachments[ind]; entry = attachments[ind];
attachment_promises[entry.value.name] = attachment_promises[entry.value.name] =
substorage.getAttachment(entry.id, entry.value.name); substorage.getAttachment(entry.id, entry.value.name);
} }
return RSVP.hash(attachment_promises); return RSVP.hash(attachment_promises);
}); });
}; };
HistoryStorage.prototype.putAttachment = function (id, name, blob) { HistoryStorage.prototype.putAttachment = function (id, name, blob) {
var timestamp = unique_timestamp(Date.now()), var timestamp = generateUniqueTimestamp(Date.now()),
metadata = { metadata = {
// XXX: remove this attribute once query can sort_on id // XXX: remove this attribute once query can sort_on id
timestamp: timestamp, timestamp: timestamp,
...@@ -212,11 +244,6 @@ ...@@ -212,11 +244,6 @@
op: "putAttachment" op: "putAttachment"
}, },
substorage = this._sub_storage; substorage = this._sub_storage;
if (this._timestamps[id].hasOwnProperty(name)) {
this._timestamps[id][name].push(timestamp);
} else {
this._timestamps[id][name] = [timestamp];
}
return this._sub_storage.put(timestamp, metadata) return this._sub_storage.put(timestamp, metadata)
.push(function () { .push(function () {
return substorage.putAttachment(timestamp, name, blob); return substorage.putAttachment(timestamp, name, blob);
...@@ -225,6 +252,17 @@ ...@@ -225,6 +252,17 @@
HistoryStorage.prototype.getAttachment = function (id, name) { HistoryStorage.prototype.getAttachment = function (id, name) {
if (isTimestamp(id)) {
return this._sub_storage.getAttachment(id, name)
.push(undefined, function (error) {
if (error.status_code === 404 &&
error instanceof jIO.util.jIOError) {
throwCantFindError(id);
}
throw error;
});
}
// 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,
...@@ -268,39 +306,16 @@ ...@@ -268,39 +306,16 @@
if (results.data.rows.length > 0) { if (results.data.rows.length > 0) {
if (results.data.rows[0].value.op === "remove" || if (results.data.rows[0].value.op === "remove" ||
results.data.rows[0].value.op === "removeAttachment") { results.data.rows[0].value.op === "removeAttachment") {
throw new jIO.util.jIOError( throwRemovedError(id);
"HistoryStorage: cannot find object '" + id + "' (removed)",
404
);
} }
return substorage.getAttachment(results.data.rows[0].id, name) return substorage.getAttachment(results.data.rows[0].id, name);
.push(undefined, function (error) {
if (error.status_code === 404 &&
error instanceof jIO.util.jIOError) {
throw new jIO.util.jIOError(
"HistoryStorage: cannot find object '" + id + "'",
404
);
}
throw error;
});
} }
return substorage.getAttachment(id, name) throwCantFindError(id);
.push(undefined, function (error) {
if (error.status_code === 404 &&
error instanceof jIO.util.jIOError) {
throw new jIO.util.jIOError(
"HistoryStorage: cannot find object '" + id + "'",
404
);
}
throw error;
});
}); });
}; };
HistoryStorage.prototype.removeAttachment = function (id, name) { HistoryStorage.prototype.removeAttachment = function (id, name) {
var timestamp = unique_timestamp(Date.now()), var timestamp = generateUniqueTimestamp(Date.now()),
metadata = { metadata = {
// XXX: remove this attribute once query can sort_on id // XXX: remove this attribute once query can sort_on id
timestamp: timestamp, timestamp: timestamp,
...@@ -308,7 +323,6 @@ ...@@ -308,7 +323,6 @@
name: name, name: name,
op: "removeAttachment" op: "removeAttachment"
}; };
this._timestamps[id][name].push(timestamp);
return this._sub_storage.put(timestamp, metadata); return this._sub_storage.put(timestamp, metadata);
}; };
HistoryStorage.prototype.repair = function () { HistoryStorage.prototype.repair = function () {
...@@ -319,49 +333,27 @@ ...@@ -319,49 +333,27 @@
}; };
HistoryStorage.prototype.buildQuery = function (options) { HistoryStorage.prototype.buildQuery = function (options) {
// XXX: if include_revisions, we should also include the document results
// for different edits of attachments
// Set default values // Set default values
if (options === undefined) { if (options === undefined) {options = {}; }
options = {}; if (options.query === undefined) {options.query = ""; }
} if (options.sort_on === undefined) {options.sort_on = []; }
if (options.query === undefined) { if (options.select_list === undefined) {options.select_list = []; }
options.query = ""; if (options.include_revisions === undefined) {
} options.include_revisions = false;
if (options.sort_on === undefined) {
options.sort_on = [];
}
if (options.select_list === undefined) {
options.select_list = [];
} }
options.sort_on.push(["timestamp", "descending"]); options.sort_on.push(["timestamp", "descending"]);
options.query = jIO.QueryFactory.create(options.query); options.query = jIO.QueryFactory.create(options.query);
var meta_options, var meta_options,
substorage = this._sub_storage, substorage = this._sub_storage,
// Check if query involved _REVISION. If not, we will later place a // Check if query involved _timestamp.
// (*) AND (_REVISION: =0) as the default handling of revisions // If not, use default behavior and only query on latest revisions
rev_query = false, rev_query = options.include_revisions,
query_obj = options.query, doc_id_name,
query_stack = [], timestamp_name;
ind;
if (query_obj instanceof ComplexQuery) {
query_stack.push(query_obj);
} else {
rev_query = (query_obj.key === "_timestamp");
}
// Traverse through query tree to find mentions of _timestamp
// and stop as soon as it is found once
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 === "_timestamp") {
rev_query = true;
break;
}
}
}
// Query for all edits putting or removing documents (and nothing about // Query for all edits putting or removing documents (and nothing about
// attachments) // attachments)
...@@ -382,7 +374,8 @@ ...@@ -382,7 +374,8 @@
.push(function (results) { .push(function (results) {
var seen = {}, var seen = {},
query_matches, query_matches,
docs_to_query; docs_to_query,
i;
// If !rev_query, then by default only consider latest revisions of // If !rev_query, then by default only consider latest revisions of
// documents // documents
results = results.filter(function (docum) { results = results.filter(function (docum) {
...@@ -395,29 +388,47 @@ ...@@ -395,29 +388,47 @@
} }
return false; return false;
}); });
// If any documents have property _doc_id, __doc_id, etc, then set
// doc_id_name to the first string which is not a property of any
// of the documents
doc_id_name = "_doc_id";
timestamp_name = "_timestamp";
for (i = 0; i < results.length; i += 1) {
while (results[i].doc.hasOwnProperty(doc_id_name)) {
doc_id_name = "_" + doc_id_name;
}
while (results[i].doc.hasOwnProperty(timestamp_name)) {
timestamp_name = "_" + timestamp_name;
}
}
docs_to_query = results.map(function (docum) { docs_to_query = results.map(function (docum) {
// If it's a "remove" operation // If it's a "remove" operation then it has no doc property
if (!docum.hasOwnProperty("doc")) { if (!docum.hasOwnProperty("doc")) {
docum.doc = {}; docum.doc = {};
} }
docum.doc._doc_id = docum.doc_id; docum.doc[doc_id_name] = docum.doc_id;
docum.doc._timestamp = docum.timestamp; docum.doc[timestamp_name] = docum.timestamp;
return docum.doc; return docum.doc;
}); });
options.select_list.push("_doc_id"); options.select_list.push(doc_id_name);
options.select_list.push(timestamp_name);
query_matches = options.query.exec(docs_to_query, options); query_matches = options.query.exec(docs_to_query, options);
return query_matches; return query_matches;
}) })
// Format the results of the query, and return // Format the results of the query, and return
.push(function (query_matches) { .push(function (query_matches) {
return query_matches.map(function (docum) { return query_matches.map(function (docum) {
var doc_id = docum._doc_id; var doc_id = docum[doc_id_name],
delete docum._timestamp; time = docum[timestamp_name];
delete docum._doc_id; delete docum[timestamp_name];
delete docum[doc_id_name];
return { return {
doc: {}, doc: {},
value: docum, value: docum,
id: doc_id id: doc_id,
timestamp: time
}; };
}); });
}); });
......
...@@ -64,36 +64,47 @@ ...@@ -64,36 +64,47 @@
test("Testing proper adding/removing attachments", test("Testing proper adding/removing attachments",
function () { function () {
stop(); stop();
expect(9); expect(10);
var jio = this.jio, var jio = this.jio,
timestamps = this.jio.__storage._timestamps, not_history = this.not_history,
timestamps,
blob2 = this.blob2, blob2 = this.blob2,
blob1 = this.blob1, blob1 = this.blob1,
other_blob = this.other_blob, other_blob = this.other_blob,
otherother_blob = new Blob(['abcabc']); otherother_blob = new Blob(['abcabc']);
jio.put("doc", {title: "foo0"}) jio.put("doc", {title: "foo0"}) // 0
.push(function () { .push(function () {
return jio.put("doc2", {key: "val"}); return jio.put("doc2", {key: "val"}); // 1
}) })
.push(function () { .push(function () {
return jio.putAttachment("doc", "attacheddata", blob1); return jio.putAttachment("doc", "attacheddata", blob1); // 2
}) })
.push(function () { .push(function () {
return jio.putAttachment("doc", "attacheddata", blob2); return jio.putAttachment("doc", "attacheddata", blob2); // 3
}) })
.push(function () { .push(function () {
return jio.putAttachment("doc", "other_attacheddata", other_blob); return jio.putAttachment("doc", "other_attacheddata", other_blob);// 4
}) })
.push(function () { .push(function () {
return jio.putAttachment( return jio.putAttachment( // 5
"doc", "doc",
"otherother_attacheddata", "otherother_attacheddata",
otherother_blob otherother_blob
); );
}) })
.push(function () { .push(function () {
return jio.removeAttachment("doc", "otherother_attacheddata"); return jio.removeAttachment("doc", "otherother_attacheddata"); // 6
})
.push(function () {
return not_history.allDocs({
sort_on: [["timestamp", "ascending"]]
});
})
.push(function (results) {
timestamps = results.data.rows.map(function (d) {
return d.id;
});
}) })
.push(function () { .push(function () {
return jio.get("doc"); return jio.get("doc");
...@@ -110,7 +121,7 @@ ...@@ -110,7 +121,7 @@
"Return the attachment information with getAttachment" "Return the attachment information with getAttachment"
); );
return jio.getAttachment( return jio.getAttachment(
timestamps.doc.attacheddata[1], timestamps[3],
"attacheddata" "attacheddata"
); );
}) })
...@@ -121,7 +132,7 @@ ...@@ -121,7 +132,7 @@
"current revision" "current revision"
); );
return jio.getAttachment( return jio.getAttachment(
timestamps.doc.attacheddata[0], timestamps[2],
"attacheddata" "attacheddata"
); );
}, function (error) { }, function (error) {
...@@ -134,7 +145,7 @@ ...@@ -134,7 +145,7 @@
"Return the attachment information with getAttachment for " + "Return the attachment information with getAttachment for " +
"previous revision" "previous revision"
); );
return jio.getAttachment(timestamps.doc[0], "attached"); return jio.getAttachment(timestamps[0], "attached");
}, function (error) { }, function (error) {
ok(false, error); ok(false, error);
}) })
...@@ -146,6 +157,9 @@ ...@@ -146,6 +157,9 @@
deepEqual(error.status_code, deepEqual(error.status_code,
404, 404,
"Error if you try to go back to a nonexistent timestamp"); "Error if you try to go back to a nonexistent timestamp");
deepEqual(error.message,
"HistoryStorage: cannot find object '" + timestamps[0] + "'",
"Error caught by history storage correctly");
return jio.getAttachment("doc", "other_attacheddata"); return jio.getAttachment("doc", "other_attacheddata");
}) })
.push(function (result) { .push(function (result) {
...@@ -208,10 +222,10 @@ ...@@ -208,10 +222,10 @@
.always(function () {start(); }); .always(function () {start(); });
}); });
test("Correctness of allAttachments method", test("Correctness of allAttachments method on current attachments",
function () { function () {
stop(); stop();
expect(11); expect(14);
var jio = this.jio, var jio = this.jio,
not_history = this.not_history, not_history = this.not_history,
blob1 = this.blob1, blob1 = this.blob1,
...@@ -317,7 +331,7 @@ ...@@ -317,7 +331,7 @@
})); }));
}) })
.push(function (results) { .push(function (results) {
equal(results.length, 7, "Seven document revisions in storage (17)"); equal(results.length, 7, "Seven document revisions in storage");
return jio.remove("doc"); return jio.remove("doc");
}) })
.push(function () { .push(function () {
...@@ -332,6 +346,132 @@ ...@@ -332,6 +346,132 @@
404, 404,
"Cannot get the attachment of a removed document"); "Cannot get the attachment of a removed document");
}) })
.push(function () {
return jio.allAttachments("doc");
})
.push(function () {
ok(false, "This query should have thrown a 404 error");
},
function (error) {
ok(error instanceof jIO.util.jIOError, "throws a jio error");
deepEqual(error.status_code,
404,
"allAttachments of a removed document throws a 404 error");
deepEqual(error.message,
"HistoryStorage: cannot find object 'doc' (removed)",
"Error is handled by Historystorage.");
})
.fail(function (error) {
//console.log(error);
ok(false, error);
})
.always(function () {start(); });
});
test("Correctness of allAttachments method on older revisions",
function () {
stop();
expect(8);
var jio = this.jio,
not_history = this.not_history,
blob1 = new Blob(['a']),
blob11 = new Blob(['ab']),
blob2 = new Blob(['abc']),
blob22 = new Blob(['abcd']),
timestamps;
jio.put("doc", {title: "foo0"}) // 0
.push(function () {
return jio.putAttachment("doc", "data", blob1);
})
.push(function () {
return jio.putAttachment("doc", "data2", blob2);
})
.push(function () {
return jio.put("doc", {title: "foo1"}); // 1
})
.push(function () {
return jio.removeAttachment("doc", "data2");
})
.push(function () {
return jio.put("doc", {title: "foo2"}); // 2
})
.push(function () {
return jio.putAttachment("doc", "data", blob11);
})
.push(function () {
return jio.remove("doc"); // 3
})
.push(function () {
return jio.put("doc", {title: "foo3"}); // 4
})
.push(function () {
return jio.putAttachment("doc", "data2", blob22);
})
.push(function () {
return not_history.allDocs({
query: "op: put OR op: remove",
sort_on: [["timestamp", "ascending"]],
select_list: ["timestamp"]
});
})
.push(function (results) {
timestamps = results.data.rows.map(function (d) {
return d.value.timestamp;
});
})
.push(function () {
return jio.allAttachments("doc");
})
.push(function (results) {
deepEqual(results, {
"data": blob11,
"data2": blob22
},
"Current state of document is correct");
return jio.allAttachments(timestamps[0]);
})
.push(function (results) {
deepEqual(results, {}, "First version of document has 0 attachments");
return jio.allAttachments(timestamps[1]);
})
.push(function (results) {
deepEqual(results, {
data: blob1,
data2: blob2
}, "Both attachments are included in allAttachments");
return jio.allAttachments(timestamps[2]);
})
.push(function (results) {
deepEqual(results, {
data: blob1
}, "Removed attachment does not show up in allAttachments");
return jio.allAttachments(timestamps[3]);
})
.push(function () {
ok(false, "This query should have thrown a 404 error");
},
function (error) {
ok(error instanceof jIO.util.jIOError, "throws a jio error");
deepEqual(error.status_code,
404,
"allAttachments of a removed document throws a 404 error");
deepEqual(error.message,
"HistoryStorage: cannot find object '" + timestamps[3] +
"' (removed)",
"Error is handled by Historystorage.");
})
.push(function () {
return jio.allAttachments(timestamps[4]);
})
.push(function (results) {
deepEqual(results, {
data: blob11
});
})
.fail(function (error) { .fail(function (error) {
//console.log(error); //console.log(error);
ok(false, error); ok(false, error);
...@@ -377,49 +517,48 @@ ...@@ -377,49 +517,48 @@
test("Handling bad input", test("Handling bad input",
function () { function () {
stop(); stop();
expect(6); expect(2);
var jio = this.jio, var jio = this.jio,
BADINPUT_ERRCODE = 422; BADINPUT_ERRCODE = 422;
jio.put("doc", { jio.put("1234567891123-ab7d", {})
"_timestamp": 3,
"other_attr": "other_val"
})
.push(function () { .push(function () {
ok(false, "This statement should not be reached"); ok(false, "This statement should not be reached");
}, function (error) { }, function (error) {
ok(error instanceof jIO.util.jIOError, "Correct type of error"); ok(error instanceof jIO.util.jIOError, "Correct type of error");
deepEqual(error.status_code, deepEqual(error.status_code,
BADINPUT_ERRCODE, BADINPUT_ERRCODE,
"Can't save a document with a reserved keyword" "Can't save a document with a timestamp-formatted id"
); );
}) })
.push(function () { .fail(function (error) {
return jio.put("doc", { //console.log(error);
"_doc_id": 3, ok(false, error);
"other_attr": "other_val"
});
})
.push(function () {
ok(false, "This statement should not be reached");
}, function (error) {
ok(error instanceof jIO.util.jIOError, "Correct type of error");
deepEqual(error.status_code,
BADINPUT_ERRCODE,
"Can't save a document with a reserved keyword"
);
}) })
.always(function () {start(); });
});
test("Getting a non-existent document",
function () {
stop();
expect(3);
var jio = this.jio;
jio.put("not_doc", {})
.push(function () { .push(function () {
return jio.put("1234567891123-ab7d", {}); return jio.get("doc");
}) })
.push(function () { .push(function () {
ok(false, "This statement should not be reached"); ok(false, "This statement should not be reached");
}, function (error) { }, function (error) {
//console.log(error);
ok(error instanceof jIO.util.jIOError, "Correct type of error"); ok(error instanceof jIO.util.jIOError, "Correct type of error");
deepEqual(error.status_code, deepEqual(error.status_code,
BADINPUT_ERRCODE, 404,
"Can't save a document with a timestamp-formatted id" "Correct status code for getting a non-existent document"
); );
deepEqual(error.message,
"HistoryStorage: cannot find object 'doc'",
"Error is handled by history storage before reaching console");
}) })
.fail(function (error) { .fail(function (error) {
//console.log(error); //console.log(error);
...@@ -431,31 +570,39 @@ ...@@ -431,31 +570,39 @@
test("Creating a document with put and retrieving it with get", test("Creating a document with put and retrieving it with get",
function () { function () {
stop(); stop();
expect(4); expect(7);
var jio = this.jio, var jio = this.jio,
not_history = this.not_history, not_history = this.not_history,
timestamps = jio.__storage._timestamps; timestamps;
jio.put("doc", {title: "version0"}) jio.put("doc", {title: "version0"})
.push(function () { .push(function () {
ok(timestamps.hasOwnProperty("doc"), return not_history.allDocs({
"jio._timestamps is updated with new document."); select_list: ["timestamp"]
equal(timestamps.doc.length, });
})
.push(function (results) {
timestamps = results.data.rows.map(function (d) {
return d.value.timestamp;
});
})
.push(function () {
equal(timestamps.length,
1, 1,
"One revision is logged in jio._timestamps" "One revision is saved in storage"
); );
return jio.get(timestamps.doc[0]); return jio.get(timestamps[0]);
}) })
.push(function (result) { .push(function (result) {
deepEqual(result, { deepEqual(result, {
title: "version0" title: "version0"
}, "Get document from history storage"); }, "Get document from history storage");
return not_history.get( return not_history.get(
timestamps.doc[0] timestamps[0]
); );
}) })
.push(function (result) { .push(function (result) {
deepEqual(result, { deepEqual(result, {
timestamp: timestamps.doc[0], timestamp: timestamps[0],
op: "put", op: "put",
doc_id: "doc", doc_id: "doc",
doc: { doc: {
...@@ -463,6 +610,31 @@ ...@@ -463,6 +610,31 @@
} }
}, "Get document from non-history storage"); }, "Get document from non-history storage");
}) })
.push(function () {
return jio.get("non-existent-doc");
})
.push(function () {
ok(false, "This should have thrown an error");
}, function (error) {
//console.log(error);
ok(error instanceof jIO.util.jIOError, "Correct type of error");
deepEqual(error.status_code,
404,
"Can't access non-existent document"
);
})
.push(function () {
return jio.get("1234567891123-abcd");
})
.push(function () {
ok(false, "Trying to get a non-existent id should have raised 404");
}, function (error) {
ok(error instanceof jIO.util.jIOError, "Correct type of error");
deepEqual(error.status_code,
404,
"Can't access document by getting with non-existent id"
);
})
.fail(function (error) { .fail(function (error) {
//console.log(error); //console.log(error);
ok(false, error); ok(false, error);
...@@ -475,7 +647,8 @@ ...@@ -475,7 +647,8 @@
stop(); stop();
expect(7); expect(7);
var jio = this.jio, var jio = this.jio,
timestamps = this.jio.__storage._timestamps; not_history = this.not_history,
timestamps;
return jio.put("doc", {title: "t0", subtitle: "s0"}) return jio.put("doc", {title: "t0", subtitle: "s0"})
.push(function () { .push(function () {
...@@ -490,6 +663,17 @@ ...@@ -490,6 +663,17 @@
.push(function () { .push(function () {
return jio.put("doc", {title: "t3", subtitle: "s3"}); return jio.put("doc", {title: "t3", subtitle: "s3"});
}) })
.push(function () {
return not_history.allDocs({
select_list: ["timestamp"],
sort_on: [["timestamp", "ascending"]]
});
})
.push(function (results) {
timestamps = results.data.rows.map(function (d) {
return d.value.timestamp;
});
})
.push(function () { .push(function () {
return jio.get("doc"); return jio.get("doc");
}) })
...@@ -498,7 +682,7 @@ ...@@ -498,7 +682,7 @@
title: "t3", title: "t3",
subtitle: "s3" subtitle: "s3"
}, "Get returns latest revision"); }, "Get returns latest revision");
return jio.get(timestamps.doc[0]); return jio.get(timestamps[0]);
}, function (err) { }, function (err) {
ok(false, err); ok(false, err);
}) })
...@@ -507,14 +691,14 @@ ...@@ -507,14 +691,14 @@
title: "t0", title: "t0",
subtitle: "s0" subtitle: "s0"
}, "Get returns first version"); }, "Get returns first version");
return jio.get(timestamps.doc[1]); return jio.get(timestamps[1]);
}) })
.push(function (result) { .push(function (result) {
deepEqual(result, { deepEqual(result, {
title: "t1", title: "t1",
subtitle: "s1" subtitle: "s1"
}, "Get returns second version"); }, "Get returns second version");
return jio.get(timestamps.doc[2]); return jio.get(timestamps[2]);
}, function (err) { }, function (err) {
ok(false, err); ok(false, err);
}) })
...@@ -523,20 +707,20 @@ ...@@ -523,20 +707,20 @@
title: "t2", title: "t2",
subtitle: "s2" subtitle: "s2"
}, "Get returns third version"); }, "Get returns third version");
return jio.get(timestamps.doc[3]); return jio.get(timestamps[3]);
}, function (err) { }, function (err) {
ok(false, err); ok(false, err);
}) })
.push(function () { .push(function () {
ok(false, "This should have thrown a 404 error"); ok(false, "This should have thrown a 404 error");
return jio.get(timestamps.doc[4]); return jio.get(timestamps[4]);
}, },
function (error) { function (error) {
ok(error instanceof jIO.util.jIOError, "Correct type of error"); ok(error instanceof jIO.util.jIOError, "Correct type of error");
deepEqual(error.status_code, deepEqual(error.status_code,
404, 404,
"Error if you try to go back more revisions than what exists"); "Error if you try to go back more revisions than what exists");
return jio.get(timestamps.doc[4]); return jio.get(timestamps[4]);
}) })
.push(function (result) { .push(function (result) {
deepEqual(result, { deepEqual(result, {
...@@ -674,8 +858,18 @@ ...@@ -674,8 +858,18 @@
stop(); stop();
expect(7); expect(7);
var jio = this.jio, var jio = this.jio,
not_history = this.not_history; not_history = this.not_history,
timestamp;
jio.put("doc", {title: "version0"}) jio.put("doc", {title: "version0"})
.push(function () {
return not_history.allDocs({
query: "doc_id: doc",
select_list: ["timestamp"]
});
})
.push(function (results) {
timestamp = results.data.rows[0].value.timestamp;
})
.push(function () { .push(function () {
return RSVP.all([ return RSVP.all([
jio.allDocs(), jio.allDocs(),
...@@ -701,7 +895,8 @@ ...@@ -701,7 +895,8 @@
deepEqual(results.data.rows[0], { deepEqual(results.data.rows[0], {
doc: {}, doc: {},
value: {}, value: {},
id: "doc" id: "doc",
timestamp: timestamp
}, },
"Correct document format is returned." "Correct document format is returned."
); );
...@@ -715,7 +910,7 @@ ...@@ -715,7 +910,7 @@
}) })
.push(function (result) { .push(function (result) {
deepEqual(result, { deepEqual(result, {
timestamp: jio.__storage._timestamps.doc[0], timestamp: timestamp,
doc_id: "doc", doc_id: "doc",
doc: { doc: {
title: "version0" title: "version0"
...@@ -733,14 +928,69 @@ ...@@ -733,14 +928,69 @@
.always(function () {start(); }); .always(function () {start(); });
}); });
test("Putting doc with _doc_id and _timestamp properties" +
"and retrieving them with allDocs",
function () {
stop();
expect(1);
var jio = this.jio,
not_history = this.not_history,
timestamp;
jio.put("doc", {
title: "version0",
_doc_id: "bar",
__doc_id: "bar2",
___doc_id: "bar3",
_timestamp: "foo",
____timestamp: "foo2"
})
.push(function () {
return not_history.allDocs({
query: "doc_id: doc",
select_list: ["timestamp"]
});
})
.push(function (results) {
timestamp = results.data.rows[0].value.timestamp;
})
.push(function () {
return jio.allDocs({
query: "title: version0 AND _timestamp: >= 0",
select_list: ["title", "_doc_id", "__doc_id", "___doc_id",
"_timestamp", "____timestamp"]
});
})
.push(function (results) {
deepEqual(results.data.rows, [
{
doc: {},
id: "doc",
value: {
title: "version0",
_doc_id: "bar",
__doc_id: "bar2",
___doc_id: "bar3",
_timestamp: "foo",
____timestamp: "foo2"
},
timestamp: timestamp
}],
"_doc_id properties are not overwritten in allDocs call");
})
.fail(function (error) {
//console.log(error);
ok(false, error);
})
.always(function () {start(); });
});
test("Putting a document, revising it, and retrieving revisions with allDocs", test("Putting a document, revising it, and retrieving revisions with allDocs",
function () { function () {
stop(); stop();
expect(14); expect(10);
var jio = this.jio, var jio = this.jio,
not_history = this.not_history, not_history = this.not_history,
timestamps = this.jio.__storage._timestamps; timestamps;
jio.put("doc", { jio.put("doc", {
title: "version0", title: "version0",
subtitle: "subvers0" subtitle: "subvers0"
...@@ -757,6 +1007,17 @@ ...@@ -757,6 +1007,17 @@
subtitle: "subvers2" subtitle: "subvers2"
}); });
}) })
.push(function () {
return not_history.allDocs({
select_list: ["timestamp"],
sort_on: [["timestamp", "ascending"]]
});
})
.push(function (results) {
timestamps = results.data.rows.map(function (d) {
return d.value.timestamp;
});
})
.push(function () { .push(function () {
return RSVP.all([ return RSVP.all([
jio.allDocs({select_list: ["title", "subtitle"]}), jio.allDocs({select_list: ["title", "subtitle"]}),
...@@ -803,73 +1064,52 @@ ...@@ -803,73 +1064,52 @@
subtitle: "subvers2" subtitle: "subvers2"
}, },
doc: {}, doc: {},
id: "doc" id: "doc",
timestamp: timestamps[2]
}, },
"Correct document format is returned." "Correct document format is returned."
); );
}) })
.push(function () { .push(function () {
// These are all equivalent queries in that they should return the
// same documents in the correct order
return RSVP.all([
jio.allDocs({
query: "_timestamp: " + timestamps.doc[1],
select_list: ["title", "subtitle"]
}),
jio.allDocs({
query: "_timestamp: =" + timestamps.doc[1],
select_list: ["title", "subtitle"]
}),
jio.allDocs({
query: "_timestamp: >" + timestamps.doc[0] +
" AND title: version1",
select_list: ["title", "subtitle"]
}),
jio.allDocs({
query: "_timestamp: > " + timestamps.doc[0] +
" AND _timestamp: < " + timestamps.doc[2],
select_list: ["title", "subtitle"]
})
]);
})
.push(function (results) {
equal(results[0].data.rows.length,
1,
"Querying a specific timestamp retrieves one document");
var ind = 0;
for (ind = 0; ind < results.length - 1; ind += 1) {
deepEqual(results[ind],
results[ind + 1],
"Each query returns exactly the same correct output"
);
}
return results[0];
})
.push(function (results) {
deepEqual(results.data.rows[0], {
id: "doc",
value: {
title: "version1",
subtitle: "subvers1"
},
doc: {}
});
return jio.allDocs({ return jio.allDocs({
query: "_timestamp: " + timestamps.doc[0], query: "",
select_list: ["title", "subtitle"] select_list: ["title", "subtitle"],
include_revisions: true
}); });
}) })
.push(function (results) { .push(function (results) {
equal(results.data.rows.length,
3,
"Querying with include_revisions retrieves all versions");
deepEqual(results.data.rows, [ deepEqual(results.data.rows, [
{ {
id: "doc",
value: {
title: "version2",
subtitle: "subvers2"
},
doc: {},
timestamp: timestamps[2]
},
{
id: "doc",
value: {
title: "version1",
subtitle: "subvers1"
},
doc: {},
timestamp: timestamps[1]
},
{
id: "doc",
value: { value: {
title: "version0", title: "version0",
subtitle: "subvers0" subtitle: "subvers0"
}, },
doc: {}, doc: {},
id: "doc" timestamp: timestamps[0]
} }
], "Query requesting one timestamp works."); ], "Full version history is included.");
return not_history.allDocs({ return not_history.allDocs({
sort_on: [["title", "ascending"]] sort_on: [["title", "ascending"]]
...@@ -883,7 +1123,7 @@ ...@@ -883,7 +1123,7 @@
.push(function (results) { .push(function (results) {
deepEqual(results, [ deepEqual(results, [
{ {
timestamp: timestamps.doc[0], timestamp: timestamps[0],
op: "put", op: "put",
doc_id: "doc", doc_id: "doc",
doc: { doc: {
...@@ -892,7 +1132,7 @@ ...@@ -892,7 +1132,7 @@
} }
}, },
{ {
timestamp: timestamps.doc[1], timestamp: timestamps[1],
op: "put", op: "put",
doc_id: "doc", doc_id: "doc",
doc: { doc: {
...@@ -901,7 +1141,7 @@ ...@@ -901,7 +1141,7 @@
} }
}, },
{ {
timestamp: timestamps.doc[2], timestamp: timestamps[2],
op: "put", op: "put",
doc_id: "doc", doc_id: "doc",
doc: { doc: {
...@@ -924,43 +1164,54 @@ ...@@ -924,43 +1164,54 @@
"Putting and removing documents, latest revisions and no removed documents", "Putting and removing documents, latest revisions and no removed documents",
function () { function () {
stop(); stop();
expect(5); expect(3);
var history = this.jio, var jio = this.jio,
timestamps = this.jio.__storage._timestamps; not_history = this.not_history,
timestamps;
history.put("doc_a", { jio.put("doc_a", {
title_a: "rev0", title_a: "rev0",
subtitle_a: "subrev0" subtitle_a: "subrev0"
}) })
.push(function () { .push(function () {
return history.put("doc_a", { return jio.put("doc_a", {
title_a: "rev1", title_a: "rev1",
subtitle_a: "subrev1" subtitle_a: "subrev1"
}); });
}) })
.push(function () { .push(function () {
return history.put("doc_b", { return jio.put("doc_b", {
title_b: "rev0", title_b: "rev0",
subtitle_b: "subrev0" subtitle_b: "subrev0"
}); });
}) })
.push(function () { .push(function () {
return history.remove("doc_b"); return jio.remove("doc_b");
}) })
.push(function () { .push(function () {
return history.put("doc_c", { return jio.put("doc_c", {
title_c: "rev0", title_c: "rev0",
subtitle_c: "subrev0" subtitle_c: "subrev0"
}); });
}) })
.push(function () { .push(function () {
return history.put("doc_c", { return jio.put("doc_c", {
title_c: "rev1", title_c: "rev1",
subtitle_c: "subrev1" subtitle_c: "subrev1"
}); });
}) })
.push(function () { .push(function () {
return history.allDocs({sort_on: [["timestamp", "descending"]]}); return not_history.allDocs({
sort_on: [["timestamp", "ascending"]]
});
})
.push(function (results) {
timestamps = results.data.rows.map(function (d) {
return d.id;
});
})
.push(function () {
return jio.allDocs({sort_on: [["timestamp", "descending"]]});
}) })
.push(function (results) { .push(function (results) {
equal(results.data.rows.length, equal(results.data.rows.length,
...@@ -971,24 +1222,20 @@ ...@@ -971,24 +1222,20 @@
{ {
id: "doc_c", id: "doc_c",
value: {}, value: {},
doc: {} doc: {},
timestamp: timestamps[5]
}, },
{ {
id: "doc_a", id: "doc_a",
value: {}, value: {},
doc: {} doc: {},
timestamp: timestamps[1]
} }
], ],
"Empty query returns latest revisions (and no removed documents)"); "Empty query returns latest revisions (and no removed documents)");
equal(timestamps.doc_a.length, equal(timestamps.length,
2, 6,
"Correct number of revisions logged in doc_a"); "Correct number of revisions logged");
equal(timestamps.doc_b.length,
2,
"Correct number of revisions logged in doc_b");
equal(timestamps.doc_c.length,
2,
"Correct number of revisions logged in doc_c");
}) })
.fail(function (error) { .fail(function (error) {
//console.log(error); //console.log(error);
...@@ -1006,7 +1253,9 @@ ...@@ -1006,7 +1253,9 @@
function () { function () {
stop(); stop();
expect(2); expect(2);
var history = this.jio, var jio = this.jio,
not_history = this.not_history,
timestamps,
docs = [ docs = [
{ {
"date": 1, "date": 1,
...@@ -1029,19 +1278,29 @@ ...@@ -1029,19 +1278,29 @@
new Blob(['bcd']), new Blob(['bcd']),
new Blob(['eeee']) new Blob(['eeee'])
]; ];
history.put("doc", {}) jio.put("doc", {}) // 0
.push(function () { .push(function () {
return putFullDoc(history, "doc", docs[0], "data", blobs[0]); return putFullDoc(jio, "doc", docs[0], "data", blobs[0]); // 1,2
}) })
.push(function () { .push(function () {
return putFullDoc(history, "second_doc", docs[1], "data", blobs[1]); return putFullDoc(jio, "second_doc", docs[1], "data", blobs[1]);// 3,4
}) })
.push(function () { .push(function () {
return putFullDoc(history, "third_doc", docs[2], "data", blobs[2]); return putFullDoc(jio, "third_doc", docs[2], "data", blobs[2]); // 5,6
}) })
.push(function () { .push(function () {
return history.allDocs({ return not_history.allDocs({
query: "(date: <= 2)", sort_on: [["timestamp", "ascending"]]
});
})
.push(function (results) {
timestamps = results.data.rows.map(function (d) {
return d.id;
});
})
.push(function () {
return jio.allDocs({
query: "NOT (date: > 2)",
select_list: ["date", "non-existent-key"], select_list: ["date", "non-existent-key"],
sort_on: [["date", "ascending"], sort_on: [["date", "ascending"],
["non-existent-key", "ascending"] ["non-existent-key", "ascending"]
...@@ -1054,17 +1313,20 @@ ...@@ -1054,17 +1313,20 @@
{ {
doc: {}, doc: {},
id: "doc", id: "doc",
value: {date: 1} value: {date: 1},
timestamp: timestamps[1]
}, },
{ {
doc: {}, doc: {},
id: "third_doc", id: "third_doc",
value: {date: 2} value: {date: 2},
timestamp: timestamps[5]
}, },
{ {
doc: {}, doc: {},
id: "second_doc", id: "second_doc",
value: {date: 2} value: {date: 2},
timestamp: timestamps[3]
} }
], ],
"Query gives correct results in correct order"); "Query gives correct results in correct order");
...@@ -1086,7 +1348,7 @@ ...@@ -1086,7 +1348,7 @@
expect(3); expect(3);
var jio = this.jio, var jio = this.jio,
not_history = this.not_history, not_history = this.not_history,
timestamps = this.jio.__storage._timestamps, timestamps,
docs = [ docs = [
{ {
"date": 1, "date": 1,
...@@ -1106,11 +1368,11 @@ ...@@ -1106,11 +1368,11 @@
new Blob(['bcd2']), new Blob(['bcd2']),
new Blob(['a3']) new Blob(['a3'])
]; ];
jio.put("doc", {}) jio.put("doc", {})// 0
.push(function () { .push(function () {// 1,2
return putFullDoc(jio, "doc", docs[0], "data", blobs[0]); return putFullDoc(jio, "doc", docs[0], "data", blobs[0]);
}) })
.push(function () { .push(function () {// 3,4
return putFullDoc(jio, "second_doc", docs[1], "data", blobs[1]); return putFullDoc(jio, "second_doc", docs[1], "data", blobs[1]);
}) })
.push(function () { .push(function () {
...@@ -1119,15 +1381,25 @@ ...@@ -1119,15 +1381,25 @@
docs[1].date = 4; docs[1].date = 4;
docs[1].type = "bar2"; docs[1].type = "bar2";
}) })
.push(function () { .push(function () {// 5,6
return putFullDoc(jio, "doc", docs[0], "data", blobs[2]); return putFullDoc(jio, "doc", docs[0], "data", blobs[2]);
}) })
.push(function () { .push(function () {// 7
return jio.remove("second_doc"); return jio.remove("second_doc");
}) })
.push(function () { .push(function () {// 8,9
return putFullDoc(jio, "second_doc", docs[1], "data", blobs[3]); return putFullDoc(jio, "second_doc", docs[1], "data", blobs[3]);
}) })
.push(function () {
return not_history.allDocs({
sort_on: [["timestamp", "ascending"]]
});
})
.push(function (results) {
timestamps = results.data.rows.map(function (d) {
return d.id;
});
})
.push(function () { .push(function () {
return not_history.allDocs({ return not_history.allDocs({
sort_on: [["timestamp", "descending"]], sort_on: [["timestamp", "descending"]],
...@@ -1138,92 +1410,92 @@ ...@@ -1138,92 +1410,92 @@
deepEqual(results.data.rows, [ deepEqual(results.data.rows, [
{ {
doc: {}, doc: {},
id: timestamps.second_doc.data[1], id: timestamps[9],
value: { value: {
"op": "putAttachment", "op": "putAttachment",
"doc_id": "second_doc", "doc_id": "second_doc",
"timestamp": timestamps.second_doc.data[1] "timestamp": timestamps[9]
} }
}, },
{ {
doc: {}, doc: {},
id: timestamps.second_doc[2], id: timestamps[8],
value: { value: {
"op": "put", "op": "put",
"doc_id": "second_doc", "doc_id": "second_doc",
"timestamp": timestamps.second_doc[2] "timestamp": timestamps[8]
} }
}, },
{ {
doc: {}, doc: {},
id: timestamps.second_doc[1], id: timestamps[7],
value: { value: {
"op": "remove", "op": "remove",
"doc_id": "second_doc", "doc_id": "second_doc",
"timestamp": timestamps.second_doc[1] "timestamp": timestamps[7]
} }
}, },
{ {
doc: {}, doc: {},
id: timestamps.doc.data[1], id: timestamps[6],
value: { value: {
"op": "putAttachment", "op": "putAttachment",
"doc_id": "doc", "doc_id": "doc",
"timestamp": timestamps.doc.data[1] "timestamp": timestamps[6]
} }
}, },
{ {
doc: {}, doc: {},
id: timestamps.doc[2], id: timestamps[5],
value: { value: {
"op": "put", "op": "put",
"doc_id": "doc", "doc_id": "doc",
"timestamp": timestamps.doc[2] "timestamp": timestamps[5]
} }
}, },
{ {
doc: {}, doc: {},
id: timestamps.second_doc.data[0], id: timestamps[4],
value: { value: {
"op": "putAttachment", "op": "putAttachment",
"doc_id": "second_doc", "doc_id": "second_doc",
"timestamp": timestamps.second_doc.data[0] "timestamp": timestamps[4]
} }
}, },
{ {
doc: {}, doc: {},
id: timestamps.second_doc[0], id: timestamps[3],
value: { value: {
"op": "put", "op": "put",
"doc_id": "second_doc", "doc_id": "second_doc",
"timestamp": timestamps.second_doc[0] "timestamp": timestamps[3]
} }
}, },
{ {
doc: {}, doc: {},
id: timestamps.doc.data[0], id: timestamps[2],
value: { value: {
"op": "putAttachment", "op": "putAttachment",
"doc_id": "doc", "doc_id": "doc",
"timestamp": timestamps.doc.data[0] "timestamp": timestamps[2]
} }
}, },
{ {
doc: {}, doc: {},
id: timestamps.doc[1], id: timestamps[1],
value: { value: {
"op": "put", "op": "put",
"doc_id": "doc", "doc_id": "doc",
"timestamp": timestamps.doc[1] "timestamp": timestamps[1]
} }
}, },
{ {
doc: {}, doc: {},
id: timestamps.doc[0], id: timestamps[0],
value: { value: {
"op": "put", "op": "put",
"doc_id": "doc", "doc_id": "doc",
"timestamp": timestamps.doc[0] "timestamp": timestamps[0]
} }
} }
], "All operations are logged correctly"); ], "All operations are logged correctly");
...@@ -1269,14 +1541,13 @@ ...@@ -1269,14 +1541,13 @@
}) })
.push(function () { .push(function () {
return jio.allDocs({ return jio.allDocs({
query: "(_timestamp: >= " + timestamps.second_doc[0] + query: "NOT (date: >= 2 AND date: <= 3)",
" OR _timestamp: <= " + timestamps.doc[1] +
") AND NOT (date: = 2)",
select_list: ["date", "non-existent-key", "type", "title"], select_list: ["date", "non-existent-key", "type", "title"],
sort_on: [["date", "descending"], sort_on: [["date", "descending"],
["non-existent-key", "ascending"], ["non-existent-key", "ascending"],
["_timestamp", "ascending"] ["_timestamp", "ascending"]
] ],
include_revisions: true
}); });
}) })
.push(function (results) { .push(function (results) {
...@@ -1288,7 +1559,8 @@ ...@@ -1288,7 +1559,8 @@
date: 4, date: 4,
title: "doc", title: "doc",
type: "foo2" type: "foo2"
} },
timestamp: timestamps[5]
}, },
{ {
doc: {}, doc: {},
...@@ -1297,7 +1569,8 @@ ...@@ -1297,7 +1569,8 @@
date: 4, date: 4,
title: "second_doc", title: "second_doc",
type: "bar2" type: "bar2"
} },
timestamp: timestamps[8]
}, },
{ {
doc: {}, doc: {},
...@@ -1306,12 +1579,14 @@ ...@@ -1306,12 +1579,14 @@
date: 1, date: 1,
title: "doc", title: "doc",
type: "foo" type: "foo"
} },
timestamp: timestamps[1]
}, },
{ {
doc: {}, doc: {},
id: "doc", id: "doc",
value: {} value: {},
timestamp: timestamps[0]
} }
], ],
"Query gives correct results in correct order"); "Query gives correct results in correct order");
......
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