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逾時。