mongo-ffb-production.repository.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. /**
  2. * * **Current State:** The `FFBProductionRepository.vectorSearch` method uses a `$project` aggregation stage targeting the legacy nested `site`, `phase`, and `block` layout.
  3. * * **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.
  4. * * **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.
  5. */
  6. import { Db, WithId } from 'mongodb';
  7. import { FFBProduction } from './ffb-production.schema';
  8. export class FFBProductionRepository {
  9. private readonly collectionName = 'FFB Production';
  10. constructor(private readonly db: Db) { }
  11. private get collection() {
  12. return this.db.collection<FFBProduction & { vector?: number[] }>(this.collectionName);
  13. }
  14. async init() {
  15. const collections = await this.db.listCollections({ name: this.collectionName }).toArray();
  16. if (collections.length === 0) {
  17. await this.db.createCollection(this.collectionName);
  18. console.log(`✅ Created collection: ${this.collectionName}`);
  19. }
  20. }
  21. async create(ffb: FFBProduction & { vector?: number[] }): Promise<FFBProduction & { vector?: number[] }> {
  22. const result = await this.collection.insertOne({
  23. ...ffb,
  24. productionDate: new Date(ffb.productionDate),
  25. vector: ffb.vector, // optional vector
  26. });
  27. return { ...ffb, _id: result.insertedId.toString() };
  28. }
  29. async findAll(filter: Record<string, any> = {}, options: { page?: number, limit?: number } = {}): Promise<{ data: (FFBProduction & { vector?: number[] })[], total: number }> {
  30. const { page = 1, limit = 10 } = options;
  31. const skip = (page - 1) * limit;
  32. const [results, total] = await Promise.all([
  33. this.collection.find(filter).skip(skip).limit(limit).toArray(),
  34. this.collection.countDocuments(filter)
  35. ]);
  36. const data = results.map((r: WithId<FFBProduction & { vector?: number[] }>) => ({
  37. ...r,
  38. _id: r._id?.toString(),
  39. }));
  40. return { data, total };
  41. }
  42. async findById(id: string): Promise<(FFBProduction & { vector?: number[] }) | null> {
  43. const ObjectId = require('mongodb').ObjectId;
  44. const result = await this.collection.findOne({ _id: new ObjectId(id) as any });
  45. return result ? { ...result, _id: result._id.toString() } : null;
  46. }
  47. async delete(id: string) {
  48. const ObjectId = require('mongodb').ObjectId;
  49. return this.collection.deleteOne({ _id: new ObjectId(id) as any });
  50. }
  51. async deleteMany(filter: Record<string, any>) {
  52. return this.collection.deleteMany(filter);
  53. }
  54. async update(id: string, update: Partial<FFBProduction>): Promise<void> {
  55. const ObjectId = require('mongodb').ObjectId;
  56. await this.collection.updateOne(
  57. { _id: new ObjectId(id) as any },
  58. { $set: update }
  59. );
  60. }
  61. async findOne(filter: Record<string, any> = {}): Promise<FFBProduction | null> {
  62. const result = await this.collection.findOne(filter);
  63. return result ? { ...result, _id: result._id.toString() } : null;
  64. }
  65. async distinct(field: string, filter: Record<string, any> = {}): Promise<any[]> {
  66. return this.collection.distinct(field, filter);
  67. }
  68. /** Optional: helper for vector search via aggregation */
  69. async vectorSearch(vector: number[], k = 5, numCandidates = 50, filter: Record<string, any> = {}) {
  70. return this.collection
  71. .aggregate([
  72. {
  73. $vectorSearch: {
  74. index: 'vector_index',
  75. queryVector: vector,
  76. path: 'vector',
  77. numCandidates,
  78. limit: k,
  79. filter // Add filter here
  80. }
  81. },
  82. {
  83. $project: {
  84. _id: 1,
  85. productionDate: 1,
  86. prjCode: 1,
  87. actCode: 1,
  88. actName: 1,
  89. entityCode: 1,
  90. orgnId: 1,
  91. orgnCode: 1,
  92. orgnFullName: 1,
  93. phaseCode: 1,
  94. phaseName: 1,
  95. phaseDesc: 1,
  96. blockCode: 1,
  97. blockName: 1,
  98. blockDesc: 1,
  99. truckNo: 1,
  100. millNo: 1,
  101. netWeight: 1,
  102. noOfBunches: 1,
  103. locArea: 1,
  104. remarks: 1,
  105. issues: 1,
  106. vector: 1,
  107. score: { "$meta": "vectorSearchScore" } // correctly get the score
  108. }
  109. }
  110. ])
  111. .toArray();
  112. }
  113. async aggregate(pipeline: Array<Record<string, any>>): Promise<any[]> {
  114. const pipelineWithDates = pipeline.map(stage => {
  115. if ('$match' in stage && 'productionDate' in stage.$match) {
  116. const pd = stage.$match.productionDate;
  117. if (pd.$gte) pd.$gte = new Date(pd.$gte);
  118. if (pd.$lte) pd.$lte = new Date(pd.$lte);
  119. }
  120. return stage;
  121. });
  122. const results = await this.collection.aggregate(pipelineWithDates).toArray();
  123. return results.map(r => {
  124. const { vector, ...rest } = r;
  125. return rest;
  126. });
  127. }
  128. }