而只要只是单个分组,就超级轻便完结了,也看一下代码

Map-Reduce

对此某些头昏眼花的查询能够转移为javascript的处理形式,那正是map
reduce,不过map
reduce的效用超低,所以能够利用聚合框架完结的就绝不采纳Map-Reduce,上边看看Map-Reduce如何实现二个行使。

率先要定义map函数,map指的是遍历文书档案中的全数键值对,来做拍卖,我们明天对user这一个collection来做map

map = function() {
    var did = this.dep.id+"-"+this.dep.name;
    var ageGt = 0;
    if(this.age>40) ageGt=1;
    emit(did,{ageGt:ageGt});
}

以上操作定义了二个map,该操作会去遍历整个collection的文档,首先鲜明了key是部门id+部门名称,接着实现下边大器晚成种存款和储蓄尽管岁数大于40囤积ageGt为1不然为0,emit表示重临值,那个时候的map文书档案中有大器晚成组数据,存款和储蓄了部门id+部门名称叫key何况存款和储蓄了年纪的文书档案(年龄的文书档案中有个别是0,一些是1),之后把这么些值交给reduce来回到一个结果集

reduce=function(key,values) {
    var result = {nums:0};
    values.forEach(function(v){
        result.nums+=v.ageGt;
    })
    return result;
}

reduce中就能够基于key的值把再次回到的数码定义成为三个数组,这些数组正是reduce的第二个参数values,那个时候定义了多个结实为nums,何况遍历整个数组,将nums的值扩展,那意味着假若发觉ageGt是1意味着有二个39周岁以上的人,那样充裕之后,就能够依据气象总结出每种机构大于41虚岁的人手。

概念好map和reduce之后,下一步就供给经过collection来调用

db.user.mapReduce(map,reduce,{query:{},out:"test"})

这时候应用user调用刚刚概念的map和reduce,第多少个参数中的query是查询条件,能够过滤风度翩翩组数据,第二个参数out表示要出口的文书档案,输出之后大家能够透过test文书档案查询

/* 1 */
{
    "_id" : "5a2947c367732445e8adfff0-教务处",
    "value" : {
        "nums" : 0.0
    }
}

/* 2 */
{
    "_id" : "5a2947c367732445e8adfff1-财务处",
    "value" : {
        "nums" : 1.0
    }
}

/* 3 */
{
    "_id" : "5a2947c367732445e8adfff2-计算机学院",
    "value" : {
        "nums" : 0.0
    }
}

本条map-reduce实现了部门大于四十二虚岁人的计算操作,Map-Reduce基本能够兑现您想要的富有机能,可是由于功用低下,依旧建议大家只要能采取聚合框架就不要用MapReduce。

什么样是jsr303?JSEscort 303 – Bean Validation 是叁个数量注明的正式,二〇〇八 年
11 月明确最终方案.Hibernate Validator 是 Bean Validation 的特级施行

