0%

MongoDB通过$lookup实现多表连接查询

MongoDB通过$lookup实现多表连接查询

聚合查询

MongoDB 中通过 aggregate([{...}]) 方法来完成聚合查询。

比较常见的聚合查询表达式有:$sum、$max、$push、$first、$last 等。

表达式只是执行具体的操作,聚合查询的核心是管道的概念,有点类似 Linux 系统的管道,用于将当前输出的结果作为下一个命令的参数。

$lookup 便是聚合框架中常见一种操作。

此外还有,$project、$match、$unwind、$group、$sort等,本文中会提供一个完整的查询实例,其中会使用到这些内容。

$lookup

$lookup 是 MongoDB 3.2 版本出现的新功能。

其主要作用是对同一数据库中未分片集合执行左外部联接,从“联接”的集合中过滤文档进行处理。

对每个输入的文档,$lookup 阶段会添加一个新的数组字段,其中元素是“联接”集合中匹配出来的文档。

基础语法

注意:在 MongoDB 3.6 版本中,$lookup 得以被增强,这里先列取基本语法,本文案例也是依照基本语法编写。

{
    $lookip:
    {
        from: "连接的附表",
        localField: "主表外联的字段",
        foreignField: "附表关联的字段",
        as: "加入字段"
    }
}

增强语法

MongoDB 3.6 版本的增强语法。

{
    $lookip:
    {
        from: "主表",
        let: {var1: exp1,var2:exp2},    // 可选参数,指定管道字段的变量,作为入参放到接下来的管道中,可以实现多条件级联
        pipeline: [{...}],    // 管道,要连接的集合管道,全部返回指定为空管道[]
        as: "加入字段"
    }
}

基本语法实例

如果您使用过传统的 SQL 数据库,将其理解为 left join 就会很快明白其中的意思。

现有 USER 和 USER_EX 两个集合,现在希望通过 USER 集合的 name 字段关联查询出 USER_EX 中的数据。

// USER
{ 
    "_id" : ObjectId("60ebf6e17b2db72987c04828"), 
    "name" : "mebugs", 
    "where" : "wuxi"
}
// USER_EX
{ 
    "_id" : ObjectId("60ebf7067b2db72987c0482d"), 
    "name" : "mebugs", 
    "size" : "17"
}

查询语句:

db.getCollection("USER").aggregate([
    {
        $lookup: {
            from: "USER_EX",
            localField: "name",
            foreignField: "name",
            as: "ex"
        }
    }
])

查询结果:

// 关联的数据将已数组的形式存在于指定的 as 字段
{ 
    "_id" : ObjectId("60ebf6e17b2db72987c04828"), 
    "name" : "mebugs", 
    "where" : "wuxi", 
    "ex" : [
        {
            "_id" : ObjectId("60ebf7067b2db72987c0482d"), 
            "name" : "mebugs", 
            "size" : "17"
        }
    ] 
}

增强示例

$unwind 组合

$unwind 组合,可以将数组的内容拆分成记录。

有的时候需要统计总量,如果关联的内容仅仅存在在数组中会使得程序处理变得麻烦,因此通过 $unwind 组合可以实现对关联的数组解开。

db.getCollection("USER").aggregate([
    {
        $lookup: {
            from: "USER_EX",
            localField: "name",
            foreignField: "name",
            as: "ex"
        }
    },
    {
        $unwind: {
            path: "$ex",
            preserveNullAndEmptyArrays: true // 空数组记录保留
        }
    }
])

/static/upload/post/1648122617742.png

$project格式化输出

数组变成对象之后,程序相对好处理多了,但是还不行,我不希望为 ex 单独定义一个子对象,而是希望直接取出 ex 中的字段。

这时候可以通过 $project 对输入的文档进行文档结构修改,可以自由定义字段映射/增加/修改/组合/拼接/格式化等操作。

关于 $project 的更多用法,后面单独去详细描述。

db.getCollection("USER").aggregate([
    {
        $lookup: {
            from: "USER_EX",
            localField: "name",
            foreignField: "name",
            as: "ex"
        }
    },
    {
        $unwind: {
            path: "$ex",
            preserveNullAndEmptyArrays: true // 空数组记录保留
        }
    },
    {
        $project: {
            _id: 0, // 或略输出这个字段
            name: "$name", // 直接映射
            city: "$where", // 修改字段名
            size: "$ex.size", // 映射子字段
            unit: { $concat: ["$ex.size", "cm"] } // 拼接输出
        }
    }
])

/static/upload/post/1648122719325.png

这样的输出结果是不是立马清爽了很多?程序应用处理起来更加方便。

增强语法实例

MongoDB 3.6 版本的增强语法,仅仅看描述会很困惑,我们直接编写一个实例,会更加容易理解。

增强语法最大的特点是提供了管道能力,可以实现更为复杂的条件过滤和自定义文档的返回。

db.getCollection("USER").aggregate([
    {
        $lookup: {
            from: "USER_EX",
            let: { queryName: "$name", queryCity: "$where" }, // 定义两个变量,分别取主表 name 和 where 字段
            pipeline: [
                {
                    $match: {
                        $expr: { // 读取管道变量
                            $and: [
                                {
                                    $eq: ["$name", "$$queryName"], // 附表的 name 字段等于 queryName 变量
                                    $eq: ["$where", "$$queryCity"] // 相当于匹配附表 name 和 where 与主表相同的内容
                                }
                            ]

                        }
                    }
                },
                {
                    $project: { // 定义 Join 进来的输出内容
                        _id: 0,
                        size: { $concat: ["$size", "cm"] }
                    }
                }
            ],
            as: "ex"
        }
    }
])

查询结果:

{ 
    "_id" : ObjectId("60ebf6e17b2db72987c04828"), 
    "name" : "mebugs", 
    "where" : "wuxi", 
    "ex" : [
        {
            "size" : "17cm"
        }, 
        {
            "size" : "19cm"
        }
    ]
}

https://www.mebugs.com/post/mgolookup.html

原文博主: 热衷开源的宝藏Boy
原文链接: http://www.fangzengye.com/article/a6d47f1a83fdf09c8c3ac54af5c861cc
版权声明: 自由转载-非商用-禁止演绎-保持署名| CC BY-NC-ND 3.0

微信扫码加入我的星球联系我

评论区