问题描述
我正在使用基于 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'
}
])