mat*_*fin 4 mongodb mongodb-query aggregation-framework
如何使用MongoDB聚合来包含来自另一个通过一对多关系链接的集合中的相关文档?
本质上,我想要做的是能够获取问题列表并包括与该问题相关的所有标志.
更新(2016年11月11日):解决了下面发布的解决方案.
更新(05/07/2016):我已经设法通过使用组合$unwind, $lookup, $project等来获得带有相关标志的问题列表.更新的查询如下.
问题(05/07/2016):我只能获取具有嵌套标志的问题.即使他们没有任何标志,我也想获取所有问题.
我有两个集合,一个用于内容,一个用于内容标志,如下所示:
{
"_id" : ObjectId("..."),
"slug" : "a-sample-title",
"content" : "Some content.",
"title" : "A Sample Title.",
"kind" : "Question",
"updated" : ISODate("2016-06-08T08:54:26.104Z"),
"isPublished" : true,
"isFeatured" : false,
"flags" : [
ObjectId("<id_of_flag_one>"),
ObjectId("<id_of_flag_two>")
],
"answers" : [
ObjectId("..."),
ObjectId("...")
],
"related" : [],
"isAnswered" : true,
"__v" : 4
}
Run Code Online (Sandbox Code Playgroud)
{
"_id" : ObjectId("..."),
"flaggedBy" : ObjectId("<a_users_id>"),
"type" : "like",
"__v" : 0
}
Run Code Online (Sandbox Code Playgroud)
在上面,一个问题可以有很多标志,一个标志只能有一个问题.我想要做的是在查询问题集时返回问题的所有标志.我尝试使用聚合运行这一点.
这是我正在使用的更新查询(05/07/2016)
fetchQuestions: (permission, params) => {
return new Promise((resolve, reject) => {
let query = Question.aggregate([
{
$lookup: {
from: 'users',
localField: 'author',
foreignField: '_id',
as: 'authorObject'
}
},
{
$unwind: '$authorObject'
},
{
$unwind: '$flags'
},
{
$lookup: {
from: 'flags',
localField: 'flags',
foreignField: '_id',
as: 'flagObjects'
}
},
{
$unwind: '$flagObjects'
},
{
$group: {
_id: {
_id: '$_id',
title: '$title',
content: '$content',
updated: '$updated',
isPublished: '$isPublished',
isFeatured: '$isFeatured',
isAnswered: '$isAnswered',
answers: '$answers',
author: '$authorObject'
},
flags: {
$push: '$flags'
},
flagObjects: {
$push: '$flagObjects'
}
}
},
{
$project: {
_id: 0,
_id: '$_id._id',
title: '$_id.title',
content: '$_id.content',
updated: '$_id.updated',
isPublished: '$_id.isPublished',
isFeatured: '$_id.isFeatured',
author: {
fullname: '$_id.author.fullname',
username: '$_id.author.username'
},
flagCount: {
$size: '$flagObjects'
},
answersCount: {
$size: '$_id.answers'
},
flags: '$flagObjects',
wasFlagged: {
$cond: {
if: {
$gt: [
{
$size: '$flagObjects'
},
0
]
},
then: true,
else: false
}
}
}
},
{
$sort: {
updated: 1
}
},
{
$skip: 0
},
// {
// $limit: 110
// }
])
.exec((error, result) => {
if(error) reject(error);
else resolve(result);
});
});
},
Run Code Online (Sandbox Code Playgroud)
我已经尝试过使用其他聚合运算符$unwind,$group但是结果集返回了五个或更少的项目,我发现很难理解这些应该如何协同工作以获得我需要的东西.
这是我得到的回应,这正是我所需要的.唯一的问题是,如上所述,我只会得到带有旗帜而不是所有问题的问题.
"questions": [
{
"_id": "5757dd42d0c2ae292f76f11a",
"flags": [
{
"_id": "5774e0a81f2874821f71ace8",
"flaggedBy": "57569d02d0c2ae292f76f0f5",
"type": "concern",
"__v": 0
},
{
"_id": "577a0f5414b834372a6ac772",
"flaggedBy": "5756aa79d0c2ae292f76f0f8",
"type": "concern",
"__v": 0
}
],
"title": "A question for the landing page.",
"content": "This is a question that will appear on the landing page.",
"updated": "2016-06-08T08:54:26.104Z",
"isPublished": true,
"isFeatured": false,
"author": {
"fullname": "Matt Finucane",
"username": "matfin-386829"
},
"flagCount": 2,
"answersCount": 2,
"wasFlagged": true
},
...,
...,
...
]
Run Code Online (Sandbox Code Playgroud)
看起来我已经找到了这个问题的解决方案,将在下面发布.
我遇到的问题概述如下:
我有一系列的Questions各种领域,如标题,内容,发布日期等,在通常的ObjectID领域之上.
我有一个Flags与问题相关的单独集合.
当标记被写入到用于在Question,所述ObjectID的那Flag应添加到称为阵列字段flags附加到Question文档中.
简而言之,Flags不直接存储在Question文档中.对它的引用Flag存储为ObjectID.
我需要做的是从Questions集合中获取所有项目并包含相关的标志.
MongoDB的聚合框架似乎为这个理想的解决方案,而是让你的头围绕它可以是一个有点棘手,尤其是在处理时$group,$lookup和$unwind运营商.
我还应该指出我正在使用NodeJS v6.x.x和Mongoose 4.4.x.
fetchQuestions: (permission, params) => {
return new Promise((resolve, reject) => {
let query = Question.aggregate([
/**
* We need to perform a lookup on the author
* so we can include the user details for the
* question. This lookup is quite easy to handle
* because a question should only have one author.
*/
{
$lookup: {
from: 'users',
localField: 'author',
foreignField: '_id',
as: 'authorObject'
}
},
/**
* We need this so that the lookup on the author
* object pulls out an author object and not an
* array containing one author. This simplifies
* the process of $project below.
*/
{
$unwind: '$authorObject'
},
/**
* We need to unwind the flags field, which is an
* array of ObjectIDs. At this stage of the aggregation
* pipeline, questions will be repeated so for example
* if there are two questions and one of them has two
* flags and the other has four flags, the result set
* will have six items and the questions will be repeated
* the same number of times as the flags they contain.
* The $group function later on will take care of this
* and return only unique questions.
*
* It is important to point out how the $unwind function
* is used here. If we did not specify the preserveNullAndEmptyArrays
* parameter then the only questions returned would be those
* that have flags. Those without would be skipped.
*/
{
$unwind: {
path: '$flags',
preserveNullAndEmptyArrays: true
}
},
/**
* Now that we have the ObjectIDs for the flags from the
* $unwind operation above, we need to perform a lookup on
* the flags collection to get our flags. We return these
* with the variable name 'flagObjects' we can use later.
*/
{
$lookup: {
from: 'flags',
localField: 'flags',
foreignField: '_id',
as: 'flagObjects'
}
},
/**
* We then need to perform another unwind on the 'flagObjects'
* and pass them into the next $group function
*/
{
$unwind: {
path: '$flagObjects',
preserveNullAndEmptyArrays: true
}
},
/**
* The next stage of the aggregation pipeline takes all
* the duplicated questions with their flags and the flagObjects
* and normalises the data. The $group aggregator requires an _id
* property to describe how a question should be unique. It also sets
* up some variables that can be used when it comes to the $project
* stage of the aggregation pipeline.
* the flagObjects property calls on the $push function to add a collection
* of flagObjects that were pulled from the $lookup above.
*/
{
$group: {
_id: {
_id: '$_id',
title: '$title',
content: '$content',
updated: '$updated',
isPublished: '$isPublished',
isFeatured: '$isFeatured',
isAnswered: '$isAnswered',
answers: '$answers',
author: '$authorObject'
},
flagObjects: {
$push: '$flagObjects'
}
}
},
/**
* The $project stage of the pipeline then puts together what the final
* result set should look like when the query is executed. Here we can use
* various Mongo functions to reshape the data and create new attributes.
*/
{
$project: {
_id: 0,
_id: '$_id._id',
title: '$_id.title',
updated: '$_id.updated',
isPublished: '$_id.isPublished',
isFeatured: '$_id.isFeatured',
author: {
fullname: '$_id.author.fullname',
username: '$_id.author.username'
},
flagCount: {
$size: '$flagObjects'
},
answersCount: {
$size: '$_id.answers'
},
flags: '$flagObjects',
wasFlagged: {
$cond: {
if: {
$gt: [
{
$size: '$flagObjects'
},
0
]
},
then: true,
else: false
}
}
}
},
/**
* Then we can sort, skip and limit if needs be.
*/
{
$sort: {
updated: -1
}
},
{
$skip: 0
},
// {
// $limit: 110
// }
]);
query.exec((error, result) => {
if(error) reject(error);
else resolve(result);
});
});
},
Run Code Online (Sandbox Code Playgroud)
"questions": [
{
"_id": "576a85d68c4333a017083fca",
"title": "How do I do this?",
"updated": "2016-06-22T12:34:30.919Z",
"isPublished": false,
"isFeatured": false,
"author": {
"fullname": "Matt Finucane",
"username": "matfin-386829"
},
"flagCount": 1,
"answersCount": 0,
"flags": [
{
"_id": "5776541a2e38844428696615",
"flaggedBy": "5756aa79d0c2ae292f76f0f8",
"type": "concern",
"__v": 0
}
],
"wasFlagged": true
},
{
"_id": "576a85d68c4333a017083fc9",
"title": "Is this a question?",
"updated": "2016-06-22T12:34:30.918Z",
"isPublished": true,
"isFeatured": false,
"author": {
"fullname": "Matt Finucane",
"username": "matfin-386829"
},
"flagCount": 2,
"answersCount": 0,
"flags": [
{
"_id": "5773ce4ea363e5161ae69e7f",
"flaggedBy": "5756aa79d0c2ae292f76f0f8",
"type": "concern",
"__v": 0
},
{
"_id": "577654382e3884442869661d",
"flaggedBy": "57569d02d0c2ae292f76f0f5",
"type": "concern",
"__v": 0
}
],
"wasFlagged": true
}
]
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2227 次 |
| 最近记录: |