Как развернуть/заполнить массив в агрегатном конвейере Mongoose

Я работаю с Mongoose и пытаюсь развернуть/заполнить поле comment.attachments в конвейере агрегации. Вот соответствующая часть моего кода:

const result = await Ticket.aggregate([
        { $match: { $expr: { $eq: ['$_id', { $toObjectId: "667293eca3a67d7c5d9ff1e2" }] } } },
        { $unwind: '$comments' },
        { $match: { $expr: { $eq: ['$comments._id', { $toObjectId: "667382f627df37eb4e7a3220" }] } } },
 ])

Этот запрос успешно извлекает документ билета и разворачивает массив комментариев. Однако поле comment.attachments остается пустым массивом, даже если с комментарием связаны вложения.

  [
     {
        _id: new ObjectId('667293eca3a67d7c5d9ff1e2'),
        referenceNo: 'TCK_06-19-2024_0001',
        subject: new ObjectId('6670dad080ea03a681f70295'),
        description: 'pero ang totoo, di bali na ako. ikaw lang iniisip ko.',
        priority: 'medium',
        status: 'open',
        initiator: new ObjectId('6670da9d80ea03a681f70292'),
        createdBy: new ObjectId('667106fff8abeeb0d87341ba'),
        assignedTo: null,
        attachments: [ [Object], [Object] ],
        comments: {
          userId: new ObjectId('6670db4180ea03a681f7029e'),
          content: '2nd comment w atta',
          attachments: [Array],
          _id: new ObjectId('667382f627df37eb4e7a3220'),
          createdAt: 2024-06-20T01:16:38.190Z,
          updatedAt: 2024-06-20T01:16:38.190Z
        },
       createdAt: 2024-06-19T08:16:44.129Z,
       updatedAt: 2024-06-20T02:14:51.282Z,
       __v: 0
      }
   ]

Вот мои модели для справки:

Модель билета

 const mongoose = require('mongoose');
 const Schema = mongoose.Schema;
 const ticketCommentSchema = require('./ticketCommentModel');
 const ticketAttachmentSchema = require('./ticketAttachmentModel');

 const ticketSchema = new Schema({
   // ... other schema properties
     comments: [ticketCommentSchema],
     attachments: {
     type: [ticketAttachmentSchema],
     required: true,
     validate: [attachmentsArray => attachmentsArray.length > 0, 'At least one attachment is   required']
   },
   }, {
     timestamps: true
   });

 module.exports = mongoose.model('Ticket', ticketSchema);

Модель комментариев к заявкам

 const mongoose = require('mongoose');
 const Schema = mongoose.Schema;
 const ticketAttachmentSchema = 
  require('./ticketAttachmentModel');

 const ticketCommentSchema = new Schema({
   userId: {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'User',
      required: true
   },
   content: {
      type: String,
      trim: true,
      required: true
   },
   attachments: [ticketAttachmentSchema]
   }, {
   timestamps: true
   });

 module.exports = ticketCommentSchema;

Модель прикрепления билета

 const mongoose = require('mongoose');
 const Schema = mongoose.Schema;

 const ticketAttachmentSchema = new Schema({
  // ... attachment schema properties
  }, {
    timestamps: true
       });

 module.exports = ticketAttachmentSchema;

Мой вопрос:

Как я могу изменить конвейер агрегации, чтобы развернуть или заполнить поле comment.attachments и получить сведения о вложении в результатах запроса?

Я искал в Интернете решения, но не нашел ничего конкретного для этого сценария. Любая помощь будет принята с благодарностью!

Я пробую этот конвейер агрегации

