@teamnet/ic-orm
Version:
Database Management System
1,105 lines (963 loc) • 30.7 kB
JavaScript
const MongoDB = require('mongodb');
const MongoClient = MongoDB.MongoClient;
const EMPTYARRAY = [];
const EMPTYOBJECT = {};
const INSERTPROJECTION = { projection: { _id: 1 } };
const BUCKETNAME = { bucketName: 'db' };
const BLACKLIST = { dbms: 1 };
global.ObjectID = MongoDB.ObjectID;
// Instancia compartida de MongoClient
let sharedClient = null;
let reconnectAttempts = 0;
const MAX_RECONNECT_ATTEMPTS = 5;
const RECONNECT_INTERVAL = 2000; // 2 segundos
/**
* Función para cerrar la conexión a MongoDB
* @param {Function} [callback] - Función opcional que se ejecutará después de cerrar la conexión
* @returns {Promise<void>} - Promesa que se resuelve cuando la conexión se ha cerrado
*/
exports.disconnect = function (callback) {
return new Promise((resolve, reject) => {
if (!sharedClient) {
if (callback) callback();
return resolve();
}
try {
if (sharedClient.topology && sharedClient.topology.isConnected()) {
sharedClient.close(false, () => {
console.log('Conexión a MongoDB cerrada explícitamente');
sharedClient = null;
if (callback) callback();
resolve();
});
} else {
sharedClient = null;
if (callback) callback();
resolve();
}
} catch (err) {
console.error('Error al cerrar la conexión con MongoDB:', err);
sharedClient = null;
if (callback) callback(err);
reject(err);
}
});
};
/**
* Intenta reconectar con MongoDB después de un error
* @param {Object} options - Opciones de conexión
* @param {Function} callback - Callback a ejecutar después de la reconexión
*/
function attemptReconnect(options, callback) {
if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
console.error(
`Máximo número de intentos de reconexión (${MAX_RECONNECT_ATTEMPTS}) alcanzado`
);
reconnectAttempts = 0;
return callback(
new Error('No se pudo reconectar a MongoDB después de múltiples intentos')
);
}
reconnectAttempts++;
console.log(
`Intentando reconectar a MongoDB (intento ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})...`
);
setTimeout(() => {
// Extraer configuración de conexión
var maxPoolSize = 10;
var connectOptions = options;
if (options && typeof options === 'object' && options.maxPoolSize) {
maxPoolSize = options.maxPoolSize;
connectOptions = options.options;
}
MongoClient.connect(
connectOptions,
{
useNewUrlParser: true,
useUnifiedTopology: true,
maxPoolSize: maxPoolSize,
serverSelectionTimeoutMS: 5000, // Timeout para la selección de servidor
},
function (err, client) {
if (err) {
console.error('Error al reconectar a MongoDB:', err);
return attemptReconnect(options, callback);
}
console.log('Reconexión a MongoDB exitosa');
reconnectAttempts = 0;
sharedClient = client;
// Configurar manejadores de eventos
setupEventHandlers(client, options);
callback(null, sharedClient);
}
);
}, RECONNECT_INTERVAL);
}
/**
* Configura los manejadores de eventos para el cliente de MongoDB
* @param {Object} client - Cliente de MongoDB
* @param {Object} options - Opciones originales de conexión (para reconexión)
*/
function setupEventHandlers(client, originalOptions) {
client.on('close', () => {
console.log('Conexión a MongoDB cerrada');
if (sharedClient === client) {
sharedClient = null;
}
});
client.on('error', (err) => {
console.error('Error en la conexión a MongoDB:', err);
// No intentamos reconectar automáticamente aquí porque el driver ya lo hace
});
client.on('timeout', () => {
console.warn('Timeout en la conexión a MongoDB');
});
}
/**
* Función para inicializar y obtener el cliente compartido de MongoDB
* @param {Object|string} options - Opciones de conexión u objeto con configuración
* @param {Function} callback - Callback a ejecutar con el cliente
*/
function getClient(options, callback) {
if (sharedClient) {
return callback(null, sharedClient);
}
// Configuraciones por defecto para el cliente MongoDB
var mongoOptions = {
useNewUrlParser: true,
useUnifiedTopology: true,
maxPoolSize: 10, // valor por defecto
serverSelectionTimeoutMS: 30000, // 30 segundos de timeout para selección de servidor
socketTimeoutMS: 45000, // 45 segundos de timeout para operaciones de socket
connectTimeoutMS: 30000 // 30 segundos de timeout para conexión inicial
};
// Extraer la URI de conexión y las opciones de configuración
var connectURI;
if (options && typeof options === 'object') {
// Si recibimos un objeto de configuración
if (options.maxPoolSize) {
mongoOptions.maxPoolSize = parseInt(options.maxPoolSize, 10) || 10;
}
connectURI = options.options; // La URI está en options.options
} else {
// Si solo recibimos la URI como string
connectURI = options;
}
// Conectar usando la URI limpia y las opciones del cliente
MongoClient.connect(connectURI, mongoOptions, function(err, client) {
if (err) {
console.error('Error al conectar con MongoDB:', err);
return callback(err);
}
sharedClient = client;
// Configurar manejadores de eventos
setupEventHandlers(client, options);
callback(null, sharedClient);
});
}
function select(client, cmd) {
var builder = cmd.builder;
var opt = builder.options;
var filter = WHERE(builder);
var options = {};
var fields = FIELDS(builder);
if (fields) options.projection = fields;
if (opt.take) options.limit = opt.take;
if (opt.skip) options.skip = opt.skip;
if (filter.sort) options.sort = filter.sort;
builder.db.$debug &&
builder.db.$debug({
collection: client.$database + '.' + opt.table,
condition: filter.where,
options: options,
});
client
.db(client.$database)
.collection(opt.table)
.find(filter.where, options)
.toArray(function (err, response) {
err && client.$opt.onerror && client.$opt.onerror(err, opt, builder);
var rows = response ? response : EMPTYARRAY;
if (opt.first) rows = rows.length ? rows[0] : null;
// checks joins
if (!err && builder.$joins) {
client.$dbms._join(rows, builder);
setImmediate(builder.db.$next);
} else builder.$callback(err, rows);
});
}
function bigselect(client, cmd) {
var builder = cmd.builder;
var opt = builder.options;
var filter = WHERE(builder);
var options = {};
var fields = FIELDS(builder);
if (fields) options.projection = fields;
if (opt.take) options.limit = opt.take;
if (opt.skip) options.skip = opt.skip;
if (!filter.sort) filter.sort = { _id: 1 };
builder.db.$debug &&
builder.db.$debug({
collection: client.$database + '.' + opt.table,
condition: filter.where,
options: options,
});
client
.db(client.$database)
.collection(opt.table)
.find(filter.where, options)
.sort(filter.sort)
.allowDiskUse()
.toArray(function (err, response) {
err && client.$opt.onerror && client.$opt.onerror(err, opt, builder);
var rows = response ? response : EMPTYARRAY;
if (opt.first) rows = rows.length ? rows[0] : null;
// checks joins
if (!err && builder.$joins) {
client.$dbms._join(rows, builder);
setImmediate(builder.db.$next);
} else builder.$callback(err, rows);
});
}
function query(client, cmd) {
var builder = cmd.builder;
var opt = builder.options;
var filter = WHERE(builder);
var options = {};
var fields = FIELDS(builder);
if (fields) options.projection = fields;
if (opt.take) options.limit = opt.take;
if (opt.skip) options.skip = opt.skip;
if (filter.sort) options.sort = filter.sort;
var col = client.db(client.$database).collection(cmd.query);
cmd.value(
col,
function (err, response) {
if (opt.first && Array.isArray(response)) response = response[0];
err && client.$opt.onerror && client.$opt.onerror(err, opt, builder);
builder.$callback(err, response);
},
filter.where,
options
);
}
function listCollections(client, cmd) {
var builder = cmd.builder;
var opt = builder.options;
client
.db(client.$database)
.listCollections()
.toArray(function (err, response) {
err && client.$opt.onerror && client.$opt.onerror(err, opt, builder);
builder.$callback(err, response);
});
}
async function listDatabases(client, cmd) {
var builder = cmd.builder;
var opt = builder.options;
var result = await client.db(client.$database).admin().listDatabases();
builder.$callback(result);
}
function pipeLines(client, cmd) {
var builder = cmd.builder;
var opt = builder.options;
var filter = WHERE(builder, true);
client
.db(client.$database)
.collection(opt.table)
.aggregate([filter, cmd.improved])
.toArray(function (err, response) {
err && client.$opt.onerror && client.$opt.onerror(err, opt, builder);
builder.$callback(err, response);
});
}
function list(client, cmd) {
var builder = cmd.builder;
var opt = builder.options;
var filter = WHERE(builder);
var options = {};
var fields = FIELDS(builder);
if (fields) options.projection = fields;
if (opt.take) options.limit = opt.take;
if (opt.skip) options.skip = opt.skip;
if (filter.sort) options.sort = filter.sort;
builder.db.$debug &&
builder.db.$debug({
collection: client.$database + '.' + opt.table,
condition: filter.where,
options: options,
});
var db = client.db(client.$database).collection(opt.table);
db.countDocuments(filter.where, function (err, count) {
if (err) {
err && client.$opt.onerror && client.$opt.onerror(err, opt, builder);
builder.$callback(err, null);
} else {
db.find(filter.where, options).toArray(function (err, response) {
if (!err && builder.$joins) {
client.$dbms._joins(response, builder, count || 0);
setImmediate(builder.db.$next);
} else builder.$callback(err, response, count);
});
}
});
}
function scalar(client, cmd) {
var builder = cmd.builder;
var opt = builder.options;
var filter = WHERE(builder, true);
var cmdgroup = '$' + cmd.name;
// builder.db.$debug && builder.db.$debug(q);
switch (cmd.scalar) {
case 'count':
client
.db(client.$database)
.collection(opt.table)
.aggregate([filter, { $count: 'count' }])
.toArray(function (err, response) {
err && client.$opt.onerror && client.$opt.onerror(err, opt, builder);
var rescount = 0;
if (response.length) rescount = response[0].count;
builder.$callback(err, rescount);
});
break;
case 'avg':
client
.db(client.$database)
.collection(opt.table)
.aggregate([
filter,
{ $group: { _id: '', average: { $avg: cmdgroup } } },
])
.toArray(function (err, response) {
err && client.$opt.onerror && client.$opt.onerror(err, opt, builder);
builder.$callback(err, response[0].average);
});
break;
case 'min':
client
.db(client.$database)
.collection(opt.table)
.aggregate([filter, { $group: { _id: '', min: { $min: cmdgroup } } }])
.toArray(function (err, response) {
err && client.$opt.onerror && client.$opt.onerror(err, opt, builder);
builder.$callback(err, response[0].min);
});
break;
case 'max':
client
.db(client.$database)
.collection(opt.table)
.aggregate([filter, { $group: { _id: '', max: { $max: cmdgroup } } }])
.toArray(function (err, response) {
err && client.$opt.onerror && client.$opt.onerror(err, opt, builder);
builder.$callback(err, response[0].max);
});
break;
case 'sum':
client
.db(client.$database)
.collection(opt.table)
.aggregate([filter, { $group: { _id: '', sum: { $sum: cmdgroup } } }])
.toArray(function (err, response) {
err && client.$opt.onerror && client.$opt.onerror(err, opt, builder);
builder.$callback(err, response[0].sum);
});
break;
case 'group':
client
.db(client.$database)
.collection(opt.table)
.aggregate([filter, { $count: 'count' }])
.toArray(function (error, count) {
error &&
client.$opt.onerror &&
client.$opt.onerror(error, opt, builder);
client
.db(client.$database)
.collection(opt.table)
.aggregate([
filter,
{ $group: { _id: cmdgroup, count: { $sum: 1 } } },
{ $sort: { count: -1 } },
])
.toArray(function (err, response) {
err &&
client.$opt.onerror &&
client.$opt.onerror(err, opt, builder);
var obj = {};
if (count && count[0] && count[0].count)
obj.total = count[0].count;
else obj.total = 0;
obj.items = response;
builder.$callback(err, obj);
});
});
break;
}
}
async function insert(client, cmd) {
var builder = cmd.builder;
var keys = Object.keys(cmd.builder.value);
var opt = builder.options;
var params = {};
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var val = cmd.builder.value[key];
if (val === undefined || BLACKLIST[key]) continue;
if (cmd.builder.options.fields && cmd.builder.options.fields.length) {
var skip = true;
for (var j = 0; j < cmd.builder.options.fields.length; j++) {
if (
cmd.builder.options.fields[j] == key ||
cmd.builder.options.fields[j] == key.substring(1)
) {
skip = false;
break;
}
}
if (skip) continue;
}
switch (key[0]) {
case '&':
key = key.substring(1);
var gencode = val;
gencode.key = key;
break;
case '%':
key = key.substring(1);
var gensearch = val;
gensearch.key = key;
break;
case '-':
case '+':
case '*':
case '/':
key = key.substring(1);
break;
case '<':
case '>':
key = key.substring(1);
break;
}
params[key] =
val == null
? null
: typeof val === 'function'
? val(cmd.builder.value)
: val;
}
if (gencode) {
var db = DBMS();
var codegen = await db
.one(gencode.indexes)
.where('code', gencode.precode)
.promise();
db.modify(gencode.indexes, { '+index': +1 }).where('code', gencode.precode);
var indexcode = codegen.index + 1;
indexcode = indexcode.toString();
while (indexcode.length < gencode.output) {
indexcode = '0' + indexcode;
}
params[gencode.key] = gencode.precode + indexcode;
if (gensearch) {
var one = gensearch.one;
var two = gensearch.two;
params[gensearch.key] = params[one] + gensearch.join + params[two];
}
}
builder.db.$debug && builder.db.$debug();
client
.db(client.$database)
.collection(opt.table)
.insertOne(params, function (err, response) {
err && client.$opt.onerror && client.$opt.onerror(err, opt, builder);
builder.$callback(
err,
response && response.result && response.result.n ? 1 : 0,
params
);
});
}
function insertexists(client, cmd) {
var builder = cmd.builder;
var opt = builder.options;
var filter = WHERE(builder);
// builder.db.$debug && builder.db.$debug(q);
client
.db(client.$database)
.collection(opt.table)
.findOne(filter.where, INSERTPROJECTION, function (err, response) {
if (err || response) {
err && client.$opt.onerror && client.$opt.onerror(err, opt, builder);
builder.$callback(err, 0);
} else insert(client, cmd);
});
}
function modify(client, cmd) {
cmd.builder.options.transform &&
cmd.builder.options.transform(
cmd.builder.value,
cmd.builder.db.$output,
cmd.builder.db.$lastoutput
);
var keys = Object.keys(cmd.builder.value);
var params = null;
var increment = null;
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var val = cmd.builder.value[key];
if (val === undefined || BLACKLIST[key]) continue;
if (cmd.builder.options.fields && cmd.builder.options.fields.length) {
var skip = true;
for (var j = 0; j < cmd.builder.options.fields.length; j++) {
if (
cmd.builder.options.fields[j] == key ||
cmd.builder.options.fields[j] == key.substring(1)
) {
skip = false;
break;
}
}
if (skip) continue;
}
var c = key[0];
if (typeof val === 'function') val = val(cmd.builder.value);
switch (c) {
case '-':
case '+':
case '*':
case '/':
!increment && (increment = {});
key = key.substring(1);
increment[key] = val ? val : 0;
break;
case '<':
case '>':
key = key.substring(1);
break;
default:
!params && (params = {});
params[key] = val;
break;
}
}
var builder = cmd.builder;
var opt = builder.options;
var filter = WHERE(builder);
// builder.db.$debug && builder.db.$debug(q);
var upd = {};
increment && (upd.$inc = increment);
params && (upd.$set = params);
var col = client.db(client.$database).collection(opt.table);
var callback = function (err, response) {
err && client.$opt.onerror && client.$opt.onerror(err, opt, builder);
var count = response && response.modifiedCount ? response.modifiedCount : 0;
if (!count && cmd.insert) {
if (cmd.insert !== true) cmd.builder.value = cmd.insert;
cmd.builder.options.insert &&
cmd.builder.options.insert(
cmd.builder.value,
cmd.builder.options.insertparams
);
insert(client, cmd);
} else {
builder.$callback(err, count);
}
};
if (opt.first) col.updateOne(filter.where, upd, callback);
else col.updateMany(filter.where, upd, callback);
}
function remove(client, cmd) {
var builder = cmd.builder;
var opt = builder.options;
var filter = WHERE(builder);
// builder.db.$debug && builder.db.$debug(q);
var col = client.db(client.$database).collection(opt.table);
var callback = function (err, response) {
err && client.$opt.onerror && client.$opt.onerror(err, opt, builder);
builder.$callback(err, response && response.result ? response.result.n : 0);
};
if (opt.first) col.deleteOne(filter.where, callback);
else col.deleteMany(filter.where, callback);
}
exports.run = function(opt, self, cmd) {
// Pasar la configuración completa a getClient, incluyendo maxPoolSize si existe
var clientOptions = {
options: opt.options, // URI de conexión
maxPoolSize: opt.maxPoolSize || 10 // Usar maxPoolSize de la configuración o valor por defecto
};
// A partir de este punto getClient se encarga de extraer maxPoolSize correctamente
getClient(clientOptions, function(err, client) {
if (err) {
opt.onerror && opt.onerror(err);
if (cmd.builder) cmd.builder.$callback(err);
else cmd.db.$next(err);
return;
}
// Configurar propiedades necesarias
client.$opt = opt;
client.$dbms = self;
client.$database = opt.database;
// Ejecutar la operación correspondiente
switch (cmd.type) {
case 'transaction':
case 'commit':
case 'rollback':
cmd.$next(new Error('"' + cmd.type + '" no está implementado.'));
break;
case 'bigFind':
bigselect(client, cmd);
break;
case 'find':
case 'read':
select(client, cmd);
break;
case 'diff':
var cb = cmd.builder.$callback;
cmd.builder.$callback = function (err, response) {
cb.call(
cmd.builder,
err,
err ? EMPTYOBJECT : compareArrayDiff(cmd.key, response, [cmd.form])
);
};
select(client, cmd);
break;
case 'list':
list(client, cmd);
break;
case 'listCollections':
listCollections(client, cmd);
break;
case 'listDatabases':
listDatabases(client, cmd);
break;
case 'pipeLines':
pipeLines(client, cmd);
break;
case 'scalar':
scalar(client, cmd);
break;
case 'insert':
if (cmd.unique) insertexists(client, cmd);
else insert(client, cmd);
break;
case 'update':
case 'modify':
modify(client, cmd);
break;
case 'remove':
remove(client, cmd);
break;
case 'query':
query(client, cmd);
break;
default:
if (cmd.builder && cmd.builder.$callback) {
cmd.builder.$callback(
new Error('Operación "' + cmd.type + '" no encontrada')
);
} else if (cmd.$callback) {
cmd.$callback(
new Error('Operación "' + cmd.type + '" no encontrada')
);
}
break;
}
});
};
exports.blob_read = function (opt, id, callback, conn) {
getClient(opt.options, function (err, client) {
if (err) {
callback(err);
return;
}
if (conn.table && conn.table !== 'default')
BUCKETNAME.bucketName = conn.table;
else BUCKETNAME.bucketName = 'db';
var done = () => {}; // No cerrar la conexión
var bucket = new MongoDB.GridFSBucket(client.db(opt.database), BUCKETNAME);
var stream = bucket.openDownloadStream(id);
stream.on('error', done);
stream.on('end', done);
callback(null, stream);
});
};
exports.blob_write = function (opt, stream, name, callback, conn) {
getClient(opt.options, function (err, client) {
if (err) {
callback(err);
return;
}
if (conn.table && conn.table !== 'default')
BUCKETNAME.bucketName = conn.table;
else BUCKETNAME.bucketName = 'db';
var options = global.U
? { contentType: U.getContentType(U.getExtension(name)) }
: null;
var bucket = new MongoDB.GridFSBucket(client.db(opt.database), BUCKETNAME);
var writer = bucket.openUploadStream(name, options);
stream
.pipe(writer)
.on('error', function (err) {
callback(err);
})
.on('finish', function () {
callback(null, writer.id);
});
});
};
exports.blob_remove = function (opt, id, callback, conn) {
getClient(opt.options, function (err, client) {
if (err) {
callback && callback(err);
return;
}
if (conn.table && conn.table !== 'default')
BUCKETNAME.bucketName = conn.table;
else BUCKETNAME.bucketName = 'db';
var bucket = new MongoDB.GridFSBucket(client.db(opt.database), BUCKETNAME);
bucket.delete(id, function (err) {
callback && callback(err);
});
});
};
function eqgtlt(cmd) {
var val;
if (cmd.compare === '=' || cmd.compare === '==') {
val = { '$eq': cmd.value };
} else if (cmd.compare === '>' || cmd.compare === 'gt') {
val = { '$gt': cmd.value };
} else if (cmd.compare === '>=' || cmd.compare === 'gte') {
val = { '$gte': cmd.value };
} else if (cmd.compare === '<' || cmd.compare === 'lt') {
val = { '$lt': cmd.value };
} else if (cmd.compare === '<=' || cmd.compare === 'lte') {
val = { '$lte': cmd.value };
}
return val;
}
/**
* Función para generar un array de diferencias entre objeto y formulario
* @param {string} key - Clave primaria
* @param {Object} data - Datos de la base de datos
* @param {Array} form - Formulario con datos a comparar
* @returns {Array} - Array con diferencias
*/
function compareArrayDiff(key, data, form) {
var arr = [];
var tmp;
if (!(data instanceof Array)) {
if (!data)
return EMPTYARRAY;
tmp = [];
tmp.push(data);
data = tmp;
}
if (!(form instanceof Array)) {
tmp = [];
tmp.push(form);
form = tmp;
}
for (var i = 0; i < form.length; i++) {
var keys = Object.keys(form[i]);
for (var j = 0; j < data.length; j++) {
if (data[j][key] == form[i][key]) {
var json = {};
json[key] = form[i][key];
var b = false;
for (var k = 0; k < keys.length; k++) {
var m = keys[k];
if (form[i][m] != data[j][m]) {
json[m] = form[i][m];
json['old_' + m] = data[j][m];
b = true;
}
}
if (b)
arr.push(json);
}
}
}
return arr;
}
function WHERE(builder, scalar) {
// , group
var condition = {};
var sort = null;
var tmp = null;
var filter;
var value;
for (var i = 0; i < builder.$commands.length; i++) {
var cmd = builder.$commands[i];
switch (cmd.type) {
case 'where':
value = cmd.compare === '=' ? cmd.value : eqgtlt(cmd);
if (tmp) {
filter = {};
filter[cmd.name] = value;
tmp.push(filter);
} else condition[cmd.name] = value;
break;
case 'in':
if (typeof cmd.value === 'function') cmd.value = cmd.value();
value = cmd.value instanceof Array ? { $in: cmd.value } : cmd.value;
if (tmp) {
filter = {};
filter[cmd.name] = value;
tmp.push(filter);
} else condition[cmd.name] = value;
break;
case 'notin':
if (typeof cmd.value === 'function') cmd.value = cmd.value();
value =
cmd.value instanceof Array ? { $nin: cmd.value } : { $ne: cmd.value };
if (tmp) {
filter = {};
filter[cmd.name] = value;
tmp.push(filter);
} else condition[cmd.name] = value;
break;
case 'between':
value = { $gte: cmd.a, $lte: cmd.b, $exists: true, $nin: [null, ''] };
if (tmp) {
filter = {};
filter[cmd.name] = value;
tmp.push(filter);
} else condition[cmd.name] = value;
break;
case 'search':
value =
(cmd.compare === '*' ? '' : cmd.compare === 'beg' ? '^' : '') +
// Escapar caracteres especiales de RegEx en cmd.value
String(cmd.value || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +
(cmd.compare === 'end' ? '$' : '');
var v = ['[aáä]', '[eéë]', '[iíï]', '[oóö]', '[uúü]'];
for (let i = 0; i < v.length; i++)
value = value.replace(new RegExp(v[i].replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'), v[i]);
value = { $regex: value, $options: 'i' };
if (tmp) {
filter = {};
filter[cmd.name] = value;
tmp.push(filter);
} else condition[cmd.name] = value;
break;
case 'fulltext':
value = new RegExp(
(cmd.compare === '*' ? '' : cmd.compare === 'beg' ? '^' : '') +
// Escapar caracteres especiales de RegEx en cmd.value
String(cmd.value || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +
(cmd.compare === 'end' ? '$' : ''),
'i'
);
if (tmp) {
filter = {};
filter[cmd.name] = value;
tmp.push(filter);
} else condition[cmd.name] = value;
break;
case 'contains':
value = { $exists: true, $nin: [null, ''] };
if (tmp) {
filter = {};
filter[cmd.name] = value;
tmp.push(filter);
} else condition[cmd.name] = value;
break;
case 'empty':
value = { $exists: false, $in: [null, ''] };
if (tmp) {
filter = {};
filter[cmd.name] = value;
tmp.push(filter);
} else condition[cmd.name] = value;
break;
case 'month':
case 'year':
case 'day':
case 'hour':
case 'minute':
new Error(
'Not implemented',
"MongoDB doesn't support date function for quering."
);
break;
case 'query':
if (tmp) {
filter = {};
tmp.push(cmd.query);
} else {
var arr = Object.keys(cmd.query);
for (var j = 0; j < arr.length; j++)
condition[arr[j]] = cmd.query[arr[j]];
}
break;
case 'or':
tmp = [];
continue;
case 'end':
condition.$or = tmp;
tmp = null;
break;
case 'and':
break;
case 'sort':
!sort && (sort = {});
sort[cmd.name] = cmd.desc ? -1 : 1;
break;
case 'regexp':
if (tmp) {
filter = {};
filter[cmd.name] = cmd.value;
tmp.push(filter);
} else condition[cmd.name] = cmd.value;
break;
}
}
if (scalar) return { $match: condition };
else return { where: condition, sort: sort };
}
function FIELDS(builder) {
var output = null;
var fields = builder.options.fields;
if (fields && fields.length) {
output = {};
for (var i = 0; i < fields.length; i++) {
var name = fields[i];
var is = name[0] === '-';
if (is) name = name.substring(1);
output[name] = is ? 0 : 1;
}
if (builder.$joinmeta) output[builder.$joinmeta.a] = 1;
}
return output;
}
/**
* Funcion para manejar el cierre de conexiones y terminar el proceso
* @param {string} signal - Señal recibida (SIGINT, SIGTERM)
*/
function handleProcessTermination(signal) {
console.log(`Recibida señal ${signal}, cerrando conexiones...`);
exports
.disconnect()
.then(() => {
console.log(`Conexiones cerradas correctamente debido a ${signal}`);
// Dar un breve tiempo para que los logs se escriban antes de salir
setTimeout(() => process.exit(0), 100);
})
.catch((err) => {
console.error(`Error al cerrar conexiones: ${err.message || err}`);
process.exit(1);
});
// Si el proceso no termina en 5 segundos, forzar la salida
setTimeout(() => {
console.error(
`Tiempo de espera agotado para cerrar conexiones, forzando salida...`
);
process.exit(1);
}, 5000);
}
// Manejar señales de terminación del proceso
process.on('SIGINT', () => handleProcessTermination('SIGINT'));
process.on('SIGTERM', () => handleProcessTermination('SIGTERM'));