// OpenSearch Index Mapping for Vendor Search // Supports faceted search: category, district, price range, rating, and full-text PUT /vendors { "settings": { "number_of_shards": 2, "number_of_replicas": 1, "analysis": { "analyzer": { "wedding_search": { "type": "custom", "tokenizer": "standard", "filter": ["lowercase", "asciifolding", "stop", "snowball"] } } } }, "mappings": { "properties": { "id": { "type": "keyword" }, "businessName": { "type": "text", "analyzer": "wedding_search", "fields": { "keyword": { "type": "keyword" } } }, "categoryId": { "type": "keyword" }, "categoryName": { "type": "keyword" }, "district": { "type": "keyword" }, "description": { "type": "text", "analyzer": "wedding_search" }, "priceRange": { "type": "nested", "properties": { "min": { "type": "double" }, "max": { "type": "double" } } }, "rating": { "type": "float" }, "reviewCount": { "type": "integer" }, "isVerified": { "type": "boolean" }, "isPublished": { "type": "boolean" }, "features": { "type": "keyword" }, "packages": { "type": "nested", "properties": { "id": { "type": "keyword" }, "name": { "type": "text" }, "price": { "type": "double" } } }, "availability": { "type": "nested", "properties": { "date": { "type": "date" }, "type": { "type": "keyword" } } }, "createdAt": { "type": "date" }, "updatedAt": { "type": "date" } } } } // Indexer Worker (BullMQ job) // On vendor create/update/publish, sync PostgreSQL -> OpenSearch: // // async function indexVendor(vendorId: string) { // const vendor = await db.vendorProfile.findUnique({ // where: { id: vendorId }, // include: { category: true, packages: true, availability: true } // }) // if (!vendor) return // await esClient.index({ // index: 'vendors', // id: vendor.id, // body: { // id: vendor.id, // businessName: vendor.businessName, // categoryId: vendor.categoryId, // categoryName: vendor.category.name, // district: vendor.district, // description: vendor.description, // priceRange: computePriceRange(vendor.packages), // rating: computeRating(vendor.id), // reviewCount: countReviews(vendor.id), // isVerified: vendor.isVerified, // isPublished: vendor.isPublished, // features: extractFeatures(vendor), // packages: vendor.packages.map(p => ({ id: p.id, name: p.name, price: Number(p.price) })), // availability: vendor.availability.map(a => ({ date: a.date, type: a.type })), // updatedAt: vendor.updatedAt, // } // }) // } // // // Faceted search example: // POST /vendors/_search // { // "query": { // "bool": { // "must": [ // { "term": { "isPublished": true } } // ], // "filter": [ // { "term": { "categoryName": "Photography" } }, // { "term": { "district": "Colombo 3" } }, // { "range": { "rating": { "gte": 4.5 } } } // ] // } // }, // "aggs": { // "by_category": { "terms": { "field": "categoryName" } }, // "by_district": { "terms": { "field": "district" } }, // "price_ranges": { // "range": { // "field": "rating", // placeholder - actual price range agg uses nested // "ranges": [ // { "key": "Under 50K", "to": 50000 }, // { "key": "50K-100K", "from": 50000, "to": 100000 }, // { "key": "100K-250K", "from": 100000, "to": 250000 }, // { "key": "250K-500K", "from": 250000, "to": 500000 }, // { "key": "Over 500K", "from": 500000 } // ] // } // } // }, // "sort": [{ "rating": "desc" }] // }