| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- /**
- * * **Current State:** The `FFBProductionRepository.vectorSearch` method uses a `$project` aggregation stage targeting the legacy nested `site`, `phase`, and `block` layout.
- * * **Intended Mutation:** We will completely rewrite the `$project` stage in `vectorSearch` to align with the new flat warehouse-style transaction ledger fields, projecting flat keys such as `phaseCode`, `blockCode`, `blockDesc`, etc.
- * * **Risk Check:** Any controller or service expecting the older nested output (`site.name`, etc.) from `vectorSearch` calls will encounter empty or undefined parameters upon this structural alignment.
- */
- import { Db, WithId } from 'mongodb';
- import { FFBProduction } from './ffb-production.schema';
- export class FFBProductionRepository {
- private readonly collectionName = 'FFB Production';
- constructor(private readonly db: Db) { }
- private get collection() {
- return this.db.collection<FFBProduction & { vector?: number[] }>(this.collectionName);
- }
- async init() {
- const collections = await this.db.listCollections({ name: this.collectionName }).toArray();
- if (collections.length === 0) {
- await this.db.createCollection(this.collectionName);
- console.log(`✅ Created collection: ${this.collectionName}`);
- }
- }
- async create(ffb: FFBProduction & { vector?: number[] }): Promise<FFBProduction & { vector?: number[] }> {
- const result = await this.collection.insertOne({
- ...ffb,
- productionDate: new Date(ffb.productionDate),
- vector: ffb.vector, // optional vector
- });
- return { ...ffb, _id: result.insertedId.toString() };
- }
- async findAll(filter: Record<string, any> = {}, options: { page?: number, limit?: number } = {}): Promise<{ data: (FFBProduction & { vector?: number[] })[], total: number }> {
- const { page = 1, limit = 10 } = options;
- const skip = (page - 1) * limit;
- const [results, total] = await Promise.all([
- this.collection.find(filter).skip(skip).limit(limit).toArray(),
- this.collection.countDocuments(filter)
- ]);
- const data = results.map((r: WithId<FFBProduction & { vector?: number[] }>) => ({
- ...r,
- _id: r._id?.toString(),
- }));
- return { data, total };
- }
- async findById(id: string): Promise<(FFBProduction & { vector?: number[] }) | null> {
- const ObjectId = require('mongodb').ObjectId;
- const result = await this.collection.findOne({ _id: new ObjectId(id) as any });
- return result ? { ...result, _id: result._id.toString() } : null;
- }
- async delete(id: string) {
- const ObjectId = require('mongodb').ObjectId;
- return this.collection.deleteOne({ _id: new ObjectId(id) as any });
- }
- async deleteMany(filter: Record<string, any>) {
- return this.collection.deleteMany(filter);
- }
- async update(id: string, update: Partial<FFBProduction>): Promise<void> {
- const ObjectId = require('mongodb').ObjectId;
- await this.collection.updateOne(
- { _id: new ObjectId(id) as any },
- { $set: update }
- );
- }
- async findOne(filter: Record<string, any> = {}): Promise<FFBProduction | null> {
- const result = await this.collection.findOne(filter);
- return result ? { ...result, _id: result._id.toString() } : null;
- }
- async distinct(field: string, filter: Record<string, any> = {}): Promise<any[]> {
- return this.collection.distinct(field, filter);
- }
- /** Optional: helper for vector search via aggregation */
- async vectorSearch(vector: number[], k = 5, numCandidates = 50, filter: Record<string, any> = {}) {
- return this.collection
- .aggregate([
- {
- $vectorSearch: {
- index: 'vector_index',
- queryVector: vector,
- path: 'vector',
- numCandidates,
- limit: k,
- filter // Add filter here
- }
- },
- {
- $project: {
- _id: 1,
- productionDate: 1,
- prjCode: 1,
- actCode: 1,
- actName: 1,
- entityCode: 1,
- orgnId: 1,
- orgnCode: 1,
- orgnFullName: 1,
- phaseCode: 1,
- phaseName: 1,
- phaseDesc: 1,
- blockCode: 1,
- blockName: 1,
- blockDesc: 1,
- truckNo: 1,
- millNo: 1,
- netWeight: 1,
- noOfBunches: 1,
- locArea: 1,
- remarks: 1,
- issues: 1,
- vector: 1,
- score: { "$meta": "vectorSearchScore" } // correctly get the score
- }
- }
- ])
- .toArray();
- }
- async aggregate(pipeline: Array<Record<string, any>>): Promise<any[]> {
- const pipelineWithDates = pipeline.map(stage => {
- if ('$match' in stage && 'productionDate' in stage.$match) {
- const pd = stage.$match.productionDate;
- if (pd.$gte) pd.$gte = new Date(pd.$gte);
- if (pd.$lte) pd.$lte = new Date(pd.$lte);
- }
- return stage;
- });
- const results = await this.collection.aggregate(pipelineWithDates).toArray();
- return results.map(r => {
- const { vector, ...rest } = r;
- return rest;
- });
- }
- }
|