我有一个庞大的集合,其中每个文档都有子文档,它们之间有关系。我的架构如下所示:
{
userName: "user44",
userID: "44",
posts : [
...
{
title : "post1",
id : "123"
...
},
{
title : "post2",
id : "124"
...
},
...
],
comments: [
...
{
id: 1910,
postId : "123",
title : "comment1",
comment : "some comment",
user: "user13"
},
{
id: 1911,
postId : "124",
title : "comment2",
comment : "some comment",
user: "user22"
},
...
],
commentUpvotes: [
...
{
id : 12,
commentId : "1910",
upvotedBy: "user91"
},
{
id: 13,
commentId : "1910",
upvotedBy: "user92"
},
...
]
}
虽然这与我的数据库无关,但原始模式与上面完全相同。因此,上面的示例是一个用户集合,我存储了用户的posts
; comments
由其他用户commentUpvotes
发布的帖子,用于存储有关谁投票的信息。不要考虑其设计的逻辑,不要建议任何其他模式。
问题:db.users.find({"commentUpvotes.id" : 12})
应该返回此集合,但仅返回此upvote所做的评论(1910)和帖子(123)。我用$ unwinding解决了它,这导致了性能问题。因此请建议在不放松的情况下解决。有什么想法?
答案 0 :(得分:2)
考虑"缩进"我在列表中使用,这实际上可能看起来比你正在做的更长,但实际上它不是。
这是使用$map
作为MongoDB 2.6及更高版本可用的另一个非常好的示例。仍然有一些$unwind
的使用,但数组是"解开"实际上只有一个元素。所以请原谅我无法抗拒的"Highlander"个引用:)
db.users.aggregate([
// Match your document or documents
{ "$match": {
"commentUpvotes.id": 12
}},
// Get the one "up-votes" entry that matches
{ "$project": {
"posts": 1,
"comments": 1,
"commentUpVotes": {
"$setDifference": [
{
"$map": {
"input": "$commentUpvotes",
"as": "el",
"in": {
"$cond": [
{ "$eq": [ "$$el.id", 12 ] },
"$$el",
false
]
}
}
},
[false]
]
}
}},
// There is only one!
{ "$unwind": "$commentUpVotes" },
// Get the one comments entry that matches
{ "$project": {
"posts": 1,
"comments": {
"$setDifference": [
{
"$map": {
"input": "$comments",
"as": "el",
"in": {
"$cond": [
{
"$eq": [
{ "$substr": [ "$$el.id", 0, 4 ] },
"$commentUpVotes.commentId"
]
},
"$$el",
false
]
}
}
},
[false]
]
},
"commentUpVotes": 1
}},
// And there is only one!
{ "$unwind": "$comments" },
// Get the one post that matches
{ "$project": {
"posts": {
"$setDifference": [
{
"$map": {
"input": "$posts",
"as": "el",
"in": {
"$cond": [
{
"$eq": [
"$$el.id",
"$comments.postId"
]
},
"$$el",
false
]
}
}
},
[false]
]
},
"comments": 1,
"commentUpVotes": 1
}},
// Optionally group back to arrays. There can be only one!
{ "$group": {
"_id": "$_id",
"posts": { "$first": "$posts" },
"comments": { "$push": "$comments" },
"commentUpVotes": { "$push": "$commentUpVotes" }
}}
])
所以最终的结果是:
{
"_id" : ObjectId("539065d3cd0f2aac5f55778e"),
"posts" : [
{
"title" : "post1",
"id" : "123"
}
],
"comments" : [
{
"id" : 1910,
"postId" : "123",
"title" : "comment1",
"comment" : "some comment",
"user" : "user13"
}
],
"commentUpVotes" : [
{
"id" : 12,
"commentId" : "1910",
"upvotedBy" : "user91"
}
]
}
我知道您要求"没有架构更改",但实际上不是架构更改,而是说将id
值保持在一致类型是个好主意。目前你在这个过程中混合了整数和字符串(我希望它只是一个例子),这不是一个好主意。
因此,有一些"有限的铸造"实际上可以使用$substr
在这里使用,但是你的实际解决方案可能会有所不同。如果确实需要修复数据,我强烈建议修复数据。
无论如何,$map