db.runCommand({  mapreduce: "M_User_Footprints",  map: function Map() {    emit(    { "UserInfoID": this.UserInfoID, "ObjID": this.PlayVideo.ObjID }    ,     { count: 1 }    );  },  reduce: function Reduce(key, values) {    total = 0;//定义一个变量total , values是一个数组    for (var i in values) {      total += values[i].count    }    return { "count": total };  },  finalize: function Finalize(key, reduced) {    return reduced;  },  out: { inline: 1 }});

unwind和out管道

第三个查询需假如得到客户对私人新闻的计算,总结每一种顾客该查询的私人音讯数据,看过的数额和还未看过的多少,那使用project就能够解决,先看看结果

db.user.aggregate({$project:
    {
        username:1,
        msgs:{$size:'$msgs.all'},
        noVisited:{$size:'$msgs.noVisited'},
        visited:{$size:'$msgs.visited'}
     }})

只是试行之后会意识报错了,错误提醒The argument to $size must be an array, but was of type: missing
提示$size
应该是本着全体的数组,我们的noVisited大概visited只怕会设有不设有的图景,这时必要通过$ifNull来解决

db.user.aggregate({$project:
   {
       username:1,
       msgs:{$size:'$msgs.all'},
       noVisited:{$size:{$ifNull:['$msgs.noVisited',[]]}},
       visited:{$size:{$ifNull:['$msgs.visited',[]]}}
    }})

注意$ifNull,有七个值,使用的[]实际不是{},第贰个是标准化,第3个是大器晚成旦不设有的交替值。

走访结果

/* 1 */
{
    "_id" : ObjectId("5a29467b67732445e8adffe5"),
    "username" : "foo1",
    "msgs" : 2,
    "noVisited" : 1,
    "visited" : 1
}

/* 2 */
{
    "_id" : ObjectId("5a2946a567732445e8adffe6"),
    "username" : "bar1",
    "msgs" : 2,
    "noVisited" : 2,
    "visited" : 0
}

下边我们要更进一层,大家意在总结每条私人音讯中从未访谈的数码,由于访谈的音讯是积存在user中,何况是以数组的议程来存款和储蓄,那样很难张开总结,可是MongoDB提供了unwind来消除此类数组查询的题目,使用unwind会把数组中的每一个文书档案都提议来作为三个独自的文书档案,注意unwind平常和project同盟使用,不然会相比占用内部存款和储蓄器。先看unwind的结果

db.user.aggregate([
    {$project:{name:1,"msgs.noVisited":1}},
    {$unwind:"$msgs.noVisited"}
])
{
    "_id" : ObjectId("5a29467b67732445e8adffe5"),
    "name" : "foo",
    "msgs" : {
        "noVisited" : ObjectId("5a294ee567732445e8adfff6")
    }
}
{
    "_id" : ObjectId("5a2946a567732445e8adffe6"),
    "name" : "bar",
    "msgs" : {
        "noVisited" : ObjectId("5a294ee567732445e8adfff3")
    }
}

世家发掘未有,通过unwind,数组中的每一个要素都形成了一个独门的文书档案,之后通过_id举办一个group就可以获得数据

db.user.aggregate([
    {$project:{name:1,"msgs.noVisited":1}},
    {$unwind:"$msgs.noVisited"},
    {$group:{_id:"$msgs.noVisited",count:{$sum:1}}}
])
{
    "_id" : ObjectId("5a29470667732445e8adffef"),
    "count" : 4.0
}
{
    "_id" : ObjectId("5a2946fc67732445e8adffed"),
    "count" : 4.0
}

那时候纵然期待把标题查询出来,对于MongoDB来说,由于尚未join,所以需求对这些数组举办一遍询问,使用out能够将查询出来的结果加多到八个文书档案中

db.user.aggregate([
    {$project:{name:1,"msgs.noVisited":1}},
    {$unwind:"$msgs.noVisited"},
    {$group:{_id:"$msgs.noVisited",count:{$sum:1}}},
    {$out:"msgsNoVisited"}
])

那儿能够通过msgsNoVisited来得到message的title,由于MongoDB未有join,所以率先种办法应用游标,利用forEach处理

db.noVisitedMsgs.remove({})
db.msgsNoVisited.find().forEach(function(e) {
    var m = db.message.findOne({_id:e._id})
    if(m!=null) {
        e.title = m.title
    } else {
        e.title = "not found"
    }
    db.noVisitedMsgs.insertOne(e)
})
db.noVisitedMsgs.find()

经过上述shell将查询的结果存款和储蓄到二个noVisitedMsgs的集合中,第一条remove是先把那个不经常数据删除,看一下结果

{
    "_id" : ObjectId("5a294ee567732445e8adfff5"),
    "count" : 9.0,
    "title" : "msg3"
}
{
    "_id" : ObjectId("5a294ee567732445e8adfff4"),
    "count" : 10.0,
    "title" : "msg2"
}

这种方案是基于伪查询的办法,由于各样游标都以透过findOne来打开二遍询问的,所以只要数据量相当的大的话,效能会异常的低,在MongoDB3.2版本之后提供了新的函数lookup来实现四个集聚的总是

db.message.aggregate([
    {$lookup:{
         from:"msgsNoVisited",
         localField:"_id",
         foreignField:"_id",
         as:"noVisited"
    }}
])

这个时候会内嵌二个文书档案noVisited到message中,如下所示

{
    "_id" : ObjectId("5a294ee567732445e8adfff3"),
    "title" : "msg1",
    "content" : "msg ...",
    "createDate" : ISODate("2017-12-15T05:22:20.103Z"),
    "user" : {
        "id" : ObjectId("5a29467b67732445e8adffe5"),
        "name" : "foo"
    },
    "attach" : {
        "name" : "world.txt",
        "type" : "txt",
        "createDate" : "Thu Dec 07 2017 23:34:54 GMT+0800",
        "size" : 23
    },
    "noVisited" : [ 
        {
            "_id" : ObjectId("5a294ee567732445e8adfff3"),
            "count" : 10.0
        }
    ]
}

假使以为里面包车型地铁数据太多了,可以率先通过project来扩充投影,最后把转换的消息拷贝到一个文书档案中

db.message.aggregate([
    {$project:{
        title:1
     }},
    {$lookup:{
         from:"msgsNoVisited",
         localField:"_id",
         foreignField:"_id",
         as:"noVisited"
    }},
    {$out:"msgsNoVisited"}
])
db.msgsNoVisited.find()
---------------------------------------
{
    "_id" : ObjectId("5a294ee567732445e8adfff3"),
    "title" : "msg1",
    "noVisited" : [ 
        {
            "_id" : ObjectId("5a294ee567732445e8adfff3"),
            "count" : 10.0
        }
    ]
}

如上正是unwind和out的各类用法,希望对大家有着帮忙

<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.2.4.Final</version></dependency>
//多分组(group+distinct)db.M_User_Footprints.aggregate([         { $group: { _id: { UserInfoID: "$UserInfoID", ObjID: "$PlayVideo.ObjID" } } },         { $group: { _id: "$_id.UserInfoID", count: { $sum: 1 } } },         { $sort: { "_id": 1 } }]);

重塑文书档案

在第一小一些已经看了哪些重塑文档,MongoDB还提供了累累好用的函数来重塑文档,重塑文书档案都以依靠project的,这种函数分成超多类,这里只好轻便演示一些,首先字符串相关的函数有

字符串相关函数
$concat   连接两个字符串
$strcasecmp 区分大小写比较数字
$substr 取子串
$toLower 转化为小写
$toUpper 转换为大写

拜望如下实例,获取客户姓名的音信

db.user.aggregate([
    {$project:{
        fullname:{$concat:['$username','-','$name']},
        fname:{$substr:['$username',0,1]}
     }}
])

{
    "_id" : ObjectId("5a29467b67732445e8adffe5"),
    "fullname" : "foo1-foo",
    "fname" : "f"
}

接下去看看算术运算函数和日期函数

算术运算函数
    $add   求和
    $divide 除法
    $mod 求余数
    $multiply 乘法
    $subtract 减法
日期函数
    $year 取年份
    $month 取月份
    $week 取一年中的某一周(0-53)
    $hour 取小时
    $minute 取分钟
    $second 取秒钟
    $millisecond 取毫秒
    $dayOfYear 一年中的某一天
    $dayOfMonth 一月中的某一天
    $dayOfWeek 一周中的某一天,1表示周日

算数运算符比较容易,大家应用以下日期函数

db.message.aggregate([
    {$match:{createDate:{$gte:new Date("2017-01-01"),$lte:new Date("2017-12-31")}}},
    {$project:{title:1,year:{$year:"$createDate"},month:{$month:"$createDate"}}},
    {$group:{_id:{year:'$year',month:'$month'},count:{$sum:1}}}
])

如上操作达成了基于year和month实行分组查询,最终能够拿到前年各样月发送的message的多少,那是老大好用的大器晚成种聚合查询。

集合操作函数也要命实用

集合操作符
    $setEquals  查看两个集合时候相同,相同返回true
    $setInterseciont 返回两个集合的公共元素
    $setDifference 返回两个集合中的不同的元素
    $setUnion 合并集合
    $setIsSubset 判断第二个集合是否是第一个的子集,如果是返回true
    $anyElementTrue 如果某个集合元素为true,就为true
    $allElementTrue 集合中所有元素为true就为true

看一下下三个实例

db.user.aggregate([
    {$project:{nov:{$setDifference:['$msgs.all',{$ifNull:['$msgs.visited',[]]}]}}}
])

会获得从未参观过的message新闻,上面看一下任何的重塑函数

逻辑函数
    $and true   与操作
    $cmp 比两个值是否相等
    $cond  if ... then ... else 条件逻辑,类似三元运算符
    $eq 等于
    $gt,$gte,$lt,$lte  大于和小于
    $ifNull 是否为空
    $ne 不等于
    $not. 取反
    $ir 或运算
其他函数
    $meta  文本搜索
    $size 取数组大小
    $map 对数组的每个成员应用表达式
    $let 定义表达式内的变量
    $literal 返回表达式的值

以上函数就比不上方了,只是让我们对这些具有领悟,今后实际应用到的时候再去询问

constraint组合

概念证明

@Max@Min@Constraint(validatedBy = {})@Documented@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)public @interface Price { String message() default "错误的价格"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {};}

上边选取map…reduce也得以完结分组的法力

总结

那有的授课了汇集框架,那是MongoDB的中央,它能够提供十一分卓绝的查询操作,那是必要求熟识的一块内容,当然聚合框架的频率难点将会在目录之后详细查究。

三个constraint由annotation和对应的 constraint validator
组成,三个annotation能够对应多少个constraint validator,constraint
之间也得以彼此组合生成更眼花缭乱的constraint,如若依旧满足不断供给则可以自定义崭新的constraint

//单分组db.M_User_Footprints.aggregate([  { $group: { _id: "$UserInfoID", result: { $sum: 1 } } }])

管道概念

切切实实的思路是把Collection加载到聚焦框架中,之后通过第三个标准限定(如:math)之后会把筛选出来的数目传给第二个标准,那实在就和管道的概念同样。常用的群集管道有如下一些部分:

聚合管理 说明
$project 输出文档的内容,其实就等于sql中的投影操作,也类似find查询中的第二个条件
$match 查询条件
$limit 取多少条数据传递给下一个管道
$skip 跳过一定数量的文档
$unwind 扩展数组
$group 分组,和sql中的group类似
$sort 文档排序
$out 把管道的结果写到一个集合中

聚拢管道的写法如下所示

db.collection.aggregate([{$project},{$match},....{$sort}])

在意写法,首先aggregate是聚合函数,和find相符选拔(卡塔尔国来调用,第叁个参数正是管道,以数组的法门来定义五个管道所以要使用中括号[
]来定义,数组中的每叁个{}就代表三个管道,上面我们会依赖上蓬蓬勃勃讲的例子来详细深入分析管道的逐生机勃勃操作。由于原先的数据库被十分的大心删除了,下图是风靡版本的数码对象模型图,数据布局未有别的的修改,无非一些名称有个别改动而已,org产生了dep之类

威尼斯人国际网址 1

集中查询数据模型

)

先来查询二个能够使用find就完结的操作,大家询问年龄大于23的装有User何况独自展现name和age,那个时候急需经过三个管道来形成,match和project,match用来张开标准同盟,project用来显明询问出来的文书档案

db.user.aggregate([
        {$match:{age:{$gt:23}}},
        {$project:{_id:0,name:1,age:1}}
])

第叁个管道落成了原则的询问,第1个管道分明了出口内容。很刚烈那些操作使用find更为直观一些,接下去将会介绍部分常用了汇集操作,通过聚合操作我们会体会到MongoDB的有力之处。

Hibernate Validator 附加的 constraint

Author

发表评论

电子邮件地址不会被公开。 必填项已用*标注