mongo-ffb-production.repository.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import { Db, ObjectId, WithId } from 'mongodb';
  2. import { FFBProduction } from 'src/FFB/ffb-production.schema';
  3. export class FFBProductionRepository {
  4. private readonly collectionName = 'FFB Production';
  5. constructor(private readonly db: Db) { }
  6. private get collection() {
  7. return this.db.collection<FFBProduction & { vector?: number[] }>(this.collectionName);
  8. }
  9. async init() {
  10. const collections = await this.db.listCollections({ name: this.collectionName }).toArray();
  11. if (collections.length === 0) {
  12. await this.db.createCollection(this.collectionName);
  13. console.log(`✅ Created collection: ${this.collectionName}`);
  14. }
  15. }
  16. async create(ffb: FFBProduction & { vector?: number[] }): Promise<FFBProduction & { vector?: number[] }> {
  17. const result = await this.collection.insertOne({
  18. ...ffb,
  19. productionDate: new Date(ffb.productionDate),
  20. vector: ffb.vector, // optional vector
  21. });
  22. return { ...ffb, _id: result.insertedId.toString() };
  23. }
  24. async findAll(filter: Record<string, any> = {}, options: { page?: number, limit?: number } = {}): Promise<{ data: (FFBProduction & { vector?: number[] })[], total: number }> {
  25. const { page = 1, limit = 10 } = options;
  26. const skip = (page - 1) * limit;
  27. const [results, total] = await Promise.all([
  28. this.collection.find(filter).skip(skip).limit(limit).toArray(),
  29. this.collection.countDocuments(filter)
  30. ]);
  31. const data = results.map((r: WithId<FFBProduction & { vector?: number[] }>) => ({
  32. ...r,
  33. _id: r._id?.toString(),
  34. }));
  35. return { data, total };
  36. }
  37. async findById(id: string): Promise<(FFBProduction & { vector?: number[] }) | null> {
  38. const result = await this.collection.findOne({ _id: new ObjectId(id) as any });
  39. return result ? { ...result, _id: result._id.toString() } : null;
  40. }
  41. async delete(id: string) {
  42. return this.collection.deleteOne({ _id: new ObjectId(id) as any });
  43. }
  44. async deleteMany(filter: Record<string, any>) {
  45. return this.collection.deleteMany(filter);
  46. }
  47. async update(id: string, update: Partial<FFBProduction>): Promise<void> {
  48. await this.collection.updateOne(
  49. { _id: new ObjectId(id) as any },
  50. { $set: update }
  51. );
  52. }
  53. async findOne(filter: Record<string, any> = {}): Promise<FFBProduction | null> {
  54. const result = await this.collection.findOne(filter);
  55. return result ? { ...result, _id: result._id.toString() } : null;
  56. }
  57. async distinct(field: string, filter: Record<string, any> = {}): Promise<any[]> {
  58. return this.collection.distinct(field, filter);
  59. }
  60. /** Optional: helper for vector search via aggregation */
  61. async vectorSearch(vector: number[], k = 5, numCandidates = 50, filter: Record<string, any> = {}) {
  62. return this.collection
  63. .aggregate([
  64. {
  65. $vectorSearch: {
  66. index: 'vector_index',
  67. queryVector: vector,
  68. path: 'vector',
  69. numCandidates,
  70. limit: k,
  71. filter // Add filter here
  72. }
  73. },
  74. {
  75. $project: {
  76. _id: 1,
  77. productionDate: 1,
  78. site: 1,
  79. phase: 1,
  80. block: 1,
  81. quantity: 1,
  82. quantityUom: 1,
  83. weight: 1,
  84. weightUom: 1,
  85. remarks: 1,
  86. score: { "$meta": "vectorSearchScore" } // correctly get the score
  87. }
  88. }
  89. ])
  90. .toArray();
  91. }
  92. async aggregate(pipeline: Array<Record<string, any>>): Promise<any[]> {
  93. // Optional: log the pipeline for debugging
  94. // console.log('Executing aggregation pipeline:', JSON.stringify(pipeline, null, 2));
  95. const pipelineWithDates = pipeline.map(stage => {
  96. if ('$match' in stage && 'productionDate' in stage.$match) {
  97. const pd = stage.$match.productionDate;
  98. if (pd.$gte) pd.$gte = new Date(pd.$gte);
  99. if (pd.$lte) pd.$lte = new Date(pd.$lte);
  100. }
  101. return stage;
  102. });
  103. // Execute aggregation
  104. const results = await this.collection.aggregate(pipelineWithDates).toArray();
  105. // console.log('Aggregation results:', results);
  106. // Optional: strip out any internal vector fields if accidentally included
  107. return results.map(r => {
  108. const { vector, ...rest } = r;
  109. return rest;
  110. });
  111. }
  112. }