@sehirapp/core-microservice
Version:
Modern mikroservis core paketi - MongoDB 6.7, Express API, Mongoose, PM2 cluster desteği
247 lines (215 loc) • 8.17 kB
JavaScript
/**
* Database Utilities - Core mikroservisler için ortak DB işlemleri
* Modern Mongoose + native MongoDB desteği
*/
/**
* Filtrelerle kayıt sayısı alma - tekrarlanan ensureConnection + getMongooseModel + countDocuments pattern'ini değiştirir
* @param {CoreClass} modelInstance - Model instance'ı
* @param {Object} filters - MongoDB filtreleri
* @returns {Promise<number>} Toplam kayıt sayısı
*/
export const countWithFilters = async (modelInstance, filters = {}) => {
try {
await modelInstance.ensureConnection();
if (modelInstance._useMongoose) {
const Model = modelInstance.getMongooseModel();
return await Model.countDocuments(filters);
} else {
return await modelInstance.dbManager.count(modelInstance.Collection(), filters);
}
} catch (error) {
throw new Error(`Count operation failed: ${error.message}`);
}
};
/**
* Pagination bilgilerini hesapla
* @param {number} page - Sayfa numarası (1'den başlar)
* @param {number} limit - Sayfa başına kayıt sayısı
* @param {number} total - Toplam kayıt sayısı
* @returns {Object} Pagination bilgileri
*/
export const paginateResults = (page, limit, total) => {
const currentPage = parseInt(page) || 1;
const pageLimit = parseInt(limit) || 20;
const totalCount = parseInt(total) || 0;
const pages = Math.ceil(totalCount / pageLimit);
return {
page: currentPage,
limit: pageLimit,
total: totalCount,
pages: pages,
hasNext: currentPage < pages,
hasPrev: currentPage > 1,
skip: (currentPage - 1) * pageLimit,
// Ek bilgiler
startRecord: totalCount > 0 ? ((currentPage - 1) * pageLimit) + 1 : 0,
endRecord: Math.min(currentPage * pageLimit, totalCount),
isEmpty: totalCount === 0
};
};
/**
* Arama query'si oluşturucu - çoklu alan arama için
* @param {string} searchTerm - Aranacak terim
* @param {Array<string>} fields - Aranacak alanlar
* @param {Object} options - Arama seçenekleri
* @returns {Object} MongoDB $or query'si
*/
export const generateSearchQuery = (searchTerm, fields = [], options = {}) => {
if (!searchTerm || !fields.length) {
return {};
}
const {
caseSensitive = false,
exactMatch = false,
wordBoundary = false
} = options;
let searchPattern = searchTerm;
// Özel karakterleri escape et
searchPattern = searchPattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
if (exactMatch) {
searchPattern = `^${searchPattern}$`;
} else if (wordBoundary) {
searchPattern = `\\b${searchPattern}\\b`;
}
const searchOptions = caseSensitive ? '' : 'i';
const searchRegex = new RegExp(searchPattern, searchOptions);
return {
$or: fields.map(field => ({ [field]: searchRegex }))
};
};
/**
* Gelişmiş filtreleme query'si oluşturucu
* @param {Object} queryParams - Request query parametreleri
* @param {Object} fieldMappings - Alan tipleri ve konfigürasyon
* @returns {Object} MongoDB query objesi
*/
export const buildAdvancedFilter = (queryParams, fieldMappings = {}) => {
const filters = {};
Object.keys(queryParams).forEach(key => {
const value = queryParams[key];
if (value === undefined || value === null || value === '') return;
const fieldConfig = fieldMappings[key] || { type: 'string' };
switch (fieldConfig.type) {
case 'string':
if (fieldConfig.exact) {
filters[key] = value;
} else {
filters[key] = { $regex: value, $options: 'i' };
}
break;
case 'number':
const numValue = Number(value);
if (!isNaN(numValue)) {
filters[key] = numValue;
}
break;
case 'range':
const rangeFilter = {};
if (queryParams[`${key}_min`]) {
rangeFilter.$gte = Number(queryParams[`${key}_min`]);
}
if (queryParams[`${key}_max`]) {
rangeFilter.$lte = Number(queryParams[`${key}_max`]);
}
if (Object.keys(rangeFilter).length > 0) {
filters[key] = rangeFilter;
}
break;
case 'date':
if (queryParams[`${key}_from`]) {
filters[key] = filters[key] || {};
filters[key].$gte = new Date(queryParams[`${key}_from`]).getTime();
}
if (queryParams[`${key}_to`]) {
filters[key] = filters[key] || {};
filters[key].$lte = new Date(queryParams[`${key}_to`]).getTime();
}
break;
case 'array':
if (Array.isArray(value)) {
filters[key] = { $in: value };
} else if (typeof value === 'string') {
filters[key] = { $in: value.split(',') };
}
break;
case 'boolean':
if (value === 'true' || value === '1') {
filters[key] = true;
} else if (value === 'false' || value === '0') {
filters[key] = false;
}
break;
}
});
return filters;
};
/**
* Toplu güncelleme işlemi
* @param {CoreClass} modelInstance - Model instance'ı
* @param {Array} updates - Güncelleme listesi [{filter, data}, ...]
* @param {Object} options - Seçenekler
* @returns {Promise<Object>} İşlem sonuçları
*/
export const bulkUpdate = async (modelInstance, updates, options = {}) => {
const { ordered = false, validateBeforeUpdate = true } = options;
try {
await modelInstance.ensureConnection();
if (modelInstance._useMongoose) {
const Model = modelInstance.getMongooseModel();
const bulkOps = updates.map(update => ({
updateOne: {
filter: update.filter,
update: { ...update.data, updatedAt: Date.now() }
}
}));
return await Model.bulkWrite(bulkOps, { ordered });
} else {
// Native MongoDB bulk operations
const results = { success: [], failed: [] };
for (const update of updates) {
try {
const result = await modelInstance.dbManager.update(
modelInstance.Collection(),
update.filter,
{ ...update.data, updatedAt: Date.now() }
);
results.success.push({ filter: update.filter, result });
} catch (error) {
results.failed.push({ filter: update.filter, error: error.message });
}
}
return results;
}
} catch (error) {
throw new Error(`Bulk update failed: ${error.message}`);
}
};
/**
* Aggregation pipeline yardımcısı
* @param {CoreClass} modelInstance - Model instance'ı
* @param {Array} pipeline - Aggregation pipeline
* @returns {Promise<Array>} Aggregation sonuçları
*/
export const aggregateData = async (modelInstance, pipeline) => {
try {
await modelInstance.ensureConnection();
if (modelInstance._useMongoose) {
const Model = modelInstance.getMongooseModel();
return await Model.aggregate(pipeline);
} else {
return await modelInstance.dbManager.aggregate(modelInstance.Collection(), pipeline);
}
} catch (error) {
throw new Error(`Aggregation failed: ${error.message}`);
}
};
// Tüm utility fonksiyonları dışa aktar
export const dbUtils = {
countWithFilters,
paginateResults,
generateSearchQuery,
buildAdvancedFilter,
bulkUpdate,
aggregateData
};
export default dbUtils;