Mongodb 嵌套数组字段上的聚合查找覆盖其他字段
Mongodb Aggregation Lookup on Nested Array Fields overwriting other fields
这是我的 collections 的详细信息,我有 4 collections。
Chat,
ChatUser,
Messages,
User
这些 collections 的字段是,
Chat:
_id,
type ('dual', 'group')
ChatUser:
_id,
userId (Ref: Users)
chatId (Ref: Chat)
Messages:
_id,
chatId (Ref: Chat)
fromUserId (Ref: Chat)
type: ('text', 'media')
message
Users:
_id,
name,
avatar
我写了一个聚合函数来收集我下面的所有聊天记录。
let chats = await Chat.aggregate([
{$match: {_id: {$in: chatsOfUser}}},
{
$lookup: {
from: "chatusers",
let: {cId: "$_id"},
pipeline: [
// <-- Added Pipeline
{
$match: {
$expr: {
$and: [
{$eq: ["$chatId", "$$cId"]},
{$ne: ["$userId", mongoose.Types.ObjectId(req.user._id)]},
],
},
},
},
],
as: "ChatUser",
},
},
{$unwind: "$ChatUser"},
{$unwind: "$ChatUser.userId"},
{
$lookup: {
from: "users",
localField: "ChatUser.userId",
foreignField: "_id",
as: "Users",
},
},
{$unwind: "$Users"},
{$project: {"Users.password": 0}},
{
$lookup: {
from: "messages",
localField: "_id",
foreignField: "chatId",
as: "Messages",
},
},
{$sort: {"Messages.createdAt": -1}},
]);
我的实际结果是,
{
"chats": [
{
"_id": "60db388deb35c276cd023f32",
"type": "dual",
"ChatUser": {
"_id": "60db388deb35c276cd023f36",
"chatId": "60db388deb35c276cd023f32",
"userId": "60db387feb35c276cd023f1f",
},
"Users": {
"_id": "60db387feb35c276cd023f1f",
"firstName": "Frodo",
"lastName": "Baggins",
"email": "Frodo.Baggins@gmail.com",
"gender": "male",
},
"Messages": [
{
"_id": "60db38f729d01c669696cb9e",
"message": "Hello friend",
"type": "text",
"chatId": "60db388deb35c276cd023f32",
"fromUserId": "60daeb5617b93e6cb968582e",// Here I want to populate the User name and avatar into a User Field
},
{
"_id": "60db38f729d01c669696cb9f",
"message": "Hi buddy",
"type": "text",
"chatId": "60db388deb35c276cd023f32",
"fromUserId": "60db387feb35c276cd023f1f", // Here I want to populate the User name and avatar into a User Field
}
]
},
]
}
这里我想在“chats.Messages.User
”中填充“fromUserId
”的用户详细信息。但是,如果我使用以下查找,它会将消息的所有字段替换为新用户 Object。在排序管道之前使用它。
{
$lookup: {
from: "users",
let: { user_id: "$Messages.fromUserId" },
pipeline : [
{ $match: { $expr: { $eq: [ "$_id", "$$user_id" ] } }, },
{ $project : { _id:1, firstName:1 } }
],
as: "Messages.User"
}
},
在上述查找之前使用展开管道,
{ "$unwind": "$Messages" },
{ "$unwind": "$Messages.fromUserId" },
但它通过消息展开了整个聊天数组。我真的只想在 'chats.Messages.User'
中提取 fromUserId
。有解决办法吗?
解决这个问题的方法有很多种,我选择我认为最简单的方法,不会对现有管道做太多改动。
我会更改消息中的第二个 $lookup
以在其中包含一个嵌套的 $lookup
以获取用户。这样我们就不需要展开整个文档并在之后重组数据。
看起来像这样:
{
$lookup: {
from: "messages",
let: {
id: "$_id"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$$id",
"$chatId"
]
}
}
},
{
$lookup: {
from: "users",
localField: "fromUserId",
foreignField: "_id",
as: "User"
}
},
{
$unwind: "$User"
},
{
$project: {
"User.password": 0
}
},
],
as: "Messages",
},
},
完整示例位于:
这是我的 collections 的详细信息,我有 4 collections。
Chat,
ChatUser,
Messages,
User
这些 collections 的字段是,
Chat:
_id,
type ('dual', 'group')
ChatUser:
_id,
userId (Ref: Users)
chatId (Ref: Chat)
Messages:
_id,
chatId (Ref: Chat)
fromUserId (Ref: Chat)
type: ('text', 'media')
message
Users:
_id,
name,
avatar
我写了一个聚合函数来收集我下面的所有聊天记录。
let chats = await Chat.aggregate([
{$match: {_id: {$in: chatsOfUser}}},
{
$lookup: {
from: "chatusers",
let: {cId: "$_id"},
pipeline: [
// <-- Added Pipeline
{
$match: {
$expr: {
$and: [
{$eq: ["$chatId", "$$cId"]},
{$ne: ["$userId", mongoose.Types.ObjectId(req.user._id)]},
],
},
},
},
],
as: "ChatUser",
},
},
{$unwind: "$ChatUser"},
{$unwind: "$ChatUser.userId"},
{
$lookup: {
from: "users",
localField: "ChatUser.userId",
foreignField: "_id",
as: "Users",
},
},
{$unwind: "$Users"},
{$project: {"Users.password": 0}},
{
$lookup: {
from: "messages",
localField: "_id",
foreignField: "chatId",
as: "Messages",
},
},
{$sort: {"Messages.createdAt": -1}},
]);
我的实际结果是,
{
"chats": [
{
"_id": "60db388deb35c276cd023f32",
"type": "dual",
"ChatUser": {
"_id": "60db388deb35c276cd023f36",
"chatId": "60db388deb35c276cd023f32",
"userId": "60db387feb35c276cd023f1f",
},
"Users": {
"_id": "60db387feb35c276cd023f1f",
"firstName": "Frodo",
"lastName": "Baggins",
"email": "Frodo.Baggins@gmail.com",
"gender": "male",
},
"Messages": [
{
"_id": "60db38f729d01c669696cb9e",
"message": "Hello friend",
"type": "text",
"chatId": "60db388deb35c276cd023f32",
"fromUserId": "60daeb5617b93e6cb968582e",// Here I want to populate the User name and avatar into a User Field
},
{
"_id": "60db38f729d01c669696cb9f",
"message": "Hi buddy",
"type": "text",
"chatId": "60db388deb35c276cd023f32",
"fromUserId": "60db387feb35c276cd023f1f", // Here I want to populate the User name and avatar into a User Field
}
]
},
]
}
这里我想在“chats.Messages.User
”中填充“fromUserId
”的用户详细信息。但是,如果我使用以下查找,它会将消息的所有字段替换为新用户 Object。在排序管道之前使用它。
{
$lookup: {
from: "users",
let: { user_id: "$Messages.fromUserId" },
pipeline : [
{ $match: { $expr: { $eq: [ "$_id", "$$user_id" ] } }, },
{ $project : { _id:1, firstName:1 } }
],
as: "Messages.User"
}
},
在上述查找之前使用展开管道,
{ "$unwind": "$Messages" },
{ "$unwind": "$Messages.fromUserId" },
但它通过消息展开了整个聊天数组。我真的只想在 'chats.Messages.User'
中提取 fromUserId
。有解决办法吗?
解决这个问题的方法有很多种,我选择我认为最简单的方法,不会对现有管道做太多改动。
我会更改消息中的第二个 $lookup
以在其中包含一个嵌套的 $lookup
以获取用户。这样我们就不需要展开整个文档并在之后重组数据。
看起来像这样:
{
$lookup: {
from: "messages",
let: {
id: "$_id"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$$id",
"$chatId"
]
}
}
},
{
$lookup: {
from: "users",
localField: "fromUserId",
foreignField: "_id",
as: "User"
}
},
{
$unwind: "$User"
},
{
$project: {
"User.password": 0
}
},
],
as: "Messages",
},
},
完整示例位于: