mongodb aggregate按日期分组统计及spring mongo实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/q383965374/article/details/85606222

如需转载请注明出处: mongodb aggregate按日期分组统计及spring mongo实现

实现的需求

传入毫秒级开始时间戳和结束的时间戳,根据当前状态currentStatus.status和当前状态时间currentStatus.datetime进行按日统计,缺少数值自动补0.

访问方式如下:

http://localhost:9999/sample/release-count?start_time=1541006872000&end_time=1544117272000

返回结果

{"code":0,"msg":"成功","data":{"list":[{"date":1541865600000,"release":1},{"date":1543248000000,"release":3},{"date":1542729600000,"release":1},{"date":1541088000000,"release":1},{"date":1541433600000,"release":17},{"date":1541779200000,"release":2},{"date":1541347200000,"release":2},{"date":1541692800000,"release":1}]}}

实现例子

以下代码实现了aggregate按日期分组统计并且当天没有数值的情况下自动补0.
ReleaseCountResultVo.java

package com.biologic.vo;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

public class ReleaseCountResultVo {

	private String date;
	private int release;
	public String getDate() {
		return date;
	}
	public void setDate(String date) {
		this.date = date;
	}
	public int getRelease() {
		return release;
	}
	public void setRelease(int release) {
		this.release = release;
	}
	
	public String toString() {
		DateFormat fmt =new SimpleDateFormat("yyyy-MM-dd");
		try {
			fmt.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
			Date datetime = fmt.parse(date);
			return "{date:"+datetime.getTime()+","+"release:"+release+"}";
		} catch (ParseException e) {
			e.printStackTrace();
			return "{date:"+date+","+"release:"+release+"}";
		}
	}
	
}

SampleController.java

@GetMapping(value = "/sample/release-count")
	@ResponseBody
	Object queryReleaseCount(@RequestParam("start_time") String startTime, @RequestParam("end_time") String endTime) throws ParseException {
		Date end = new Date(Long.parseLong(String.valueOf(endTime)));
		Date start = new Date(Long.parseLong(String.valueOf(startTime)));
		Criteria criteria = Criteria.where("currentStatus.status").is(RELEASED_STATUS).andOperator(
				Criteria.where("currentStatus.datetime").lte(end), Criteria.where("currentStatus.datetime").gte(start));
		long count = mongoTemplate.count(new Query(criteria), Person.class);
		System.out.println(count);
		List<ReleaseCountResultVo> resultVos = new ArrayList<ReleaseCountResultVo>();
		if (count == 0) {
			resultVos =repairEmptyDate(end, start, resultVos);
		} else {
			// 匹配查询
			MatchOperation matchOperation = Aggregation.match(criteria);
			// 返回参数
			ProjectionOperation return1 = Aggregation.project("barCode")
					.and(DateOperators.DateToString.dateOf("currentStatus.datetime").toString("%Y-%m-%d")).as("date");
			// 按条件分组
			GroupOperation go2 = Aggregation.group("date").count().as("release");
			// 设置排序
			SortOperation sortOperation = Aggregation.sort(Sort.Direction.ASC, "date");
			// 返回参数2
			ProjectionOperation return2 = Aggregation.project("release").and("_id").as("date");
			// ProjectionOperation return3 =
			// Aggregation.project("release").andExpression("dateFromString(date)")
			// .as("date");
			// 构建参数
			Aggregation aggregation = Aggregation.newAggregation(matchOperation, return1, go2, sortOperation, return2);
			// 分组聚合查询
			AggregationResults<ReleaseCountResultVo> aggregate = mongoTemplate.aggregate(aggregation,
					Person.class, ReleaseCountResultVo.class);
			// 获取结果
			resultVos = aggregate.getMappedResults();
			resultVos =repairEmptyDate(end, start, resultVos);
		}
		JSONArray jSONArray = new JSONArray();
		JSONObject jSONList = new JSONObject();
		for (ReleaseCountResultVo resultVo : resultVos) {
			System.out.println("toString: "+resultVo.toString());
			jSONArray.add(resultVo.toString());
		}
		jSONList.put("list", jSONArray);
		return jSONList;
	}

	private List<ReleaseCountResultVo> repairEmptyDate(Date end, Date start, List<ReleaseCountResultVo> resultVos) {
		List<ReleaseCountResultVo> resultVoNews = new ArrayList<ReleaseCountResultVo>();
		List<String> resultVoIds =new ArrayList<String>();
		System.out.println("resultVos长度:"+resultVos.size());
		for (ReleaseCountResultVo resultVo : resultVos) {
			resultVoIds.add(resultVo.getDate());
			resultVoNews.add(resultVo);
		}
		Calendar endAdd1 = Calendar.getInstance();
		endAdd1.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
		endAdd1.setTime(new Date(end.getTime()));
		endAdd1.add(Calendar.DAY_OF_MONTH, +1);
		Date endAdd1Date = endAdd1.getTime();
		for (long i = start.getTime(); i <= endAdd1Date.getTime();) {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
			sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
			String startTimeFormat = sdf.format(i);
			System.out.println("nowiString: "+String.valueOf(i));
			System.out.println("nowformatString: "+startTimeFormat);
			if(i<=end.getTime()&&!resultVoIds.contains(startTimeFormat)) {
				resultVoIds.add(startTimeFormat);
				ReleaseCountResultVo releaseCountResultVo = new ReleaseCountResultVo();
				releaseCountResultVo.setDate(startTimeFormat);
				releaseCountResultVo.setRelease(0);
				resultVoNews.add(releaseCountResultVo);
				System.out.println("addString: "+startTimeFormat);
			}
			Calendar rightNow = Calendar.getInstance();
			rightNow.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
			rightNow.setTime(new Date(i));
			rightNow.add(Calendar.DAY_OF_MONTH, +1);
			Date dt1 = rightNow.getTime();
			i = dt1.getTime();
		}
		return resultVoNews;
	}

细节和坑

Java中的时间戳有个需要注意的细节是通过new Date().getTime()获取的时间是毫秒级的
如果需要秒级的除以1000才能得到秒级的unix时间戳。

// pure java
(int) (System.currentTimeMillis() / 1000)
// joda
(int) (DateTime.now().getMillis() / 1000)

而接收到秒级的unix时间戳后也需要乘以1000才能用于转换为 Java的Date类型,否则会精度不对应,导致转换出来的时间是1970年。

所以在交互时一定要沟通好是使用毫秒级的时间戳还是秒级的。

更多参考

更多Spring Data MongoDB中的aggregate操作可查看
Spring Data MongoDB - Reference Documentation

注意事项

因为对aggregate的支持是新特性,所以需要注意版本问题。

一般要求mongodb版本3.6以上,spring的用法还需要注意引入的包版本,例如dateFromString就需要2.1的版本

关于Java中的DateOperators.DateFromString
Class DateOperators.DateFromString

关于mongo-js中的dateFromString
https://docs.mongodb.com/manual/reference/operator/aggregation/dateFromString/

关于mongo-js中的dateToString
https://docs.mongodb.com/manual/reference/operator/aggregation/dateToString/

aggregate可用表达式

Aggregation Pipeline Quick Reference

如需转载请注明出处: mongodb aggregate按日期分组统计及spring mongo实现

猜你喜欢

转载自blog.csdn.net/q383965374/article/details/85606222
今日推荐