Files
elasticsearch/ts/examples/basic/complete-example.ts

330 lines
11 KiB
TypeScript
Raw Normal View History

/**
* 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<string, number>;
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<Product>({
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<ProductSnapshot>(
async (iterator, previousSnapshot) => {
console.log(' 🔄 Processing snapshot...');
let totalPrice = 0;
let productCount = 0;
const categoryCounts: Record<string, number> = {};
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<Product>({
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 };