const result = await Ticket.aggregate([
        { $match: { $expr: { $eq: ['$_id', { $toObjectId: "667293eca3a67d7c5d9ff1e2" }] } } },
        { $unwind: '$comments' },
        { $match: { $expr: { $eq: ['$comments._id', { $toObjectId: "667382f627df37eb4e7a3220" }] } } },
        {
            $unwind: {
                path: "$comments.attachments",
                preserveNullAndEmptyArrays: true
            }
        },
       ])`

и он возвращает эти данные

[
  {
   _id: new ObjectId('667293eca3a67d7c5d9ff1e2'),
   referenceNo: 'TCK_06-19-2024_0001',
   subject: new ObjectId('6670dad080ea03a681f70295'),
   description: 'pero ang totoo, di bali na ako. ikaw lang iniisip ko.',
   priority: 'medium',
   status: 'open',
   initiator: new ObjectId('6670da9d80ea03a681f70292'),
   createdBy: new ObjectId('667106fff8abeeb0d87341ba'),
   assignedTo: null,
   attachments: [ [Object], [Object] ],
   comments: {
     userId: new ObjectId('6670db4180ea03a681f7029e'),
     content: '2nd comment w atta',
     attachments: [Object],
     _id: new ObjectId('667382f627df37eb4e7a3220'),
     createdAt: 2024-06-20T01:16:38.190Z,
     updatedAt: 2024-06-20T01:16:38.190Z
   },
   createdAt: 2024-06-19T08:16:44.129Z,
   updatedAt: 2024-06-20T02:14:51.282Z,
   __v: 0
    },
  {
   _id: new ObjectId('667293eca3a67d7c5d9ff1e2'),
   referenceNo: 'TCK_06-19-2024_0001',
   subject: new ObjectId('6670dad080ea03a681f70295'),
   description: 'pero ang totoo, di bali na ako. ikaw lang iniisip ko.',
   priority: 'medium',
   status: 'open',
   initiator: new ObjectId('6670da9d80ea03a681f70292'),
   createdBy: new ObjectId('667106fff8abeeb0d87341ba'),
   assignedTo: null,
   attachments: [ [Object], [Object] ],
   comments: {
     userId: new ObjectId('6670db4180ea03a681f7029e'),
     content: '2nd comment w atta',
     attachments: [Object],
     _id: new ObjectId('667382f627df37eb4e7a3220'),
     createdAt: 2024-06-20T01:16:38.190Z,
     updatedAt: 2024-06-20T01:16:38.190Z
    },
    createdAt: 2024-06-19T08:16:44.129Z,
    updatedAt: 2024-06-20T02:14:51.282Z,
    __v: 0
    }
   ]

он превращает comment.attachments в объект, но все равно не заполняет данные вложений, Также к комментарию есть 3 вложения.

🤔 А знаете ли вы, что...
MongoDB предоставляет средства для создания резервных копий и восстановления данных...


1
62
1

Ответ:

Решено

Эта агрегация должна дать вам желаемый результат.

  1. $match: тот ticket, который вы хотите. Обратите внимание на использование конструктора new mongoose.Types.ObjectId(), чтобы вам не приходилось использовать $expr и конвертировать $toObjectId.
  2. $set: массив comments для вывода только $filter, совпадающего comments, у которого _id равен ObjectId("6678d63114c2fef2955395f0").
  3. $unwind: массив comments для следующего этапа. Объект должен быть только один, так как вы совпали по _id.
  4. $set: массив comments.attachments для вывода только $filter, совпадающего attachments, у которого _id равен ObjectId("6678d63114c2fef2955395f1").
const result = await Ticket.aggregate([
  {
    $match: {
      _id: new mongoose.Types.ObjectId("6678d3b714c2fef2955395ca"),
      "comments._id": new mongoose.Types.ObjectId("6678d63114c2fef2955395f0")
    }
  },
  {
    $set: {
      comments: {
        $filter: {
          input: "$comments",
          as: "c",
          cond: {
            $eq: [
              "$$c._id",
              new mongoose.Types.ObjectId("6678d63114c2fef2955395f0")
            ]
          }
        }
      }
    }
  },
  {
    $unwind: "$comments"
  },
  {
    $set: {
      "comments.attachments": {
        $filter: {
          input: "$comments.attachments",
          as: "ca",
          cond: {
            $eq: [
              "$$ca._id",
              new mongoose.Types.ObjectId("6678d63114c2fef2955395f1")
            ]
          }
        }
      }
    }
  }
]);

Рабочий пример смотрите ЗДЕСЬ.