简体   繁体   中英

An aggregate query with subdocuments with Mongoose

I am working on a chat project with no-sql based mongoose. I got stuck in the query part. I leave my current models and queries I use below. You can find the expected results below. Thanks for your help in advance.

First of all, to summarize the subject. We have room and Message collections. Each message contains one roomId. And some messages also contain a field called quoteMessage that references its own diagram. When querying rooms, I want to bring messages that contain the _id value of this room and at the same time fill the sub-document of the message, namely the quoteMessage field.

Room Schema:

const roomSchema = new Schema({
  _id: {
    type: String,
    required: true,
    // unique: true,
    immutable: true,
    uppercase: true
  },
  guidesId: {
    type: [Number],
    required: true
  },
  members: [{
    member: {
      type: String,
      required: true,
      refPath: 'members.memberRole'
    },
    memberRole: {
      type: String,
      required: true,
      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 });

Message Schema:

const messageSchema = new Schema({
  roomId: {
    type: String,
    required: true
  },
  ownerId: {
    type: String
  },
  message: {
    type: String,
    required: true
  },
  quoteMessage: { type: String, ref: 'message' },
  messageType: {
    type: String,
    default: messageTypes.text
  },
  createdAt: {
    type: String,
    default: () => moment().format()
  },
  updatedAt: {
    type: String,
    default: () => moment().format()
  }
}, { versionKey: false });

The query I am using:

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']
})

My result:

[
  {
    "_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",
        "messageType": "Text",
        "roomId": "1_BKN1",
        "message": "Hi",
        "ownerId": "606195183815891ca821bd4a",
        "createdAt": "2021-04-07T14:36:02+03:00",
        "updatedAt": "2021-04-07T14:36:02+03:00"
      }
    ]
  }
]

The Expected Result:

[
  {
    "_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": {
          "_id": "606d992294c3aa28b4bad411",
          "messageType": "Text",
          "roomId": "1_BKN1",
          "message": "Hi",
          "ownerId": "606195183815891ca821bd4a",
          "createdAt": "2021-04-07T14:36:02+03:00",
          "updatedAt": "2021-04-07T14:36:02+03:00"
        },
        "createdAt": "2021-04-08T12:42:55+03:00",
        "updatedAt": "2021-04-08T12:42:55+03:00"
      },
      {
        "_id": "606d992294c3aa28b4bad411",
        "messageType": "Text",
        "roomId": "1_BKN1",
        "message": "Hi",
        "ownerId": "606195183815891ca821bd4a",
        "createdAt": "2021-04-07T14:36:02+03:00",
        "updatedAt": "2021-04-07T14:36:02+03:00"
      }
    ]
  }
]

After some research I learned that the.populate function can take a list and the objects in it are given model references. I reached the desired result with the following renewed query.

Updated query:

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'
    }
  ])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM