Mongodb 多集合 多表 统计实战
完成以下报表:
涉及到的表person、appointmentRecord
人数统计
在person表使用聚合函数
db.Person.aggregate()就行
[
{
$match: {
'dateOfBirth': {
$gt: '1986-12-02',
$lt: '1996-12-02',
}
}
}, {
$group: {
_id: '$SEX',
count: {
$sum: 1
},
}
},
]
统计常挂科室
科室和用户通过userid关联
如何管理两个集合呢
- Person 有1058万条数据 分片sharded = true
- AppointmentRecord 有300来万数据 没分片
逻辑很简单
* 1.根据用户筛选挂号记录
* 2.在挂号记录 中按照医院、科室分组计算数量
* 3.选择数量前10的分组
错误方案一 用聚合函数$lookup
db.AppointmentRecord.aggregate([
{//加limit是为了用小数据测试
$limit: 10000
},{
$lookup: {
from: "Person",
localField: "userid",
foreignField: "userid",
as: "ar_docs",
}
},
// {'arr':{'$elemMatch':{'addr':'ddr2','dept':'oo'}}}
{
$match: {
'ar_docs': {
'$elemMatch': {
'channelCode': 'WECHAT'
}
}
}, {
$group : {
_id: {
hospitalCode: '$hospitalCode',
hospitalName: '$hospitalName',
LOC: '$cTLOCDesc',
},
count: {
$sum: 1
},
}
}, {
$sort : {
count: -1
}
}, {
$limit : 10
},
])
报错 因为lookup不支持分片的集合!
错误方案二 用mapReduce
mapReduce不直接支持多个集合的操作除非你用dbref。
我们的表没有dbref
那试试外面的变量它能接受不
var userArr2 = [];
db.PAPerson.find({'iDCard':{$ne:null},$where: function() {
var birth = this.iDCard.substr(6,8);
if (birth > 19861202 && birth < 19961202) {
return true;
}
}},{
uRHospitalID:1,
userid:1,
_id:0
}).limit(5000).forEach((it)=> {
userArr2.push(it.userid +'-' + it.uRHospitalID);
});
print(userArr2.length)
var mapfun = function(){
if (userArr2.indexOf(this.userid +'-' + this.uRHospitalID) !== -1) {
emit(this.hospitalName + "-" + this.cTLOCDesc,1);
}
};
db.AppointmentRecord.mapReduce(
mapfun, //mapFunction
function(key, values){
return Array.sum(values);
},//reduceFunction
{
limit: 10000,
query:{ 'channelCode': 'WECHAT' },
out: "output_user2_loc"
})
结果还是失败 map方法不能接收到外面的变量,在里面写查询的话db这关键字都找不到!
错误方案三 建立新表用聚合
lookup不支持分片的 那么我就建立一个筛选person表插入一个新的集合好了:
db.Person.find({'iDCard':{$ne:null},$where: function() {
var birth = this.iDCard.substr(6,8);
if (birth > 19661202 && birth < 19761202) {
return true;
}
}},{
uRHospitalID:1,
userid:1
}).limit(10).batchSize(80000).forEach(function(it) {
db.xtemp_user4.insert(it);//40岁+就是user4
});
新集合很快建立好了
那么来聚合吧!
db.AppointmentRecord.aggregate([
{$limit: 10000},
{
$match: {
'channelCode': 'WECHAT',
}
},
{
$lookup: {
from: "xtemp_user4",
localField: "userid",
foreignField: "userid",
as: "ar_docs",
}
},
{
$match: {
'ar_docs': {$elemMatch:{'userid':{$ne:null}}},
}
},
{
$group: {
_id: {
hospitalCode: '$hospitalCode',
hospitalName: '$hospitalName',
LOC: '$cTLOCDesc',
},
count: {
$sum: 1
},
}
}, {
$sort: {
count: -1
}
},
{
$limit: 10
},
])
小数据测试也通过了 。
然而执行了一晚上也没出结果!
方案四
用in
var parr4 = db.xtemp_user4.distinct("userid");
db.AppointmentRecord.aggregate([
//{$limit: 1000},
{
$match:
{'channelCode':'WECHAT','userid':{$in: parr4}}
},
{
$group: {
_id: {
LOC: '$cTLOCDesc',
hospitalCode: '$hospitalCode',
hospitalName: '$hospitalName',
},
count: {
$sum: 1
},
}
}, {
$sort: {
count: -1
}
},
{
$limit: 10
},
])
只用了50s左右结果就出来了!!
因为mongodb接触得不多搞了几天才搞好。查了很多博客很多官网资料,mongodb版本有些函数不支持的坑就不说了。看见例子不是很多就写了这篇博客希望大家少踩坑。
有更好的方案欢迎反馈!
对了,以上代码运行在mongodb 3.2.9。
加batchSize是因为不加会报cursor超时。