UNPKG

@onurege3467/zerohelper

Version:

ZeroHelper is a versatile JavaScript library offering helper functions, validation, logging, database utilities and migration system for developers. It supports MongoDB, MySQL, SQLite, Redis, and PostgreSQL with increment/decrement operations.

343 lines (293 loc) 10.7 kB
// redis.js - Redis Database Adapter (v4.x Uyumlu) const { createClient } = require('redis'); class RedisDatabase { constructor(config = {}) { this.config = { host: config.host || '127.0.0.1', port: config.port || 6379, password: config.password, db: config.db || 0, connectTimeout: config.connectTimeout || 5000, commandTimeout: config.commandTimeout || 5000, }; this.keyPrefix = config.keyPrefix || 'app:'; this.client = null; this.isConnecting = false; // Eşzamanlı bağlantı girişimlerini önlemek için } async connect() { if (this.client && this.client.isReady) { return this.client; } // Eğer zaten bağlantı girişimi yapılıyorsa, bekle if (this.isConnecting) { while (this.isConnecting) { await new Promise(resolve => setTimeout(resolve, 100)); } return this.client; } this.isConnecting = true; try { this.client = createClient({ socket: { host: this.config.host, port: this.config.port, connectTimeout: this.config.connectTimeout, }, password: this.config.password, database: this.config.db, }); this.client.on('error', (err) => { console.error('Redis Error:', err.message); }); //this.client.on('connect', () => console.log('Redis Connected')); //this.client.on('ready', () => console.log('Redis Ready')); this.client.on('end', () => { console.log('Redis Connection Ended'); this.client = null; }); // Timeout ile bağlantı kontrolü await Promise.race([ this.client.connect(), new Promise((_, reject) => setTimeout(() => reject(new Error('Redis connection timeout')), this.config.connectTimeout) ), ]); console.log('Redis bağlantısı başarılı'); } catch (err) { this.client = null; throw new Error(`Redis bağlantısı başarısız: ${err.message}`); } finally { this.isConnecting = false; } return this.client; } _getKey(table, id) { return `${this.keyPrefix}${table}:${id}`; } _getTableKey(table) { return `${this.keyPrefix}${table}:*`; } async select(table, where = {}) { try { const client = await this.connect(); const pattern = this._getTableKey(table); const keys = await client.keys(pattern); if (!keys.length) return []; const values = await client.mGet(keys); return values .map(v => { try { return v ? JSON.parse(v) : null; } catch (parseErr) { console.error('JSON parse error:', parseErr); return null; } }) .filter(Boolean) .filter(item => Object.entries(where).every(([k, val]) => item[k] === val)); } catch (err) { console.error('Select error:', err); throw err; } } async selectOne(table, where = {}) { const results = await this.select(table, where); return results.length ? results[0] : null; } async insert(table, data) { try { const client = await this.connect(); const insertData = { ...data }; if (!insertData.id) { insertData.id = Date.now().toString() + Math.random().toString(36).slice(2, 9); } const key = this._getKey(table, insertData.id); await client.set(key, JSON.stringify(insertData)); return insertData; } catch (err) { console.error('Insert error:', err); throw err; } } async update(table, data, where) { try { const existing = await this.select(table, where); if (!existing.length) return []; const client = await this.connect(); const updated = []; for (const item of existing) { const merged = { ...item, ...data }; await client.set(this._getKey(table, item.id), JSON.stringify(merged)); updated.push(merged); } return updated; } catch (err) { console.error('Update error:', err); throw err; } } async updateOne(table, data, where) { const results = await this.update(table, data, where); return results.length ? results[0] : null; } async set(table, data, where) { try { const existing = await this.selectOne(table, where); if (existing) { return await this.updateOne(table, data, where); } else { return await this.insert(table, { ...data, ...where }); } } catch (err) { console.error('Set error:', err); throw err; } } async delete(table, where) { try { const existing = await this.select(table, where); if (!existing.length) return []; const client = await this.connect(); const keys = existing.map(item => this._getKey(table, item.id)); await client.del(keys); return existing; } catch (err) { console.error('Delete error:', err); throw err; } } async deleteOne(table, where) { const results = await this.delete(table, where); return results.length ? results[0] : null; } async bulkInsert(table, dataArray) { if (!Array.isArray(dataArray) || !dataArray.length) { return []; } try { const results = []; for (const data of dataArray) { results.push(await this.insert(table, data)); } return results; } catch (err) { console.error('Bulk insert error:', err); throw err; } } async ensureTable(table) { // Redis'te tablo kavramı yoktur, her zaman true döner return true; } async close() { if (this.client) { try { await this.client.quit(); } catch (err) { console.error('Redis close error:', err); } finally { this.client = null; } } } async ping() { try { const client = await this.connect(); return await client.ping(); } catch (err) { console.error('Ping error:', err); throw err; } } async flushTable(table) { try { const client = await this.connect(); const pattern = this._getTableKey(table); const keys = await client.keys(pattern); if (keys.length > 0) { await client.del(keys); } return keys.length; } catch (err) { console.error('Flush table error:', err); throw err; } } // Yardımcı metodlar async getStats(table) { try { const client = await this.connect(); const pattern = this._getTableKey(table); const keys = await client.keys(pattern); return { table, keyCount: keys.length, pattern }; } catch (err) { console.error('Get stats error:', err); throw err; } } async exists(table, where) { const result = await this.selectOne(table, where); return result !== null; } /** * Numerik alanları artırır (increment). * Redis için hash field'ları üzerinde HINCRBY kullanır. * @param {string} table - Verinin güncelleneceği tablo adı. * @param {object} increments - Artırılacak alanlar ve miktarları. * @param {object} where - Güncelleme koşulları. * @returns {Promise<number>} Etkilenen kayıt sayısı. */ async increment(table, increments, where = {}) { try { const client = await this.connect(); // Önce mevcut kayıtları bul const existingRecords = await this.select(table, where); let affectedCount = 0; for (const record of existingRecords) { const key = this._getRecordKey(table, record._id); // Her increment field için HINCRBY kullan for (const [field, value] of Object.entries(increments)) { await client.hIncrBy(key, field, value); } affectedCount++; } return affectedCount; } catch (err) { console.error('Increment error:', err); throw err; } } /** * Numerik alanları azaltır (decrement). * Redis için hash field'ları üzerinde HINCRBY ile negatif değer kullanır. * @param {string} table - Verinin güncelleneceği tablo adı. * @param {object} decrements - Azaltılacak alanlar ve miktarları. * @param {object} where - Güncelleme koşulları. * @returns {Promise<number>} Etkilenen kayıt sayısı. */ async decrement(table, decrements, where = {}) { try { const client = await this.connect(); // Önce mevcut kayıtları bul const existingRecords = await this.select(table, where); let affectedCount = 0; for (const record of existingRecords) { const key = this._getRecordKey(table, record._id); // Her decrement field için HINCRBY ile negatif değer kullan for (const [field, value] of Object.entries(decrements)) { await client.hIncrBy(key, field, -value); } affectedCount++; } return affectedCount; } catch (err) { console.error('Decrement error:', err); throw err; } } } module.exports = RedisDatabase;