如何使用条件加入另外两个集合

时间:2017-07-06 12:18:14

标签: 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

实际上我将sql转换为mongo sql,如下所示

我使用的

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 
         } 
}

])

但没有达到预期的结果..

任何帮助将不胜感激..

1 个答案:

答案 0 :(得分:6)

你在这里缺少的是$lookup产生一个&#34;数组&#34;在as中由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 } } ]) 指定的输出字段中。这是MongoDB&#34;关系&#34;的一般概念,即&#34;关系&#34;文档之间表示为&#34;子属性&#34;那是&#34;在&#34;内文档本身,可以是单数或&#34;数组&#34;很多人。

由于MongoDB是&#34;无模式&#34;,$lookup的一般假设是你的意思是&#34;很多&#34;结果因此总是&#34;数组。所以寻找与SQL相同的结果&#34;然后你需要在$unwind之后$lookup那个数组。是否&#34;一个&#34;或者&#34;很多&#34;没有任何意义,因为它仍然总是&#34;一个数组:

ps -ef  | grep mongod | grep -v grep | awk '{ print $1 }'

在这里,您需要注意翻译中缺少的其他内容:

序列是重要的&#34;

聚合管道更具表现力和表达能力。比SQL。事实上,它们最好被视为&#34;一系列步骤&#34; 应用于数据源以整理和转换数据。对此最好的模拟是&#34;管道&#34;命令行指令,例如:

|

&#34;管道&#34; grep可被视为&#34;管道阶段&#34;在MongoDB聚合&#34;管道&#34;。

因此我们想要$match来过滤来自&#34;来源&#34;收集作为我们的第一次操作这通常是一种很好的做法,因为它可以从其他条件中删除任何不符合要求条件的文档。就像在我们的&#34;命令行管道中发生的那样#34;例如,我们采取&#34;输入&#34;然后&#34;管#34;到"from"到&#34;删除&#34;或&#34;过滤&#34;。

路径重要

你在这里做的下一件事是&#34;加入&#34;通过$lookup。结果是&#34;数组&#34;来自"as"集合参数的项目与提供的字段匹配,以输出"tb2"&#34;字段路径&#34;作为&#34;阵列&#34;。

这里选择的命名很重要,因为现在&#34;文件&#34;来自源集合考虑来自&#34; join&#34;的所有项目。现在存在于那条特定的道路上。为了方便起见,我使用相同的&#34;集合&#34;命名为&#34;加入&#34;对于新的&#34;路径&#34;。

从第一次开始#34;加入&#34;输出到$lookup,这将保存该集合的所有结果。关于MongoDB如何实际处理查询,以下$unwind$match序列还有一点需要注意。

某些序列&#34;真的&#34;物质

因为它看起来像&#34;有&#34;三&#34;管道阶段,{ "explain": true }然后$unwind然后$match。但在&#34;事实&#34; MongoDB确实做了其他事情,这在添加到.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 } } } }, 输出中得到了证明:

unwinding

除了&#34;序列&#34;的第一点之外应用于需要将$match语句放在需要的位置并执行“最好的”#34;这实际上变得非常重要&#34;与&#34;加入&#34;的概念。这里要注意的是,$lookup然后$unwind然后$match的序列实际上被MongoDB处理为$lookup阶段,其他操作&# 34;卷起&#34;进入每个管道阶段。

这是对过滤&#34;过滤&#34;的其他方式的重要区别。 $lookup获得的结果。因为在这种情况下,实际的&#34;查询&#34; &#34;加入&#34;的条件从$match开始对集合进行加入&#34;之前&#34;结果将返回给父母。

这与$unwind(被翻译为preserveNullAndEmptyArrays: false)结合如上所示是MongoDB如何实际处理&#34;加入&#34;可能导致在源文档中生成一系列内容,导致其超过16MB BSON限制。这只会在加入的结果非常大的情况下发生,但同样的优势在于&#34;过滤器&#34;实际上是应用于目标集合&#34;之前&#34;结果返回。

这种处理最好&#34;关联&#34;与SQL JOIN的行为相同。因此,它也是获得$lookup结果的最有效方法,除了简单的&#34; local&#34;之外还有其他条件适用于JOIN。 &#34;外国&#34;关键值。

另请注意,其他行为更改来自$lookup执行的LEFT JOIN,其中&#34; source&#34;无论“目标”中是否存在匹配的文档,都将始终保留文档。采集。相反,$unwind通过&#34;丢弃&#34;来自&#34;来源&#34;的任何结果它与#34; target&#34;没有任何匹配。根据{{​​3}}中的其他条件。

  

事实上,由于隐含的 { "$group": { "_id": "$_id", "tb1_field": { "$first": "$tb1_field" }, "tb1_another": { "$first": "$tb1_another" }, "tb3": { "$push": "$tb3" } }} 被包括在内,它们甚至被预先丢弃,并且会丢弃任何地方&#34; local&#34;和&#34;外国&#34;两个集合之间的密钥甚至不匹配。对于这种特殊类型的查询,这是一件好事,因为&#34; join&#34;是为了平等#34;关于这些价值观。

由此得出结论

如前所述,MongoDB通常会处理&#34;关系&#34;与你如何使用&#34;关系数据库&#34;有很大的不同。或者RDBMS。 &#34;关系的一般概念&#34;事实上&#34;嵌入&#34;数据,可以是单个属性,也可以是数组。

您实际上可能需要这样的输出,这也是为什么在没有$match序列的情况下$unwind的输出实际上是&#34;数组&#34;的原因的一部分。但是,在这种情况下使用$lookup实际上是最有效的事情,并且保证&#34;加入&#34;数据实际上不会导致上述BSON限制超过&#34;加入&#34;。

如果你真的想要输出数组,那么在这里做的最好的事情就是使用$unwind流水线阶段,并且可能作为多个阶段,以便&#34;规范化&#34;和&#34;撤消结果&#34; $group

"tb1"

事实上,对于此案例,您需要使用$unwind列出"tb2"所需的所有字段,仅使用$first来保留&#34;第一个&#34;发生(基本上由"tb3""tb3"展开的结果重复)然后$push&#34;详细信息&#34;从"tb1"到#34;阵列&#34;表示与{{1}}的关系。

但是给定的聚合管道的一般形式是如何从原始SQL获得结果的精确表示,其是“非规范化的”#34;输出&#34;加入&#34;。您是否想要&#34;正常化&#34;这取决于你的结果。