380 lines
11 KiB
TypeScript
380 lines
11 KiB
TypeScript
/**
|
|
* Comprehensive Schema Management Example
|
|
*
|
|
* Demonstrates index mapping management, templates, and migrations
|
|
*/
|
|
|
|
import {
|
|
createConfig,
|
|
ElasticsearchConnectionManager,
|
|
LogLevel,
|
|
createSchemaManager,
|
|
type IndexSchema,
|
|
type SchemaMigration,
|
|
type IndexTemplate,
|
|
} from '../../index.js';
|
|
|
|
async function main() {
|
|
console.log('=== Schema Management Example ===\n');
|
|
|
|
// ============================================================================
|
|
// Step 1: Configuration
|
|
// ============================================================================
|
|
|
|
console.log('Step 1: Configuring Elasticsearch connection...');
|
|
const config = createConfig()
|
|
.fromEnv()
|
|
.nodes(process.env.ELASTICSEARCH_URL || 'http://localhost:9200')
|
|
.basicAuth(
|
|
process.env.ELASTICSEARCH_USERNAME || 'elastic',
|
|
process.env.ELASTICSEARCH_PASSWORD || 'changeme'
|
|
)
|
|
.timeout(30000)
|
|
.retries(3)
|
|
.logLevel(LogLevel.INFO)
|
|
.build();
|
|
|
|
// ============================================================================
|
|
// Step 2: Initialize
|
|
// ============================================================================
|
|
|
|
console.log('Step 2: Initializing connection and schema manager...');
|
|
const connectionManager = ElasticsearchConnectionManager.getInstance(config);
|
|
await connectionManager.initialize();
|
|
|
|
const schemaManager = createSchemaManager({
|
|
historyIndex: '.test_migrations',
|
|
dryRun: false,
|
|
strict: true,
|
|
enableLogging: true,
|
|
enableMetrics: true,
|
|
validateBeforeApply: true,
|
|
});
|
|
|
|
await schemaManager.initialize();
|
|
console.log('✓ Connection and schema manager initialized\n');
|
|
|
|
// ============================================================================
|
|
// Step 3: Create Index with Schema
|
|
// ============================================================================
|
|
|
|
console.log('Step 3: Creating index with schema...');
|
|
|
|
const productSchema: IndexSchema = {
|
|
name: 'products-v1',
|
|
version: 1,
|
|
settings: {
|
|
number_of_shards: 1,
|
|
number_of_replicas: 0,
|
|
refresh_interval: '1s',
|
|
analysis: {
|
|
analyzer: {
|
|
product_analyzer: {
|
|
type: 'custom',
|
|
tokenizer: 'standard',
|
|
filter: ['lowercase', 'snowball'],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
mappings: {
|
|
dynamic: 'strict',
|
|
properties: {
|
|
id: { type: 'keyword' },
|
|
name: {
|
|
type: 'text',
|
|
analyzer: 'product_analyzer',
|
|
properties: {
|
|
keyword: { type: 'keyword', ignore_above: 256 },
|
|
},
|
|
},
|
|
description: { type: 'text' },
|
|
price: { type: 'scaled_float', scaling_factor: 100 },
|
|
category: { type: 'keyword' },
|
|
tags: { type: 'keyword' },
|
|
inStock: { type: 'boolean' },
|
|
createdAt: { type: 'date' },
|
|
updatedAt: { type: 'date' },
|
|
},
|
|
},
|
|
aliases: {
|
|
products: { is_write_index: true },
|
|
},
|
|
metadata: {
|
|
description: 'Product catalog index',
|
|
owner: 'catalog-team',
|
|
tags: ['catalog', 'products'],
|
|
},
|
|
};
|
|
|
|
// Validate schema first
|
|
const validation = schemaManager.validateSchema(productSchema);
|
|
console.log(` Schema validation: ${validation.valid ? 'PASSED' : 'FAILED'}`);
|
|
|
|
if (validation.warnings.length > 0) {
|
|
console.log(' Warnings:');
|
|
for (const warning of validation.warnings) {
|
|
console.log(` - ${warning.field}: ${warning.message}`);
|
|
}
|
|
}
|
|
|
|
// Create the index
|
|
await schemaManager.createIndex(productSchema);
|
|
console.log(' ✓ Index created with mappings and aliases\n');
|
|
|
|
// ============================================================================
|
|
// Step 4: Schema Migrations
|
|
// ============================================================================
|
|
|
|
console.log('Step 4: Running schema migrations...');
|
|
|
|
const migrations: SchemaMigration[] = [
|
|
{
|
|
version: 1,
|
|
name: 'add_rating_field',
|
|
description: 'Add product rating field',
|
|
type: 'update',
|
|
index: 'products-v1',
|
|
changes: {
|
|
mappings: {
|
|
properties: {
|
|
rating: { type: 'float' },
|
|
reviewCount: { type: 'integer' },
|
|
},
|
|
},
|
|
},
|
|
rollback: {
|
|
// Note: Can't remove fields in ES, but document for reference
|
|
},
|
|
metadata: {
|
|
author: 'dev-team',
|
|
ticket: 'PROD-123',
|
|
},
|
|
},
|
|
{
|
|
version: 2,
|
|
name: 'add_brand_field',
|
|
description: 'Add brand field with keyword type',
|
|
type: 'update',
|
|
index: 'products-v1',
|
|
changes: {
|
|
mappings: {
|
|
properties: {
|
|
brand: { type: 'keyword' },
|
|
sku: { type: 'keyword' },
|
|
},
|
|
},
|
|
},
|
|
metadata: {
|
|
author: 'dev-team',
|
|
ticket: 'PROD-456',
|
|
},
|
|
},
|
|
{
|
|
version: 3,
|
|
name: 'add_inventory_object',
|
|
description: 'Add nested inventory object',
|
|
type: 'update',
|
|
index: 'products-v1',
|
|
changes: {
|
|
mappings: {
|
|
properties: {
|
|
inventory: {
|
|
type: 'object',
|
|
properties: {
|
|
quantity: { type: 'integer' },
|
|
warehouse: { type: 'keyword' },
|
|
lastRestocked: { type: 'date' },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
metadata: {
|
|
author: 'inventory-team',
|
|
ticket: 'INV-789',
|
|
},
|
|
},
|
|
];
|
|
|
|
const migrationResults = await schemaManager.migrate(migrations);
|
|
|
|
console.log(' Migration results:');
|
|
for (const result of migrationResults) {
|
|
console.log(` v${result.version} (${result.name}): ${result.status} (${result.duration}ms)`);
|
|
}
|
|
|
|
console.log();
|
|
|
|
// ============================================================================
|
|
// Step 5: Get Applied Migrations
|
|
// ============================================================================
|
|
|
|
console.log('Step 5: Viewing migration history...');
|
|
|
|
const appliedMigrations = await schemaManager.getAppliedMigrations();
|
|
|
|
console.log(' Applied migrations:');
|
|
for (const migration of appliedMigrations) {
|
|
console.log(` v${migration.version}: ${migration.name} (${migration.status})`);
|
|
}
|
|
|
|
console.log();
|
|
|
|
// ============================================================================
|
|
// Step 6: Compare Schemas
|
|
// ============================================================================
|
|
|
|
console.log('Step 6: Comparing schemas...');
|
|
|
|
const oldSchema = productSchema;
|
|
const newSchema: IndexSchema = {
|
|
...productSchema,
|
|
version: 2,
|
|
mappings: {
|
|
...productSchema.mappings,
|
|
properties: {
|
|
...productSchema.mappings.properties,
|
|
rating: { type: 'float' },
|
|
discount: { type: 'float' },
|
|
},
|
|
},
|
|
};
|
|
|
|
const diff = schemaManager.diffSchemas(oldSchema, newSchema);
|
|
|
|
console.log(' Schema diff:');
|
|
console.log(` Identical: ${diff.identical}`);
|
|
console.log(` Added fields: ${diff.added.join(', ') || 'none'}`);
|
|
console.log(` Removed fields: ${diff.removed.join(', ') || 'none'}`);
|
|
console.log(` Modified fields: ${diff.modified.length}`);
|
|
console.log(` Breaking changes: ${diff.breakingChanges.length}`);
|
|
|
|
console.log();
|
|
|
|
// ============================================================================
|
|
// Step 7: Index Templates
|
|
// ============================================================================
|
|
|
|
console.log('Step 7: Managing index templates...');
|
|
|
|
const template: IndexTemplate = {
|
|
name: 'products-template',
|
|
index_patterns: ['products-*'],
|
|
priority: 100,
|
|
version: 1,
|
|
template: {
|
|
settings: {
|
|
number_of_shards: 1,
|
|
number_of_replicas: 0,
|
|
},
|
|
mappings: {
|
|
dynamic: 'strict',
|
|
properties: {
|
|
id: { type: 'keyword' },
|
|
name: { type: 'text' },
|
|
createdAt: { type: 'date' },
|
|
},
|
|
},
|
|
aliases: {
|
|
'all-products': {},
|
|
},
|
|
},
|
|
_meta: {
|
|
description: 'Template for product indices',
|
|
},
|
|
};
|
|
|
|
await schemaManager.putTemplate(template);
|
|
console.log(' ✓ Index template created');
|
|
|
|
// Get template
|
|
const retrievedTemplate = await schemaManager.getTemplate('products-template');
|
|
console.log(` Template patterns: ${retrievedTemplate?.index_patterns.join(', ')}`);
|
|
|
|
console.log();
|
|
|
|
// ============================================================================
|
|
// Step 8: Alias Management
|
|
// ============================================================================
|
|
|
|
console.log('Step 8: Managing aliases...');
|
|
|
|
// Add alias
|
|
await schemaManager.addAlias('products-v1', 'products-read', {
|
|
filter: { term: { inStock: true } },
|
|
});
|
|
console.log(' ✓ Added filtered read alias');
|
|
|
|
// Get schema to see aliases
|
|
const currentSchema = await schemaManager.getSchema('products-v1');
|
|
console.log(` Current aliases: ${Object.keys(currentSchema?.aliases || {}).join(', ')}`);
|
|
|
|
console.log();
|
|
|
|
// ============================================================================
|
|
// Step 9: Update Settings
|
|
// ============================================================================
|
|
|
|
console.log('Step 9: Updating index settings...');
|
|
|
|
await schemaManager.updateSettings('products-v1', {
|
|
refresh_interval: '5s',
|
|
max_result_window: 20000,
|
|
});
|
|
|
|
console.log(' ✓ Settings updated');
|
|
|
|
console.log();
|
|
|
|
// ============================================================================
|
|
// Step 10: Statistics
|
|
// ============================================================================
|
|
|
|
console.log('Step 10: Schema manager statistics...\n');
|
|
|
|
const stats = schemaManager.getStats();
|
|
|
|
console.log('Schema Manager Statistics:');
|
|
console.log(` Total migrations: ${stats.totalMigrations}`);
|
|
console.log(` Successful: ${stats.successfulMigrations}`);
|
|
console.log(` Failed: ${stats.failedMigrations}`);
|
|
console.log(` Rolled back: ${stats.rolledBackMigrations}`);
|
|
console.log(` Total indices: ${stats.totalIndices}`);
|
|
console.log(` Total templates: ${stats.totalTemplates}`);
|
|
console.log(` Avg migration duration: ${stats.avgMigrationDuration.toFixed(2)}ms`);
|
|
|
|
console.log();
|
|
|
|
// ============================================================================
|
|
// Step 11: Cleanup
|
|
// ============================================================================
|
|
|
|
console.log('Step 11: Cleanup...');
|
|
|
|
await schemaManager.deleteTemplate('products-template');
|
|
await schemaManager.deleteIndex('products-v1');
|
|
await connectionManager.destroy();
|
|
|
|
console.log('✓ Cleanup complete\n');
|
|
|
|
console.log('=== Schema Management Example Complete ===');
|
|
console.log('\nKey Features Demonstrated:');
|
|
console.log(' ✓ Index creation with mappings and settings');
|
|
console.log(' ✓ Schema validation before apply');
|
|
console.log(' ✓ Versioned migrations with history tracking');
|
|
console.log(' ✓ Schema diff and comparison');
|
|
console.log(' ✓ Index templates');
|
|
console.log(' ✓ Alias management with filters');
|
|
console.log(' ✓ Settings updates');
|
|
console.log(' ✓ Migration rollback support');
|
|
console.log(' ✓ Dry run mode');
|
|
console.log(' ✓ Comprehensive statistics');
|
|
}
|
|
|
|
// Run the example
|
|
main().catch((error) => {
|
|
console.error('Example failed:', error);
|
|
process.exit(1);
|
|
});
|