import { Injectable, OnModuleInit } from '@nestjs/common'; import { MongoCoreService } from 'src/mongo/mongo-core.service'; import { FFBProductionRepository } from 'src/FFB/mongo-ffb-production.repository'; import { FFBProduction } from '../ffb-production.schema'; import { GeminiEmbeddingService } from '../gemini-embedding.service'; @Injectable() export class FFBVectorService implements OnModuleInit { private repo: FFBProductionRepository; constructor( private readonly mongoCore: MongoCoreService, private readonly embeddingService: GeminiEmbeddingService ) { } async onModuleInit() { // Initialize Mongo repository const db = await this.mongoCore.getDb(); this.repo = new FFBProductionRepository(db); await this.repo.init(); console.log('✅ Gemini embedding service ready. Repository initialized.'); } /** Convert a record to a string suitable for embedding */ private recordToText(record: FFBProduction): string { return `Production on ${new Date(record.productionDate).toISOString()} at ${record.site} in ${record.phase} ${record.block} produced ${record.quantity} ${record.quantityUom} with a total weight of ${record.weight} ${record.weightUom}.`; } /** Insert a single record with embedding vector */ async insertWithVector(record: FFBProduction) { const text = this.recordToText(record); const vector = await this.embeddingService.embedText(text); const data: FFBProduction & { vector: number[] } = { ...record, vector }; return this.repo.create(data); } /** Search for top-k similar records using a text query */ async vectorSearch(query: string, k = 5, filter: Record = {}) { if (!query) throw new Error('Query string cannot be empty'); const vector = await this.embeddingService.embedText(query); const results = await this.repo.vectorSearch(vector, k, 50, filter); return results.map((r) => ({ ...r, _id: r._id.toString(), score: r.score, })); } /* For traditional operation that requires arithematic operations. */ async aggregate(pipeline: Array>): Promise { return this.repo.aggregate(pipeline); } }