日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

MongoDB 聚合查詢在數據統計中的應用

作者:一勺菠蘿丶 更新時間: 2024-01-30 編程語言

引言

在處理大量數據時,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

  • 上一篇:沒有了
  • 下一篇:沒有了
欄目分類
最近更新