Ann*_*nne 4 mongodb mongodb-query aggregation-framework
select tb1.*,tb3 from tb1,tb2,tb3
where tb1.id=tb2.profile_id and tb2.field='<text>'
and tb3.user_id = tb2.id and tb3.status =0
Run Code Online (Sandbox Code Playgroud)
实际上我将sql转换为mongosql,如下所示
mongo 我用过的sql
db.getCollection('tb1').aggregate
([
{ $lookup:
{ from: 'tb2',
localField: 'id',
foreignField: 'profile_id',
as: 'tb2detail'
}
},
{ $lookup:
{ from: 'tb3',
localField: 'tb2.id',
foreignField: 'user_id',
as: 'tb3details'
}
},
{ $match:
{ 'status':
{ '$ne': 'closed'
},
'tb2.profile_type': 'agent',
'tb3.status': 0
}
}
])
Run Code Online (Sandbox Code Playgroud)
但没有达到预期的结果..
任何帮助将不胜感激..
Nei*_*unn 11
你在这里缺少的是在它的参数$lookup指定的输出字段中产生一个"数组" as.这是MongoDB"关系"的一般概念,因为文档之间的"关系"表示为文档本身"内部"的"子属性",对于许多人来说是单数或"数组".
由于MongoDB是"无模式的",因此一般的假设$lookup是你的意思是"很多",因此结果"总是"一个数组.因此,寻找"与SQL相同的结果",然后你需要在$unwind那之后的那个数组$lookup.无论是"一个"还是"多个"都无关紧要,因为它仍然"总是"一个数组:
db.getCollection.('tb1').aggregate([
// Filter conditions from the source collection
{ "$match": { "status": { "$ne": "closed" } }},
// Do the first join
{ "$lookup": {
"from": "tb2",
"localField": "id",
"foreignField": "profileId",
"as": "tb2"
}},
// $unwind the array to denormalize
{ "$unwind": "$tb2" },
// Then match on the condtion for tb2
{ "$match": { "tb2.profile_type": "agent" } },
// join the second additional collection
{ "$lookup": {
"from": "tb3",
"localField": "tb2.id",
"foreignField": "id",
"as": "tb3"
}},
// $unwind again to de-normalize
{ "$unwind": "$tb3" },
// Now filter the condition on tb3
{ "$match": { "tb3.status": 0 } },
// Project only wanted fields. In this case, exclude "tb2"
{ "$project": { "tb2": 0 } }
])
Run Code Online (Sandbox Code Playgroud)
在这里,您需要注意翻译中缺少的其他内容:
聚合管道比SQL更"简洁".事实上,它们最好被视为应用于数据源的"一系列步骤",以便整理和转换数据.最好的模拟是"管道"命令行指令,例如:
ps -ef | grep mongod | grep -v grep | awk '{ print $1 }'
Run Code Online (Sandbox Code Playgroud)
"管道" |可以被视为MongoDB聚合"管道"中的"管道阶段".
因此我们想要$match从"源"集合中过滤掉第一个操作.这通常是一种很好的做法,因为它可以从其他条件中删除任何不符合要求条件的文档.就像在我们的"命令行管道"示例中发生的那样,我们将"输入"然后"管道"转换grep为"删除"或"过滤器".
你在这里做的下一件事是"加入"通过$lookup.结果是来自"from"集合参数的项的"数组" 与提供的字段匹配,以在"as""字段路径"中作为"数组"输出.
这里选择的命名很重要,因为现在源集合中的"文档"会将"连接"中的所有项目视为现在存在于该给定路径中.为了简化这一点,我使用与"路径"的"连接"相同的"集合"名称.
因此,从第一个"连接"开始,输出将是,"tb2"并且将保存该集合的所有结果.还有要注意与下列顺序一个重要的事情$unwind,然后$match,以MongoDB的实际上是如何处理查询.
因为它"看起来像",也有"三"管道阶段,是$lookup再$unwind然后$match.但在"事实"中,MongoDB确实做了其他事情,这{ "explain": true }在.aggregate()命令中添加的输出中得到了证明:
{
"$lookup" : {
"from" : "tb2",
"as" : "tb2",
"localField" : "id",
"foreignField" : "profileId",
"unwinding" : {
"preserveNullAndEmptyArrays" : false
},
"matching" : {
"profile_type" : {
"$eq" : "agent"
}
}
}
},
{
"$lookup" : {
"from" : "tb3",
"as" : "tb3",
"localField" : "tb2.id",
"foreignField" : "id",
"unwinding" : {
"preserveNullAndEmptyArrays" : false
},
"matching" : {
"status" : {
"$eq" : 0.0
}
}
}
},
Run Code Online (Sandbox Code Playgroud)
因此,除了应用"序列"的第一点之外,您需要将$match语句放在需要的位置并执行"最佳",这实际上变得"非常重要",具有"连接"的概念.这里要注意的一点是,我们的序列,$lookup然后$unwind,然后$match,真正得到通过MongoDB的处理为刚$lookup阶段,与其他业务"卷起"到每个一个流水线阶段.
这是对"过滤"所得结果的其他方式的重要区别$lookup.由于在这种情况下,对"加入"的实际"查询"条件$match是在集合上执行的,以便在"之前"将结果返回给父级.
如上所示,这与$unwind(其被翻译成unwinding)的结合是MongoDB实际上如何处理"连接"可能导致在源文档中产生内容数组的可能性,这导致它超过16MB BSON限制.这只会在加入的结果非常大的情况下发生,但同样的优点是"过滤器"实际应用的位置,在"之前"返回结果的目标集合上.
正是这种处理最好地"关联"到与SQL JOIN相同的行为.因此,$lookup除了简单地"外部"键值的"本地"之外,它也是从其他条件应用于JOIN的结果获得结果的最有效方式.
另请注意,其他行为更改来自于本质上执行的LEFT JOIN,$lookup无论"target"集合中是否存在匹配文档,都将始终保留"源"文档.相反,$unwind通过"丢弃"来自"源"的任何结果来增加这一点,"源"没有任何与"目标"匹配的附加条件$match.
事实上,由于
preserveNullAndEmptyArrays: false包括隐含的内容,它们甚至被预先丢弃,并且会丢弃"本地"和"外国"键在两个集合之间甚至不匹配的任何内容.对于这种特定类型的查询,这是一件好事,因为"join"旨在对这些值进行"相等".
如前所述,MongoDB通常将"关系"视为与使用"关系数据库"或RDBMS的方式有很大不同."关系"的一般概念实际上是"嵌入"数据,可以是单个属性,也可以是数组.
你可能实际上需要这样的输出,这也是为什么没有$unwind这里的序列输出$lookup实际上是"数组"的部分原因.然而,$unwind在这种情况下使用实际上是最有效的事情,并且保证"加入"数据实际上不会导致上述BSON限制因"加入"而超过.
如果你真的想要输出数组,那么在这里做的最好的事情就是使用$group管道阶段,并且可能作为多个阶段来"规范化"和"撤消结果"$unwind
{ "$group": {
"_id": "$_id",
"tb1_field": { "$first": "$tb1_field" },
"tb1_another": { "$first": "$tb1_another" },
"tb3": { "$push": "$tb3" }
}}
Run Code Online (Sandbox Code Playgroud)
事实上你在这个案例中列出了你所需要的所有字段,"tb1"通过它们的属性名称$first来保持"第一次"出现(基本上是由结果"tb2"和"tb3"展开的重复),然后$push将"细节"从"tb3""数组"变为"数组"代表与...的关系"tb1".
但是给定的聚合管道的一般形式是如何从原始SQL获得结果的精确表示,原始SQL是"连接"的结果的"非规范化"输出.在此之后,您是否想要再次"标准化"结果.
| 归档时间: |
|
| 查看次数: |
3598 次 |
| 最近记录: |