我正在尝试编写一个聚合查询来过滤两个子文档并对它们进行分组,但我似乎无法弄清楚如何使用单个查询来完成它,无论是否可行。我尝试了一些聚合和mapReduce查询,但无法使它们工作。
以下是一个示例文档:
[
{
id: "first user",
read: [{
"id" : 'A',
free: true
},
{
"id" : 'B',
free: false
},
{
"id" : 'C',
free: true
}],
saved: [{
"id" : 'B',
free: true
},
{
"id" : 'C',
free: false
}]
},
{
id: "second user",
read: [{
"id" : 'B',
free: true
},
{
"id" : 'C',
free: true
}],
saved: [{
"id" : 'A',
free: true
}]
}
]
基本上,我希望在过滤掉非免费文件时,分别对用户分组读取和保存的子文档。这是我们想要的输出:
[
{
id: 'A',
freeRead: [ 'first user'],
freeSaved: ['second user']
},
{
id: 'B',
freeRead: [ 'second user'],
freeSaved: ['first user']
},
{
id: 'C',
freeRead: ['first user', 'second user'],
freeSaved: []
}
]
希望这是有道理的。
答案 0 :(得分:0)
只要您在每个数组结果中使用不同的值,这不是问题:
db.subs.aggregate([
// Match valid documents only
{ "$match": {
"$or": [
{ "read.free": true },
{ "saved.free": true }
]
}},
// Unwind arrays
{ "$unwind": "$read" },
{ "$unwind": "$saved" },
// Add type array and unwind
{ "$project": {
"_id": 0,
"id": 1,
"read": 1,
"saved": 1,
"type": { "$literal": [ "read", "saved" ] }
}},
{ "$unwind": "$type" },
// Group distinct values conditionally by "type"
{ "$group": {
"_id": {
"_id": { "$cond": [
{ "$eq": [ "$type", "read" ] },
{ "id": "$read.id", "free": "$read.free" },
{ "id": "$saved.id", "free": "$saved.free" }
]},
"id": "$id",
"type": "$type"
}
}},
// Only interested in free true
{ "$match": { "_id._id.free": true } },
// Group conditionally, one document two array fields
{ "$group": {
"_id": "$_id._id.id",
"freeRead": { "$addToSet": { "$cond": [
{ "$eq": [ "$_id.type", "read" ] },
"$_id.id",
false
]}},
"freeSaved": { "$addToSet": { "$cond": [
{ "$eq": [ "$_id.type", "saved" ] },
"$_id.id",
false
]}}
}},
// Filter the `false` values
{ "$project": {
"freeRead": { "$setDifference": [ "$freeRead", [false] ] },
"freeSaved": { "$setDifference": [ "$freeSaved", [false] ] }
}},
// Sort in order
{ "$sort": { "_id": 1 } }
])
通常的想法是改变结构,以便将“免费”和“保存”的结果移动到他们自己的文档中,标记为“类型”。然后,在分组回所需的_id
时,您有条件地选择要使用$cond
通过“类型”添加到任一阵列的项目,或者选择值false
。
唯一剩下的就是“过滤”数组中的false
值。最好在现代版本中使用$setDifference
,但也可以谨慎使用$unwind
和$match
。
您可能需要考虑通过“类型”将此数据存储为更扁平的形式,因为这是此管道中的大部分工作正在进行的操作。从那个地方开始,最后四个管道阶段相当简单。