带有 Mongoose 的子文档的聚合查询

问题描述

我正在使用基于 no-sql 的猫鼬进行聊天项目。我被困在查询部分。我在下面留下我当前使用的模型和查询。您可以在下面找到预期的结果。提前感谢您的帮助。

首先总结一下题目。我们有房间和消息集合。每条消息都包含一个 roomId。并且一些消息还包含一个名为 quoteMessage 的字段,该字段引用其自己的图表。查询房间时,我想带上包含该房间_id值的消息,同时填写消息的子文档,即quoteMessage字段。

房间结构:

const roomSchema = new Schema({
  _id: {
    type: String,required: true,// unique: true,immutable: true,uppercase: true
  },guidesId: {
    type: [Number],required: true
  },members: [{
    member: {
      type: String,refPath: 'members.memberRole'
    },memberRole: {
      type: String,enum: ['staff','customer']
    }
  }],success: {
    type: Boolean,default: false
  },staffLastSeen: {
    type: String,default: () => null
  },createdAt: {
    type: String,default: () => moment().format()
  },updatedAt: {
    type: String,default: () => moment().format()
  }
},{ versionKey: false });

消息架构:

const messageSchema = new Schema({
  roomId: {
    type: String,ownerId: {
    type: String
  },message: {
    type: String,quoteMessage: { type: String,ref: 'message' },messageType: {
    type: String,default: messageTypes.text
  },{ versionKey: false });

我使用的查询

const Room = require('../../../models/Room');

const rooms = await Room.aggregate([
  {
    $sort: { createdAt: -1 }
  },{
    $lookup: {
      from: 'messages',as: 'messages',let: { id: '$_id' },pipeline: [
        {
          $match: {
            $expr: { $eq: ['$$id','$roomId'] }
          }
        },// messages filter last 30 data.

        { $sort: { createdAt: -1 } },{ $limit: 30 }
      ]
    }
  },{ $addFields: { lastMessage: { $first: '$messages.createdAt' } } },{
    $sort: { lastMessage: -1 }
  }
])

const result = await Room.populate(rooms,{
  path: 'members.member',select: ['-createdAt','-updatedAt','-password','-email']
})

我的结果:

[
  {
    "_id": "1_BKN1","guidesId": [
      44,45
    ],"success": false,"members": [
      {
        "_id": "606d77df4321821b8484e0a8","member": {
          "role": "Customer","_id": "1_BKN1","meetingId": 1,"bookingId": 1,"bookingReferenceCode": "BKN1","name": "Barney","surname": "Simpsons"
        },"memberRole": "customer"
      },{
        "_id": "606d77f74321821b8484e0ad","member": {
          "role": "Staff","_id": "606195183815891ca821bd4a","name": "Amy","surname": "Dark"
        },"memberRole": "staff"
      }
    ],"staffLastSeen": "2021-04-07T15:22:46+03:00","createdAt": "2021-04-07T12:14:07+03:00","updatedAt": "2021-04-07T15:22:46+03:00","messages": [
      {
        "_id": "606ed01fbe800510641f6990","messageType": "Text","roomId": "1_BKN1","message": "Hello","ownerId": "1_BKN1","quoteMessage": "606d992294c3aa28b4bad411","createdAt": "2021-04-08T12:42:55+03:00","updatedAt": "2021-04-08T12:42:55+03:00"
      },{
        "_id": "606d992294c3aa28b4bad411","message": "Hi","ownerId": "606195183815891ca821bd4a","createdAt": "2021-04-07T14:36:02+03:00","updatedAt": "2021-04-07T14:36:02+03:00"
      }
    ]
  }
]

预期结果:

[
  {
    "_id": "1_BKN1","quoteMessage": {
          "_id": "606d992294c3aa28b4bad411","updatedAt": "2021-04-07T14:36:02+03:00"
        },"updatedAt": "2021-04-07T14:36:02+03:00"
      }
    ]
  }
]

解决方法

经过一些研究,我了解到 .populate 函数可以接受一个列表,并且其中的对象会被赋予模型引用。我通过以下更新的查询达到了预期的结果。

更新的查询:

const Room = require('../../../models/Room');

const rooms = await Room.aggregate([
  {
    $sort: { createdAt: -1 }
  },{
    $lookup: {
      from: 'messages',as: 'messages',let: { id: '$_id' },pipeline: [
        {
          $match: {
            $expr: { $eq: ['$$id','$roomId'] }
          }
        },// messages filter last 30 data.

        { $sort: { createdAt: -1 } },{ $limit: 30 }
      ]
    }
  },{ $addFields: { lastMessage: { $first: '$messages.createdAt' } } },{
    $sort: { lastMessage: -1 }
  }
])

const result = await Room.populate(rooms,[
    {
      path: 'members.member',select: ['-createdAt','-updatedAt','-password','-email']
    },{
      path: 'messages.quoteMessage',model: 'message'
    }
  ])