bonsaif 1.10.39 → 1.10.40
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 +0 -133
- package/lib/execute.js +0 -28
- package/lib/hookup/exec.js +2 -4
- package/lib/hookup/mariadb.js +10 -28
- package/lib/hookup/mongodb.js +177 -193
- package/lib/hookup/postgres.js +27 -35
- package/lib/hookup/redis.js +42 -162
- package/package.json +3 -6
- package/lib/hookup/mongoose.js +0 -988
- package/tests/README.md +0 -245
- package/tests/config.example.js +0 -61
- package/tests/docker-restart.sh +0 -20
- package/tests/docker-start.sh +0 -192
- package/tests/docker-stop.sh +0 -41
- package/tests/run-all-tests.js +0 -76
- package/tests/test-mariadb.js +0 -85
- package/tests/test-mongodb.js +0 -133
- package/tests/test-mongoose.js +0 -408
- package/tests/test-postgres.js +0 -84
- package/tests/test-redis.js +0 -85
package/lib/hookup/mongoose.js
DELETED
|
@@ -1,988 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ::: B[]NSAIF() ::: => 2024
|
|
3
|
-
* Hookup para Mongoose (MongoDB ORM)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
'use strict';
|
|
7
|
-
|
|
8
|
-
const utl = require('../utl');
|
|
9
|
-
let debug = false;
|
|
10
|
-
let tag = ` ::: B[]NSAIF() ::: => mongoose.js `;
|
|
11
|
-
|
|
12
|
-
// Cache de modelos dinámicos por base de datos y colección
|
|
13
|
-
const modelCache = new Map();
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Parsear schema dinámico recibido del cliente
|
|
17
|
-
* Convierte definición simple a Schema de Mongoose
|
|
18
|
-
*/
|
|
19
|
-
const parseSchema = (schemaDefinition) => {
|
|
20
|
-
const mongoose = require('mongoose');
|
|
21
|
-
const parsedSchema = {};
|
|
22
|
-
|
|
23
|
-
for (const [field, def] of Object.entries(schemaDefinition)) {
|
|
24
|
-
if (typeof def === 'string') {
|
|
25
|
-
// Formato simple: { "name": "String" }
|
|
26
|
-
parsedSchema[field] = { type: mongoose.Schema.Types[def] || String };
|
|
27
|
-
} else if (typeof def === 'object') {
|
|
28
|
-
// Formato completo: { "name": { "type": "String", "required": true } }
|
|
29
|
-
const fieldDef = { ...def };
|
|
30
|
-
|
|
31
|
-
// Convertir string del type a tipo real de Mongoose
|
|
32
|
-
if (def.type && typeof def.type === 'string') {
|
|
33
|
-
fieldDef.type = mongoose.Schema.Types[def.type] || String;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Convertir enum si existe
|
|
37
|
-
if (def.enum && Array.isArray(def.enum)) {
|
|
38
|
-
fieldDef.enum = def.enum;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Convertir default
|
|
42
|
-
if (def.default !== undefined) {
|
|
43
|
-
fieldDef.default = def.default;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Validaciones
|
|
47
|
-
if (def.required !== undefined) fieldDef.required = def.required;
|
|
48
|
-
if (def.unique !== undefined) fieldDef.unique = def.unique;
|
|
49
|
-
if (def.min !== undefined) fieldDef.min = def.min;
|
|
50
|
-
if (def.max !== undefined) fieldDef.max = def.max;
|
|
51
|
-
if (def.minlength !== undefined) fieldDef.minlength = def.minlength;
|
|
52
|
-
if (def.maxlength !== undefined) fieldDef.maxlength = def.maxlength;
|
|
53
|
-
if (def.trim !== undefined) fieldDef.trim = def.trim;
|
|
54
|
-
if (def.lowercase !== undefined) fieldDef.lowercase = def.lowercase;
|
|
55
|
-
if (def.uppercase !== undefined) fieldDef.uppercase = def.uppercase;
|
|
56
|
-
if (def.match !== undefined) fieldDef.match = new RegExp(def.match);
|
|
57
|
-
|
|
58
|
-
// Referencias (populate)
|
|
59
|
-
if (def.ref) {
|
|
60
|
-
fieldDef.ref = def.ref;
|
|
61
|
-
fieldDef.type = mongoose.Schema.Types.ObjectId;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
parsedSchema[field] = fieldDef;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return parsedSchema;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Conectar a MongoDB usando Mongoose
|
|
73
|
-
*/
|
|
74
|
-
const connect = async (options) => {
|
|
75
|
-
const mongoose = require('mongoose');
|
|
76
|
-
const { uri = '', poolSize = 10, debug: debugMode = false } = options.endpoint;
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
if (mongoose.connection.readyState === 1) {
|
|
80
|
-
debugMode ? utl.log(`${tag} [Mongoose] ya conectado`) : '';
|
|
81
|
-
return mongoose.connection;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
await mongoose.connect(uri, {
|
|
85
|
-
maxPoolSize: poolSize,
|
|
86
|
-
minPoolSize: 2,
|
|
87
|
-
serverSelectionTimeoutMS: 15000,
|
|
88
|
-
socketTimeoutMS: 45000,
|
|
89
|
-
connectTimeoutMS: 10000
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
debugMode ? utl.log(`${tag} [Mongoose] conectado a ${uri}`) : '';
|
|
93
|
-
return mongoose.connection;
|
|
94
|
-
} catch (e) {
|
|
95
|
-
utl.log(`${tag} [Mongoose] error de conexión:`, e);
|
|
96
|
-
throw e;
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Obtener o crear modelo dinámico para una colección
|
|
102
|
-
*/
|
|
103
|
-
const getModel = (db, collection, schemaDefinition = null) => {
|
|
104
|
-
const mongoose = require('mongoose');
|
|
105
|
-
const cacheKey = schemaDefinition
|
|
106
|
-
? `${db}.${collection}.custom.${JSON.stringify(schemaDefinition)}`
|
|
107
|
-
: `${db}.${collection}.generic`;
|
|
108
|
-
|
|
109
|
-
if (modelCache.has(cacheKey)) {
|
|
110
|
-
return modelCache.get(cacheKey);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
let schema;
|
|
114
|
-
|
|
115
|
-
if (schemaDefinition) {
|
|
116
|
-
// Schema personalizado con validaciones
|
|
117
|
-
const parsedSchema = parseSchema(schemaDefinition);
|
|
118
|
-
schema = new mongoose.Schema(parsedSchema, {
|
|
119
|
-
strict: true,
|
|
120
|
-
timestamps: true,
|
|
121
|
-
collection: collection
|
|
122
|
-
});
|
|
123
|
-
} else {
|
|
124
|
-
// Schema genérico (acepta cualquier campo)
|
|
125
|
-
schema = new mongoose.Schema({}, {
|
|
126
|
-
strict: false,
|
|
127
|
-
timestamps: true,
|
|
128
|
-
collection: collection
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Crear modelo en la conexión específica de la BD
|
|
133
|
-
const conn = mongoose.connection.useDb(db);
|
|
134
|
-
|
|
135
|
-
// Nombre único para evitar conflictos
|
|
136
|
-
const modelName = schemaDefinition
|
|
137
|
-
? `${collection}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
|
138
|
-
: collection;
|
|
139
|
-
|
|
140
|
-
const Model = conn.model(modelName, schema);
|
|
141
|
-
|
|
142
|
-
modelCache.set(cacheKey, Model);
|
|
143
|
-
return Model;
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* FIND - Búsqueda dinámica
|
|
148
|
-
*/
|
|
149
|
-
const find = async (options, db, collection, query) => {
|
|
150
|
-
const start = Date.now();
|
|
151
|
-
|
|
152
|
-
try {
|
|
153
|
-
await connect(options);
|
|
154
|
-
|
|
155
|
-
const { filter = {}, select = '', sort = {}, limit = 0, skip = 0, populate = '', schema = null } = query;
|
|
156
|
-
|
|
157
|
-
const Model = getModel(db, collection, schema);
|
|
158
|
-
let mongoQuery = Model.find(filter);
|
|
159
|
-
|
|
160
|
-
if (select) mongoQuery = mongoQuery.select(select);
|
|
161
|
-
if (Object.keys(sort).length > 0) mongoQuery = mongoQuery.sort(sort);
|
|
162
|
-
if (limit > 0) mongoQuery = mongoQuery.limit(limit);
|
|
163
|
-
if (skip > 0) mongoQuery = mongoQuery.skip(skip);
|
|
164
|
-
if (populate) mongoQuery = mongoQuery.populate(populate);
|
|
165
|
-
|
|
166
|
-
const data = await mongoQuery.lean().exec();
|
|
167
|
-
|
|
168
|
-
const end = Date.now();
|
|
169
|
-
const time = end - start;
|
|
170
|
-
|
|
171
|
-
return {
|
|
172
|
-
result: {
|
|
173
|
-
dml: 'find',
|
|
174
|
-
collection,
|
|
175
|
-
db,
|
|
176
|
-
time: utl.milisegundosASegundos(time),
|
|
177
|
-
code: 200,
|
|
178
|
-
error: 0
|
|
179
|
-
},
|
|
180
|
-
data
|
|
181
|
-
};
|
|
182
|
-
} catch (e) {
|
|
183
|
-
const end = Date.now();
|
|
184
|
-
const time = end - start;
|
|
185
|
-
utl.log(`${tag} find error:`, e);
|
|
186
|
-
return {
|
|
187
|
-
result: {
|
|
188
|
-
dml: 'find',
|
|
189
|
-
time: utl.milisegundosASegundos(time),
|
|
190
|
-
code: 500,
|
|
191
|
-
error: 1,
|
|
192
|
-
msg: e.message
|
|
193
|
-
},
|
|
194
|
-
data: []
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* FINDONE - Buscar un solo documento
|
|
201
|
-
*/
|
|
202
|
-
const findOne = async (options, db, collection, query) => {
|
|
203
|
-
const start = Date.now();
|
|
204
|
-
|
|
205
|
-
try {
|
|
206
|
-
await connect(options);
|
|
207
|
-
|
|
208
|
-
const { filter = {}, select = '', populate = '', schema = null } = query;
|
|
209
|
-
|
|
210
|
-
const Model = getModel(db, collection, schema);
|
|
211
|
-
let mongoQuery = Model.findOne(filter);
|
|
212
|
-
|
|
213
|
-
if (select) mongoQuery = mongoQuery.select(select);
|
|
214
|
-
if (populate) mongoQuery = mongoQuery.populate(populate);
|
|
215
|
-
|
|
216
|
-
const data = await mongoQuery.lean().exec();
|
|
217
|
-
|
|
218
|
-
const end = Date.now();
|
|
219
|
-
const time = end - start;
|
|
220
|
-
|
|
221
|
-
return {
|
|
222
|
-
result: {
|
|
223
|
-
dml: 'findOne',
|
|
224
|
-
collection,
|
|
225
|
-
db,
|
|
226
|
-
time: utl.milisegundosASegundos(time),
|
|
227
|
-
code: 200,
|
|
228
|
-
error: 0
|
|
229
|
-
},
|
|
230
|
-
data: data ? [data] : []
|
|
231
|
-
};
|
|
232
|
-
} catch (e) {
|
|
233
|
-
const end = Date.now();
|
|
234
|
-
const time = end - start;
|
|
235
|
-
utl.log(`${tag} findOne error:`, e);
|
|
236
|
-
return {
|
|
237
|
-
result: {
|
|
238
|
-
dml: 'findOne',
|
|
239
|
-
time: utl.milisegundosASegundos(time),
|
|
240
|
-
code: 500,
|
|
241
|
-
error: 1,
|
|
242
|
-
msg: e.message
|
|
243
|
-
},
|
|
244
|
-
data: []
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* COUNT - Contar documentos
|
|
251
|
-
*/
|
|
252
|
-
const count = async (options, db, collection, query) => {
|
|
253
|
-
const start = Date.now();
|
|
254
|
-
|
|
255
|
-
try {
|
|
256
|
-
await connect(options);
|
|
257
|
-
|
|
258
|
-
const { filter = {}, schema = null } = query;
|
|
259
|
-
|
|
260
|
-
const Model = getModel(db, collection, schema);
|
|
261
|
-
const total = await Model.countDocuments(filter);
|
|
262
|
-
|
|
263
|
-
const end = Date.now();
|
|
264
|
-
const time = end - start;
|
|
265
|
-
|
|
266
|
-
return {
|
|
267
|
-
result: {
|
|
268
|
-
dml: 'count',
|
|
269
|
-
collection,
|
|
270
|
-
db,
|
|
271
|
-
time: utl.milisegundosASegundos(time),
|
|
272
|
-
code: 200,
|
|
273
|
-
error: 0
|
|
274
|
-
},
|
|
275
|
-
data: { count: total }
|
|
276
|
-
};
|
|
277
|
-
} catch (e) {
|
|
278
|
-
const end = Date.now();
|
|
279
|
-
const time = end - start;
|
|
280
|
-
utl.log(`${tag} count error:`, e);
|
|
281
|
-
return {
|
|
282
|
-
result: {
|
|
283
|
-
dml: 'count',
|
|
284
|
-
time: utl.milisegundosASegundos(time),
|
|
285
|
-
code: 500,
|
|
286
|
-
error: 1,
|
|
287
|
-
msg: e.message
|
|
288
|
-
},
|
|
289
|
-
data: { count: 0 }
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* INSERT - Crear documento
|
|
296
|
-
*/
|
|
297
|
-
const insert = async (options, db, collection, query) => {
|
|
298
|
-
const start = Date.now();
|
|
299
|
-
|
|
300
|
-
try {
|
|
301
|
-
await connect(options);
|
|
302
|
-
|
|
303
|
-
const { document = {}, schema = null } = query;
|
|
304
|
-
|
|
305
|
-
const Model = getModel(db, collection, schema);
|
|
306
|
-
const doc = new Model(document);
|
|
307
|
-
const result = await doc.save();
|
|
308
|
-
|
|
309
|
-
const end = Date.now();
|
|
310
|
-
const time = end - start;
|
|
311
|
-
|
|
312
|
-
return {
|
|
313
|
-
result: {
|
|
314
|
-
dml: 'insert',
|
|
315
|
-
collection,
|
|
316
|
-
db,
|
|
317
|
-
insertId: result._id,
|
|
318
|
-
time: utl.milisegundosASegundos(time),
|
|
319
|
-
code: 200,
|
|
320
|
-
error: 0
|
|
321
|
-
},
|
|
322
|
-
data: [result.toObject()]
|
|
323
|
-
};
|
|
324
|
-
} catch (e) {
|
|
325
|
-
const end = Date.now();
|
|
326
|
-
const time = end - start;
|
|
327
|
-
utl.log(`${tag} insert error:`, e);
|
|
328
|
-
return {
|
|
329
|
-
result: {
|
|
330
|
-
dml: 'insert',
|
|
331
|
-
time: utl.milisegundosASegundos(time),
|
|
332
|
-
code: 500,
|
|
333
|
-
error: 1,
|
|
334
|
-
msg: e.message,
|
|
335
|
-
validationErrors: e.errors ? Object.keys(e.errors).map(key => ({
|
|
336
|
-
field: key,
|
|
337
|
-
message: e.errors[key].message
|
|
338
|
-
})) : undefined
|
|
339
|
-
},
|
|
340
|
-
data: []
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
};
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* INSERTMANY - Crear múltiples documentos
|
|
347
|
-
*/
|
|
348
|
-
const insertMany = async (options, db, collection, query) => {
|
|
349
|
-
const start = Date.now();
|
|
350
|
-
|
|
351
|
-
try {
|
|
352
|
-
await connect(options);
|
|
353
|
-
|
|
354
|
-
const { documents = [], schema = null } = query;
|
|
355
|
-
|
|
356
|
-
const Model = getModel(db, collection, schema);
|
|
357
|
-
const results = await Model.insertMany(documents);
|
|
358
|
-
|
|
359
|
-
const end = Date.now();
|
|
360
|
-
const time = end - start;
|
|
361
|
-
|
|
362
|
-
return {
|
|
363
|
-
result: {
|
|
364
|
-
dml: 'insertMany',
|
|
365
|
-
collection,
|
|
366
|
-
db,
|
|
367
|
-
insertedCount: results.length,
|
|
368
|
-
time: utl.milisegundosASegundos(time),
|
|
369
|
-
code: 200,
|
|
370
|
-
error: 0
|
|
371
|
-
},
|
|
372
|
-
data: results.map(doc => doc.toObject())
|
|
373
|
-
};
|
|
374
|
-
} catch (e) {
|
|
375
|
-
const end = Date.now();
|
|
376
|
-
const time = end - start;
|
|
377
|
-
utl.log(`${tag} insertMany error:`, e);
|
|
378
|
-
return {
|
|
379
|
-
result: {
|
|
380
|
-
dml: 'insertMany',
|
|
381
|
-
time: utl.milisegundosASegundos(time),
|
|
382
|
-
code: 500,
|
|
383
|
-
error: 1,
|
|
384
|
-
msg: e.message
|
|
385
|
-
},
|
|
386
|
-
data: []
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
};
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* UPDATE - Actualizar documento(s)
|
|
393
|
-
*/
|
|
394
|
-
const update = async (options, db, collection, query) => {
|
|
395
|
-
const start = Date.now();
|
|
396
|
-
|
|
397
|
-
try {
|
|
398
|
-
await connect(options);
|
|
399
|
-
|
|
400
|
-
const { filter = {}, update: updateDoc = {}, options: updateOpts = {}, schema = null } = query;
|
|
401
|
-
|
|
402
|
-
const Model = getModel(db, collection, schema);
|
|
403
|
-
const result = await Model.updateOne(filter, updateDoc, updateOpts);
|
|
404
|
-
|
|
405
|
-
const end = Date.now();
|
|
406
|
-
const time = end - start;
|
|
407
|
-
|
|
408
|
-
return {
|
|
409
|
-
result: {
|
|
410
|
-
dml: 'update',
|
|
411
|
-
collection,
|
|
412
|
-
db,
|
|
413
|
-
matchedCount: result.matchedCount,
|
|
414
|
-
modifiedCount: result.modifiedCount,
|
|
415
|
-
time: utl.milisegundosASegundos(time),
|
|
416
|
-
code: 200,
|
|
417
|
-
error: 0
|
|
418
|
-
},
|
|
419
|
-
data: {
|
|
420
|
-
matchedCount: result.matchedCount,
|
|
421
|
-
modifiedCount: result.modifiedCount
|
|
422
|
-
}
|
|
423
|
-
};
|
|
424
|
-
} catch (e) {
|
|
425
|
-
const end = Date.now();
|
|
426
|
-
const time = end - start;
|
|
427
|
-
utl.log(`${tag} update error:`, e);
|
|
428
|
-
return {
|
|
429
|
-
result: {
|
|
430
|
-
dml: 'update',
|
|
431
|
-
time: utl.milisegundosASegundos(time),
|
|
432
|
-
code: 500,
|
|
433
|
-
error: 1,
|
|
434
|
-
msg: e.message
|
|
435
|
-
},
|
|
436
|
-
data: {}
|
|
437
|
-
};
|
|
438
|
-
}
|
|
439
|
-
};
|
|
440
|
-
|
|
441
|
-
/**
|
|
442
|
-
* UPDATEMANY - Actualizar múltiples documentos
|
|
443
|
-
*/
|
|
444
|
-
const updateMany = async (options, db, collection, query) => {
|
|
445
|
-
const start = Date.now();
|
|
446
|
-
|
|
447
|
-
try {
|
|
448
|
-
await connect(options);
|
|
449
|
-
|
|
450
|
-
const { filter = {}, update: updateDoc = {}, options: updateOpts = {}, schema = null } = query;
|
|
451
|
-
|
|
452
|
-
const Model = getModel(db, collection, schema);
|
|
453
|
-
const result = await Model.updateMany(filter, updateDoc, updateOpts);
|
|
454
|
-
|
|
455
|
-
const end = Date.now();
|
|
456
|
-
const time = end - start;
|
|
457
|
-
|
|
458
|
-
return {
|
|
459
|
-
result: {
|
|
460
|
-
dml: 'updateMany',
|
|
461
|
-
collection,
|
|
462
|
-
db,
|
|
463
|
-
matchedCount: result.matchedCount,
|
|
464
|
-
modifiedCount: result.modifiedCount,
|
|
465
|
-
time: utl.milisegundosASegundos(time),
|
|
466
|
-
code: 200,
|
|
467
|
-
error: 0
|
|
468
|
-
},
|
|
469
|
-
data: {
|
|
470
|
-
matchedCount: result.matchedCount,
|
|
471
|
-
modifiedCount: result.modifiedCount
|
|
472
|
-
}
|
|
473
|
-
};
|
|
474
|
-
} catch (e) {
|
|
475
|
-
const end = Date.now();
|
|
476
|
-
const time = end - start;
|
|
477
|
-
utl.log(`${tag} updateMany error:`, e);
|
|
478
|
-
return {
|
|
479
|
-
result: {
|
|
480
|
-
dml: 'updateMany',
|
|
481
|
-
time: utl.milisegundosASegundos(time),
|
|
482
|
-
code: 500,
|
|
483
|
-
error: 1,
|
|
484
|
-
msg: e.message
|
|
485
|
-
},
|
|
486
|
-
data: {}
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
/**
|
|
492
|
-
* UPSERT - Actualizar o insertar
|
|
493
|
-
*/
|
|
494
|
-
const upsert = async (options, db, collection, query) => {
|
|
495
|
-
const start = Date.now();
|
|
496
|
-
|
|
497
|
-
try {
|
|
498
|
-
await connect(options);
|
|
499
|
-
|
|
500
|
-
const { filter = {}, document = {}, schema = null } = query;
|
|
501
|
-
|
|
502
|
-
const Model = getModel(db, collection, schema);
|
|
503
|
-
|
|
504
|
-
// Buscar documento existente
|
|
505
|
-
const existing = await Model.findOne(filter);
|
|
506
|
-
|
|
507
|
-
let result, operation;
|
|
508
|
-
|
|
509
|
-
if (existing) {
|
|
510
|
-
// Actualizar
|
|
511
|
-
Object.assign(existing, document);
|
|
512
|
-
result = await existing.save();
|
|
513
|
-
operation = 'update';
|
|
514
|
-
} else {
|
|
515
|
-
// Insertar
|
|
516
|
-
const doc = new Model({ ...filter, ...document });
|
|
517
|
-
result = await doc.save();
|
|
518
|
-
operation = 'insert';
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
const end = Date.now();
|
|
522
|
-
const time = end - start;
|
|
523
|
-
|
|
524
|
-
return {
|
|
525
|
-
result: {
|
|
526
|
-
dml: 'upsert',
|
|
527
|
-
operation,
|
|
528
|
-
collection,
|
|
529
|
-
db,
|
|
530
|
-
time: utl.milisegundosASegundos(time),
|
|
531
|
-
code: 200,
|
|
532
|
-
error: 0
|
|
533
|
-
},
|
|
534
|
-
data: [result.toObject()]
|
|
535
|
-
};
|
|
536
|
-
} catch (e) {
|
|
537
|
-
const end = Date.now();
|
|
538
|
-
const time = end - start;
|
|
539
|
-
utl.log(`${tag} upsert error:`, e);
|
|
540
|
-
return {
|
|
541
|
-
result: {
|
|
542
|
-
dml: 'upsert',
|
|
543
|
-
time: utl.milisegundosASegundos(time),
|
|
544
|
-
code: 500,
|
|
545
|
-
error: 1,
|
|
546
|
-
msg: e.message
|
|
547
|
-
},
|
|
548
|
-
data: []
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
};
|
|
552
|
-
|
|
553
|
-
/**
|
|
554
|
-
* DELETE - Eliminar documento(s)
|
|
555
|
-
*/
|
|
556
|
-
const deleteOne = async (options, db, collection, query) => {
|
|
557
|
-
const start = Date.now();
|
|
558
|
-
|
|
559
|
-
try {
|
|
560
|
-
await connect(options);
|
|
561
|
-
|
|
562
|
-
const { filter = {}, schema = null } = query;
|
|
563
|
-
|
|
564
|
-
const Model = getModel(db, collection, schema);
|
|
565
|
-
const result = await Model.deleteOne(filter);
|
|
566
|
-
|
|
567
|
-
const end = Date.now();
|
|
568
|
-
const time = end - start;
|
|
569
|
-
|
|
570
|
-
return {
|
|
571
|
-
result: {
|
|
572
|
-
dml: 'delete',
|
|
573
|
-
collection,
|
|
574
|
-
db,
|
|
575
|
-
deletedCount: result.deletedCount,
|
|
576
|
-
time: utl.milisegundosASegundos(time),
|
|
577
|
-
code: 200,
|
|
578
|
-
error: 0
|
|
579
|
-
},
|
|
580
|
-
data: { deletedCount: result.deletedCount }
|
|
581
|
-
};
|
|
582
|
-
} catch (e) {
|
|
583
|
-
const end = Date.now();
|
|
584
|
-
const time = end - start;
|
|
585
|
-
utl.log(`${tag} delete error:`, e);
|
|
586
|
-
return {
|
|
587
|
-
result: {
|
|
588
|
-
dml: 'delete',
|
|
589
|
-
time: utl.milisegundosASegundos(time),
|
|
590
|
-
code: 500,
|
|
591
|
-
error: 1,
|
|
592
|
-
msg: e.message
|
|
593
|
-
},
|
|
594
|
-
data: { deletedCount: 0 }
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
};
|
|
598
|
-
|
|
599
|
-
/**
|
|
600
|
-
* DELETEMANY - Eliminar múltiples documentos
|
|
601
|
-
*/
|
|
602
|
-
const deleteMany = async (options, db, collection, query) => {
|
|
603
|
-
const start = Date.now();
|
|
604
|
-
|
|
605
|
-
try {
|
|
606
|
-
await connect(options);
|
|
607
|
-
|
|
608
|
-
const { filter = {}, schema = null } = query;
|
|
609
|
-
|
|
610
|
-
const Model = getModel(db, collection, schema);
|
|
611
|
-
const result = await Model.deleteMany(filter);
|
|
612
|
-
|
|
613
|
-
const end = Date.now();
|
|
614
|
-
const time = end - start;
|
|
615
|
-
|
|
616
|
-
return {
|
|
617
|
-
result: {
|
|
618
|
-
dml: 'deleteMany',
|
|
619
|
-
collection,
|
|
620
|
-
db,
|
|
621
|
-
deletedCount: result.deletedCount,
|
|
622
|
-
time: utl.milisegundosASegundos(time),
|
|
623
|
-
code: 200,
|
|
624
|
-
error: 0
|
|
625
|
-
},
|
|
626
|
-
data: { deletedCount: result.deletedCount }
|
|
627
|
-
};
|
|
628
|
-
} catch (e) {
|
|
629
|
-
const end = Date.now();
|
|
630
|
-
const time = end - start;
|
|
631
|
-
utl.log(`${tag} deleteMany error:`, e);
|
|
632
|
-
return {
|
|
633
|
-
result: {
|
|
634
|
-
dml: 'deleteMany',
|
|
635
|
-
time: utl.milisegundosASegundos(time),
|
|
636
|
-
code: 500,
|
|
637
|
-
error: 1,
|
|
638
|
-
msg: e.message
|
|
639
|
-
},
|
|
640
|
-
data: { deletedCount: 0 }
|
|
641
|
-
};
|
|
642
|
-
}
|
|
643
|
-
};
|
|
644
|
-
|
|
645
|
-
/**
|
|
646
|
-
* AGGREGATE - Pipeline de agregación
|
|
647
|
-
*/
|
|
648
|
-
const aggregate = async (options, db, collection, query) => {
|
|
649
|
-
const start = Date.now();
|
|
650
|
-
|
|
651
|
-
try {
|
|
652
|
-
await connect(options);
|
|
653
|
-
|
|
654
|
-
const { pipeline = [], schema = null } = query;
|
|
655
|
-
|
|
656
|
-
const Model = getModel(db, collection, schema);
|
|
657
|
-
const data = await Model.aggregate(pipeline);
|
|
658
|
-
|
|
659
|
-
const end = Date.now();
|
|
660
|
-
const time = end - start;
|
|
661
|
-
|
|
662
|
-
return {
|
|
663
|
-
result: {
|
|
664
|
-
dml: 'aggregate',
|
|
665
|
-
collection,
|
|
666
|
-
db,
|
|
667
|
-
time: utl.milisegundosASegundos(time),
|
|
668
|
-
code: 200,
|
|
669
|
-
error: 0
|
|
670
|
-
},
|
|
671
|
-
data
|
|
672
|
-
};
|
|
673
|
-
} catch (e) {
|
|
674
|
-
const end = Date.now();
|
|
675
|
-
const time = end - start;
|
|
676
|
-
utl.log(`${tag} aggregate error:`, e);
|
|
677
|
-
return {
|
|
678
|
-
result: {
|
|
679
|
-
dml: 'aggregate',
|
|
680
|
-
time: utl.milisegundosASegundos(time),
|
|
681
|
-
code: 500,
|
|
682
|
-
error: 1,
|
|
683
|
-
msg: e.message
|
|
684
|
-
},
|
|
685
|
-
data: []
|
|
686
|
-
};
|
|
687
|
-
}
|
|
688
|
-
};
|
|
689
|
-
|
|
690
|
-
/**
|
|
691
|
-
* DISTINCT - Valores únicos
|
|
692
|
-
*/
|
|
693
|
-
const distinct = async (options, db, collection, query) => {
|
|
694
|
-
const start = Date.now();
|
|
695
|
-
|
|
696
|
-
try {
|
|
697
|
-
await connect(options);
|
|
698
|
-
|
|
699
|
-
const { field = '', filter = {}, schema = null } = query;
|
|
700
|
-
|
|
701
|
-
const Model = getModel(db, collection, schema);
|
|
702
|
-
const data = await Model.distinct(field, filter);
|
|
703
|
-
|
|
704
|
-
const end = Date.now();
|
|
705
|
-
const time = end - start;
|
|
706
|
-
|
|
707
|
-
return {
|
|
708
|
-
result: {
|
|
709
|
-
dml: 'distinct',
|
|
710
|
-
collection,
|
|
711
|
-
db,
|
|
712
|
-
time: utl.milisegundosASegundos(time),
|
|
713
|
-
code: 200,
|
|
714
|
-
error: 0
|
|
715
|
-
},
|
|
716
|
-
data
|
|
717
|
-
};
|
|
718
|
-
} catch (e) {
|
|
719
|
-
const end = Date.now();
|
|
720
|
-
const time = end - start;
|
|
721
|
-
utl.log(`${tag} distinct error:`, e);
|
|
722
|
-
return {
|
|
723
|
-
result: {
|
|
724
|
-
dml: 'distinct',
|
|
725
|
-
time: utl.milisegundosASegundos(time),
|
|
726
|
-
code: 500,
|
|
727
|
-
error: 1,
|
|
728
|
-
msg: e.message
|
|
729
|
-
},
|
|
730
|
-
data: []
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
};
|
|
734
|
-
|
|
735
|
-
/**
|
|
736
|
-
* DBS - Listar todas las bases de datos
|
|
737
|
-
*/
|
|
738
|
-
const dbs = async (options) => {
|
|
739
|
-
const start = Date.now();
|
|
740
|
-
|
|
741
|
-
try {
|
|
742
|
-
const mongoose = require('mongoose');
|
|
743
|
-
await connect(options);
|
|
744
|
-
|
|
745
|
-
const adminDb = mongoose.connection.db.admin();
|
|
746
|
-
const result = await adminDb.listDatabases();
|
|
747
|
-
|
|
748
|
-
const end = Date.now();
|
|
749
|
-
const time = end - start;
|
|
750
|
-
|
|
751
|
-
return {
|
|
752
|
-
result: {
|
|
753
|
-
dml: 'dbs',
|
|
754
|
-
headers: 'name|sizeOnDisk|empty',
|
|
755
|
-
time: utl.milisegundosASegundos(time),
|
|
756
|
-
code: 200,
|
|
757
|
-
error: 0
|
|
758
|
-
},
|
|
759
|
-
results: { totalSize: result.totalSize || 0 },
|
|
760
|
-
data: result.databases || []
|
|
761
|
-
};
|
|
762
|
-
} catch (e) {
|
|
763
|
-
const end = Date.now();
|
|
764
|
-
const time = end - start;
|
|
765
|
-
utl.log(`${tag} dbs error:`, e);
|
|
766
|
-
return {
|
|
767
|
-
result: {
|
|
768
|
-
dml: 'dbs',
|
|
769
|
-
time: utl.milisegundosASegundos(time),
|
|
770
|
-
code: 500,
|
|
771
|
-
error: 1,
|
|
772
|
-
msg: e.message
|
|
773
|
-
},
|
|
774
|
-
data: []
|
|
775
|
-
};
|
|
776
|
-
}
|
|
777
|
-
};
|
|
778
|
-
|
|
779
|
-
/**
|
|
780
|
-
* COLLECTIONS - Listar todas las colecciones de una base de datos
|
|
781
|
-
*/
|
|
782
|
-
const collections = async (options, db) => {
|
|
783
|
-
const start = Date.now();
|
|
784
|
-
|
|
785
|
-
try {
|
|
786
|
-
const mongoose = require('mongoose');
|
|
787
|
-
await connect(options);
|
|
788
|
-
|
|
789
|
-
const dbConn = mongoose.connection.useDb(db);
|
|
790
|
-
const colls = await dbConn.db.listCollections().toArray();
|
|
791
|
-
|
|
792
|
-
const end = Date.now();
|
|
793
|
-
const time = end - start;
|
|
794
|
-
|
|
795
|
-
return {
|
|
796
|
-
result: {
|
|
797
|
-
dml: 'collections',
|
|
798
|
-
headers: 'name|type',
|
|
799
|
-
db,
|
|
800
|
-
time: utl.milisegundosASegundos(time),
|
|
801
|
-
code: 200,
|
|
802
|
-
error: 0
|
|
803
|
-
},
|
|
804
|
-
results: colls,
|
|
805
|
-
data: colls
|
|
806
|
-
};
|
|
807
|
-
} catch (e) {
|
|
808
|
-
const end = Date.now();
|
|
809
|
-
const time = end - start;
|
|
810
|
-
utl.log(`${tag} collections error:`, e);
|
|
811
|
-
return {
|
|
812
|
-
result: {
|
|
813
|
-
dml: 'collections',
|
|
814
|
-
time: utl.milisegundosASegundos(time),
|
|
815
|
-
code: 500,
|
|
816
|
-
error: 1,
|
|
817
|
-
msg: e.message
|
|
818
|
-
},
|
|
819
|
-
data: []
|
|
820
|
-
};
|
|
821
|
-
}
|
|
822
|
-
};
|
|
823
|
-
|
|
824
|
-
/**
|
|
825
|
-
* DROP - Eliminar una colección completa
|
|
826
|
-
*/
|
|
827
|
-
const drop = async (options, db, collection) => {
|
|
828
|
-
const start = Date.now();
|
|
829
|
-
|
|
830
|
-
try {
|
|
831
|
-
const mongoose = require('mongoose');
|
|
832
|
-
await connect(options);
|
|
833
|
-
|
|
834
|
-
const dbConn = mongoose.connection.useDb(db);
|
|
835
|
-
const result = await dbConn.db.dropCollection(collection);
|
|
836
|
-
|
|
837
|
-
const end = Date.now();
|
|
838
|
-
const time = end - start;
|
|
839
|
-
|
|
840
|
-
return {
|
|
841
|
-
result: {
|
|
842
|
-
dml: 'drop',
|
|
843
|
-
collection,
|
|
844
|
-
db,
|
|
845
|
-
time: utl.milisegundosASegundos(time),
|
|
846
|
-
code: 200,
|
|
847
|
-
error: 0
|
|
848
|
-
},
|
|
849
|
-
results: result,
|
|
850
|
-
data: { drop: result }
|
|
851
|
-
};
|
|
852
|
-
} catch (e) {
|
|
853
|
-
const end = Date.now();
|
|
854
|
-
const time = end - start;
|
|
855
|
-
utl.log(`${tag} drop error:`, e);
|
|
856
|
-
return {
|
|
857
|
-
result: {
|
|
858
|
-
dml: 'drop',
|
|
859
|
-
time: utl.milisegundosASegundos(time),
|
|
860
|
-
code: 500,
|
|
861
|
-
error: 1,
|
|
862
|
-
msg: e.message
|
|
863
|
-
},
|
|
864
|
-
data: { drop: false }
|
|
865
|
-
};
|
|
866
|
-
}
|
|
867
|
-
};
|
|
868
|
-
|
|
869
|
-
/**
|
|
870
|
-
* API - Router principal para todas las operaciones
|
|
871
|
-
*/
|
|
872
|
-
const api = async (options, db, body) => {
|
|
873
|
-
let { dml = '', collection = '' } = body;
|
|
874
|
-
|
|
875
|
-
// Si dml es 'api', buscar el DML real en el body
|
|
876
|
-
if (dml === 'api' || !dml) {
|
|
877
|
-
const possibleDmls = ['find', 'findOne', 'insert', 'insertMany', 'update', 'updateMany',
|
|
878
|
-
'delete', 'deleteMany', 'upsert', 'count', 'aggregate', 'distinct',
|
|
879
|
-
'dbs', 'collections', 'drop'];
|
|
880
|
-
for (const possibleDml of possibleDmls) {
|
|
881
|
-
if (body[possibleDml] !== undefined) {
|
|
882
|
-
dml = possibleDml;
|
|
883
|
-
// En formato MongoDB, la colección viene en body.[dml]
|
|
884
|
-
if (typeof body[possibleDml] === 'string') {
|
|
885
|
-
collection = body[possibleDml];
|
|
886
|
-
}
|
|
887
|
-
break;
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
// Compatibilidad con formato MongoDB: collection puede venir en json.[dml]
|
|
893
|
-
if (!collection && dml && dml !== 'api') {
|
|
894
|
-
collection = body[dml] || '';
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
// Si aún no hay collection pero hay document/documents, usar dml por defecto
|
|
898
|
-
if (!collection && body.document) {
|
|
899
|
-
dml = dml || 'insert';
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
// Validar que collection tenga un valor (excepto para dbs y collections que no lo necesitan)
|
|
903
|
-
const requiresCollection = !['dbs', 'collections'].includes(dml);
|
|
904
|
-
|
|
905
|
-
if (requiresCollection && (!collection || collection === '')) {
|
|
906
|
-
const errorMsg = collection === ''
|
|
907
|
-
? 'La "collection" está vacía. Asegúrate de que la variable tenga un valor válido.'
|
|
908
|
-
: 'Se requiere especificar "collection" en body.collection o body.[dml]';
|
|
909
|
-
|
|
910
|
-
return {
|
|
911
|
-
result: {
|
|
912
|
-
dml: dml || 'unknown',
|
|
913
|
-
time: 0,
|
|
914
|
-
code: 400,
|
|
915
|
-
error: 1,
|
|
916
|
-
msg: errorMsg,
|
|
917
|
-
debug: {
|
|
918
|
-
receivedDml: body.dml,
|
|
919
|
-
detectedDml: dml,
|
|
920
|
-
collectionValue: collection,
|
|
921
|
-
collectionIsEmpty: collection === '',
|
|
922
|
-
bodyKeys: Object.keys(body)
|
|
923
|
-
}
|
|
924
|
-
},
|
|
925
|
-
data: []
|
|
926
|
-
};
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
let r;
|
|
930
|
-
|
|
931
|
-
switch (dml) {
|
|
932
|
-
case 'find': r = await find(options, db, collection, body); break;
|
|
933
|
-
case 'findOne': r = await findOne(options, db, collection, body); break;
|
|
934
|
-
case 'count': r = await count(options, db, collection, body); break;
|
|
935
|
-
case 'insert': r = await insert(options, db, collection, body); break;
|
|
936
|
-
case 'insertMany': r = await insertMany(options, db, collection, body); break;
|
|
937
|
-
case 'update': r = await update(options, db, collection, body); break;
|
|
938
|
-
case 'updateMany': r = await updateMany(options, db, collection, body); break;
|
|
939
|
-
case 'upsert': r = await upsert(options, db, collection, body); break;
|
|
940
|
-
case 'delete': r = await deleteOne(options, db, collection, body); break;
|
|
941
|
-
case 'deleteMany': r = await deleteMany(options, db, collection, body); break;
|
|
942
|
-
case 'aggregate': r = await aggregate(options, db, collection, body); break;
|
|
943
|
-
case 'distinct': r = await distinct(options, db, collection, body); break;
|
|
944
|
-
case 'dbs': r = await dbs(options); break;
|
|
945
|
-
case 'collections': r = await collections(options, db); break;
|
|
946
|
-
case 'drop': r = await drop(options, db, collection); break;
|
|
947
|
-
default:
|
|
948
|
-
r = {
|
|
949
|
-
result: {
|
|
950
|
-
dml: dml || 'unknown',
|
|
951
|
-
time: 0,
|
|
952
|
-
code: 400,
|
|
953
|
-
error: 1,
|
|
954
|
-
msg: `Operación "${dml}" no soportada`
|
|
955
|
-
},
|
|
956
|
-
supportedOperations: [
|
|
957
|
-
'find', 'findOne', 'count', 'insert', 'insertMany',
|
|
958
|
-
'update', 'updateMany', 'upsert', 'delete', 'deleteMany',
|
|
959
|
-
'aggregate', 'distinct', 'dbs', 'collections', 'drop'
|
|
960
|
-
]
|
|
961
|
-
};
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
return new Promise((resolve) => {
|
|
965
|
-
resolve(r);
|
|
966
|
-
});
|
|
967
|
-
};
|
|
968
|
-
|
|
969
|
-
module.exports = {
|
|
970
|
-
api,
|
|
971
|
-
find,
|
|
972
|
-
findOne,
|
|
973
|
-
count,
|
|
974
|
-
insert,
|
|
975
|
-
insertMany,
|
|
976
|
-
update,
|
|
977
|
-
updateMany,
|
|
978
|
-
upsert,
|
|
979
|
-
deleteOne,
|
|
980
|
-
deleteMany,
|
|
981
|
-
aggregate,
|
|
982
|
-
distinct,
|
|
983
|
-
dbs,
|
|
984
|
-
collections,
|
|
985
|
-
drop,
|
|
986
|
-
connect,
|
|
987
|
-
getModel
|
|
988
|
-
};
|