viewdb 0.5.12 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.prettierrc ADDED
@@ -0,0 +1 @@
1
+ "prettier-config-surikaterna"
package/README.md CHANGED
@@ -1 +1,416 @@
1
- # viewdb
1
+ ViewDB
2
+ ======
3
+
4
+ [ViewDB](https://github.com/surikaterna/viewdb) is an database facade for JavaScript. It can be configured with a custom
5
+ Store in order to provide support for different persistence sources, such as MongoDB or IndexedDB, but comes with
6
+ a default in-memory Store for easy testing.
7
+
8
+ * [Purpose](#purpose)
9
+ * [Installation](#installation)
10
+ * [Usage](#usage-example)
11
+ * [Components](#components)
12
+ * [ViewDB](#viewdb-1)
13
+ * [Methods](#methods)
14
+ * [open](#open)
15
+ * [collection](#collection)
16
+ * [Plugins](#plugins)
17
+ * [TimeStampPlugin](#timestampplugin)
18
+ * [VersioningPlugin](#versioningplugin)
19
+ * [Store](#store)
20
+ * [Methods](#methods-1)
21
+ * [open](#open-1)
22
+ * [collection](#collection-1)
23
+ * [InMemoryStore](#inmemorystore)
24
+ * [Collection](#collection-2)
25
+ * [Methods](#methods-2)
26
+ * [count](#count)
27
+ * [createIndex](#createindex)
28
+ * [drop](#drop)
29
+ * [ensureIndex](#ensureindex)
30
+ * [find](#find)
31
+ * [findAndModify](#findandmodify)
32
+ * [insert](#insert)
33
+ * [remove](#remove)
34
+ * [save](#save)
35
+ * [InMemoryCollection](#inmemorycollection)
36
+ * [Cursor](#cursor)
37
+ * [Methods](#methods-3)
38
+ * [count](#count-1)
39
+ * [forEach](#foreach)
40
+ * [limit](#limit)
41
+ * [observe](#observe)
42
+ * [skip](#skip)
43
+ * [sort](#sort)
44
+ * [toArray](#toarray)
45
+ * [Observer](#observer)
46
+ * [Methods](#methods-4)
47
+ * [stop](#stop)
48
+ * [Merger](#merger)
49
+
50
+ ## Purpose
51
+
52
+ Provide a MongoDB like syntax for managing documents while providing customization for where to store the data.
53
+
54
+ ## Installation
55
+
56
+ ```shell
57
+ npm install viewdb
58
+ ```
59
+
60
+ ## Usage Example
61
+
62
+ ### In-Memory Database
63
+
64
+ Create a ViewDB instance keeping data in memory.
65
+
66
+ ```js
67
+ import ViewDB from 'viewdb';
68
+ import { processUsers, User, usersData } from './users';
69
+
70
+ const viewDB = new ViewDB();
71
+ const userCollection = viewDB.collection('user');
72
+ userCollection.insert(usersData, () => {
73
+ userCollection.find({ name: 'Jeff' }, (err, users) => {
74
+ if (err ?? users.length === 0) {
75
+ return;
76
+ }
77
+
78
+ processUsers(users);
79
+ });
80
+ });
81
+ ```
82
+
83
+ ## Components
84
+
85
+ ### ViewDB
86
+
87
+ The database facade that manages a persistence [Store](#store). Manages [Collections](#collection-1) through the Store.
88
+
89
+ #### Methods
90
+
91
+ ##### open
92
+
93
+ ```js
94
+ // Use a MongoDB Store for persistence
95
+ const viewDB = new ViewDB(mongoDBStore);
96
+ // Make sure the Store is open and ready to use
97
+ await viewDB.open();
98
+ ```
99
+
100
+ ##### collection
101
+
102
+ Get a [Collection](#collection-1) from the provided [Store](#store). Will create and return a new Collection if one
103
+ doesn't already exist.
104
+
105
+ ```js
106
+ const userCollection = viewDB.collection('user');
107
+ userCollection.find({ name: 'Jeff' }, (err, users) => {
108
+ if (err ?? users.length === 0) {
109
+ return;
110
+ }
111
+
112
+ processUsers(users);
113
+ });
114
+ ```
115
+
116
+ #### Plugins
117
+
118
+ ViewDB can be extended by plugins to intercept data manipulation. Two plugins are included in this repository:
119
+
120
+ * [TimeStampPlugin](#timestampplugin)
121
+ * [VersioningPlugin](#versioningplugin)
122
+
123
+ ##### TimeStampPlugin
124
+
125
+ Intercepts the `save`, `insert` & `findAndModify` _Collection_ methods with timestamps for creation and latest update.
126
+ The time inserted is a unix timestamp in milliseconds. It provides the following changes.
127
+
128
+ When _inserting_ documents without providing a `skipTimestamp` option, the inserted documents will get
129
+ a `createDateTime` and a `changeDateTime` property will be added to the documents.
130
+
131
+ When _saving_ documents without providing a `skipTimestamp` option, the inserted documents will get an
132
+ updated `changeDateTime` and a `changeDateTime` value. If there is no `createDateTime` property on the document, it will
133
+ be added as well.
134
+
135
+ When _finding and modifying_ an existing document, the `changeDateTime` value will be update to the current time.
136
+
137
+ ```js
138
+ const viewDB = new ViewDB();
139
+
140
+ // Apply the plugin to the ViewDB Store
141
+ new TimeStampPlugin(viewDB);
142
+
143
+ const collection = viewDB.collection('user');
144
+
145
+ // User data for Jeff will get `createDateTime` and `changeDateTime` properties
146
+ await collection.insert({ name: 'Jeff' });
147
+ ```
148
+
149
+ ##### VersioningPlugin
150
+
151
+ Intercepts the `save`, `insert` & `findAndModify` _Collection_ methods with an incremented version, unless
152
+ the `skipVersioning` option is passed and set to `true`.
153
+
154
+ Note that when _finding and modifying_ existing documents, if `version` is explicitly set in the `update` query, that
155
+ value will be replaced with the incremented version.
156
+
157
+ ```js
158
+ const viewDB = new ViewDB();
159
+
160
+ // Apply the plugin to the ViewDB Store
161
+ new VersioningPlugin(viewDB);
162
+
163
+ const collection = viewDB.collection('user');
164
+
165
+ // User data for Jeff will get a `version` property
166
+ await collection.insert({ name: 'Jeff' });
167
+ ```
168
+
169
+ ### Store
170
+
171
+ Responsible for retrieving a [Collection](#collection-2) of documents. If the store needs some setup to be ready, it
172
+ shall provide an `open` method for that.
173
+
174
+ Used through the [ViewDB](#viewdb-1) methods.
175
+
176
+ #### Methods
177
+
178
+ ##### open
179
+
180
+ Optional method on the Store, to prepare the Store for usage.
181
+
182
+ ```js
183
+ await store.open();
184
+ ```
185
+
186
+ ##### collection
187
+
188
+ Returns a named [Collection](#collection-2), kept in the Store for multiple retrievals.
189
+
190
+ ```js
191
+ const userCollection = store.collection('user');
192
+
193
+ // Same instance of the collection retrieved above
194
+ const userCollectionRetrievedLater = store.collection('user');
195
+ ```
196
+
197
+ #### InMemoryStore
198
+
199
+ A basic implementation of a Store, which keeps a record of the [in memory collections](#InMemoryCollection) by name in
200
+ memory. Used if no Store is provided as an argument to the ViewDB constructor.
201
+
202
+ ### Collection
203
+
204
+ Responsible for managing the data.
205
+
206
+ #### Methods
207
+
208
+ ##### count
209
+
210
+ Retrieve the current amount of documents in the collection.
211
+
212
+ ```js
213
+ collection.count((err, count) => {
214
+ });
215
+ ```
216
+
217
+ ##### createIndex
218
+
219
+ Creates an `index` to be used when looking up data.
220
+
221
+ ```js
222
+ collection.createIndex({ 'contactMeans.identifier': 1 }, null, (err, result) => {
223
+ });
224
+ ```
225
+
226
+ ##### drop
227
+
228
+ Clear all data in the collection.
229
+
230
+ ```js
231
+ // Whether the collection was successfully dropped
232
+ const isSuccess = collection.drop();
233
+ ```
234
+
235
+ ##### ensureIndex
236
+
237
+ Creates an `index` for the collection unless it already exists.
238
+
239
+ ```js
240
+ collection.ensureIndex({ 'contactMeans.identifier': 1 }, null, (err, result) => {
241
+ });
242
+ ```
243
+
244
+ ##### find
245
+
246
+ Returns a [Cursor](#cursor) for the list of documents from the collection matching the query.
247
+
248
+ ```js
249
+ await cursor = collection.find({});
250
+ ```
251
+
252
+ ##### findAndModify
253
+
254
+ Retrieve a list of documents from the collection matching the query, and update the resulting documents based on the
255
+ update query.
256
+
257
+ ```js
258
+ collection.findAndModify(query, null, update, options, (err, res) => {
259
+ });
260
+ ```
261
+
262
+ ##### insert
263
+
264
+ Insert new documents into the collection.
265
+
266
+ ```js
267
+ collection.insert(newDocs, (err, insertedDocs) => {
268
+ });
269
+ ```
270
+
271
+ ##### remove
272
+
273
+ Remove documents matching the query from the collection.
274
+
275
+ ```js
276
+ collection.remove({ name: 'Jeff' }, null, (err, result) => {
277
+ });
278
+ ```
279
+
280
+ ##### save
281
+
282
+ Replace the documents in the collection matching the provided documents by `_id`.
283
+
284
+ ```js
285
+ collection.save(updatedDocs, (err, savedDocs) => {
286
+ });
287
+ ```
288
+
289
+ #### InMemoryCollection
290
+
291
+ A basic implementation of a [Collection](#collection-2) managing the data in memory.
292
+
293
+ ### Cursor
294
+
295
+ A result set of the queried [Collection](#collection-2). Contains methods to operate on the result set.
296
+
297
+ Used through the [Collection.find](#find) method, but a Cursor can be constructed manually.
298
+
299
+ ```js
300
+ const collection = viewDB.collection('user');
301
+ const queryObj = { query: {} };
302
+ const cursorOptions = null;
303
+ const getDocuments = (query, callback) => {
304
+ // Logic for retreving documents based on query and passing them to the callback
305
+ };
306
+
307
+ const cursor = new Cursor(collection, queryObj, cursorOptions, getDocuments);
308
+ ```
309
+
310
+ #### Methods
311
+
312
+ ##### count
313
+
314
+ Get the amount of matches for the [Cursor](#cursor).
315
+
316
+ ```js
317
+ cursor.count((err, count) => {
318
+ });
319
+ ```
320
+
321
+ ##### forEach
322
+
323
+ Iterate through the matched documents and run the callback for each document.
324
+
325
+ ```js
326
+ cursor.forEach((doc) => {
327
+ });
328
+ ```
329
+
330
+ ##### limit
331
+
332
+ Limit the amount of documents to be retrieved.
333
+
334
+ ```js
335
+ // Get maximum 5 documents
336
+ cursor.limit(5).toArray((err, docs) => {
337
+ });
338
+ ```
339
+
340
+ ##### observe
341
+
342
+ Retrieves an [Observer](#observer) listening for changes for the documents matched in the [Cursor](#cursor).
343
+
344
+ ```js
345
+ const observeOptions = {
346
+ added: (user, index) => {
347
+ // Called when a user matching the cursor has been inserted to the collection
348
+ },
349
+ changed: (currentUser, newUser, index) => {
350
+ // Called when a user matching the cursor has been updated in the collection
351
+ },
352
+ moved: (user, oldIndex, newIndex) => {
353
+ // Called when a user matching the cursor has been moved to another position
354
+ },
355
+ removed: (user, index) => {
356
+ // Called when a user matching the cursor has been removed from the collection
357
+ }
358
+ };
359
+
360
+ const observer = cursor.observe(observeOptions);
361
+ ```
362
+
363
+ ##### skip
364
+
365
+ Amounts of documents in the [Cursor](#cursor) that should be skipped.
366
+
367
+ ```js
368
+ // Get documents starting from the 6th match
369
+ cursor.skip(5).toArray((err, docs) => {
370
+ });
371
+ ```
372
+
373
+ ##### sort
374
+
375
+ Sort the resulting documents based on a query.
376
+
377
+ ```js
378
+ // Get documents sorted by creation time in descending order
379
+ cursor.sort({ createDateTime: -1 }).toArray((err, docs) => {
380
+ });
381
+ ```
382
+
383
+ ##### toArray
384
+
385
+ Retrieve the list of documents matching the query.
386
+
387
+ ```js
388
+ cursor.toArray((err, docs) => {
389
+ });
390
+ ```
391
+
392
+ ### Observer
393
+
394
+ Observe changes to documents for a [Cursor](#cursor). With provide information about initial data, added, changed,
395
+ moved & removed data.
396
+
397
+ Used through the [Collection.observe](#observe) method, but an Observer can be constructed manually.
398
+
399
+ ```js
400
+ const observer = new Observe(query, cursorOptions, collection, observerOptions);
401
+ ```
402
+
403
+ #### Methods
404
+
405
+ ##### stop
406
+
407
+ Stop listening to changes matching the query.
408
+
409
+ ```js
410
+ const observer = collection.find({}).observe(observeOptions);
411
+ observer.stop();
412
+ ```
413
+
414
+ ### Merger
415
+
416
+ Check the provided data and provide information about how the values have changed.
package/jest.config.js ADDED
@@ -0,0 +1,11 @@
1
+ /** @type {import('jest').Config} */
2
+ module.exports = {
3
+ roots: ['<rootDir>/lib', '<rootDir>/test'],
4
+ transform: {
5
+ '^.+\\.js$': ['es-jest']
6
+ },
7
+
8
+ testRegex: '(/test/.*|(\\.|/)(test|spec))\\.js$',
9
+ moduleDirectories: ['node_modules', 'lib'],
10
+ moduleFileExtensions: ['js', 'json', 'node']
11
+ };
package/lib/cursor.js CHANGED
@@ -7,18 +7,18 @@ var Cursor = function (collection, query, options, getDocuments) {
7
7
  this._options = options;
8
8
  this._getDocuments = getDocuments;
9
9
  this._isObserving = false;
10
- }
10
+ };
11
11
 
12
- Cursor.prototype.forEach = function (callback, thiz) {
13
- var docs = this._getDocuments(this._query, function (err, result) {
14
- _.forEach(result, function (n) {
15
- callback(result)
12
+ Cursor.prototype.forEach = function (callback) {
13
+ this._getDocuments(this._query, function (err, result) {
14
+ _.forEach(result, function () {
15
+ callback(result);
16
16
  });
17
17
  });
18
18
  };
19
19
 
20
20
  Cursor.prototype.toArray = function (callback) {
21
- var docs = this._getDocuments(this._query, callback);
21
+ this._getDocuments(this._query, callback);
22
22
  };
23
23
 
24
24
  Cursor.prototype.observe = function (options) {
@@ -26,6 +26,11 @@ Cursor.prototype.observe = function (options) {
26
26
  return new Observe(this._query, this._options, this._collection, options);
27
27
  };
28
28
 
29
+ Cursor.prototype.updateQuery = function (query) {
30
+ this._query.query = query;
31
+ this._refresh();
32
+ };
33
+
29
34
  Cursor.prototype.skip = function (skip) {
30
35
  this._query.skip = skip;
31
36
  if (this._isObserving) {
@@ -74,8 +79,7 @@ Cursor.prototype.count = function (applySkipLimit, callback) {
74
79
  }
75
80
  this._getDocuments(query, function (err, res) {
76
81
  callback(err, res && res.length);
77
- })
82
+ });
78
83
  };
79
84
 
80
-
81
- module.exports = Cursor;
85
+ module.exports = Cursor;
package/lib/index.js CHANGED
@@ -3,4 +3,4 @@ module.exports = require('./viewdb');
3
3
  module.exports.Cursor = require('./cursor');
4
4
  module.exports.Observer = require('./observe');
5
5
  module.exports.merge = require('./merger');
6
- module.exports.plugins = require('./plugins');
6
+ module.exports.plugins = require('./plugins');
@@ -1,5 +1,5 @@
1
1
  var _ = require('lodash');
2
- var uuid = require('node-uuid').v4;
2
+ var { v4: uuid } = require('uuid');
3
3
 
4
4
  var EventEmitter = require('events').EventEmitter;
5
5
  var util = require('util');
@@ -17,15 +17,13 @@ var Collection = function (collectionName) {
17
17
 
18
18
  util.inherits(Collection, EventEmitter);
19
19
 
20
-
21
20
  Collection.prototype.count = function (callback) {
22
21
  callback(null, this._documents.length);
23
22
  };
24
23
 
25
24
  Collection.prototype._write = function (op, documents, options, callback) {
26
- if(_.isFunction(options)) {
25
+ if (_.isFunction(options)) {
27
26
  callback = options;
28
- options = undefined;
29
27
  }
30
28
  if (!_.isArray(documents)) {
31
29
  documents = [documents];
@@ -50,11 +48,11 @@ Collection.prototype._write = function (op, documents, options, callback) {
50
48
  this._documents[idx] = document;
51
49
  }
52
50
  }
53
- this.emit("change", documents);
51
+ this.emit('change', documents);
54
52
  if (callback) {
55
53
  callback(null, documents);
56
54
  }
57
- }
55
+ };
58
56
 
59
57
  Collection.prototype.insert = function (documents, options, callback) {
60
58
  return this._write('insert', documents, options, callback);
@@ -85,11 +83,11 @@ Collection.prototype.remove = function (query, options, callback) {
85
83
  callback(null);
86
84
  });
87
85
  };
88
- Collection.prototype.ensureIndex = function(options, callback) {
89
- throw new Error('ensureIndex not supported!')
86
+ Collection.prototype.ensureIndex = function () {
87
+ throw new Error('ensureIndex not supported!');
90
88
  };
91
- Collection.prototype.createIndex = function(options, callback) {
92
- throw new Error('createIndex not supported!')
89
+ Collection.prototype.createIndex = function () {
90
+ throw new Error('createIndex not supported!');
93
91
  };
94
92
 
95
93
  Collection.prototype._getDocuments = function (queryObject, callback) {
@@ -1,19 +1,19 @@
1
1
  var Collection = require('./collection');
2
2
 
3
- var Store = function() {
4
- this._collections = {};
3
+ var Store = function () {
4
+ this._collections = {};
5
5
  };
6
6
 
7
- Store.prototype.collection = function(collectionName, callback) {
8
- var coll = this._collections[collectionName];
9
- if(coll === undefined) {
10
- coll = new Collection(collectionName);
11
- this._collections[collectionName] = coll;
12
- }
13
- if(callback) {
14
- callback(coll);
15
- }
16
- return coll;
7
+ Store.prototype.collection = function (collectionName, callback) {
8
+ var coll = this._collections[collectionName];
9
+ if (coll === undefined) {
10
+ coll = new Collection(collectionName);
11
+ this._collections[collectionName] = coll;
12
+ }
13
+ if (callback) {
14
+ callback(coll);
15
+ }
16
+ return coll;
17
17
  };
18
18
 
19
19
  module.exports = Store;
package/lib/merger.js CHANGED
@@ -1,63 +1,62 @@
1
1
  var _ = require('lodash');
2
2
 
3
3
  function contains(list, element, comparator) {
4
- for(var i in list) {
5
- var n = list[i];
6
- if(comparator(element, n)) {
7
- return n;
8
- }
9
- }
10
- return undefined;
4
+ for (var i in list) {
5
+ var n = list[i];
6
+ if (comparator(element, n)) {
7
+ return n;
8
+ }
9
+ }
10
+ return undefined;
11
11
  }
12
12
 
13
13
  function merge(asis, tobe, options) {
14
- options = options || {};
15
- var comparator = options.comparator || _.isEqual
16
- var comparatorId = options.comparatorId || comparator;
17
- var list = _.slice(asis);
18
- //check removed
19
- _.forEach(asis, function(e) {
20
- var found = contains(tobe, e, comparatorId);
21
- if(found === undefined) {
22
- var index = list.indexOf(e);
23
- list.splice(index, 1);
24
- if(options.removed) {
25
- options.removed(e, index);
26
- }
27
- }
28
- });
29
- var indexInNew = -1;
30
- _.forEach(tobe, function(e) {
31
- indexInNew++;
32
- var found = contains(list, e, comparatorId);
33
- //added
34
- if(found === undefined) {
35
- list.splice(indexInNew, 0, e);
36
- if(options.added) {
37
- options.added(e, indexInNew);
38
- }
39
- } else {
40
- //existed before
41
- var indexInOld = list.indexOf(found);
42
- if(indexInOld!==indexInNew) {
43
- //remove
44
- list.splice(indexInOld, 1);
45
- //add
46
- list.splice(indexInNew, 0, e);
47
- if(options.moved) {
48
- options.moved(e, indexInOld, indexInNew);
49
- }
50
- } //else not moved
51
- if(!comparator(found, e)) {
52
- list[indexInNew] = e;
53
- if(options.changed) {
54
- options.changed(found, e, indexInNew);
55
- }
56
- }
57
-
58
- }
59
- });
60
- return list;
61
- //list = _.exclude()v
14
+ options = options || {};
15
+ var comparator = options.comparator || _.isEqual;
16
+ var comparatorId = options.comparatorId || comparator;
17
+ var list = _.slice(asis);
18
+ //check removed
19
+ _.forEach(asis, function (e) {
20
+ var found = contains(tobe, e, comparatorId);
21
+ if (found === undefined) {
22
+ var index = list.indexOf(e);
23
+ list.splice(index, 1);
24
+ if (options.removed) {
25
+ options.removed(e, index);
26
+ }
27
+ }
28
+ });
29
+ var indexInNew = -1;
30
+ _.forEach(tobe, function (e) {
31
+ indexInNew++;
32
+ var found = contains(list, e, comparatorId);
33
+ //added
34
+ if (found === undefined) {
35
+ list.splice(indexInNew, 0, e);
36
+ if (options.added) {
37
+ options.added(e, indexInNew);
38
+ }
39
+ } else {
40
+ //existed before
41
+ var indexInOld = list.indexOf(found);
42
+ if (indexInOld !== indexInNew) {
43
+ //remove
44
+ list.splice(indexInOld, 1);
45
+ //add
46
+ list.splice(indexInNew, 0, e);
47
+ if (options.moved) {
48
+ options.moved(e, indexInOld, indexInNew);
49
+ }
50
+ } //else not moved
51
+ if (!comparator(found, e)) {
52
+ list[indexInNew] = e;
53
+ if (options.changed) {
54
+ options.changed(found, e, indexInNew);
55
+ }
56
+ }
57
+ }
58
+ });
59
+ return list;
60
+ //list = _.exclude()v
62
61
  }
63
- module.exports=merge;
62
+ module.exports = merge;