1 |
|
2 |
|
3 |
|
4 | 'use strict';
|
5 |
|
6 | const Base = require('./Database');
|
7 | const mongodb = require('mongodb');
|
8 | const ObjectId = mongodb.ObjectID;
|
9 |
|
10 | module.exports = class MongoDatabase extends Base {
|
11 |
|
12 | static normalizeId (value) {
|
13 | return Array.isArray(value)
|
14 | ? value.map(this.normalizeObjectId, this)
|
15 | : this.normalizeObjectId(value);
|
16 | }
|
17 |
|
18 | static normalizeObjectId (value) {
|
19 | return value instanceof ObjectId ? value : ObjectId.isValid(value) ? ObjectId(value) : null;
|
20 | }
|
21 |
|
22 | constructor (config) {
|
23 | super({
|
24 | schema: 'mongodb',
|
25 | QueryBuilder: require('./MongoBuilder'),
|
26 | client: mongodb.MongoClient,
|
27 | ...config
|
28 | });
|
29 | this.settings.options = {
|
30 | bufferMaxEntries: 0,
|
31 | keepAlive: true,
|
32 | readPreference: 'primary',
|
33 | useNewUrlParser: true,
|
34 | useUnifiedTopology: true,
|
35 | ...this.settings.options
|
36 | };
|
37 | this._tableMap = {};
|
38 | }
|
39 |
|
40 | async openConnection () {
|
41 | this._client = await this.client.connect(this.getUri(true), this.settings.options);
|
42 | return this._client.db();
|
43 | }
|
44 |
|
45 | async closeConnection () {
|
46 | if (this._client) {
|
47 | await this._client.close(true);
|
48 | this._client = null;
|
49 | }
|
50 | }
|
51 |
|
52 | async isTableExists (name) {
|
53 | for (const {collectionName} of await this._connection.collections()) {
|
54 | if (name === collectionName) {
|
55 | return true;
|
56 | }
|
57 | }
|
58 | }
|
59 |
|
60 | async getTableNames () {
|
61 | const names = [];
|
62 | for (const {collectionName} of await this._connection.collections()) {
|
63 | names.push(collectionName);
|
64 | }
|
65 | return names;
|
66 | }
|
67 |
|
68 | getTable (name) {
|
69 | if (!this._tableMap[name]) {
|
70 | this._tableMap[name] = this._connection.collection(name);
|
71 | }
|
72 | return this._tableMap[name];
|
73 | }
|
74 |
|
75 | startSession () {
|
76 | this.traceCommand('Start session');
|
77 | return this._client.startSession();
|
78 | }
|
79 |
|
80 | endSession (session) {
|
81 | this.traceCommand('End session');
|
82 | session.endSession();
|
83 | }
|
84 |
|
85 |
|
86 |
|
87 | create (table) {
|
88 | this.traceCommand('create', {table});
|
89 | return this._connection.createCollection(table);
|
90 | }
|
91 |
|
92 | find (table, query, options) {
|
93 | this.traceCommand('find', {table, query});
|
94 | return this.getTable(table).find(query, options).toArray();
|
95 | }
|
96 |
|
97 | distinct (table, key, query, options) {
|
98 | this.traceCommand('distinct', {table, key, query});
|
99 | return this.getTable(table).distinct(key, query, options);
|
100 | }
|
101 |
|
102 | async insert (table, data, options) {
|
103 | this.traceCommand('insert', {table, data});
|
104 | if (Array.isArray(data)) {
|
105 | const result = await this.getTable(table).insertMany(data, options);
|
106 | return result.insertedIds;
|
107 | }
|
108 | const result = await this.getTable(table).insertOne(data, options);
|
109 | return result.insertedId;
|
110 | }
|
111 |
|
112 | upsert (table, query, data, options) {
|
113 | this.traceCommand('upsert', {table, query, data});
|
114 | return this.getTable(table).updateOne(query, {$set: data}, {upsert: true, ...options});
|
115 | }
|
116 |
|
117 | update (table, query, data, options) {
|
118 | this.traceCommand('update', {table, query, data});
|
119 | return this.getTable(table).updateOne(query, {$set: data}, options);
|
120 | }
|
121 |
|
122 | updateAll (table, query, data, options) {
|
123 | this.traceCommand('updateAll', {table, query, data});
|
124 | return this.getTable(table).updateMany(query, {$set: data}, options);
|
125 | }
|
126 |
|
127 | updateAllPull (table, query, data, options) {
|
128 | this.traceCommand('updateAllPull', {table, query, data});
|
129 | return this.getTable(table).updateMany(query, {$pull: data}, options);
|
130 | }
|
131 |
|
132 | updateAllPush (table, query, data, options) {
|
133 | this.traceCommand('updateAllPush', {table, query, data});
|
134 | return this.getTable(table).updateMany(query, {$push: data}, options);
|
135 | }
|
136 |
|
137 | unset (table, query, data, options) {
|
138 | this.traceCommand('unset', {table, query, data});
|
139 | return this.getTable(table).updateOne(query, {$unset: data}, options);
|
140 | }
|
141 |
|
142 | unsetAll (table, query, data, options) {
|
143 | this.traceCommand('unsetAll', {table, query, data});
|
144 | return this.getTable(table).updateMany(query, {$unset: data}, options);
|
145 | }
|
146 |
|
147 | delete (table, query = {}, options) {
|
148 | this.traceCommand('delete', {table, query});
|
149 | return this.getTable(table).deleteMany(query, options);
|
150 | }
|
151 |
|
152 | async drop (table) {
|
153 | if (await this.isTableExists(table)) {
|
154 | this.traceCommand('drop', {table});
|
155 | return this.getTable(table).drop();
|
156 | }
|
157 | }
|
158 |
|
159 | async dropAll () {
|
160 | this.traceCommand('dropAll');
|
161 | for (const name of await this.getTableNames()) {
|
162 | await this.drop(name);
|
163 | }
|
164 | }
|
165 |
|
166 | truncate (table) {
|
167 | return this.drop(table);
|
168 | }
|
169 |
|
170 | async rename (table, target) {
|
171 | if (await this.isTableExists(table)) {
|
172 | this.traceCommand('rename', {table, target});
|
173 | return this._connection.renameCollection(...arguments);
|
174 | }
|
175 | }
|
176 |
|
177 |
|
178 |
|
179 | count (table, query) {
|
180 | this.traceCommand('count', {table, query});
|
181 | return this.getTable(table).countDocuments(query);
|
182 | }
|
183 |
|
184 |
|
185 |
|
186 | async queryAll (query) {
|
187 | const cmd = await this.buildQuery(query);
|
188 | const cursor = this.getTable(cmd.from).find(cmd.where, query.getOptions());
|
189 | if (cmd.select) {
|
190 | cursor.project(cmd.select);
|
191 | }
|
192 | if (cmd.order) {
|
193 | cursor.sort(cmd.order);
|
194 | }
|
195 | if (cmd.offset) {
|
196 | cursor.skip(cmd.offset);
|
197 | }
|
198 | if (cmd.limit) {
|
199 | cursor.limit(cmd.limit);
|
200 | }
|
201 | this.traceCommand('find', cmd);
|
202 | let docs = await cursor.toArray();
|
203 | if (!cmd.order) {
|
204 | docs = query.sortOrderByKeys(docs);
|
205 | }
|
206 | return query.populate(docs);
|
207 | }
|
208 |
|
209 | async queryOne (query) {
|
210 | const docs = await this.queryAll(query.limit(1));
|
211 | return docs.length ? docs[0] : null;
|
212 | }
|
213 |
|
214 | async queryColumn (query, key) {
|
215 | const data = await this.queryAll(query.raw().select(key));
|
216 | if (Array.isArray(data)) {
|
217 | return data.map(doc => doc[key]);
|
218 | }
|
219 | for (const name of Object.keys(data)) {
|
220 | data[name] = data[name][key];
|
221 | }
|
222 | return data;
|
223 | }
|
224 |
|
225 | async queryDistinct (query, key) {
|
226 | const cmd = await this.buildQuery(query);
|
227 | return this.distinct(cmd.from, key, cmd.where, query.getOptions());
|
228 | }
|
229 |
|
230 | async queryScalar (query, key) {
|
231 | const docs = await this.queryAll(query.raw().select(key).limit(1));
|
232 | return docs.length ? docs[0][key] : undefined;
|
233 | }
|
234 |
|
235 | async queryInsert (query, data) {
|
236 | const cmd = await this.buildQuery(query);
|
237 | return this.insert(cmd.from, data, query.getOptions());
|
238 | }
|
239 |
|
240 | async queryUpdate (query, data) {
|
241 | const cmd = await this.buildQuery(query);
|
242 | return this.update(cmd.from, cmd.where, data, query.getOptions());
|
243 | }
|
244 |
|
245 | async queryUpdateAll (query, data) {
|
246 | const cmd = await this.buildQuery(query);
|
247 | return this.updateAll(cmd.from, cmd.where, data, query.getOptions());
|
248 | }
|
249 |
|
250 | async queryUpsert (query, data) {
|
251 | const cmd = await this.buildQuery(query);
|
252 | return this.upsert(cmd.from, cmd.where, data, query.getOptions());
|
253 | }
|
254 |
|
255 | async queryDelete (query) {
|
256 | const cmd = await this.buildQuery(query);
|
257 | return this.delete(cmd.from, cmd.where, query.getOptions());
|
258 | }
|
259 |
|
260 | async queryCount (query) {
|
261 | const cmd = await this.buildQuery(query);
|
262 | return this.count(cmd.from, cmd.where, query.getOptions());
|
263 | }
|
264 |
|
265 |
|
266 |
|
267 | getIndexes (table, params = {full: true}) {
|
268 | return this.getTable(table).indexInformation(params);
|
269 | }
|
270 |
|
271 | |
272 |
|
273 |
|
274 |
|
275 | async createIndex (table, data) {
|
276 | if (!await this.isTableExists(table)) {
|
277 | await this.create(table);
|
278 | }
|
279 | this.traceCommand('createIndex', {table, data});
|
280 | return this.getTable(table).createIndex(...data);
|
281 | }
|
282 |
|
283 | async dropIndex (table, name) {
|
284 | if (await this.isTableExists(table)) {
|
285 | this.traceCommand('dropIndex', {table, name});
|
286 | return this.getTable(table).dropIndex(name);
|
287 | }
|
288 | }
|
289 |
|
290 | async dropIndexes (table) {
|
291 | if (await this.isTableExists(table)) {
|
292 | this.traceCommand('dropIndexes', {table});
|
293 | return this.getTable(table).dropIndexes();
|
294 | }
|
295 | }
|
296 |
|
297 | async reindex (table) {
|
298 | if (await this.isTableExists(table)) {
|
299 | this.traceCommand('reindex', {table});
|
300 | return this.getTable(table).reIndex();
|
301 | }
|
302 | }
|
303 |
|
304 | async transact (handler) {
|
305 | if (!this.enableTransactions) {
|
306 | return handler();
|
307 | }
|
308 | const session = this.startSession();
|
309 | session.startTransaction();
|
310 | try {
|
311 | await handler({session});
|
312 | } finally {
|
313 | await session.abortTransaction();
|
314 | this.endSession(session);
|
315 | }
|
316 | }
|
317 | };
|
318 | module.exports.init(); |
\ | No newline at end of file |