網站首頁 編程語言 正文
引言
在處理大量數據時,MongoDB 的聚合框架是一個非常強大的工具。它允許執行復雜的數據聚合和轉換任務。本文將通過一個實際案例來展示如何使用 MongoDB 的聚合框架來統計特定日期范圍內每月的記錄數量。
使用場景
在本例中,我們面對的是一個專利數據庫。我們的任務是統計在給定日期范圍內(以年月格式提供,例如“202301”至“202312”),每個月的專利狀態變更記錄數。挑戰在于數據庫中的日期是以“yyyy.MM.dd”格式存儲的,而查詢參數是以“yyyyMM”格式提供的。
原代碼
public FlztStatisticVo flzdStatistics(PatentStatisticRequest request) {
String startmonth = request.getStartmonth();
String endmonth = request.getEndmonth();
if (StringUtils.isEmpty(startmonth) || StringUtils.isEmpty(endmonth)) {
throw new RuntimeException("開始日期和截止日期都不能為空");
}
// 生成符合數據庫格式的月份范圍
LocalDate[] dateRange = convertMonthRangeToDates(startmonth, endmonth);
DateTimeFormatter dbFormatter = DateTimeFormatter.ofPattern("yyyy.MM.dd");
// 構建聚合查詢
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("lawdate")
.gte(dateRange[0].format(dbFormatter))
.lte(dateRange[1].format(dbFormatter))),
Aggregation.project("lawdate")
.andExpression("substr(lawdate, 0, 4)").as("year") // 提取年份
.andExpression("substr(lawdate, 5, 2)").as("month"), // 提取月份
Aggregation.group(Fields.fields().and("year").and("month"))
.count().as("flzt"),
Aggregation.project().andExpression("concat(_id.year, _id.month)").as("month").andInclude("flzt"),
Aggregation.sort(Sort.Direction.ASC, "month") // 按月份排序
);
// 執行聚合查詢
AggregationResults<FlztVo> results = fullTextFLZTMongoTemplate.aggregate(aggregation, "patents", FlztVo.class);
// 處理聚合查詢結果
List<String> months = new ArrayList<>(); // 用于輸出的月份列表,格式為 yyyyMM
List<Integer> flzts = new ArrayList<>();
Set<String> foundMonths = new HashSet<>();
for (FlztVo result : results.getMappedResults()) {
months.add(result.getMonth());
flzts.add(result.getFlzt());
foundMonths.add(result.getMonth());
}
// 創建一個映射來保存月份和對應的記錄數
Map<String, Integer> monthToFlztMap = new HashMap<>();
for (int i = 0; i < months.size(); i++) {
monthToFlztMap.put(months.get(i), flzts.get(i));
}
// 檢查并添加缺失的月份
for (LocalDate date = dateRange[0]; !date.isAfter(dateRange[1]); date = date.plusMonths(1)) {
String monthStr = date.format(DateTimeFormatter.ofPattern("yyyyMM"));
if (!monthToFlztMap.containsKey(monthStr)) {
months.add(monthStr);
monthToFlztMap.put(monthStr, 0);
}
}
// 對months進行排序
Collections.sort(months);
// 重構flzts列表以確保記錄數與月份的排序一致
List<Integer> sortedFlzt = months.stream()
.map(monthToFlztMap::get)
.collect(Collectors.toList());
// 使用sortedFlzt和months作為返回結果
FlztStatisticVo vo = new FlztStatisticVo();
vo.setFlzt(sortedFlzt);
vo.setMonth(months);
return vo;
}
/**
* 生成符合數據庫格式的月份范圍
*
* @param startMonth 開始月份 202301
* @param endMonth 結束月份 202312
* @return [2023-01-01, 2023-12-31]
*/
private LocalDate[] convertMonthRangeToDates(String startMonth, String endMonth) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM");
YearMonth startYearMonth = YearMonth.parse(startMonth, formatter);
YearMonth endYearMonth = YearMonth.parse(endMonth, formatter);
LocalDate startDate = startYearMonth.atDay(1); // 月份的第一天
LocalDate endDate = endYearMonth.atEndOfMonth(); // 月份的最后一天
return new LocalDate[]{startDate, endDate};
}
代碼解釋
轉換日期格式
首先,我們需要一個方法 convertMonthRangeToDates
將用戶提供的年月格式轉換為 LocalDate 對象,這樣可以更方便地與數據庫中存儲的日期進行比較。
private LocalDate[] convertMonthRangeToDates(String startMonth, String endMonth) {
// ... 方法實現 ...
}
這個方法接受開始和結束年月,將它們解析為 LocalDate
對象,分別代表查詢范圍的第一天和最后一天。
構建聚合查詢
有了適當的日期范圍后,我們構建了一個聚合查詢,該查詢篩選出所需的日期范圍內的記錄,然后按月份對記錄進行分組和計數。
Aggregation aggregation = Aggregation.newAggregation(
// ... 聚合步驟 ...
);
聚合查詢的關鍵步驟包括匹配符合日期范圍的記錄、提取年份和月份、按月份分組、計數每組的記錄數,以及按月份排序結果。
處理聚合查詢結果
在查詢執行后,我們需要處理聚合查詢的結果。此過程包括確保包含了查詢日期范圍內的所有月份(即使某些月份在數據庫中沒有記錄),并將這些月份的記錄數設為0。
// ... 執行聚合查詢和結果處理 ...
我們創建了一個映射來保存每個月份及其對應的記錄數,然后為未在數據庫中找到的月份添加了記錄數為0的條目。
代碼解釋
flzdStatistics
方法
這個方法是統計邏輯的主體。它接受包含起始和結束月份的請求對象,并構建一個聚合查詢來計算每個月的記錄數。我們處理聚合查詢結果,確保包括所有月份,然后返回一個包含月份和每月記錄數的數據對象。
convertMonthRangeToDates
方法
此方法用于轉換查詢的年月范圍為 LocalDate
對象。通過解析起始和結束年月,我們得到代表查詢范圍的兩個 LocalDate
對象,分別表示范圍的起始和結束。
結論
本文展示了如何在 MongoDB 中使用聚合查詢來處理復雜的數據統計任務。我們成功地將用戶提供的年月格式轉換為適用于數據庫查詢的格式,并確保了結果中包含了整個查詢范圍內的所有月份。這種方法適用于需要進行時間序列分析的各種場景,尤其是當數據在某些時間段內可能不存在時。
原文鏈接:https://blog.csdn.net/weixin_39973810/article/details/135819083
- 上一篇:沒有了
- 下一篇:沒有了
相關推薦
- 2022-04-15 C語言的程序環境與預處理你真的了解嗎_C 語言
- 2022-04-12 解決error: failed to push some refs to ‘xxx(遠程倉庫)‘
- 2022-06-09 Nginx速查手冊及常見問題_nginx
- 2022-04-11 項目視圖以及項目小部件的基本用法(View表和Widget表)
- 2022-06-01 C#類型轉換之自定義隱式轉換和顯式轉換_C#教程
- 2022-07-07 Python數據分析Matplotlib?柱狀圖繪制_python
- 2022-09-05 docker-compose啟動redis集群的實現步驟_docker
- 2024-03-18 JedisDataException: READONLY You can‘t write again
- 欄目分類
-
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支