/** * Complete Example - Enterprise Elasticsearch Client * * This example demonstrates: * - Configuration with environment variables * - Connection management with health checks * - Document operations with sessions * - Snapshot functionality * - Error handling and observability */ import { createConfig, ElasticsearchConnectionManager, LogLevel, defaultLogger, defaultMetricsCollector, } from '../../core/index.js'; import { DocumentManager } from '../../domain/documents/index.js'; // ============================================================================ // Type Definitions // ============================================================================ interface Product { name: string; description: string; price: number; category: string; inStock: boolean; tags: string[]; createdAt: Date; updatedAt: Date; } interface ProductSnapshot { totalProducts: number; averagePrice: number; categoryCounts: Record; outOfStockCount: number; } // ============================================================================ // Main Example // ============================================================================ async function main() { // -------------------------------------------------------------------------- // Step 1: Configuration // -------------------------------------------------------------------------- console.log('๐Ÿ”ง Step 1: Creating configuration...\n'); const config = createConfig() // Load from environment variables (ELASTICSEARCH_URL, etc.) .fromEnv() // Or specify directly .nodes(process.env.ELASTICSEARCH_URL || 'http://localhost:9200') .basicAuth( process.env.ELASTICSEARCH_USERNAME || 'elastic', process.env.ELASTICSEARCH_PASSWORD || 'changeme' ) // Request settings .timeout(30000) .retries(3) .compression(true) // Connection pool .poolSize(10, 2) // Observability .logLevel(LogLevel.INFO) .enableRequestLogging(true) .enableMetrics(true) .enableTracing(true, { serviceName: 'product-catalog', serviceVersion: '1.0.0', }) .build(); console.log('โœ… Configuration created successfully\n'); // -------------------------------------------------------------------------- // Step 2: Initialize Connection Manager // -------------------------------------------------------------------------- console.log('๐Ÿ”Œ Step 2: Initializing connection manager...\n'); const connectionManager = ElasticsearchConnectionManager.getInstance(config); await connectionManager.initialize(); console.log('โœ… Connection established'); console.log(` Health Status: ${connectionManager.getHealthStatus()}`); console.log(` Circuit State: ${connectionManager.getCircuitState()}\n`); // -------------------------------------------------------------------------- // Step 3: Create Document Manager // -------------------------------------------------------------------------- console.log('๐Ÿ“ฆ Step 3: Creating document manager...\n'); const productManager = new DocumentManager({ index: 'products', connectionManager, autoCreateIndex: true, }); await productManager.initialize(); console.log('โœ… Document manager initialized\n'); // -------------------------------------------------------------------------- // Step 4: Individual Document Operations // -------------------------------------------------------------------------- console.log('๐Ÿ“ Step 4: Individual document operations...\n'); // Create a product await productManager.create('prod-001', { name: 'Premium Widget', description: 'A high-quality widget for all your needs', price: 99.99, category: 'widgets', inStock: true, tags: ['premium', 'bestseller'], createdAt: new Date(), updatedAt: new Date(), }); console.log(' โœ“ Created product prod-001'); // Upsert (create or update) await productManager.upsert('prod-002', { name: 'Deluxe Gadget', description: 'The ultimate gadget', price: 149.99, category: 'gadgets', inStock: true, tags: ['deluxe', 'featured'], createdAt: new Date(), updatedAt: new Date(), }); console.log(' โœ“ Upserted product prod-002'); // Get a product const product = await productManager.get('prod-001'); console.log(` โœ“ Retrieved product: ${product?._source.name}`); // Update a product await productManager.update('prod-001', { price: 89.99, // Price reduction! updatedAt: new Date(), }); console.log(' โœ“ Updated product prod-001\n'); // -------------------------------------------------------------------------- // Step 5: Session-Based Batch Operations // -------------------------------------------------------------------------- console.log('๐Ÿ”„ Step 5: Session-based batch operations...\n'); const session = productManager.session({ cleanupStale: true, // Delete documents not in this session batchSize: 100, }); const batchResult = await session .start() .upsert('prod-003', { name: 'Standard Widget', description: 'A reliable widget', price: 49.99, category: 'widgets', inStock: true, tags: ['standard'], createdAt: new Date(), updatedAt: new Date(), }) .upsert('prod-004', { name: 'Mini Gadget', description: 'Compact and efficient', price: 29.99, category: 'gadgets', inStock: false, tags: ['compact', 'mini'], createdAt: new Date(), updatedAt: new Date(), }) .upsert('prod-005', { name: 'Mega Widget Pro', description: 'Professional grade widget', price: 199.99, category: 'widgets', inStock: true, tags: ['professional', 'premium'], createdAt: new Date(), updatedAt: new Date(), }) .commit(); console.log(` โœ“ Batch operation completed:`); console.log(` - Successful: ${batchResult.successful}`); console.log(` - Failed: ${batchResult.failed}`); console.log(` - Time: ${batchResult.took}ms\n`); // -------------------------------------------------------------------------- // Step 6: Iteration Over Documents // -------------------------------------------------------------------------- console.log('๐Ÿ” Step 6: Iterating over documents...\n'); let count = 0; for await (const doc of productManager.iterate()) { count++; console.log(` ${count}. ${doc._source.name} - $${doc._source.price}`); } console.log(`\n โœ“ Iterated over ${count} documents\n`); // -------------------------------------------------------------------------- // Step 7: Create Snapshot with Analytics // -------------------------------------------------------------------------- console.log('๐Ÿ“ธ Step 7: Creating snapshot with analytics...\n'); const snapshot = await productManager.snapshot( async (iterator, previousSnapshot) => { console.log(' ๐Ÿ”„ Processing snapshot...'); let totalPrice = 0; let productCount = 0; const categoryCounts: Record = {}; let outOfStockCount = 0; for await (const doc of iterator) { productCount++; totalPrice += doc._source.price; const category = doc._source.category; categoryCounts[category] = (categoryCounts[category] || 0) + 1; if (!doc._source.inStock) { outOfStockCount++; } } const analytics: ProductSnapshot = { totalProducts: productCount, averagePrice: productCount > 0 ? totalPrice / productCount : 0, categoryCounts, outOfStockCount, }; if (previousSnapshot) { console.log(` ๐Ÿ“Š Previous snapshot had ${previousSnapshot.totalProducts} products`); } return analytics; } ); console.log('\n โœ… Snapshot created:'); console.log(` - Total Products: ${snapshot.data.totalProducts}`); console.log(` - Average Price: $${snapshot.data.averagePrice.toFixed(2)}`); console.log(` - Out of Stock: ${snapshot.data.outOfStockCount}`); console.log(` - Categories:`); for (const [category, count] of Object.entries(snapshot.data.categoryCounts)) { console.log(` โ€ข ${category}: ${count}`); } console.log(` - Processing Time: ${snapshot.processingTime}ms\n`); // -------------------------------------------------------------------------- // Step 8: Health Check & Metrics // -------------------------------------------------------------------------- console.log('โค๏ธ Step 8: Health check and metrics...\n'); const healthResult = await connectionManager.healthCheck(); console.log(' Health Check:'); console.log(` - Status: ${healthResult.status}`); console.log(` - Cluster Health: ${healthResult.clusterHealth}`); console.log(` - Active Nodes: ${healthResult.activeNodes}`); console.log(` - Response Time: ${healthResult.responseTimeMs}ms\n`); // Export metrics in Prometheus format const metricsExport = defaultMetricsCollector.export(); console.log(' ๐Ÿ“Š Metrics (sample):'); console.log(metricsExport.split('\n').slice(0, 20).join('\n')); console.log(' ...\n'); // -------------------------------------------------------------------------- // Step 9: Error Handling Demo // -------------------------------------------------------------------------- console.log('โš ๏ธ Step 9: Error handling demo...\n'); try { await productManager.get('non-existent-id'); } catch (error) { console.log(' โœ“ Gracefully handled non-existent document (returns null)\n'); } try { const nonExistentManager = new DocumentManager({ index: 'non-existent-index', connectionManager, autoCreateIndex: false, }); await nonExistentManager.initialize(); } catch (error: any) { console.log(` โœ“ Caught expected error: ${error.message}`); console.log(` Error Code: ${error.code}\n`); } // -------------------------------------------------------------------------- // Step 10: Cleanup // -------------------------------------------------------------------------- console.log('๐Ÿงน Step 10: Cleanup...\n'); // Optional: Delete the index // await productManager.deleteIndex(); // console.log(' โœ“ Index deleted'); // Close connections await connectionManager.destroy(); console.log(' โœ“ Connections closed\n'); console.log('โœจ Example completed successfully!\n'); } // ============================================================================ // Run Example // ============================================================================ if (import.meta.url === `file://${process.argv[1]}`) { main().catch((error) => { console.error('โŒ Example failed:', error); defaultLogger.error('Example failed', error); process.exit(1); }); } export { main };