基于两个子文档的 Mondo-DB 分组
Mondo-DB Grouping based on two subdocuments
我正在尝试编写一个聚合查询来过滤两个子文档并将它们分组,但我似乎无法弄清楚如何使用单个查询来完成它,无论是否可能。我尝试了一些聚合和 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: []
}
]
希望这是有道理的。
只要您在每个数组结果中使用不同的值,这就不是问题:
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 } }
])
一般的想法是改变结构,以便 "free" 和 "saved" 结果都被移动到它们自己的文档中,并用 "type" 标记它们是什么。然后,在分组回所需的 _id
时,您有条件地 select 使用 "type" 使用 $cond
将哪些项目添加到任一数组,或者 select 值 false
.
剩下的唯一事情就是 "filter" 数组中的 false
值。最好使用 $setDifference
in modern versions but can also be done with careful use of $unwind
and $match
.
您可能希望通过 "type" 考虑以更扁平的形式存储此数据,因为这是此管道中的大部分工作正在做的事情。从那里开始,最后四个流水线阶段相当简单。
我正在尝试编写一个聚合查询来过滤两个子文档并将它们分组,但我似乎无法弄清楚如何使用单个查询来完成它,无论是否可能。我尝试了一些聚合和 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: []
}
]
希望这是有道理的。
只要您在每个数组结果中使用不同的值,这就不是问题:
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 } }
])
一般的想法是改变结构,以便 "free" 和 "saved" 结果都被移动到它们自己的文档中,并用 "type" 标记它们是什么。然后,在分组回所需的 _id
时,您有条件地 select 使用 "type" 使用 $cond
将哪些项目添加到任一数组,或者 select 值 false
.
剩下的唯一事情就是 "filter" 数组中的 false
值。最好使用 $setDifference
in modern versions but can also be done with careful use of $unwind
and $match
.
您可能希望通过 "type" 考虑以更扁平的形式存储此数据,因为这是此管道中的大部分工作正在做的事情。从那里开始,最后四个流水线阶段相当简单。