将条件匹配的字段添加到嵌套数组
Posted
技术标签:
【中文标题】将条件匹配的字段添加到嵌套数组【英文标题】:add fields where condition match to nested array 【发布时间】:2018-10-27 08:18:10 【问题描述】:我有关注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
匹配friends
的recipient
现在我必须添加一个字段friendsStatus
,其中包含来自friends
集合的status
...如果不匹配数组中的任何recipient
,那么它的状态应该是0
所以当我得到所有用户时,我的输出应该是
[
"_id" : ObjectId("5afadfdf08a7aa6f1a27d986"),
"firstName" : "bruce",
"friends" : [ ObjectId("5afd1c42af18d985a06ac306") ],
"friendStatus": 2
,
"_id" : ObjectId("5afbfe21daf4b13ddde07dbe"),
"firstName" : "clerk",
"friends" : [],
"friendStatus": 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]
])
这里与最最佳形式存在“一个区别”,因为管道优化实际上并未将$match
条件“汇总”到@ 987654325@自己:
"$lookup" :
"from" : "friends",
"as" : "friends",
"localField" : "friends",
"foreignField" : "_id",
"unwinding" :
"preserveNullAndEmptyArrays" : true
,
"$match" : // <-- outside will preserved array
由于preserveNullAndEmptyArrays
选项为true
,因此“完全优化” 操作将条件实际应用于外部集合“之前” 结果不会被退回。
因此,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"
的附加约束。
不太确定您对"friendsStatus"
的期望是什么,因为结果是一个数组并且可能不止一个(据我所知),因此只需在此处应用$min
以从数组中提取一个值无论哪种情况。
每种情况下的控制条件是$ifNull
,它适用于"friends"
输出数组中没有任何内容可供提取的情况,然后您只需返回0
的结果即可。
所有输出都一样:
"_id" : ObjectId("5afadfdf08a7aa6f1a27d986"),
"firstName" : "bruce",
"friends" : [
ObjectId("5afd1c42af18d985a06ac306")
],
"friendsStatus" : 2
"_id" : ObjectId("5afbfe21daf4b13ddde07dbe"),
"firstName" : "clerk",
"friends" : [ ],
"friendsStatus" : 0
【讨论】:
$lookup argument 'unwinding: preserveNullAndEmptyArrays: true ' must be a string, is type object
当我使用"unwinding" : "preserveNullAndEmptyArrays" : true
时
@AshishChoudhary 您实际上并没有自己输入。您正在查看我正在显示的“解释”输出。代码和输出之间的区别应该很清楚。有关详细说明,请参阅Aggregate $lookup Total size of documents in matching pipeline exceeds maximum document size。以上是关于将条件匹配的字段添加到嵌套数组的主要内容,如果未能解决你的问题,请参考以下文章
MongoDB/Mongoose - 仅当某个字段是唯一的时才将对象添加到对象数组中