/** * Comprehensive Query Builder Example * * Demonstrates type-safe query construction with the QueryBuilder */ import { createConfig, ElasticsearchConnectionManager, LogLevel, } from '../../core/index.js'; import { DocumentManager } from '../../domain/documents/index.js'; import { QueryBuilder, createQuery } from '../../domain/query/index.js'; interface Product { name: string; description: string; category: string; brand: string; price: number; rating: number; stock: number; tags: string[]; createdAt: Date; updatedAt: Date; } async function main() { console.log('=== Query Builder 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) .enableMetrics(true) .enableTracing(true, { serviceName: 'query-example', serviceVersion: '1.0.0' }) .build(); // ============================================================================ // Step 2: Initialize Connection // ============================================================================ console.log('Step 2: Initializing connection manager...'); const connectionManager = ElasticsearchConnectionManager.getInstance(config); await connectionManager.initialize(); console.log('✓ Connection manager initialized\n'); // ============================================================================ // Step 3: Setup Sample Data // ============================================================================ console.log('Step 3: Setting up sample data...'); const products = new DocumentManager({ index: 'products-query-example', autoCreateIndex: true, }); await products.initialize(); // Create sample products const sampleProducts: Array<{ id: string; data: Product }> = [ { id: 'laptop-1', data: { name: 'Professional Laptop Pro', description: 'High-performance laptop for professionals', category: 'Electronics', brand: 'TechBrand', price: 1299.99, rating: 4.5, stock: 15, tags: ['laptop', 'professional', 'high-end'], createdAt: new Date('2024-01-15'), updatedAt: new Date('2024-01-20'), }, }, { id: 'laptop-2', data: { name: 'Budget Laptop Basic', description: 'Affordable laptop for everyday use', category: 'Electronics', brand: 'ValueBrand', price: 499.99, rating: 3.8, stock: 30, tags: ['laptop', 'budget', 'student'], createdAt: new Date('2024-02-01'), updatedAt: new Date('2024-02-05'), }, }, { id: 'phone-1', data: { name: 'Smartphone X', description: 'Latest flagship smartphone', category: 'Electronics', brand: 'PhoneBrand', price: 899.99, rating: 4.7, stock: 25, tags: ['smartphone', 'flagship', '5g'], createdAt: new Date('2024-01-20'), updatedAt: new Date('2024-01-25'), }, }, { id: 'tablet-1', data: { name: 'Tablet Pro', description: 'Professional tablet for creative work', category: 'Electronics', brand: 'TechBrand', price: 799.99, rating: 4.6, stock: 20, tags: ['tablet', 'creative', 'professional'], createdAt: new Date('2024-02-10'), updatedAt: new Date('2024-02-15'), }, }, { id: 'monitor-1', data: { name: '4K Monitor', description: 'Ultra HD monitor for gaming and design', category: 'Electronics', brand: 'DisplayBrand', price: 599.99, rating: 4.4, stock: 12, tags: ['monitor', '4k', 'gaming'], createdAt: new Date('2024-01-25'), updatedAt: new Date('2024-01-30'), }, }, ]; // Index sample data const session = products.session(); session.start(); for (const product of sampleProducts) { session.upsert(product.id, product.data); } await session.commit(); console.log(`✓ Indexed ${sampleProducts.length} sample products\n`); // Wait for indexing to complete await new Promise((resolve) => setTimeout(resolve, 1000)); // ============================================================================ // Step 4: Simple Queries // ============================================================================ console.log('Step 4: Running simple queries...\n'); // 4.1: Match query - search by name console.log('4.1: Match query - search for "laptop"'); const laptopResults = await createQuery('products-query-example') .match('name', 'laptop') .size(10) .execute(); console.log(`Found ${laptopResults.hits.total.value} laptops`); console.log('Laptops:', laptopResults.hits.hits.map((h) => h._source.name)); console.log(); // 4.2: Term query - exact match on category console.log('4.2: Term query - exact category match'); const electronicsResults = await createQuery('products-query-example') .term('category.keyword', 'Electronics') .execute(); console.log(`Found ${electronicsResults.hits.total.value} electronics`); console.log(); // 4.3: Range query - price between 500 and 1000 console.log('4.3: Range query - price between $500 and $1000'); const midPriceResults = await createQuery('products-query-example') .range('price', { gte: 500, lte: 1000 }) .sort('price', 'asc') .execute(); console.log(`Found ${midPriceResults.hits.total.value} products in price range`); midPriceResults.hits.hits.forEach((hit) => { console.log(` - ${hit._source.name}: $${hit._source.price}`); }); console.log(); // 4.4: Multi-match query - search across multiple fields console.log('4.4: Multi-match query - search "professional" in name and description'); const professionalResults = await createQuery('products-query-example') .multiMatch('professional', ['name', 'description']) .execute(); console.log(`Found ${professionalResults.hits.total.value} professional products`); console.log(); // ============================================================================ // Step 5: Boolean Queries // ============================================================================ console.log('Step 5: Running boolean queries...\n'); // 5.1: Must + Filter - combine multiple conditions console.log('5.1: Boolean query - TechBrand products over $700'); const techBrandResults = await createQuery('products-query-example') .term('brand.keyword', 'TechBrand') .range('price', { gte: 700 }) .sort('price', 'desc') .execute(); console.log(`Found ${techBrandResults.hits.total.value} matching products`); techBrandResults.hits.hits.forEach((hit) => { console.log(` - ${hit._source.name} (${hit._source.brand}): $${hit._source.price}`); }); console.log(); // 5.2: Should clause - match any condition console.log('5.2: Should query - products matching "laptop" OR "tablet"'); const laptopOrTabletResults = await new QueryBuilder('products-query-example') .should({ match: { name: { query: 'laptop' } } }) .should({ match: { name: { query: 'tablet' } } }) .minimumMatch(1) .execute(); console.log(`Found ${laptopOrTabletResults.hits.total.value} laptops or tablets`); console.log(); // 5.3: Must not - exclude results console.log('5.3: Must not query - electronics excluding laptops'); const noLaptopsResults = await createQuery('products-query-example') .term('category.keyword', 'Electronics') .mustNot({ match: { name: { query: 'laptop' } } }) .execute(); console.log(`Found ${noLaptopsResults.hits.total.value} non-laptop electronics`); console.log(); // ============================================================================ // Step 6: Aggregations // ============================================================================ console.log('Step 6: Running aggregations...\n'); // 6.1: Terms aggregation - group by brand console.log('6.1: Terms aggregation - products by brand'); const brandAggResults = await createQuery('products-query-example') .matchAll() .size(0) // We only want aggregations, not documents .aggregations((agg) => { agg.terms('brands', 'brand.keyword', { size: 10 }); }) .execute(); if (brandAggResults.aggregations && 'brands' in brandAggResults.aggregations) { const brandsAgg = brandAggResults.aggregations.brands as { buckets: Array<{ key: string; doc_count: number }> }; console.log('Products by brand:'); brandsAgg.buckets.forEach((bucket) => { console.log(` - ${bucket.key}: ${bucket.doc_count} products`); }); } console.log(); // 6.2: Metric aggregations - price statistics console.log('6.2: Metric aggregations - price statistics'); const priceStatsResults = await createQuery('products-query-example') .matchAll() .size(0) .aggregations((agg) => { agg.stats('price_stats', 'price'); agg.avg('avg_rating', 'rating'); agg.sum('total_stock', 'stock'); }) .execute(); if (priceStatsResults.aggregations) { console.log('Price statistics:', priceStatsResults.aggregations.price_stats); console.log('Average rating:', priceStatsResults.aggregations.avg_rating); console.log('Total stock:', priceStatsResults.aggregations.total_stock); } console.log(); // 6.3: Nested aggregations - brands with average price console.log('6.3: Nested aggregations - average price per brand'); const nestedAggResults = await createQuery('products-query-example') .matchAll() .size(0) .aggregations((agg) => { agg.terms('brands', 'brand.keyword', { size: 10 }).subAggregation('avg_price', (sub) => { sub.avg('avg_price', 'price'); }); }) .execute(); if (nestedAggResults.aggregations && 'brands' in nestedAggResults.aggregations) { const brandsAgg = nestedAggResults.aggregations.brands as { buckets: Array<{ key: string; doc_count: number; avg_price: { value: number } }>; }; console.log('Average price by brand:'); brandsAgg.buckets.forEach((bucket) => { console.log(` - ${bucket.key}: $${bucket.avg_price.value.toFixed(2)} (${bucket.doc_count} products)`); }); } console.log(); // ============================================================================ // Step 7: Advanced Features // ============================================================================ console.log('Step 7: Advanced query features...\n'); // 7.1: Pagination console.log('7.1: Pagination - page 1 of results (2 per page)'); const page1Results = await createQuery('products-query-example') .matchAll() .paginate(1, 2) .sort('price', 'asc') .execute(); console.log(`Page 1: ${page1Results.hits.hits.length} results`); page1Results.hits.hits.forEach((hit) => { console.log(` - ${hit._source.name}: $${hit._source.price}`); }); console.log(); // 7.2: Source filtering - only return specific fields console.log('7.2: Source filtering - only name and price'); const filteredResults = await createQuery('products-query-example') .matchAll() .fields(['name', 'price']) .size(3) .execute(); console.log('Filtered results:'); filteredResults.hits.hits.forEach((hit) => { console.log(` - Name: ${hit._source.name}, Price: ${hit._source.price}`); }); console.log(); // 7.3: Count documents console.log('7.3: Count documents matching query'); const count = await createQuery('products-query-example') .range('price', { gte: 500 }) .count(); console.log(`Count of products over $500: ${count}`); console.log(); // 7.4: Get only sources (convenience method) console.log('7.4: Get sources only'); const sources = await createQuery('products-query-example') .term('brand.keyword', 'TechBrand') .executeAndGetSources(); console.log(`TechBrand products: ${sources.map((s) => s.name).join(', ')}`); console.log(); // ============================================================================ // Step 8: Complex Real-World Query // ============================================================================ console.log('Step 8: Complex real-world query...\n'); console.log('Finding high-rated electronics in stock, sorted by best deals:'); const complexResults = await createQuery('products-query-example') .term('category.keyword', 'Electronics') .range('rating', { gte: 4.0 }) .range('stock', { gt: 0 }) .range('price', { lte: 1000 }) .sort('rating', 'desc') .size(5) .aggregations((agg) => { agg.terms('top_brands', 'brand.keyword', { size: 5 }); agg.avg('avg_price', 'price'); agg.max('max_rating', 'rating'); }) .execute(); console.log(`Found ${complexResults.hits.total.value} matching products`); console.log('\nTop results:'); complexResults.hits.hits.forEach((hit, index) => { console.log(` ${index + 1}. ${hit._source.name}`); console.log(` Brand: ${hit._source.brand}`); console.log(` Price: $${hit._source.price}`); console.log(` Rating: ${hit._source.rating}⭐`); console.log(` Stock: ${hit._source.stock} units`); }); if (complexResults.aggregations) { console.log('\nAggregated insights:'); console.log(' Average price:', complexResults.aggregations.avg_price); console.log(' Max rating:', complexResults.aggregations.max_rating); if ('top_brands' in complexResults.aggregations) { const topBrands = complexResults.aggregations.top_brands as { buckets: Array<{ key: string; doc_count: number }> }; console.log(' Top brands:'); topBrands.buckets.forEach((bucket) => { console.log(` - ${bucket.key}: ${bucket.doc_count} products`); }); } } console.log(); // ============================================================================ // Step 9: Cleanup // ============================================================================ console.log('Step 9: Cleanup...'); await products.deleteIndex(); console.log('✓ Test index deleted'); await connectionManager.destroy(); console.log('✓ Connection closed\n'); console.log('=== Query Builder Example Complete ==='); } // Run the example main().catch((error) => { console.error('Example failed:', error); process.exit(1); });