MongoDB查询实现 笛卡尔积,Union All 和Union 功能

转载自   MongoDB查询实现 笛卡尔积,Union All 和Union 功能javascript

此篇文章及之后的文章大部分都是从聚合管道(aggregation pipeline)的一些语法为基础讲解的,若是不理解聚合管道的话,能够先学习一下会比较容易理解.
能够参考 mongoDB Documentation 的 Pipeline Aggregaion Stages.html

何为Union All 和 Union

Union All指令的目的是将两个结果放在一块儿而且无论是否有重复,Union指令则把结果合而且去掉重复结果.java


SQL中的实现Union All

在sql中,咱们能够很简单的就实现 Union All 的效果.好比在sql中,咱们的数据是sql

tableA id type tableB id type
  1 OPEN   1 OPEN
  2 CLOSE   2 ISSUE
  3 REJECT   3 VOID
  4 REQUEST      

咱们在sql中 Union All 的写法是:mongodb

select a.type as type from tableA a Union All select b.type from tableB b;

获得的结果是:数据库

type
OPEN
CLOSE
REJECT
REQUEST
OPEN
ISSUE
VOID

MongoDB 的语法实现

在MongoDB中,对于给咱们表联结相关使用的函数,有aggregate中的$lookup函数,
参照咱们的官方例子,咱们很容易就能理解$lookup函数的做用,至关于咱们在sql里面的表联结,如:数组

select a.*,b.* from tableA a,tableB as b where a._id = b.tableAId;

$lookup函数中,有如下参数为必填:函数

  • from: <collection to join>, //等价于上面的 tableB学习

  • localField: <field from the input documents>,  //  等价于上面的 a._id测试

  • foreignField: <field from the documents of the "from" collection>, //等价于上面的 b.tableAId

  • as: <output array field> // 等价于上面as后面的 b

那么如何使用联结做用的函数来实现Union All 的做用呢?其实很简单,在上面的4个参数里面,localField 和 foreignField 是必填的,可是在mongo里面,咱们能够填写一个无效的field(不存在表里面的field)来实现咱们的效果

咱们的测试数据以下:

tableA
{"_id":"1","type":"OPEN"}
{"_id":"2","type":"CLOSE"}
{"_id":"3","type":"REJECT"}
{"_id":"4","type":"REQUECT"}
tableB
{"_id":"1","type":"OPEN"}
{"_id":"2","type":"ISSUE"}
{"_id":"3","type":"VOID"}

1. 实现笛卡尔积

首先咱们写的查询语句以下,将localField 和 foreignField随便填写一个String语句,只要是不在表里面存在的field便可

db.tableA.aggregate([
    {
        $lookup:{
            from:"tableB",
            localField:"invalidField",
            foreignField:"testField",
            as:"tableB"
        }
    }
])

查询结果:

_id type tableB
1 OPEN [3 elements]
1 CLOSE [3 elements]
1 REJECT [3 elements]
1 REQUEST [3 elements]

在MongoDB里面,field的判断是空等于空的,value的判断空是不等于空的. 两个等于空的field去比较,至关于 在sql 里面 where 1=1 的写法.

能够看到,咱们tableA的每一条记录都匹配到了tableB的3个元素(全部数据),此时只要咱们将tableB的记录 $unwind出来,就实现了笛卡尔积的效果了.

$unwind语法以下:

db.tableA.aggregate([
    {
        $lookup:{
            from:"tableB",
            localField:"invalidField",
            foreignField:"testField",
            as:"tableB"
        }
    },
    {
        $unwind:{
            path:"$tableB"
        }
    },
    {
        $project:{
            _id:1,
            type:1,
            tableBId:"$tableB._id",
            tableBType:"$tableB.type"
            
        }
    }
])

等价于sql语句:

select a.id,a.type,b.id as tableId,b.type as tableBType from tableA a,tableB bwhere 1=1;

查询结果能够自行测试


2. 实现Union

