@abtnode/db 1.6.7
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/README.md +11 -0
- package/lib/db.js +224 -0
- package/lib/factory.js +20 -0
- package/package.json +41 -0
package/README.md
ADDED
package/lib/db.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const util = require('util');
|
|
4
|
+
const cloneDeep = require('lodash.clonedeep');
|
|
5
|
+
const { EventEmitter } = require('events');
|
|
6
|
+
const packageName = require('../package.json').name;
|
|
7
|
+
|
|
8
|
+
const DataStore =
|
|
9
|
+
process.env.NODE_ENV === 'test' ? require('@nedb/core') : require('@nedb/multi')(Number(process.env.NEDB_MULTI_PORT));
|
|
10
|
+
// eslint-disable-next-line import/order
|
|
11
|
+
const logger = require('@abtnode/logger')(packageName);
|
|
12
|
+
|
|
13
|
+
class DB extends EventEmitter {
|
|
14
|
+
constructor(baseDir, options) {
|
|
15
|
+
super();
|
|
16
|
+
|
|
17
|
+
const dbOptions = options.db || {};
|
|
18
|
+
this.filename = path.join(baseDir, options.filename);
|
|
19
|
+
this.options = Object.freeze(cloneDeep(options));
|
|
20
|
+
this.db = new DataStore({
|
|
21
|
+
filename: this.filename,
|
|
22
|
+
timestampData: true,
|
|
23
|
+
...dbOptions,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
logger.info('initialized', { filename: this.filename });
|
|
27
|
+
|
|
28
|
+
this.ready = false;
|
|
29
|
+
this.readyCallbacks = [];
|
|
30
|
+
this.db.loadDatabase((err) => {
|
|
31
|
+
if (err) {
|
|
32
|
+
logger.error(`failed to load disk database ${this.filename}`, { error: err });
|
|
33
|
+
console.error(err);
|
|
34
|
+
} else {
|
|
35
|
+
this.ready = true;
|
|
36
|
+
if (this.readyCallbacks.length) {
|
|
37
|
+
this.readyCallbacks.forEach((x) => x());
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
this.asyncDB = new Proxy(this.db, {
|
|
43
|
+
get(target, property) {
|
|
44
|
+
if (typeof target[property] === 'function') {
|
|
45
|
+
return util
|
|
46
|
+
.promisify((...args) => {
|
|
47
|
+
const cb = args[args.length - 1];
|
|
48
|
+
const rest = args.slice(0, args.length - 1);
|
|
49
|
+
|
|
50
|
+
target[property](...rest, (err, ...result) => {
|
|
51
|
+
if (err) {
|
|
52
|
+
return cb(err);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (result.length === 1) {
|
|
56
|
+
return cb(null, result[0]);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return cb(null, result);
|
|
60
|
+
});
|
|
61
|
+
})
|
|
62
|
+
.bind(target);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return target[property];
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
onReady(cb) {
|
|
71
|
+
if (this.ready) {
|
|
72
|
+
cb();
|
|
73
|
+
} else {
|
|
74
|
+
this.readyCallbacks.push(cb);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
paginate(conditions, sort, paging) {
|
|
79
|
+
const { pageSize: size = 20, page = 1 } = paging || {};
|
|
80
|
+
const pageSize = Math.min(100, size);
|
|
81
|
+
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
this.db
|
|
84
|
+
.find(conditions)
|
|
85
|
+
.sort(sort)
|
|
86
|
+
.exec((err, docs) => {
|
|
87
|
+
if (err) {
|
|
88
|
+
return reject(err);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const pageCount = Math.ceil(docs.length / pageSize);
|
|
92
|
+
const total = docs.length;
|
|
93
|
+
const skip = (page - 1) * pageSize;
|
|
94
|
+
const list = docs.slice(skip, skip + pageSize);
|
|
95
|
+
|
|
96
|
+
list.forEach((doc) => {
|
|
97
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
98
|
+
doc.id = doc._id;
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return resolve({
|
|
102
|
+
list,
|
|
103
|
+
paging: {
|
|
104
|
+
total,
|
|
105
|
+
pageSize,
|
|
106
|
+
pageCount,
|
|
107
|
+
page,
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async updateById(id, updates, options = {}) {
|
|
115
|
+
const [, doc] = await this.asyncDB.update({ _id: id }, updates, {
|
|
116
|
+
multi: false,
|
|
117
|
+
upsert: false,
|
|
118
|
+
returnUpdatedDocs: true,
|
|
119
|
+
...options,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return doc;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
update(...args) {
|
|
126
|
+
if (args.length === 0) {
|
|
127
|
+
throw new Error('param is required by update method');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (typeof args[0] === 'string') {
|
|
131
|
+
return this.updateById(...args);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return this.asyncDB.update(args[0], args[1], { returnUpdatedDocs: true, ...(args[2] || {}) });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
count(conditions = {}) {
|
|
138
|
+
return new Promise((resolve, reject) => {
|
|
139
|
+
this.db.count(conditions, (err, num) => {
|
|
140
|
+
if (err) {
|
|
141
|
+
return reject(err);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return resolve(num);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
remove(conditions = {}, options = { multi: false }) {
|
|
150
|
+
return new Promise((resolve, reject) => {
|
|
151
|
+
this.db.remove(conditions, options, (err, num) => {
|
|
152
|
+
if (err) {
|
|
153
|
+
return reject(err);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return resolve(num);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
reset() {
|
|
162
|
+
fs.unlinkSync(this.filename);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
find(...args) {
|
|
166
|
+
if (args.length === 0) {
|
|
167
|
+
return this.asyncDB.find({});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return this.asyncDB.find(...args);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
findOne(...args) {
|
|
174
|
+
if (args.length === 0) {
|
|
175
|
+
return this.asyncDB.findOne({});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return this.asyncDB.findOne(...args);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
insert(...args) {
|
|
182
|
+
return this.asyncDB.insert(...args);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
compactDatafile() {
|
|
186
|
+
this.db.persistence.compactDatafile((err) => {
|
|
187
|
+
if (err) {
|
|
188
|
+
console.error(`failed to compact datafile: ${this.filename}`, err);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Rename _id field name to id, this method has side effects
|
|
196
|
+
* @param {object} entities
|
|
197
|
+
*/
|
|
198
|
+
const renameIdFiledName = (entities, from = '_id', to = 'id') => {
|
|
199
|
+
/* eslint-disable no-underscore-dangle, no-param-reassign */
|
|
200
|
+
|
|
201
|
+
if (!entities) {
|
|
202
|
+
return entities;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const mapEntity = (entity) => {
|
|
206
|
+
if (entity[from]) {
|
|
207
|
+
entity[to] = entity[from];
|
|
208
|
+
delete entity[from];
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
if (!Array.isArray(entities)) {
|
|
213
|
+
mapEntity(entities);
|
|
214
|
+
return entities;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
entities.forEach(mapEntity);
|
|
218
|
+
|
|
219
|
+
return entities;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
DB.renameIdFiledName = renameIdFiledName;
|
|
223
|
+
|
|
224
|
+
module.exports = DB;
|
package/lib/factory.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module.exports = (initializer) => {
|
|
2
|
+
const states = {};
|
|
3
|
+
|
|
4
|
+
return new Proxy(
|
|
5
|
+
{},
|
|
6
|
+
{
|
|
7
|
+
get(target, prop) {
|
|
8
|
+
if (prop === 'init') {
|
|
9
|
+
return (...args) => Object.assign(states, initializer(...args));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (states[prop]) {
|
|
13
|
+
return states[prop];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
throw new Error(`State ${String(prop)} may not be initialized`);
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@abtnode/db",
|
|
3
|
+
"version": "1.6.7",
|
|
4
|
+
"description": "@nedb/core wrapper",
|
|
5
|
+
"author": "polunzh <polunzh@gmail.com>",
|
|
6
|
+
"homepage": "https://github.com/ArcBlock/blocklet-server#readme",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"main": "lib/db.js",
|
|
9
|
+
"directories": {
|
|
10
|
+
"lib": "lib"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"lib"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/ArcBlock/blocklet-server.git"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"lint": "eslint tests lib",
|
|
24
|
+
"lint:fix": "eslint --fix tests lib",
|
|
25
|
+
"test": "node tools/jest.js",
|
|
26
|
+
"coverage": "npm run test -- --coverage"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/ArcBlock/blocklet-server/issues"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@abtnode/logger": "1.6.7",
|
|
33
|
+
"@nedb/core": "^1.2.2",
|
|
34
|
+
"@nedb/multi": "^1.2.2",
|
|
35
|
+
"lodash.clonedeep": "^4.5.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"jest": "^27.4.5"
|
|
39
|
+
},
|
|
40
|
+
"gitHead": "d681c03a398e3c7d4dbc878db93b2ab778dded81"
|
|
41
|
+
}
|