BREAKING CHANGE(core): Refactor to v3: introduce modular core/domain architecture, plugin system, observability and strict TypeScript configuration; remove legacy classes
This commit is contained in:
329
ts/examples/basic/complete-example.ts
Normal file
329
ts/examples/basic/complete-example.ts
Normal file
@@ -0,0 +1,329 @@
|
||||
/**
|
||||
* 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 };
|
||||
Reference in New Issue
Block a user