在MongoDB里面,有一个$setUnion的函数,$setUnion函数被union的参数必需是数组,
在咱们tableA lookup tableB以后返回来的结果,tableB已是一个一个数组了,可是咱们的tableA的type是一个字符串值,因此咱们须要先将tableA的内容先转为数组,才能进行union.

All operands of $setUnion must be arrays.

将tableA里面的全部记录转为一个数组须要用到$gourp函数里面的$push功能.
查询语法以下:

db.tableA.aggregate([
    {
        $group:{
            _id:"any",
            tableA:{
                $push: "$$ROOT"
            }
        }
    }
])

查询结果:

_id tableA
any [4 elements]

由于$gourp函数里面的_id属性是必选的,可是这里咱们不用到,因此填任意字符串或者null均可以.使用$push以后,tableA的全部记录,都被push到了咱们命名为tableA的数组里面,
此时咱们在$lookup tableB看看结果如何.
查询语法以下:

db.tableA.aggregate([
    {
        $group:{
            _id:"any",
            typeArray:{
                $push: "$$ROOT"
            }
        }
    },
    {
        $lookup:{
            from:"tableB",
            localField:"invalidField",
            foreignField:"testField",
            as:"tableB"
        }
    } 
])

查询结果:

_id tableA tableB
any [4 elements] [3 elements]

能够看到,咱们的tableA,和tableB的结果都变成了数组,此时咱们已经可使用$setUnion函数去实现咱们的Union效果了

db.tableA.aggregate([
    {
        $group:{
            _id:"any",
            tableA:{
                $push: "$$ROOT"
            }
        }
    },
    {
        $lookup:{
            from:"tableB",
            localField:"invalidField",
            foreignField:"testField",
            as:"tableB"
        }
    },
    {
        $project:{
            _id:0,
            allValue:{
                $setUnion:["$tableA","$tableB"]
            }
        }
    }
])

查询结果:

allValue
[6 elements]

此处只有6个元素在数组里面,已经把重复的去掉了,能够说咱们的Union效果已经实现,以后在把结果用$unwind展开便可.
查询语法以下:

db.tableA.aggregate([
    {
        $group:{
            _id:"any",
            tableA:{
                $push: "$$ROOT"
            }
        }
    },
    {
        $lookup:{
            from:"tableB",
            localField:"invalidField",
            foreignField:"testField",
            as:"tableB"
        }
    },
    {
        $project:{
            _id:0,
            allValue:{
                $setUnion:["$tableA","$tableB"]
            }
        }
    },
    {
        $unwind:{
            path:"$allValue"
        }
    },
    {
        $project:{
            _id:0,
            type:"$allValue.type"
            
        }
    },
    
])

3. 实现Union All

实现Union All 的原理与union 的相似,咱们能够在把tableA push 成一个数组前,新增一个field,或者只push type,那么在union的时候,由于table A 和 table B field 数量不一致,那么永远不会合并成一行,由于它们任意一行都是不同的.
查询语法以下:

db.tableA.aggregate([
    {
        $group:{
            _id:"any",
            tableA:{
                $push: {type:"$type"}
            }
        }
    },
    {
        $lookup:{
            from:"tableB",
            localField:"invalidField",
            foreignField:"testField",
            as:"tableB"
        }
    },
    {
        $project:{
            _id:0,
            allValue:{
                $setUnion:["$tableA","$tableB"]
            }
        }
    },
    {$unwind:"$allValue"},
    {
        $project:{
            _id:0,
            type:"$allValue.type"
            
        }
    }
])

Union All 的结果,能够经过group的方式去重来变成 Union 的效果. $group函数在此就再也不细讲,能够参考官网.


总结

在咱们的MongoDB官方文档里面介绍了一些函数的基本语法,可是功能方面比较Oracle等传统关系型数据库来讲仍是比较少的,由于一些如本文讲的Union等这些功能,只能根据现有的功能去实现. 而在官网和网上现有的资料里面,是没有实现Union这些功能的介绍的,所以写下了这篇文档.