Skip to content

Commit

Permalink
Add timestamp methods
Browse files Browse the repository at this point in the history
This change adds [new milestone database methods][1] for fetching
milestones by timestamp.

In order to make this performant, this change also adds a new index to
the `m.mtime` (modified timestamp) field.

[1]: share/sharedb#262
  • Loading branch information
Alec Gibson committed Jan 30, 2019
1 parent e630382 commit 46ef109
Show file tree
Hide file tree
Showing 5 changed files with 388 additions and 329 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ const milestoneDb = new MongoMilestoneDB({
* 4101 - Must provide valid collection name
* 4102 - Must provide valid ID
* 4103 - Must provide valid snapshot
* 4104 - Must provide valid integer version
* 4104 - Must provide valid integer version or null
* 4105 - Must provide valid integer timestamp or null

### 5100 - Internal error - DB

Expand Down
88 changes: 70 additions & 18 deletions lib/mongo-milestone-db.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,21 @@ class MongoMilestoneDB extends MilestoneDB {
}

getMilestoneSnapshot(collectionName, id, version, callback) {
if (!collectionName) return process.nextTick(callback, new InvalidCollectionNameError());
if (!id) return process.nextTick(callback, new InvalidIdError());
if (!this._isValidVersion(version)) {
return process.nextTick(callback, new InvalidVersionError());
}
this._getMilestoneSnapshotByVersion(collectionName, id, version)
.then(snapshot => process.nextTick(callback, null, snapshot))
.catch(error => process.nextTick(callback, error));
}

getMilestoneSnapshotAtOrBeforeTime(collection, id, timestamp, callback) {
const isAfterTimestamp = false;
this._getMilestoneSnapshotByTimestamp(collection, id, timestamp, isAfterTimestamp)
.then(snapshot => process.nextTick(callback, null, snapshot))
.catch(error => process.nextTick(callback, error));
}

this._getMilestoneSnapshot(collectionName, id, version)
getMilestoneSnapshotAtOrAfterTime(collection, id, timestamp, callback) {
const isAfterTimestamp = true;
this._getMilestoneSnapshotByTimestamp(collection, id, timestamp, isAfterTimestamp)
.then(snapshot => process.nextTick(callback, null, snapshot))
.catch(error => process.nextTick(callback, error));
}
Expand All @@ -79,19 +87,52 @@ class MongoMilestoneDB extends MilestoneDB {
});
}

_getMilestoneSnapshot(collectionName, id, version) {
return this._collection(collectionName)
.then((collection) => {
const query = {d: id};
if (version != null) {
query.v = {$lte: version};
}
_getMilestoneSnapshotByVersion(collectionName, id, version) {
if (!id) return Promise.reject(new InvalidIdError());
if (!this._isValidVersion(version)) {
return Promise.reject(new InvalidVersionError());
}

const query = {d: id};
if (version != null) {
query.v = {$lte: version};
}

const sort = {v: -1};
const options = {
sort: {v: -1},
};

return collection
.findOne(query, {sort: sort});
})
return this._getMilestoneSnapshotByQuery(collectionName, query, options);
}

_getMilestoneSnapshotByTimestamp(collectionName, id, timestamp, isAfterTimestamp) {
if (!id) return Promise.reject(new InvalidIdError());
if (!this._isValidTimestamp(timestamp)) {
return Promise.reject(new InvalidTimestampError());
}

const nullSortOrder = isAfterTimestamp ? -1 : 1;

const query = {d: id};
const options = {
limit: 1,
sort: {'m.mtime': nullSortOrder},
};

if (timestamp !== null) {
const comparator = isAfterTimestamp ? {$gte: timestamp} : {$lte: timestamp};
query['m.mtime'] = comparator;
options.sort['m.mtime'] = -nullSortOrder;
}

return this._getMilestoneSnapshotByQuery(collectionName, query, options);
}

_getMilestoneSnapshotByQuery(collectionName, query, options) {
if (!collectionName) return Promise.reject(new InvalidCollectionNameError());

return this._collection(collectionName)
.then(collection => collection.findOne(query, options))
.then(MongoMilestoneDB._databaseRepresentationToSnapshot);
}

Expand Down Expand Up @@ -129,7 +170,10 @@ class MongoMilestoneDB extends MilestoneDB {
// collection this won't be a problem, but this is a dangerous mechanism.
// Perhaps we should only warn instead of creating the indexes, especially
// when there is a lot of data in the collection.
return collection.createIndex({d: 1, v: 1}, {background: true, unique: true});
return Promise.all([
collection.createIndex({d: 1, v: 1}, {background: true, unique: true}),
collection.createIndex({'m.mtime': 1}, {background: true}),
]);
}

return Promise.resolve();
Expand Down Expand Up @@ -227,6 +271,14 @@ class InvalidVersionError extends Error {
}
}

class InvalidTimestampError extends Error {
constructor() {
super();
this.code = 4105;
this.message = 'Must provide valid integer timestamp or null';
}
}

class MongoClosedError extends Error {
constructor() {
super();
Expand Down
Loading

0 comments on commit 46ef109

Please sign in to comment.