-
Notifications
You must be signed in to change notification settings - Fork 459
Expand file tree
/
Copy pathmemory.ts
More file actions
189 lines (170 loc) · 6.81 KB
/
memory.ts
File metadata and controls
189 lines (170 loc) · 6.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
var DB = require('./index');
var Snapshot = require('../snapshot');
var util = require('../util');
var clone = util.clone;
// In-memory ShareDB database
//
// This adapter is not appropriate for production use. It is intended for
// testing and as an API example for people implementing database adaptors. It
// is fully functional, except it stores all documents & operations forever in
// memory. As such, memory usage will grow without bound, it doesn't scale
// across multiple node processes and you'll lose all your data if the server
// restarts. Query APIs are adapter specific. Use with care.
function MemoryDB(options) {
if (!(this instanceof MemoryDB)) return new MemoryDB(options);
DB.call(this, options);
// Map from collection name -> doc id -> doc snapshot ({v:, type:, data:})
this.docs = Object.create(null);
// Map from collection name -> doc id -> list of operations. Operations
// don't store their version - instead their version is simply the index in
// the list.
this.ops = Object.create(null);
this.closed = false;
};
module.exports = MemoryDB;
MemoryDB.prototype = Object.create(DB.prototype);
MemoryDB.prototype.close = function(callback) {
this.closed = true;
if (callback) callback();
};
// Persists an op and snapshot if it is for the next version. Calls back with
// callback(err, succeeded)
MemoryDB.prototype.commit = function(collection, id, op, snapshot, options, callback) {
var db = this;
if (typeof callback !== 'function') throw new Error('Callback required');
util.nextTick(function() {
var version = db._getVersionSync(collection, id);
if (snapshot.v !== version + 1) {
var succeeded = false;
return callback(null, succeeded);
}
var err = db._writeOpSync(collection, id, op);
if (err) return callback(err);
err = db._writeSnapshotSync(collection, id, snapshot);
if (err) return callback(err);
var succeeded = true;
callback(null, succeeded);
});
};
// Get the named document from the database. The callback is called with (err,
// snapshot). A snapshot with a version of zero is returned if the docuemnt
// has never been created in the database.
MemoryDB.prototype.getSnapshot = function(collection, id, fields, options, callback) {
var includeMetadata = (fields && fields.$submit) || (options && options.metadata);
var db = this;
if (typeof callback !== 'function') throw new Error('Callback required');
util.nextTick(function() {
var snapshot = db._getSnapshotSync(collection, id, includeMetadata);
callback(null, snapshot);
});
};
// Get operations between [from, to) noninclusively. (Ie, the range should
// contain start but not end).
//
// If end is null, this function should return all operations from start onwards.
//
// The operations that getOps returns don't need to have a version: field.
// The version will be inferred from the parameters if it is missing.
//
// Callback should be called as callback(error, [list of ops]);
MemoryDB.prototype.getOps = function(collection, id, from, to, options, callback) {
var includeMetadata = options && options.metadata;
var db = this;
if (typeof callback !== 'function') throw new Error('Callback required');
util.nextTick(function() {
var opLog = db._getOpLogSync(collection, id);
if (!from) from = 0;
if (to == null) to = opLog.length;
var ops = clone(opLog.slice(from, to).filter(Boolean));
if (ops.length < to - from) {
return callback(new Error('Missing ops'));
}
if (!includeMetadata) {
for (var i = 0; i < ops.length; i++) {
delete ops[i].m;
}
}
callback(null, ops);
});
};
MemoryDB.prototype.deleteOps = function(collection, id, from, to, options, callback) {
if (typeof callback !== 'function') throw new Error('Callback required');
var db = this;
util.nextTick(function() {
var opLog = db._getOpLogSync(collection, id);
if (!from) from = 0;
if (to == null) to = opLog.length;
for (var i = from; i < to; i++) opLog[i] = null;
callback(null);
});
};
// The memory database query function returns all documents in a collection
// regardless of query by default
MemoryDB.prototype.query = function(collection, query, fields, options, callback) {
var includeMetadata = options && options.metadata;
var db = this;
if (typeof callback !== 'function') throw new Error('Callback required');
util.nextTick(function() {
var collectionDocs = db.docs[collection];
var snapshots = [];
for (var id in collectionDocs || {}) {
var snapshot = db._getSnapshotSync(collection, id, includeMetadata);
snapshots.push(snapshot);
}
try {
var result = db._querySync(snapshots, query, options);
callback(null, result.snapshots, result.extra);
} catch (err) {
callback(err);
}
});
};
// For testing, it may be useful to implement the desired query
// language by defining this function. Returns an object with
// two properties:
// - snapshots: array of query result snapshots
// - extra: (optional) other types of results, such as counts
MemoryDB.prototype._querySync = function(snapshots) {
return {snapshots: snapshots};
};
MemoryDB.prototype._writeOpSync = function(collection, id, op) {
var opLog = this._getOpLogSync(collection, id);
// This will write an op in the log at its version, which should always be
// the next item in the array under normal operation
opLog[op.v] = clone(op);
};
// Create, update, and delete snapshots. For creates and updates, a snapshot
// object will be passed in with a type property. If there is no type property,
// it should be considered a delete
MemoryDB.prototype._writeSnapshotSync = function(collection, id, snapshot) {
var collectionDocs = this.docs[collection] || (this.docs[collection] = Object.create(null));
if (!snapshot.type) {
delete collectionDocs[id];
} else {
collectionDocs[id] = clone(snapshot);
}
};
MemoryDB.prototype._getSnapshotSync = function(collection, id, includeMetadata) {
var collectionDocs = this.docs[collection];
// We need to clone the snapshot, because ShareDB assumes each call to
// getSnapshot returns a new object
var doc = collectionDocs && collectionDocs[id];
var snapshot;
if (doc) {
var data = clone(doc.data);
var meta = (includeMetadata) ? clone(doc.m) : null;
snapshot = new Snapshot(id, doc.v, doc.type, data, meta);
} else {
var version = this._getVersionSync(collection, id);
snapshot = new Snapshot(id, version, null, undefined, null);
}
return snapshot;
};
MemoryDB.prototype._getOpLogSync = function(collection, id) {
var collectionOps = this.ops[collection] || (this.ops[collection] = Object.create(null));
return collectionOps[id] || (collectionOps[id] = []);
};
MemoryDB.prototype._getVersionSync = function(collection, id) {
var collectionOps = this.ops[collection];
return (collectionOps && collectionOps[id] && collectionOps[id].length) || 0;
};