@electerm/nedb 1.8.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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2013 Louis Chatriot <louis.chatriot@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,22 @@
1
+
2
+ # @electerm/nedb
3
+
4
+ **Note: This is an fork of [yetzt/nedb](https://github.com/yetzt/nedb), which itself is a fork of the original [nedb](https://github.com/louischatriot/nedb) by [Louis Chatriot](https://github.com/louischatriot). This version includes additional improvements and maintenance updates.**
5
+
6
+ **Embedded persistent or in memory database for Node.js, nw.js, Electron, 100% JavaScript, no binary dependency**. API is a subset of MongoDB's and it's <a href="#speed">plenty fast</a>.
7
+
8
+ ## About this Fork
9
+
10
+ This fork specially for electerm use, removed async/underscore for smaller size
11
+
12
+ ## Installation, tests
13
+ Module name on npm is `@electerm/nedb`.
14
+
15
+ ```bash
16
+ npm install @electerm/nedb --save # Put latest version in your package.json
17
+ npm test # You'll need the dev dependencies to launch tests
18
+ ```
19
+
20
+ ## License
21
+
22
+ See [License](LICENSE)
package/index.js ADDED
@@ -0,0 +1,3 @@
1
+ var Datastore = require('./lib/datastore');
2
+
3
+ module.exports = Datastore;
package/lib/async.js ADDED
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Custom async utilities to replace the async library
3
+ * Uses modern async/await internally while maintaining callback compatibility
4
+ */
5
+
6
+ /**
7
+ * Promisify a callback-based function
8
+ */
9
+ function promisify(fn) {
10
+ return function(...args) {
11
+ return new Promise((resolve, reject) => {
12
+ fn(...args, (err, ...results) => {
13
+ if (err) reject(err);
14
+ else resolve(results.length <= 1 ? results[0] : results);
15
+ });
16
+ });
17
+ };
18
+ }
19
+
20
+ /**
21
+ * Execute functions in series (waterfall pattern)
22
+ * Each function receives the result of the previous function as its parameters (except the last which is always the callback)
23
+ */
24
+ function waterfall(tasks, finalCallback) {
25
+ finalCallback = finalCallback || function() {};
26
+
27
+ if (!Array.isArray(tasks) || tasks.length === 0) {
28
+ return finalCallback(null);
29
+ }
30
+
31
+ let taskIndex = 0;
32
+
33
+ function runNextTask(err, ...results) {
34
+ if (err) {
35
+ return finalCallback(err);
36
+ }
37
+
38
+ if (taskIndex >= tasks.length) {
39
+ return finalCallback(null, ...results);
40
+ }
41
+
42
+ const task = tasks[taskIndex++];
43
+
44
+ try {
45
+ if (typeof task === 'function') {
46
+ // Regular function
47
+ if (taskIndex === 1) {
48
+ // First task gets only the callback
49
+ task(runNextTask);
50
+ } else {
51
+ // Subsequent tasks get results from previous task, then callback
52
+ task(...results, runNextTask);
53
+ }
54
+ } else if (task && task.__async_apply) {
55
+ // Handle async.apply case
56
+ const args = [...task.args];
57
+ if (taskIndex > 1) {
58
+ // If not the first task, add results from previous task
59
+ args.push(...results);
60
+ }
61
+ args.push(runNextTask);
62
+ task.fn.apply(null, args);
63
+ } else {
64
+ return finalCallback(new Error('Invalid task in waterfall'));
65
+ }
66
+ } catch (error) {
67
+ return finalCallback(error);
68
+ }
69
+ }
70
+
71
+ runNextTask();
72
+ }
73
+
74
+ /**
75
+ * Apply partial application to a function (like async.apply)
76
+ */
77
+ function apply(fn, ...args) {
78
+ return {
79
+ __async_apply: true,
80
+ fn: fn,
81
+ args: args
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Execute a function for each item in an array, in series
87
+ */
88
+ function eachSeries(array, iterator, callback) {
89
+ callback = callback || function() {};
90
+
91
+ let index = 0;
92
+
93
+ function processNext(err) {
94
+ if (err) {
95
+ return callback(err);
96
+ }
97
+
98
+ if (index >= array.length) {
99
+ return callback(null);
100
+ }
101
+
102
+ const item = array[index++];
103
+
104
+ try {
105
+ iterator(item, processNext);
106
+ } catch (error) {
107
+ return callback(error);
108
+ }
109
+ }
110
+
111
+ processNext();
112
+ }
113
+
114
+ /**
115
+ * Execute a function for each item in an array, in parallel
116
+ */
117
+ function each(array, iterator, callback) {
118
+ callback = callback || function() {};
119
+
120
+ if (array.length === 0) {
121
+ return callback(null);
122
+ }
123
+
124
+ let completed = 0;
125
+ let hasError = false;
126
+
127
+ array.forEach(function(item) {
128
+ iterator(item, function(err) {
129
+ if (hasError) return;
130
+
131
+ if (err) {
132
+ hasError = true;
133
+ return callback(err);
134
+ }
135
+
136
+ completed++;
137
+ if (completed === array.length) {
138
+ callback(null);
139
+ }
140
+ });
141
+ });
142
+ }
143
+
144
+ /**
145
+ * Execute a function repeatedly while a condition is true
146
+ */
147
+ function whilst(test, iterator, callback) {
148
+ callback = callback || function() {};
149
+
150
+ function iterate(err) {
151
+ if (err) {
152
+ return callback(err);
153
+ }
154
+
155
+ if (!test()) {
156
+ return callback(null);
157
+ }
158
+
159
+ try {
160
+ iterator(iterate);
161
+ } catch (error) {
162
+ return callback(error);
163
+ }
164
+ }
165
+
166
+ iterate();
167
+ }
168
+
169
+ /**
170
+ * Task queue implementation
171
+ */
172
+ function queue(worker, concurrency = 1) {
173
+ const tasks = [];
174
+ let running = 0;
175
+ let paused = false;
176
+
177
+ function processTasks() {
178
+ while (tasks.length > 0 && running < concurrency && !paused) {
179
+ const task = tasks.shift();
180
+ running++;
181
+
182
+ worker(task.data, function(err) {
183
+ running--;
184
+ task.callback(err);
185
+
186
+ // Process next tasks
187
+ if (tasks.length > 0) {
188
+ setImmediate(processTasks);
189
+ }
190
+ });
191
+ }
192
+ }
193
+
194
+ return {
195
+ push: function(data, callback) {
196
+ callback = callback || function() {};
197
+ tasks.push({ data, callback });
198
+ setImmediate(processTasks);
199
+ },
200
+
201
+ unshift: function(data, callback) {
202
+ callback = callback || function() {};
203
+ tasks.unshift({ data, callback });
204
+ setImmediate(processTasks);
205
+ },
206
+
207
+ pause: function() {
208
+ paused = true;
209
+ },
210
+
211
+ resume: function() {
212
+ paused = false;
213
+ setImmediate(processTasks);
214
+ },
215
+
216
+ length: function() {
217
+ return tasks.length;
218
+ },
219
+
220
+ running: function() {
221
+ return running;
222
+ },
223
+
224
+ idle: function() {
225
+ return tasks.length === 0 && running === 0;
226
+ }
227
+ };
228
+ }
229
+
230
+ module.exports = {
231
+ waterfall,
232
+ apply,
233
+ eachSeries,
234
+ each,
235
+ whilst,
236
+ queue
237
+ };
package/lib/cursor.js ADDED
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Manage access to data, be it to find, update or remove it
3
+ */
4
+ var model = require('./model')
5
+ , _ = require('./underscore')
6
+ ;
7
+
8
+
9
+
10
+ /**
11
+ * Create a new cursor for this collection
12
+ * @param {Datastore} db - The datastore this cursor is bound to
13
+ * @param {Query} query - The query this cursor will operate on
14
+ * @param {Function} execFn - Handler to be executed after cursor has found the results and before the callback passed to find/findOne/update/remove
15
+ */
16
+ function Cursor (db, query, execFn) {
17
+ this.db = db;
18
+ this.query = query || {};
19
+ if (execFn) { this.execFn = execFn; }
20
+ }
21
+
22
+
23
+ /**
24
+ * Set a limit to the number of results
25
+ */
26
+ Cursor.prototype.limit = function(limit) {
27
+ this._limit = limit;
28
+ return this;
29
+ };
30
+
31
+
32
+ /**
33
+ * Skip a the number of results
34
+ */
35
+ Cursor.prototype.skip = function(skip) {
36
+ this._skip = skip;
37
+ return this;
38
+ };
39
+
40
+
41
+ /**
42
+ * Sort results of the query
43
+ * @param {SortQuery} sortQuery - SortQuery is { field: order }, field can use the dot-notation, order is 1 for ascending and -1 for descending
44
+ */
45
+ Cursor.prototype.sort = function(sortQuery) {
46
+ this._sort = sortQuery;
47
+ return this;
48
+ };
49
+
50
+
51
+ /**
52
+ * Add the use of a projection
53
+ * @param {Object} projection - MongoDB-style projection. {} means take all fields. Then it's { key1: 1, key2: 1 } to take only key1 and key2
54
+ * { key1: 0, key2: 0 } to omit only key1 and key2. Except _id, you can't mix takes and omits
55
+ */
56
+ Cursor.prototype.projection = function(projection) {
57
+ this._projection = projection;
58
+ return this;
59
+ };
60
+
61
+
62
+ /**
63
+ * Apply the projection
64
+ */
65
+ Cursor.prototype.project = function (candidates) {
66
+ var res = [], self = this
67
+ , keepId, action, keys
68
+ ;
69
+
70
+ if (this._projection === undefined || Object.keys(this._projection).length === 0) {
71
+ return candidates;
72
+ }
73
+
74
+ keepId = this._projection._id === 0 ? false : true;
75
+ this._projection = _.omit(this._projection, '_id');
76
+
77
+ // Check for consistency
78
+ keys = Object.keys(this._projection);
79
+ keys.forEach(function (k) {
80
+ if (action !== undefined && self._projection[k] !== action) { throw new Error("Can't both keep and omit fields except for _id"); }
81
+ action = self._projection[k];
82
+ });
83
+
84
+ // Do the actual projection
85
+ candidates.forEach(function (candidate) {
86
+ var toPush;
87
+ if (action === 1) { // pick-type projection
88
+ toPush = { $set: {} };
89
+ keys.forEach(function (k) {
90
+ toPush.$set[k] = model.getDotValue(candidate, k);
91
+ if (toPush.$set[k] === undefined) { delete toPush.$set[k]; }
92
+ });
93
+ toPush = model.modify({}, toPush);
94
+ } else { // omit-type projection
95
+ toPush = { $unset: {} };
96
+ keys.forEach(function (k) { toPush.$unset[k] = true });
97
+ toPush = model.modify(candidate, toPush);
98
+ }
99
+ if (keepId) {
100
+ toPush._id = candidate._id;
101
+ } else {
102
+ delete toPush._id;
103
+ }
104
+ res.push(toPush);
105
+ });
106
+
107
+ return res;
108
+ };
109
+
110
+
111
+ /**
112
+ * Get all matching elements
113
+ * Will return pointers to matched elements (shallow copies), returning full copies is the role of find or findOne
114
+ * This is an internal function, use exec which uses the executor
115
+ *
116
+ * @param {Function} callback - Signature: err, results
117
+ */
118
+ Cursor.prototype._exec = function(_callback) {
119
+ var res = [], added = 0, skipped = 0, self = this
120
+ , error = null
121
+ , i, keys, key
122
+ ;
123
+
124
+ function callback (error, res) {
125
+ if (self.execFn) {
126
+ return self.execFn(error, res, _callback);
127
+ } else {
128
+ return _callback(error, res);
129
+ }
130
+ }
131
+
132
+ this.db.getCandidates(this.query, function (err, candidates) {
133
+ if (err) { return callback(err); }
134
+
135
+ try {
136
+ for (i = 0; i < candidates.length; i += 1) {
137
+ if (model.match(candidates[i], self.query)) {
138
+ // If a sort is defined, wait for the results to be sorted before applying limit and skip
139
+ if (!self._sort) {
140
+ if (self._skip && self._skip > skipped) {
141
+ skipped += 1;
142
+ } else {
143
+ res.push(candidates[i]);
144
+ added += 1;
145
+ if (self._limit && self._limit <= added) { break; }
146
+ }
147
+ } else {
148
+ res.push(candidates[i]);
149
+ }
150
+ }
151
+ }
152
+ } catch (err) {
153
+ return callback(err);
154
+ }
155
+
156
+ // Apply all sorts
157
+ if (self._sort) {
158
+ keys = Object.keys(self._sort);
159
+
160
+ // Sorting
161
+ var criteria = [];
162
+ for (i = 0; i < keys.length; i++) {
163
+ key = keys[i];
164
+ criteria.push({ key: key, direction: self._sort[key] });
165
+ }
166
+ res.sort(function(a, b) {
167
+ var criterion, compare, i;
168
+ for (i = 0; i < criteria.length; i++) {
169
+ criterion = criteria[i];
170
+ compare = criterion.direction * model.compareThings(model.getDotValue(a, criterion.key), model.getDotValue(b, criterion.key), self.db.compareStrings);
171
+ if (compare !== 0) {
172
+ return compare;
173
+ }
174
+ }
175
+ return 0;
176
+ });
177
+
178
+ // Applying limit and skip
179
+ var limit = self._limit || res.length
180
+ , skip = self._skip || 0;
181
+
182
+ res = res.slice(skip, skip + limit);
183
+ }
184
+
185
+ // Apply projection
186
+ try {
187
+ res = self.project(res);
188
+ } catch (e) {
189
+ error = e;
190
+ res = undefined;
191
+ }
192
+
193
+ return callback(error, res);
194
+ });
195
+ };
196
+
197
+ Cursor.prototype.exec = function () {
198
+ this.db.executor.push({ this: this, fn: this._exec, arguments: arguments });
199
+ };
200
+
201
+
202
+
203
+ // Interface
204
+ module.exports = Cursor;
@@ -0,0 +1,22 @@
1
+ var crypto = require('crypto')
2
+ ;
3
+
4
+ /**
5
+ * Return a random alphanumerical string of length len
6
+ * There is a very small probability (less than 1/1,000,000) for the length to be less than len
7
+ * (il the base64 conversion yields too many pluses and slashes) but
8
+ * that's not an issue here
9
+ * The probability of a collision is extremely small (need 3*10^12 documents to have one chance in a million of a collision)
10
+ * See http://en.wikipedia.org/wiki/Birthday_problem
11
+ */
12
+ function uid (len) {
13
+ return crypto.randomBytes(Math.ceil(Math.max(8, len * 2)))
14
+ .toString('base64')
15
+ .replace(/[+\/]/g, '')
16
+ .slice(0, len);
17
+ }
18
+
19
+
20
+ // Interface
21
+ module.exports.uid = uid;
22
+