mongo 这种 NoSQL 要进行多表查询一种方法是在 client 端进行多次查询操作另一种是利用 aggregation pipeline
这种聚合流操作,前一种方法非常不推荐使用,特别涉及到分页时这几乎是个噩梦,后面一条命令组合直接在服务端运作非常推荐。说一下第二种方法的要点。
数据涉及到多表的逻辑操作,mongo 提供了 lookup
, 使用方法直接在官网上查间即可,现有三个表 Slot,SlotOrder, 和 Order,SlotOrder 保存了另外两张的关联关系,当然并不是所 Slot 都会关联到 Order。
结构如下
Slot{
_id,
xxx,
}
SlotOrder{
_id,
SlotID,
OrderID,
xxx,
}
Order{
_id,
Begin,
End,
xxx,
}
现在要根据 Order 中的 Begin 和 End 是否在某一个时间范围来判断 Slot 的使用状态,如 now
db.Slot.aggregate([
{$lookup:
{
from: "SlotOrder",
localField: "_id",
foreignField: "SlotId",
as: "SO"
}
},
{$unwind:"$SO"},
{$lookup:
{
from: "Order",
localField: "SO.OrderId",
foreignField: "_id",
as: "Order"
}
},
{$unwind:"$Order"},
{$project:{_id:1,Order:1,OrderStat:{$cond:[{$lt:["$Order.Begin",now]},
scheduled,
$cond:[{$eq:["$Order.End",0]},
busy,
$cond:[{$lte:["$Order.End",now]},busy,idle]]}}},
{$match:{"OrderStat":{$eq:statYouNeed}}},
{$group:
{
"_id":"$_id",
"Order":{"$AddToSet":"$Order"},
"OrderStat":{"$AddToSet":"$OrderStat"},
}
},
])
这里产生的 SO(SlotOrder[]) 是一个数组,要获得每个 Order.ID 可以用 $unwind
把数据进行拆分,拆分时其它 Field 都会相应复制原来的数据,如
{1,[a,b]}
变成
{1,a}
{1,b}
同样的在进行第二次 lookup 之后要对每一个 Order 数据进行分析也要对 lookup 出来的数组进行拆分。
得到三个表中所需要的数据之后,就可以进行数据的分析和统计了,这里使用 $project
来处理原数据产生需要的数据模型,多次使用了 $cond 这个条件语句,有一点函数式编程的味道,想想
$cond:[logic,$cond,$cond]
最后使用 $match
将处理好的数据再进行过滤,
为了让之前 $unwind 后的数据合并起来,可使用 $group() 使用_id 来作为主键合并。在这里 OrderStat 就是 Slot 状态相关的数组展现。
相应的 go 代码实现点这里
如果分页的话可结合 $skip, $limit 进行操作,相关操作可参考之前我写的这篇文章
—– update —–
测试了不使用 lookup(即第一种方法)和使用 lookup(即第二种) 方法的效率,发现 lookup 方法耗时要比手动合并数据要长,而且随着数据量的增加差距更加明显,下图是测试记录,这里是测试代码