Mongoose findOne — возвращает только поля, соответствующие условию

#database #mongodb #mongoose #nosql #conditional-statements

#База данных #mongodb #мангуст #nosql #условные операторы

Вопрос:

Я пытаюсь запросить свою коллекцию совпадений (игр) и выяснить, отправил ли определенный пользователь уже данные в массив объектов reportMessages.

 const results = await Match.findOne({ 'users': req.params.userIdOfReportSender, '_id': req.params.matchId, 'reportMessages.sentBy': req.params.userIdOfReportSender }, 'reportMessages' )
 

Однако приведенный выше запрос возвращает следующее:

 {
  _id: 5fd382c65d5395e0778f2f8a,
  reportMessages: [
    {
      _id: 5fd610f27ae587189c45b6ca,
      content: 'jajatest',
      timeStamp: 2020-12-13T13:02:42.102Z,
      sentBy: 'XbVvm6g3nsRmPg3P1pBvVl84h6C2'
    },
    { sentBy: "'anotheruser123" }
  ]
}
 

Как я могу заставить его возвращать только первое сообщение reportMessage, то есть отправленное XbVvm6g3nsRmPg3P1pBvVl84h6C2?

Документы Mongoose findOne (https://mongoosejs.com/docs/api.html#model_Model.findOne ) покажите, что вы можете указать аргументы, указывающие, какие поля следует выбрать (в их случае «длина имени»), но не показывайте способ выбора полей только в том случае, если они соответствуют определенному условию.

Возможно ли это вообще? Пытался погуглить этот, казалось бы, простой вопрос в течение довольно долгого времени, но безуспешно

С уважением

Ответ №1:

С помощью этого запроса агрегации вы можете получить только тот вложенный документ, который вам нужен:

 Match.aggregate([
    {
        $match: { _id: req.params.matchId }
    },
    {
        $project: {
            reportMessages: {
                $filter: {
                    input: '$reportMessages',
                    as: 'msg',
                    cond: { $eq: ['$msg.sentBy', req.params.userIdOfReportSender] }
                }
            }
        }
    },
    {
        $project: {
            reportMessage: { $arrayElemAt: [ '$reportMessages', 0 ] },
        }
    },
    { $replaceWith: '$reportMessage' }
]);
 

Обратите внимание, что вам нужно указать документ только _id для получения одного результата, поскольку _id s уникальны.

Комментарии:

1. Спасибо. После обертывания ObjectId вокруг req.params.MatchID в условии $match это сработало. Все еще не уверен, почему это должно быть сделано для этого запроса агрегации, но, например, в операции ‘updateOne’ мне не нужно оборачивать вокруг него ObjectId и я могу продолжать использовать значение req.params.MatchID (которое, я полагаю, является строкой).