为了账号安全,请及时绑定邮箱和手机立即绑定

使用$ lookup运算符的多个联接条件

/ 猿问

使用$ lookup运算符的多个联接条件

HUH函数 2019-09-24 16:50:53

这是我的收藏:


collection1:


{

    user1: 1,

    user2: 2,

    percent: 0.56

}

collection2:


{

    user1: 1,

    user2: 2,

    percent: 0.3

}

我想通过“ user1”和“ user2”加入两个集合。


结果如下:


{

    user1: 1,

    user2: 2,

    percent1: 0.56,

    percent2: 0.3

}

如何编写管道?


查看完整描述

3 回答

?
慕的地8271018

我们可以使用$lookup3.6版及更高版本中的聚合管道运算符执行多个加入条件。


我们需要使用let可选字段将字段的值分配给变量;然后,您可以在pipeline字段阶段访问那些变量,在其中指定要在集合上运行的管道。


请注意,在此$match阶段中,我们使用$expr评估查询运算符来比较字段的值。


流水线的最后一个阶段是$replaceRoot聚合流水线阶段,在此阶段,我们仅使用运算符将$lookup结果与$$ROOT文档的一部分合并$mergeObjects。


db.collection2.aggregate([

       {

          $lookup: {

             from: "collection1",

             let: {

                firstUser: "$user1",

                secondUser: "$user2"

             },

             pipeline: [

                {

                   $match: {

                      $expr: {

                         $and: [

                            {

                               $eq: [

                                  "$user1",

                                  "$$firstUser"

                               ]

                            },

                            {

                               $eq: [

                                  "$user2",

                                  "$$secondUser"

                               ]

                            }

                         ]

                      }

                   }

                }

             ],

             as: "result"

          }

       },

       {

          $replaceRoot: {

             newRoot: {

                $mergeObjects:[

                   {

                      $arrayElemAt: [

                         "$result",

                         0

                      ]

                   },

                   {

                      percent1: "$$ROOT.percent1"

                   }

                ]

             }

          }

       }

    ]

)

该管道产生如下所示的内容:


{

    "_id" : ObjectId("59e1ad7d36f42d8960c06022"),

    "user1" : 1,

    "user2" : 2,

    "percent" : 0.3,

    "percent1" : 0.56

}

如果您使用的不是3.6+版本,则可以先使用您的一个字段(例如说“ user1”)加入,然后使用$unwind聚合管道运算符从此处展开匹配文档的数组。管道的下一个阶段是$redact使用$$KEEP和$$PRUNE变量过滤掉那些来自“ joined”集合和输入文档中“ user2”值不相等的文档的阶段。然后,您可以$project分阶段调整文档的形状。


db.collection1.aggregate([

    { "$lookup": { 

        "from": "collection2", 

        "localField": "user1", 

        "foreignField": "user1", 

        "as": "collection2_doc"

    }}, 

    { "$unwind": "$collection2_doc" },

    { "$redact": { 

        "$cond": [

            { "$eq": [ "$user2", "$collection2_doc.user2" ] }, 

            "$$KEEP", 

            "$$PRUNE"

        ]

    }}, 

    { "$project": { 

        "user1": 1, 

        "user2": 1, 

        "percent1": "$percent", 

        "percent2": "$collection2_doc.percent"

    }}

])

产生:


{

    "_id" : ObjectId("572daa87cc52a841bb292beb"),

    "user1" : 1,

    "user2" : 2,

    "percent1" : 0.56,

    "percent2" : 0.3

}

如果集合中的文档具有相同的结构,并且发现自己经常执行此操作,则应考虑将两个集合合并为一个或将这些集合中的文档插入到新集合中。


db.collection3.insertMany(

    db.collection1.find({}, {"_id": 0})

    .toArray()

    .concat(db.collection2.find({}, {"_id": 0}).toArray())

)

然后$group按“ user1”和“ user2”的文件


db.collection3.aggregate([

    { "$group": {

        "_id": { "user1": "$user1", "user2": "$user2" }, 

        "percent": { "$push": "$percent" }

    }}

])

产生:


{ "_id" : { "user1" : 1, "user2" : 2 }, "percent" : [ 0.56, 0.3 ] }


查看完整回答
反对 回复 2019-09-24
?
红颜莎娜

如果您想对数据建模,并且在决定这样做之前来这里检查mongodb是否可以对多个字段执行联接,请继续阅读。


尽管MongoDB可以执行联接,但是您也可以根据应用程序访问模式自由地对数据建模。如果数据与问题中显示的一样简单,我们可以简单地维护一个如下所示的单个集合:


{

    user1: 1,

    user2: 2,

    percent1: 0.56,

    percent2: 0.3

}

现在,您可以对本集合执行所有操作,而这些操作本来可以加入的。我们为什么要避免加入?由于分片集合(docs)不支持它们,这将阻止您在需要时进行横向扩展。规范化数据(具有单独的表/集合)在SQL中效果很好,但是在Mongo中,避免联接可以带来很多好处,而在大多数情况下不会产生任何后果。仅当您别无选择时,才在MongoDB中使用规范化。从文档:


通常,使用规范化的数据模型:


嵌入时将导致数据重复,但无法提供足够的读取性能优势,无法胜过重复的影响。

代表更复杂的多对多关系。

为大型分层数据集建模。

检查此处以了解有关嵌入的更多信息以及为什么要选择它而不是标准化。


查看完整回答
反对 回复 2019-09-24
?
呼如林

您可以使用$ match和$ project管道进行多个字段匹配。(请参阅此处的详细答案-mongoDB在多个字段上联接)


db.collection1.aggregate([

                    {"$lookup": {

                    "from": "collection2",

                    "localField": "user1",

                    "foreignField": "user1",

                    "as": "c2"

                    }},

                    {"$unwind": "$c2"},


                    {"$project": {

                    "user2Eq": {"$eq": ["$user2", "$c2.user2"]},

                    "user1": 1, "user2": 1, 

                    "percent1": "$percent", "percent2": "$c2.percent"

                    }},


                    {"$match": {

                    {"user2Eq": {"$eq": True}}

                    }},


                    {"$project": {

                    "user2Eq": 0

                    }}


                    ])


查看完整回答
反对 回复 2019-09-24

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信