首页 热点资讯 义务教育 高等教育 出国留学 考研考公
您的当前位置:首页正文

mongodb分组排序

2023-11-12 来源:花图问答

@Overridepublic MessageDto getCheckInMembersByFlight(String fltDt, String fltNr, String channel,String origincd,String destcd) {log.info("请求参数:"+"fltDt:"+fltDt+",fltNr:"+fltNr+",channel:"+channel+",origincd:"+origincd+",destcd:"+destcd);MessageDto messageDto = new MessageDto();boolean flag = (null !=fltDt&&!("").equals(fltDt)&&null !=fltNr&&!("").equals(fltNr)&&null !=channel&&!("").equals(channel)&&null !=origincd&&!("").equals(origincd)&&null !=destcd&&!("").equals(destcd))&&(channel.equals("WX")|channel.equals("APP"));if (flag) {String fltDtStr = fltDt.replace("-","")+"000000";//分组的依据psgIdDBObject keys = new BasicDBObject("psgId", true);//dbObject为检索条件,检索出符合业务的数据BasicDBObject dbObject = new BasicDBObject();dbObject.put("fltDt", fltDtStr);String fltNrSub = fltNr.substring(2);dbObject.put("fltNr", fltNrSub);dbObject.put("checkinStatus", "AC");dbObject.put("originCd", origincd);dbObject.put("destCd",destcd );//设定下面函数的初始值:prev的字段dcsTimestamp的初始值,用于做比较取dcsTimestamp的最大值DBObject initial = new BasicDBObject("dcsTimestamp", "0");//这个属于js的语法,这个脚本用于在psgId组内,比较出较大的dcsTimestamp,并且取出这条数据中要返回的值//注意后面的赋值要有dcsTimestamp的赋值,不然得出的数据不准确String reduce = "function(doc,prev){" +"var docDcsTimestamp=doc.dcsTimestamp;" +"var prevDcsTimestamp=prev.dcsTimestamp;" +"var docDcsTimestampInt=parseInt(docDcsTimestamp);" +"var prevDcsTimestampInt=parseInt(prevDcsTimestamp);" +"if(docDcsTimestampInt > prevDcsTimestampInt){" +"prev.fqNo=doc.fqNo;" +"prev.credentNo=doc.credentNo;" +"prev.chnName=doc.chnName;prev.dcsTimestamp=doc.dcsTimestamp;}" +"}";BasicDBList dbList = (BasicDBList) mongoTemplate.getCollection("b_dep_pnr_psg").group(keys, dbObject, initial, reduce);log.info("分组处理成功");JSONArray resultJson = new JSONArray();for (int i = 0; i < dbList.size(); i++) {//取出最终的得到数据中的要返回的参数,封装到map转json返回。添加name和证件号不为空判断,其中一个为空不返回JSONObject jsStr = JSONObject.parseObject(dbList.get(i).toString());String fqNoStr = (String) jsStr.get("fqNo");String chnName = (String) jsStr.get("chnName");String credentNo = (String) jsStr.get("credentNo");Map<String, String> map = new HashMap<String, String>();ObjectMapper mapJson = new ObjectMapper();if(null!=chnName&&!chnName.equals("")&&null!=credentNo&&!credentNo.equals("")){map.put("fqNo", fqNoStr);map.put("name", chnName);map.put("credentNo", credentNo);}else{continue;}String params = null;JSONObject resultJsonObiect = null;try {params = mapJson.writeValueAsString(map);resultJsonObiect = JSONObject.parseObject(params);} catch (JsonProcessingException e) {e.printStackTrace();}resultJson.add(resultJsonObiect);}messageDto.setMessage("成功");messageDto.setCode("0");messageDto.setContent(resultJson);} else {messageDto.setMessage("参数个数不对");messageDto.setCode("10001");messageDto.setContent(null);}return messageDto;}

mongodb分组排序

标签:got   java   map   fun   cdc   stack   ini   parse   脚本   

小编还为您整理了以下内容,可能对您也有帮助:

MongoDB自动分片介绍

1. 片键介绍

  数据划分(partitioning)关键问题是怎么样将一个集合中的数据均衡的分布在集群中的节点上。 MongoDB 数据划分的是在集合的层面上进行的,它根据片键来划分集合中的数据。

(1)使用片键的取值范围指定数据块

  设置分片的时候,需要从集合里选出一个字段,用该字段的值作为数据拆分的依据,这个字段称为片键(shard key),文档中的数据按照这个字段排序切分成块,分布到各个片上。比如说有个表示人员的集合,如果选择名字(“name”)字段作为片键,第一片可能会存放名字以A F开头的文档,第二个存放的是以G P开头的文档,第三个存的Q~Z的名字。随着添加(删除)片,MonogDB会重新平衡数据,使每片的流量都比较均衡,数据量也在合理范围内。

按照片键取值范围来作为数据块划分的区间依据,优点是按范围查询的时候它的效率很高,当给定一个查询范围,根据mongos中的映射表可以很快的定位到分片上的数据块。除此之外当两个分片的键取值比较靠近的时候,会被放到相近的块中,由于数据的局部性原理,这样的话可以加快查询效率,同时也可以减少内存换页次数。

缺点是可能会导致数据分布不均衡,如果选择的片键具有线性的性质,例如时间,将其作为片键的话 ,在某个时间段的写请求(读请求)都会被映射到同一个分片的同一个数据块上, 这样的话不仅会降低系统的读写性能,而且也会因写操作过于集中导致片间的不平衡。

(2)按照片键哈希值来作为数据块的划分区间依据

  优点是可以确保一个比较均衡的数据分布,因为即使当两个文档的片键取值很接近的时候,例如上面例子中一个x=25,一个x=26,它们的哈希结果也会有很大的差别,这样的话数据会随机的分布到集群中,有利于数据的均衡的分布,减少数据块的移动次数,同时由于数据分散会减少单个数据块的写操作的压力,提高写入速度。

缺点是随机划分导致数据过于分散,当要查询某个范围内的数据时比如年龄大于20小于25的所有男生信息,如果直接使用范围划分的话,由于其具有良好的数据局部性特点,可能只要访问几个相邻的数据块就行了, 但是如果要使用哈希划分的方法很可能要访问所有的数据块。

(3)取值有限的片键

  这是一种粗力度的片键,比如上边说的用户ID。如果按照用户ID分片,你可以预料到插入会分布在各个分片上,因为无法预知哪个用户何时会插入数据。这样一来,粗粒度分片键也能拥有随机性,还能发挥分片集群的优势。而且粗粒度的片键还能使用局部性带来的效率提升。当某个用户上传100个文件,基于用户ID字段的分片建能确保这些插入都落到同一个分片上,并几乎能写入索引的同一部分,这样效率很高。粗粒度分片键在分布性和局部性上都表现很好,但是它也有一个很难解决的问题:块有可能无的增长。想想基于用户ID的片键,假如有几个特殊用户,他们上传了上百万个文件,那么一个块里就可能只有一个用户ID,这个块能拆分么?不能,因为用户ID是最小的粒度,拆分了查询就没法路由到数据。这就造成分片之间数据量不均衡。更典型的就是type,status这类的字段,因为它们的选择性实在是太低,导致无法拆分。片键基比较小时,所有的键值相同导致MongoDB不能Chunk,迁移这些不可的Chunk将更加耗时,即使迁移后也难以保证数据在各个分片上的平衡。Chunk数量被基约束住后,我们就不能利用MongoD分片集群特性将集合部署到更多的机器。

2. 片键的选取原则

  在Sharding结构中,分片策略,片键选择是影响性能的关键因素,片键不仅影响数据分布,而且影响业务逻辑,所以片键的选择不单单是均匀的将数据分布到各个片上,而且要考虑查询的性能。坏的片键有时候会导致数据分布很差,有时候会导致无法使用局部性原理,还有一些会影响数据块的拆分。

  上边我们讨论了低效片键的问题和原因,理想的片键应该结合粗粒度分片键与细粒度片键两者的优势。

一个好的片键必须包含的特性:

1、保证CRUD能利用局限性 ==》升序片键的优点

2、将插入数据均匀分布到各个分片上 ==》随机片键的优点

3、有足够的粒度进行块拆分 ==》粗粒度片键的优点

满足这些要求的的片键通常由两个字段组成,第一个是粗粒度,第二个是粒度较细。那么我们需要使用复合片键。例如对上面的例子,选取{userid:1,_id:1}作为片键,当用户同时插入数据时,我们可以预见大多数情况下,这些数据会被均匀的分布到所有的片上,而且分片里的唯一字段_id能保证对任意一个文档的查询和更新始终都能指向单个分片。如果对用户ID执行更复杂的查询,那么路由也只会将查询路由包含此用户ID存在的片上,而不会发到所有分片。由于_id(升序)的存在,保证了块始终是能继续拆分的,哪怕用户创建了大量文档,情况也是如此。

所以在选择片键时尽量能保持良好的数据局部性而又不会导致过度热点的出现,很多时候,组合片键是一种比较常用的做法。

除此之外,也可以选择我们经常查询的字段作为片键,这类分片键可以使得查询时mongos仅仅将查询发送给特定的mongod实例,不需要等待多个实例返回数据后再进行合并。

MongoDB文档中字段是否有先后顺序之分

Mongodb中的文档是一种Key:Value对组成的Json字符串,按理说,字段的先后顺序是不应该影响到查询操作的。
我们简单做个实验,往集合中插入一条记录:
db.testcollection.insert({"apple":2, "banana":3});
这时候,我们通过以下两种方式查询都是一样的:
第一种方式:
db.testcollection.find({"apple":2,"banana":3})
第二种方式:
db.testcollection.find({"banana":3,"apple":2})
通过上面两种方式查询都可以查询出来结果,都是同一条记录。那是否就可以说明,字段顺序是没有区别的呢?别着急,我们再看一个例子。
比方我们在集合中插入了下面一条记录:
db.testcollection.insert({"type":"fruit", "detail":{"apple":2, "banana":3}});
此时我们和上面一样,采用两种方式进行查询:
第一种方式:
db.testcollection.find({"detail":{"apple":2,"banana":3}})
第二种方式:
db.testcollection.find({"detail":{"banana":3,"apple":2}})
从两种查询方式的结果来看,其实结果是不一样的,第一种方式查询到了结果,但是第二种方式是没有查询到结果的。从这个实例来看,就可以告诉我们,当我们在查询具有嵌套文档的集合中,还是要稍微注意一点的,否则很有可能就会出现上面的情况。
我们如何避免出现上面这样因为书写顺序而导致的查询结果不一样的情况呢?有个办法,那就是在查询时不要使用json字符串进行查询,在这种具有嵌套文档的集合中,可以使用点符号进行操作,我们采用下面两种方式进行查询。
第一种方式:
db.testcollection.find({"detail.apple":2,"detail.banana":3})
第二种方式:
db.testcollection.find({"detail.banana":3,"detail.apple":2})
通过结果可以知道,这两种方式查询出来的结果是一样的,由此可见,不管是对于简单的查询,还是对于具有嵌套文档的查询,不在查询字符串中对字段值使用Json串书写格式,而是使用点号进行嵌套引用查询才是比较合适的做法,可以防止因书写字段顺序和文档中的字段顺序不一样导致的查询错误。

MongoDB文档中字段是否有先后顺序之分

Mongodb中的文档是一种Key:Value对组成的Json字符串,按理说,字段的先后顺序是不应该影响到查询操作的。
我们简单做个实验,往集合中插入一条记录:
db.testcollection.insert({"apple":2, "banana":3});
这时候,我们通过以下两种方式查询都是一样的:
第一种方式:
db.testcollection.find({"apple":2,"banana":3})
第二种方式:
db.testcollection.find({"banana":3,"apple":2})
通过上面两种方式查询都可以查询出来结果,都是同一条记录。那是否就可以说明,字段顺序是没有区别的呢?别着急,我们再看一个例子。
比方我们在集合中插入了下面一条记录:
db.testcollection.insert({"type":"fruit", "detail":{"apple":2, "banana":3}});
此时我们和上面一样,采用两种方式进行查询:
第一种方式:
db.testcollection.find({"detail":{"apple":2,"banana":3}})
第二种方式:
db.testcollection.find({"detail":{"banana":3,"apple":2}})
从两种查询方式的结果来看,其实结果是不一样的,第一种方式查询到了结果,但是第二种方式是没有查询到结果的。从这个实例来看,就可以告诉我们,当我们在查询具有嵌套文档的集合中,还是要稍微注意一点的,否则很有可能就会出现上面的情况。
我们如何避免出现上面这样因为书写顺序而导致的查询结果不一样的情况呢?有个办法,那就是在查询时不要使用json字符串进行查询,在这种具有嵌套文档的集合中,可以使用点符号进行操作,我们采用下面两种方式进行查询。
第一种方式:
db.testcollection.find({"detail.apple":2,"detail.banana":3})
第二种方式:
db.testcollection.find({"detail.banana":3,"detail.apple":2})
通过结果可以知道,这两种方式查询出来的结果是一样的,由此可见,不管是对于简单的查询,还是对于具有嵌套文档的查询,不在查询字符串中对字段值使用Json串书写格式,而是使用点号进行嵌套引用查询才是比较合适的做法,可以防止因书写字段顺序和文档中的字段顺序不一样导致的查询错误。

java怎么做到使用mongodb来进行分组查询统

java操作mongodb进行查询,常用筛选条件的设置如下:

条件列表:
BasicDBList condList = new BasicDBList();
临时条件对象:
BasicDBObject cond = null;
DBCollection coll = db.getCollection("A");

1、$where
在某种应用场合,若要集合A查询文档且要满足文档中某些属性运算结果,可以编写一脚本函数用where进行设置,比如:
某集合中存放的用户信息,包括姓名、年龄、手机号、地址等,要筛选出年龄大于20且小于等于40的用户,我们可以这样:
String ageStr = "function (){return parseFloat(this.age) > 20 && parseFloat(this.age) <= 40};";
cond = new BasicDBObject();
cond.put("$where",ageStr);

放入条件列表
condList.add(cond);

2、$in
接1实例中,要查询年龄为23、40、50的用户信息,我们可以这样:
创建一个临时的条件列表对象,将条件值分别添加进去
BasicDBList values = new BasicDBList();
values.add(23);
values.add(40);
values.add(50);

cond = new BasicDBObject();
cond.put("age",new BasicDBObject("$in",values));

放入条件列表
condList.add(cond);

3、模糊匹配
接1实例中,要按照用户的姓名进行模糊查询,如:王,我们可以这样做:

使用不区分大小写的模糊查询
3.1完全匹配

java怎么做到使用mongodb来进行分组查询统

java操作mongodb进行查询,常用筛选条件的设置如下:

条件列表:
BasicDBList condList = new BasicDBList();
临时条件对象:
BasicDBObject cond = null;
DBCollection coll = db.getCollection("A");

1、$where
在某种应用场合,若要集合A查询文档且要满足文档中某些属性运算结果,可以编写一脚本函数用where进行设置,比如:
某集合中存放的用户信息,包括姓名、年龄、手机号、地址等,要筛选出年龄大于20且小于等于40的用户,我们可以这样:
String ageStr = "function (){return parseFloat(this.age) > 20 && parseFloat(this.age) <= 40};";
cond = new BasicDBObject();
cond.put("$where",ageStr);

放入条件列表
condList.add(cond);

2、$in
接1实例中,要查询年龄为23、40、50的用户信息,我们可以这样:
创建一个临时的条件列表对象,将条件值分别添加进去
BasicDBList values = new BasicDBList();
values.add(23);
values.add(40);
values.add(50);

cond = new BasicDBObject();
cond.put("age",new BasicDBObject("$in",values));

放入条件列表
condList.add(cond);

3、模糊匹配
接1实例中,要按照用户的姓名进行模糊查询,如:王,我们可以这样做:

使用不区分大小写的模糊查询
3.1完全匹配

mongoDB应用篇-mongo聚合查询

如果我们在日常操作中,将部分数据存储在了MongoDB中,但是有需求要求我们将存储进去的文档数据,按照一定的条件进行查询过滤,得到想要的结果便于二次利用,那么我们就可以尝试使用MongoDB的聚合框架。

前面我们在学习文档查询的过程中,也介绍过一些查询的操作符,其中就有一部分是简单的查询聚合函数,例如 count 、 distinct 、 group 等,如果是简单的数据分析过滤,完全可以使用这些自带的聚合函数以及查询的操作符来完成文档的过滤查询操作

如果我们遇到了一些数据需要跨多个文本或者统计等操作,这个时候可能文档自身也较为复杂,查询操作符已经无法满足的时候,这个时候就需要使用MongoDB的聚合查询框架了。

使用聚合框架可以对集合中的文档进行变换和组合查询,基本上我们使用的时候,都是使用多个构件创建一个管道,用于对一连串的文档进行处理。这里的构件包括 筛选(filter) 、 投射(projecting) 、 分组(grouping) 、 排序(sorting) 、 (limiting) 以及 跳过(skipping)

MongoDB中需要使用聚合操作,一般使用 aggregate 函数来完成多个聚合之间的连接,aggregate() 方法的基本语法格式如下 :

现在假设我们有个集合articles,里面存储了文章的集合,大致如下:

但这时我们需要查询出来每一个作者写的文章数量,需要使用aggregate()计算 ,大致如下:

输出的结果为:

通过这个简单的案例我们就能输出想要的数据和属性名,大概分析一下刚刚的聚合查询语句, by_user字段进行分组,代表每个用户一条数据,而num_tutorial则是定义了数值类型计算的结果字段,$sum则是计算总和,相当于每个用户出现一次,都会+1,最终计算出来的总和通过num_tutorial字段进行输出

注:如果管道没有给出预期的结果,就需要进行调试操作,调试的时候,可以尝试先给一个管道操作符的条件,如果这个时候查询出来的结果是我们想要的,那么我们需要再去指定第二个管道操作符,依次操作,最后就会定位到出了问题的操作符

前面我们提到聚合查询会使用管道操作符,而每一个操作符就会接受一连串的文档,对这些文档进行一些类型转换,最后将转换以后的文档结果传递给下一个管道操作符来执行后续的操作,如果当前是最后一个管道操作符,那么则会显示给用户最后的文档数据。不同的管道操作符是可以按照顺序组合在一起使用,并且可以被重复执行多次,例如我们可以先使用$match然后再去、 match操作。

match管道操作符可以使用$gt、$lt、$in等操作符,进行过滤,不过需要注意的是不能在$match管道操作符中使用空间地理操作符。

在实际使用的过程中,尽可能的将 match操作符以后,再去投射或者执行分组操作的话,是可以利用索引的。

相比较一般的查询操作而言,使用管道操作,尤其是其中的投射操作更加强大。我们可以在查询文档结束以后利用 $project 操作符从文档中进行字段的提取,甚至于我们可以重命名字段,将部分字段映射成我们想要展示出去的字段,也可以对一部分字段进行一些有意义的处理。需要注意的是, $project 操作符可以传入两个参数,第一个是需要处理的属性名称,第二个则是0或者1,如果传入1,则代表当前的属性是需要显示出来的,如果是0或者不写,默认都是代表这个字段不需要显示出来

当然第二个参数也可以是一个表达式或者查询条件,满足当前表达式的数据也可以进行显示,接下来我们先准备一点数据:

接下来,我们来查询,条件是item字段为abc,quantity要大于5,并且我们只要item和price字段的结果,其他都排除掉:

可以看到结果为:

如果我们想要在原基础上改变某个字段的名称,例如将item改为item_code,可以利用$来完成,如下:

可以看到我们指定的名称item_code,而这个别名对应的字段item使用$作为前缀标记,代表将item字段映射为item_code,可以看到结果:

我们在投影的时候,除了可以将某个字段映射成其他字段以外,还可以针对某个字段进行一些简单的运算,最常见的就是 四则运算 ,即

加法( subtract )、乘法( divide )、求模( $mod ) ,

除此之外,还支持对字段进行 关系运算 (大小比较( " eq" )、大于( " gte" )、小于( " lte" )、不等于( " ifNull" ) )、

逻辑运算 (与( " or" )、非 ( " concat" )、截取( " toLower" ) )等

我们基于上面的需求,假设每一个价格是按照元为单位,现在要求输出W为单位,那么我们就需要对price进行除法运算,如下:

除此之外,我们也可以将计算完毕的price改名为priceW,即:

可以看到输出的结果为:

这时有一个需求,要求我们返回数据的同时还要yyyy-MM-dd格式的时间字符串,这个时候我们就需要对date字段进行时间函数和字符串混合处理了,如下:

这里需要注意的一点是, year:" substr函数将date字段的结果截取成字符串即可实现拼接

group的_id上,代表按照当前字段进行分组,例如,我们这里根据item进行分组:

在我们针对某个字段进行分组以后,我们可以针对每个分组进行一些操作符的使用,常见的例如: $sum 、 $avg 、 $min 、 $max 、 $first 、 $last 。

$avg 操作符用来返回每一个分组内的平均值

现在我们基于前面item的分组,我们想要算出来每个组内的平均价格是多少,如下:

$min 和 $max 操作符用于返回分组内最大的值和最小的值

除了平均值以外,我们现在将最贵的和最便宜的价格也要列出来,这个时候就可以使用这两个操作符了,如下:

$first 、 $last 则是可以获取当前分组中第一个或者最后一个的某个字段的结果,如下:

除此之外,我们还可以在分组的时候使用数组操作符,例如 $addToSet 可以判断,当前数组如果不包含某个条件,就添加到当前数组中, $push 则不管元素是否存在,都直接添加到数组中

注意:大部分管道操作符都是流式处理的,只要有新的文档进入,就可以对新的文档进行处理,但是 $group 代表必须收到全部文档以后才可以进行分组操作,才会将结果传递给后续的管道操作符,这就意味着,如果当前mongo是存在分片的,会先在每个分片上执行完毕以后,再把结果传递mongos进行统一的分组,剩下的管道操作符也不会在每个分片,而是mongos上执行了

如果我们现在遇到一些文档比较复杂,比如存在内嵌文档的存在,某个属性里面嵌套了一个数组,但是我们需要对内嵌的数组文档进行分析过滤等查询处理,这个时候就可以使用 $unwind 操作符将每一个文档中的嵌套数组文件拆分为一个个的文档便于进行后续的处理,例如我们需要将之前的set集合中关于请求的url以及ip的信息拆分出来,原始的格式如下:

我们可以使用命令进行拆分,如下:

结果为:

可以看到数据则是按照每一条信息的方式展示出来了,方便后续的计算以及输出,但是需要注意的一点是,这种方式,如果该文档中没有拆分的字段,或者是空数组,默认会直接排除,如果我们需要空数组等也输出计算出来,则可以指定 preserveNullAndEmptyArrays 参数,设置为true,则代表空数组或者不存在的文档也要拆分输出出来,即:

我们可以在管道查询的过程中,按照某个属性值或者多个属性的结果进行顺序排序,排序的方式与普通查询操作符中的sort操作符表现一致,与其他管道操作符一样,可以在任何阶段使用,但是,需要注意的一点是,建议在管道操作符第一阶段进行排序,因为此时的排序是可以触发索引的,如果在后续阶段进行排序,会消耗大量内存,并且耗时会很久,尤其是在有 $group 的情况下,如果放在 $group 操作符后面,会发现等到的时间很久,不仅仅是无法触发索引的问题,还和 $group 操作符是等待所有数据完毕才会触发的特性有关,因此需要格外注意。

结果如下,按照我们想要的结果进行了排序:

limit,只返回前两条数据,如下:

结果如下:

除了 skip,与之前的查询操作符作用也是一样的,用于在已经查询完毕的结果集中跳过前N条数据以后进行返回,我们将$skip加在刚刚的查询后面,如下:

这个时候可以看到返回的结果为空,什么结果都没有了,这是因为前一步管道已经了仅仅返回2条,而接着我们又跳过了前两条文档,因此返回的结果为空,我们将顺序调换一下,看看:

可以看到结果如下,与刚才的结果无异:

管道查询操作符有很多,除了上面学习的常用的部分,还有几十个,需要了解全部的可以参考官网:

https://docs.mongodb.com/manual/reference/command/aggregate/

除此之外,我们在学习的过程中了解到,部分查询操作符是可以触发索引的,例如 $project 、 $group 或者 $unwind 操作符,因此我们也建议 如果可以的话,尽量先使用这类管道操作符进行数据过滤,可以有效减少数据集大小和数量,而且管道如果不是直接从原先的集合中使用数据,那就无

法在筛选和排序中使用索引 ,例如我们先进行管道操作,再去将过滤好的数据进行 $sort 排序,会导致无法使用索引,效率大幅度下降,因此如果我们需要涉及到 $sort 操作的时候,如果可以尽可能在最开始就处理,这个时候可以使用索引,效率较高,然后再去进行管道查询筛选与分组等其他操作,可以有效的提高查询的效率。另外需要注意的一点是,在 MongoDB中会对每一个管道查询做,例如某一步管道查询操作导致内存占用超过20%,这个时候就会报错,无法继续使用管道 ,因为mongoDB本身每次最大是16Mb的数据量,为了尽可能避免或者减少这种问题,建议可以考虑尽可能的使用 $match 操作符过滤无用数据,减少数据总大小。同时也 因为管道查询是多步执行,例如 $group 则是等待所有数据完毕才会执行,因此可能会导致整体执行时间较久 ,也因为这样,才不建议在较高的实时查询需求上使用管道和查询,而是在 设计的时候尽可能直接使用查询操作符进行数据查询,触发更多的索引,更快的销量查询出来想要的结果。

mongodb 分组的语句怎么写

/ 首先利$match筛选出where条件 此处看你的查询范围条件了BasicDBObject[] array = {new BasicDBObject("startTimeLong", new BasicDBObject("$gte",beginTime)), new BasicDBObject("startTimeLong", new BasicDBObject("$lt",endTime)) }; BasicDBObject cond = new BasicDBObject(); cond.put("$and", array); DBObject match = new BasicDBObject("$match", cond); // 利用$project拼装group需要的数据,包含name列、age列 DBObject fields = new BasicDBObject("name", 1); fields.put("age", 1); DBObject project = new BasicDBObject("$project", fields); // 利用$group进行分组 DBObject _group = new BasicDBObject("name", "$name");_group.put("age", "$age");DBObject groupFields = new BasicDBObject("_id", _group); //总数groupFields.put("count", new BasicDBObject("$sum", 1));DBObject group = new BasicDBObject("$group", groupFields); AggregationOutput output = MongodbUtil.getLogInfoCollection().aggregate(match, project, group);

mongodb 分组的语句怎么写

/ 首先利$match筛选出where条件 此处看你的查询范围条件了BasicDBObject[] array = {new BasicDBObject("startTimeLong", new BasicDBObject("$gte",beginTime)), new BasicDBObject("startTimeLong", new BasicDBObject("$lt",endTime)) }; BasicDBObject cond = new BasicDBObject(); cond.put("$and", array); DBObject match = new BasicDBObject("$match", cond); // 利用$project拼装group需要的数据,包含name列、age列 DBObject fields = new BasicDBObject("name", 1); fields.put("age", 1); DBObject project = new BasicDBObject("$project", fields); // 利用$group进行分组 DBObject _group = new BasicDBObject("name", "$name");_group.put("age", "$age");DBObject groupFields = new BasicDBObject("_id", _group); //总数groupFields.put("count", new BasicDBObject("$sum", 1));DBObject group = new BasicDBObject("$group", groupFields); AggregationOutput output = MongodbUtil.getLogInfoCollection().aggregate(match, project, group);

MongoDB Aggregation

对数据进行聚合操作,然后将计算之后的数据返回。聚合操作将多个文档的值组合在依赖,并且可以对分组数据执行各种操作返回单个结果。
MongoDB提供三种方式来执行聚合操作:aggregation pipeline、map-rece function、single purpose aggregation methods。

MongoDB 聚合操作是在数据处理管道的逻辑上建模的。documents可以进入一个用于处理docuemnt然后返回聚合值的多阶段管道。

底层的管道提供了filters(类似于查询的操作)和document transformations(修改document的输出形式)操作。

其他管道操作为document指定具体的属性或者多个属性进行分组和排序,以及用array内容的聚合工具一样。管道的阶段可以使用运算符执行任务。

管道使用MongoDB自带的本地操作来执行聚合操作更高效,管道是MongoDB执行聚合操作的首先。

聚合管道可以操作分片collection。聚合管道可以通过使用索引来提高性能。聚合管道内部会进行优化阶段。

可以使用db.collection.aggregate()的explain参数看到执行计划。

聚合管道来决定需要返回的字段。如果使用只需要的字段,这样可以减少数据量。

addFields + match放入到 addFields之前(如果是 project / $addFields的结果,就不能移动),减少数据量。

match: 先执行$match来减少数据量,然后在执行排序操作。

match: 如果在前面添加$match操作,可以使用索引来减少数据操作。

skip(在3.2开始可以使用):将 project操作之前,可以减少数据量。

通常情况下,在重新排序优化之后才会发生阶段合并。

limit:如果不能减少数据量,不会将这两个阶段合并。否则先进行排序,然后获取指定的数量,放入内存。如果在中间含有$skip操作,将其放入最后。
在数据量超过内存*,这个操作需要设置 allowDiskUse=true。

从MongoDB3.6开始,删除了aggregate的选项,将结果作为一条数据的返回。

aggregate可以返回cursor或者数据结果集。在返回的结果中,每个document的大小不能超过16M(这个*只针对返回的document)。
documents有可能会超过这个*,db.collection.aggregate()默认返回cursor。

从MongoDB2.6开始,管道阶段的RAM*为100M。如果超过*,出错。如果为了处理大量的数据集,使用allowDiskUse选项开启管道阶段的聚合操作将数据写入到临时文件。

从MongoDB3.4之后, graphLookup操作会忽略这个选项。如果其他阶段有aggregate()操作,allowDiskUse=true将会影响这些阶段。

从MongoDB3.2开始,如果按照分片来匹配值,只会在这个分片内进行计算。

聚合操作在多个分片上执行操作,如果没有指定主分片,这些操作会被路由到其他分片上,来减少主分片的负载。

lookup阶段需要在主分片上执行查询。

将聚合管道拆分为两部分,这是为了在分片上执行优化。

操作可以 参考实例

MapRece可以在分片上执行操作,分片集合可以作为输入或者输出。

使用分片集合作为MapRece输入源,mongos将作业并行派发到各个分片。mongos会等待所有的作业完成。

如果MapRece的out字段有分片值,MongoDB使用_id字段作为分片的依据。

作为一个分片集合输出:

在操作的过程中,maprece有很多锁:

相关 练习教程

MongoDB Aggregation

对数据进行聚合操作,然后将计算之后的数据返回。聚合操作将多个文档的值组合在依赖,并且可以对分组数据执行各种操作返回单个结果。
MongoDB提供三种方式来执行聚合操作:aggregation pipeline、map-rece function、single purpose aggregation methods。

MongoDB 聚合操作是在数据处理管道的逻辑上建模的。documents可以进入一个用于处理docuemnt然后返回聚合值的多阶段管道。

底层的管道提供了filters(类似于查询的操作)和document transformations(修改document的输出形式)操作。

其他管道操作为document指定具体的属性或者多个属性进行分组和排序,以及用array内容的聚合工具一样。管道的阶段可以使用运算符执行任务。

管道使用MongoDB自带的本地操作来执行聚合操作更高效,管道是MongoDB执行聚合操作的首先。

聚合管道可以操作分片collection。聚合管道可以通过使用索引来提高性能。聚合管道内部会进行优化阶段。

可以使用db.collection.aggregate()的explain参数看到执行计划。

聚合管道来决定需要返回的字段。如果使用只需要的字段,这样可以减少数据量。

addFields + match放入到 addFields之前(如果是 project / $addFields的结果,就不能移动),减少数据量。

match: 先执行$match来减少数据量,然后在执行排序操作。

match: 如果在前面添加$match操作,可以使用索引来减少数据操作。

skip(在3.2开始可以使用):将 project操作之前,可以减少数据量。

通常情况下,在重新排序优化之后才会发生阶段合并。

limit:如果不能减少数据量,不会将这两个阶段合并。否则先进行排序,然后获取指定的数量,放入内存。如果在中间含有$skip操作,将其放入最后。
在数据量超过内存*,这个操作需要设置 allowDiskUse=true。

从MongoDB3.6开始,删除了aggregate的选项,将结果作为一条数据的返回。

aggregate可以返回cursor或者数据结果集。在返回的结果中,每个document的大小不能超过16M(这个*只针对返回的document)。
documents有可能会超过这个*,db.collection.aggregate()默认返回cursor。

从MongoDB2.6开始,管道阶段的RAM*为100M。如果超过*,出错。如果为了处理大量的数据集,使用allowDiskUse选项开启管道阶段的聚合操作将数据写入到临时文件。

从MongoDB3.4之后, graphLookup操作会忽略这个选项。如果其他阶段有aggregate()操作,allowDiskUse=true将会影响这些阶段。

从MongoDB3.2开始,如果按照分片来匹配值,只会在这个分片内进行计算。

聚合操作在多个分片上执行操作,如果没有指定主分片,这些操作会被路由到其他分片上,来减少主分片的负载。

lookup阶段需要在主分片上执行查询。

将聚合管道拆分为两部分,这是为了在分片上执行优化。

操作可以 参考实例

MapRece可以在分片上执行操作,分片集合可以作为输入或者输出。

使用分片集合作为MapRece输入源,mongos将作业并行派发到各个分片。mongos会等待所有的作业完成。

如果MapRece的out字段有分片值,MongoDB使用_id字段作为分片的依据。

作为一个分片集合输出:

在操作的过程中,maprece有很多锁:

相关 练习教程

mongoDB - Collation

Collation特性(排序规则) 是mongoDB 3.4 版本新增的。 允许MongoDB的用户根据不同的语言定制排序规则。

排序规则有如下的字段属性

locale字段是强制性必选参数;所有其他排序规则字段都是可选的。

国家地区语言设置, 要指定简单的二进制比较,将locale 值设定为 "simple" 即可

具体local 及国家地区语言对照表如下

使用语法, @collation=<variant> 可选

可选参数, 表示要执行的比较级别。有如下几种参数值

可选参数,当strength 参数为1和2 时 该参数标识是否支持大小写比较

如果caseLevel =true,表示支持大小写比较

如果caseLevel =false , 表示不支持大小写比较

Flag that determines whether to include case comparison at strength level 1 or 2

可选参数 在比较时 确定大小写差异排序顺序的字段

可选参数,确定是将数字字符串作为数字还是字符串进行比较的标志, 默认值为fasle

如果 numericOrdering= true,表示将数字字符串作为数字比较,

例如,“10”大于“2”

反之,numericOrdering= false,则作为字符串进行比较。例如,“10”小于“2”

局限

示例:

// 设置排序后

结果:

可选参数。确定排序规则是否应将空格和标点符号视为基本字符以进行比较的字段。

可选参数, 当 alternate参数是 "shifted"时,确定哪些字符被视为可忽略的字段

当alternate= "non-ignorable"时, 该参数不生效

可选参数,确定带有变音符号的字符串是否从字符串后面排序的标志,例如某些法语字典排序。 如果为true,请从后到前进行比较。 如果为false,则从前到后进行比较。 默认值为false

可选参数。确定是否检查文本是否需要规范化并执行规范化的标志。通常,大多数文本不需要这种规范化处理。 如果为true,检查是否完全规范化并执行规范化以比较文本。 如果为false,则不检查。 默认值为fasle

Collation 参数中,locale 是必选参数,其他的都是可选参数,以下参数默认在所有语言环境中(locales)都是一致的:

下图是在不同locale 参数中,其他可选参数的默认值,供参考

支持排序规则的操作指指令如下图所示

使用示例

对于索引前缀键不是字符串、数组和嵌入文档的复合索引,指定不同排序规则的操作仍然可以使用索引来支持对索引前缀键的比较。

参考文档: https://www.mongodb.com/docs/manual/reference/collation/

MongoDB分页获取数据排序阶段缓存溢出问题

查询语法如下:

报错信息如下:

1、 扩大排序内存的,例如扩大10倍至320M。如:

2、 给排序字段加索引。如:

3、 在执行一个更大规模排序时,即使已经加了索引依然超过,可以使用aggregate()方法的 allowDiskUse 参数设置将数据写到临时文件进行排序。如:

显示全文