Mongodb 多集合 多表 统计实战

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超时。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值