先附上两个很好用也常见的链接:
- MongoDB 教程-RUNOOB
- The MongoDB 3.4 Manual
- 用通俗易懂的大白话讲解Map/Reduce原理(很通俗但是也很浅)。
查询某个月某医生有出诊计划的日期。(在mongo中去重能减轻传输网络负担以及程序的计算量)
{
"_id" : ObjectId("586db879a8dab2b023fe2aa8"),
"name" : "陈三1",
"planId" : ObjectId("586db879a8dab2b023fe2aa7"),
"drId" : ObjectId("586daf5aa8dab2b023fe2a87"),
"timeStart" : ISODate("2017-10-21T16:00:00.000+0000"),
"TimeEnd" : ISODate("2017-11-29T16:00:00.000+0000")
}
{
"_id" : ObjectId("586db879a8dab2b023fe2aaa"),
"name" : "陈三2",
"planId" : ObjectId("586db879a8dab2b023fe2aa9"),
"drId" : ObjectId("586daf5aa8dab2b023fe2a87"),
"timeStart" : ISODate("2017-10-21T16:00:00.000+0000"),
"TimeEnd" : ISODate("2017-11-29T16:00:00.000+0000")
}
{
"_id" : ObjectId("586db879a8dab2b023fe2aac"),
"name" : "陈三3",
"planId" : ObjectId("586db879a8dab2b023fe2aab"),
"drId" : ObjectId("586daf5aa8dab2b023fe2a87"),
"timeStart" : ISODate("2017-10-21T16:00:00.000+0000"),
"TimeEnd" : ISODate("2017-11-29T16:00:00.000+0000")
}
{
"_id" : ObjectId("586db879a8dab2b023fe2aae"),
"name" : "陈三4",
"planId" : ObjectId("586db879a8dab2b023fe2aad"),
"drId" : ObjectId("586daf5aa8dab2b023fe2a87"),
"timeStart" : ISODate("2017-10-21T16:00:00.000+0000"),
"TimeEnd" : ISODate("2017-11-29T16:00:00.000+0000")
}
for (var i = 1; i < 5; i++) db.test.insert({
"name":"陈三"+i,
"planId":ObjectId(),
"drId":ObjectId("586daf5aa8dab2b023fe2a87"),
"timeStart":ISODate("2017-10-21T16:00:00.000+0000"),
"TimeEnd":ISODate("2017-11-29T16:00:00.000+0000")
});
//将 201710 字符串 转换为 time类型 的 startYM(2017-10-01 00:00:00 +0800 CST),endYM(2017-10-31 00:00:00 +0800 CST)
startYM, err := time.ParseInLocation("200601", "201710", time.Local)//time.Local是指你的字符串201710是你本地时区的201710,注意看转换结果:+0800 CST
if err != nil {
return nil, err
}//错误处理的必要性见上一篇文
endYM := startYM.AddDate(0, 1, -1)//+一月-一天得出最后一天
最后的限制条件的数据字段应是:
endYM:2017-10-31 00:00:00 +0800 CST
注:由于使用的是mgo的ORM,它会将time类型的+0800转换为+0000再去查询。 mgo的内容见mgo使用指南,mgo初探笔记。
db.test.find({
"drId":ObjectId("586daf5aa8dab2b023fe2a87"),
"timeStart":{"$lte":ISODate("2017-10-31T16:00:00.000+0000")},
"TimeEnd":{"$gte":ISODate("2017-10-01T16:00:00.000+0000")}
})
不同于其他的关系型数据库,Mongo中的distinct只能用于一个字段,也只能返回这个字段,见此篇文章的例子:MongoDB如何去除组合重复项。
db.test.aggregate([
{$match:
{"drId":ObjectId("586daf5aa8dab2b023fe2a87"),
"timeStart":{"$lte":ISODate("2017-10-31T16:00:00.000+0000")},
"timeEnd":{"$gte":ISODate("2017-10-01T16:00:00.000+0000")}
}
},
{$group:{_id:"$planId",
timeStart:{$first:"$timeStart"},
timeEnd:{$first:"$timeEnd"},
}},//需要注意,group是分组,如果要取这个分组每个集合的timeStart就用push,一条就用first,而不是意义不明的timeStart:"$timeStart",会报错
])
最后的结果是:
{
"_id" : ObjectId("586db879a8dab2b023fe2aa7"),
"timeStart" : ISODate("2017-10-21T16:00:00.000+0000"),
"timeEnd" : ISODate("2017-11-29T16:00:00.000+0000")
}
一种错误写法:
db.test.mapReduce(
function() {
if (this.planId in planIds) {
return;
}else{
planIds[this.planId] = 1;
emit(this.planId,{"timeStart":this.timeStart,"timeEnd":this.timeEnd});
}
},
function() {},
{
query: {"drId":ObjectId("586daf5aa8dab2b023fe2a87"),
"timeStart":{"$lte":ISODate("2017-10-31T16:00:00.000+0000")},
"timeEnd":{"$gte":ISODate("2017-10-01T16:00:00.000+0000")}
},
scope: {planIds:{}},//全局变量
out: "result"
}
).find()
结果:
{
"_id" : ObjectId("586db879a8dab2b023fe2aa7"),
"value" : {
"timeStart" : ISODate("2017-10-21T16:00:00.000+0000"),
"timeEnd" : ISODate("2017-11-29T16:00:00.000+0000")
}
}
遇到的坑:
var a = ['A','B','C']; if ('A' in a)
是永假,因为’A’是和脚标(属性)进行的比对。如果是map,if in当然就是个map的key比较。所以这里用了map。具体见for of的讲解,有提到这个问题,,但是for of用于遍历。{
"result" : "result",
"timeMillis" : NumberInt(521),
"counts" : {
"input" : NumberInt(4),
"emit" : NumberInt(1), //传递给reduce的数据本身只有一条,不存在被结果去重,具体emit的形式及错误原因见下文。
"reduce" : NumberInt(0),
"output" : NumberInt(1)
},
"ok" : NumberInt(1)
}
另外的错误写法:
db.test.mapReduce(
function() {
emit(this.planId,{"timeStart":this.timeStart,"timeEnd":this.timeEnd});
},
function() {},
{
query: {"drId":ObjectId("586daf5aa8dab2b023fe2a87"),
"timeStart":{"$lte":ISODate("2017-10-31T16:00:00.000+0000")},
"timeEnd":{"$gte":ISODate("2017-10-01T16:00:00.000+0000")}
},
out: "result"
}
).find()
结果是错误的:
{
"_id" : ObjectId("586db879a8dab2b023fe2aa7"),
"value" : null
}
[x1, x2, x3, x4].reduce(f) = f(f(f(x1, x2), x3), x4)
中的[x1,x2,x3,x4]
,并且顺序可能是f(f(x1,x2),f(x3,x4))
,具体见《MongoDB权威指南》85页。正确写法:
db.test.mapReduce(
function() {
emit(this.planId,{"timeStart":this.timeStart,"timeEnd":this.timeEnd});
},
function(key,values) {
return {"timeStart":values[0].timeStart,"timeEnd":values[0].timeEnd}
},
{
query: {"drId":ObjectId("586daf5aa8dab2b023fe2a87"),
"timeStart":{"$lte":ISODate("2017-10-31T16:00:00.000+0000")},
"timeEnd":{"$gte":ISODate("2017-10-01T16:00:00.000+0000")}
},
out: "result"
}
).find()
tempDays := make(map[string]bool)
var timeStart, timeEnd time.Time
for _, v := range snPlan {
if v.Value.TimeEnd.Before(endYM) {
timeEnd = v.Value.TimeEnd
} else {
timeEnd = endYM
}//如果计划时间过长,要根据本月最后一天截取
if v.Value.TimeStart.After(startYM) {
timeStart = v.Value.TimeStart
} else {
timeStart = startYM
}
for i := timeStart; i.Before(timeEnd) || i.Equal(timeEnd); i = i.AddDate(0, 0, 1) {
tempDays[i.Format(constants.DateFormatYYYYMMdd)] = true
}
}
这里能看出如果不去重对程序的影响很大,因为有双重循环+多个if语句。