我已关注users
集合
[{
"_id" : ObjectId("5afadfdf08a7aa6f1a27d986"),
"firstName" : "bruce",
"friends" : [ ObjectId("5afd1c42af18d985a06ac306"),ObjectId("5afd257daf18d985a06ac6ac") ]
},
{
"_id" : ObjectId("5afbfe21daf4b13ddde07dbe"),
"firstName" : "clerk",
"friends" : [],
}]
并拥有friends
集合
[{
"_id" : ObjectId("5afd1c42af18d985a06ac306"),
"recipient" : ObjectId("5afaab572c4ec049aeb0bcba"),
"requester" : ObjectId("5afadfdf08a7aa6f1a27d986"),
"status" : 2,
},
{
"_id" : ObjectId("5afd257daf18d985a06ac6ac"),
"recipient" : ObjectId("5afadfdf08a7aa6f1a27d986"),
"requester" : ObjectId("5afbfe21daf4b13ddde07dbe"),
"status" : 1,
}]
假设我有一位用户使用_id: "5afaab572c4ec049aeb0bcba"
登录,而此_id
与recipient
friends
匹配
现在我必须添加一个字段friendsStatus
,其中包含来自status
集合的friends
...如果不匹配数组中的任何recipient
,那么状态应为0
所以当我得到所有用户时,我的输出应该是
[{
"_id" : ObjectId("5afadfdf08a7aa6f1a27d986"),
"firstName" : "bruce",
"friends" : [ ObjectId("5afd1c42af18d985a06ac306") ],
"friendStatus": 2
},
{
"_id" : ObjectId("5afbfe21daf4b13ddde07dbe"),
"firstName" : "clerk",
"friends" : [],
"friendStatus": 0
}]
提前致谢!!!
答案 0 :(得分:1)
如果您有MongoDB 3.6,那么您可以将$lookup
与“子管道”一起使用
User.aggregate([
{ "$lookup": {
"from": Friend.collection.name,
"let": { "friends": "$friends" },
"pipeline": [
{ "$match": {
"recipient": ObjectId("5afaab572c4ec049aeb0bcba"),
"$expr": { "$in": [ "$_id", "$$friends" ] }
}},
{ "$project": { "status": 1 } }
],
"as": "friends"
}},
{ "$addFields": {
"friends": {
"$map": {
"input": "$friends",
"in": "$$this._id"
}
},
"friendsStatus": {
"$ifNull": [ { "$min": "$friends.status" }, 0 ]
}
}}
])
对于早期版本,实际使用$unwind
是理想的,以确保您不会违反BSON Limit:
User.aggregate([
{ "$lookup": {
"from": Friend.collection.name,
"localField": "friends",
"foreignField": "_id",
"as": "friends"
}},
{ "$unwind": { "path": "$friends", "preserveNullAndEmptyArrays": true } },
{ "$match": {
"$or": [
{ "friends.recipient": ObjectId("5afaab572c4ec049aeb0bcba") },
{ "friends": null }
]
}},
{ "$group": {
"_id": "$_id",
"firstName": { "$first": "$firstName" },
"friends": { "$push": "$friends._id" },
"friendsStatus": {
"$min": {
"$ifNull": ["$friends.status",0]
}
}
}}
])
此处最佳表单中存在“one difference”,因为管道优化实际上并未将$match
条件“汇总”到$lookup
本身:
{
"$lookup" : {
"from" : "friends",
"as" : "friends",
"localField" : "friends",
"foreignField" : "_id",
"unwinding" : {
"preserveNullAndEmptyArrays" : true
}
}
},
{
"$match" : { // <-- outside will preserved array
由于preserveNullAndEmptyArrays
选项为true
,因此“完全优化”操作,其中条件实际将应用于外部集合“之前”< / em>返回的结果不会发生。
所以这里unwinding
的唯一目的纯粹是为了避免$lookup
结果中通常为目标的“数组”导致父文档超出BSON限制。然后在“此阶段之后”应用$match
的附加条件。没有该选项的默认$unwind
会假设保留false
,而是添加matching
条件来执行此操作。这当然会导致没有外国比赛的文件被排除在外。
由于BSON Limit而不是真的可取,但是$filter
的结果数组也应用了$lookup
:
User.aggregate([
{ "$lookup": {
"from": Friend.collection.name,
"localField": "friends",
"foreignField": "_id",
"as": "friends"
}},
{ "$addFields": {
"friends": {
"$map": {
"input": {
"$filter": {
"input": "$friends",
"cond": {
"$eq": [
"$$this.recipient",
ObjectId("5afaab572c4ec049aeb0bcba")
]
}
}
},
"in": "$$this._id"
}
},
"friendsStatus": {
"$ifNull": [
{ "$min": {
"$map": {
"input": {
"$filter": {
"input": "$friends",
"cond": {
"$eq": [
"$$this.recipient",
ObjectId("5afaab572c4ec049aeb0bcba")
]
}
}
},
"in": "$$this.status"
}
}},
0
]
}
}}
])
在任何一种情况下,我们基本上都将“附加条件”添加到连接中,不仅仅是在直接相关的字段上,还包括ObjectId
的查询"recipient"
值的附加约束。 / p>
不确定你对"friendsStatus"
的期望是什么,因为结果是一个数组,并且可能有多个(据我所知),因此只需在此处应用$min
来提取一个在任何一种情况下都来自数组的值。
每种情况下的管理条件是$ifNull
,适用于"friends"
输出数组中没有任何内容要提取的情况,然后您只需返回0
的结果就是这样。
所有输出相同的东西:
{
"_id" : ObjectId("5afadfdf08a7aa6f1a27d986"),
"firstName" : "bruce",
"friends" : [
ObjectId("5afd1c42af18d985a06ac306")
],
"friendsStatus" : 2
}
{
"_id" : ObjectId("5afbfe21daf4b13ddde07dbe"),
"firstName" : "clerk",
"friends" : [ ],
"friendsStatus" : 0
}