UNPKG

8.61 kBJavaScriptView Raw
1/**
2 * @copyright Copyright (c) 2019 Maxim Khorin <maksimovichu@gmail.com>
3 */
4'use strict';
5
6const Base = require('./Database');
7const mongodb = require('mongodb');
8const ObjectId = mongodb.ObjectID;
9
10module.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 useNewUrlParser: true,
33 useUnifiedTopology: true,
34 ...this.settings.options
35 };
36 this._tableMap = {};
37 }
38
39 async openConnection () {
40 this._client = await this.client.connect(this.getUri(true), this.settings.options);
41 return this._client.db();
42 }
43
44 async closeConnection () {
45 if (this._client) {
46 await this._client.close(true);
47 this._client = null;
48 }
49 }
50
51 async isTableExists (name) {
52 for (const {collectionName} of await this._connection.collections()) {
53 if (name === collectionName) {
54 return true;
55 }
56 }
57 }
58
59 async getTableNames () {
60 const names = [];
61 for (const {collectionName} of await this._connection.collections()) {
62 names.push(collectionName);
63 }
64 return names;
65 }
66
67 getTable (name) {
68 if (!this._tableMap[name]) {
69 this._tableMap[name] = this._connection.collection(name);
70 }
71 return this._tableMap[name];
72 }
73
74 // OPERATIONS
75
76 create (table) {
77 this.logCommand('create', {table});
78 return this._connection.createCollection(table);
79 }
80
81 find (table, query) {
82 this.logCommand('find', {table, query});
83 return this.getTable(table).find(query).toArray();
84 }
85
86 distinct (table, key, query, options) {
87 this.logCommand('distinct', {table, query});
88 return this.getTable(table).distinct(key, query, options);
89 }
90
91 async insert (table, data) {
92 this.logCommand('insert', {table, data});
93 if (Array.isArray(data)) {
94 const result = await this.getTable(table).insertMany(data);
95 return result.insertedIds;
96 }
97 const result = await this.getTable(table).insertOne(data);
98 return result.insertedId;
99 }
100
101 upsert (table, query, data) {
102 this.logCommand('upsert', {table, query, data});
103 return this.getTable(table).updateOne(query, {$set: data}, {upsert: true});
104 }
105
106 update (table, query, data) {
107 this.logCommand('update', {table, query, data});
108 return this.getTable(table).updateOne(query, {$set: data});
109 }
110
111 updateAll (table, query, data) {
112 this.logCommand('updateAll', {table, query, data});
113 return this.getTable(table).updateMany(query, {$set: data});
114 }
115
116 updateAllPull (table, query, data) {
117 this.logCommand('updateAllPull', {table, query, data});
118 return this.getTable(table).updateMany(query, {$pull: data});
119 }
120
121 updateAllPush (table, query, data) {
122 this.logCommand('updateAllPush', {table, query, data});
123 return this.getTable(table).updateMany(query, {$push: data});
124 }
125
126 unset (table, query, data) {
127 this.logCommand('unset', {table, query, data});
128 return this.getTable(table).updateOne(query, {$unset: data});
129 }
130
131 unsetAll (table, query, data) {
132 this.logCommand('unsetAll', {table, query, data});
133 return this.getTable(table).updateMany(query, {$unset: data});
134 }
135
136 delete (table, query = {}) {
137 this.logCommand('delete', {table, query});
138 return this.getTable(table).deleteMany(query);
139 }
140
141 async drop (table) {
142 if (await this.isTableExists(table)) {
143 this.logCommand('drop', {table});
144 return this.getTable(table).drop();
145 }
146 }
147
148 async dropAll () {
149 this.logCommand('dropAll');
150 for (const name of await this.getTableNames()) {
151 await this.drop(name);
152 }
153 }
154
155 truncate (table) {
156 return this.drop(table);
157 }
158
159 async rename (table, target) {
160 if (await this.isTableExists(table)) {
161 this.logCommand('rename', {table, target});
162 return this._connection.renameCollection(...arguments);
163 }
164 }
165
166 // AGGREGATE
167
168 count (table, query) {
169 this.logCommand('count', {table, query});
170 return this.getTable(table).countDocuments(query);
171 }
172
173 // QUERY
174
175 async queryAll (query) {
176 const cmd = await this.buildQuery(query);
177 const cursor = this.getTable(cmd.from).find(cmd.where);
178 if (cmd.select) {
179 cursor.project(cmd.select);
180 }
181 if (cmd.order) {
182 cursor.sort(cmd.order);
183 }
184 if (cmd.offset) {
185 cursor.skip(cmd.offset);
186 }
187 if (cmd.limit) {
188 cursor.limit(cmd.limit);
189 }
190 this.logCommand('find', cmd);
191 let docs = await cursor.toArray();
192 if (!cmd.order) {
193 docs = query.sortOrderByKeys(docs);
194 }
195 return query.populate(docs);
196 }
197
198 async queryOne (query) {
199 const docs = await this.queryAll(query.limit(1));
200 return docs.length ? docs[0] : null;
201 }
202
203 async queryColumn (query, key) {
204 const data = await this.queryAll(query.raw().select(key));
205 if (Array.isArray(data)) {
206 return data.map(doc => doc[key]);
207 }
208 for (const name of Object.keys(data)) {
209 data[name] = data[name][key];
210 }
211 return data;
212 }
213
214 async queryDistinct (query, key) {
215 const cmd = await this.buildQuery(query);
216 return this.distinct(cmd.from, key, cmd.where, {});
217 }
218
219 async queryScalar (query, key) {
220 const docs = await this.queryAll(query.raw().select(key).limit(1));
221 return docs.length ? docs[0][key] : undefined;
222 }
223
224 async queryInsert (query, data) {
225 const cmd = await this.buildQuery(query);
226 return this.insert(cmd.from, data);
227 }
228
229 async queryUpdate (query, data) {
230 const cmd = await this.buildQuery(query);
231 return this.update(cmd.from, cmd.where, data);
232 }
233
234 async queryUpdateAll (query, data) {
235 const cmd = await this.buildQuery(query);
236 return this.updateAll(cmd.from, cmd.where, data);
237 }
238
239 async queryUpsert (query, data) {
240 const cmd = await this.buildQuery(query);
241 return this.upsert(cmd.from, cmd.where, data);
242 }
243
244 async queryDelete (query) {
245 const cmd = await this.buildQuery(query);
246 return this.delete(cmd.from, cmd.where);
247 }
248
249 async queryCount (query) {
250 const cmd = await this.buildQuery(query);
251 return this.count(cmd.from, cmd.where);
252 }
253
254 // INDEXES
255
256 getIndexes (table, params = {full: true}) {
257 return this.getTable(table).indexInformation(params);
258 }
259
260 /**
261 * @param table
262 * @param data [{key: 1}, { name: [name], unique: true, ... }]
263 */
264 async createIndex (table, data) {
265 await this.create(table);
266 this.logCommand('createIndex', {table, data});
267 return this.getTable(table).createIndex(...data);
268 }
269
270 async dropIndex (table, name) {
271 if (await this.isTableExists(table)) {
272 this.logCommand('dropIndex', {table, name});
273 return this.getTable(table).dropIndex(name);
274 }
275 }
276
277 async dropIndexes (table) {
278 if (await this.isTableExists(table)) {
279 this.logCommand('dropIndexes', {table});
280 return this.getTable(table).dropIndexes();
281 }
282 }
283
284 async reindex (table) {
285 if (await this.isTableExists(table)) {
286 this.logCommand('reindex', {table});
287 return this.getTable(table).reIndex();
288 }
289 }
290};
291module.exports.init();
\No newline at end of file