You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Elasticsearch聚合中获取每月最后一个工作日的统计数据?

解决Elasticsearch按每月最后一个工作日聚合的问题

方法一:脚本计算+Top Hits聚合(实时处理)

直接在聚合中通过Painless脚本计算每个my_date所属月份的最后一个工作日,再按wallet和月份分组,用top_hits提取目标日期的value数据:

{
  "size": 0,
  "aggs": {
    "group_by_wallet": {
      "terms": {
        "field": "wallet.keyword"
      },
      "aggs": {
        "group_by_month": {
          "date_histogram": {
            "field": "my_date",
            "calendar_interval": "month",
            "format": "yyyy-MM"
          },
          "aggs": {
            "last_workday_data": {
              "top_hits": {
                "size": 1,
                "sort": [
                  {
                    "_script": {
                      "type": "number",
                      "script": {
                        "source": """
                          // 获取当月最后一天
                          ZonedDateTime endOfMonth = doc['my_date'].value.withDayOfMonth(doc['my_date'].value.lengthOfMonth());
                          // 倒推跳过周末,找到最后一个工作日
                          while (endOfMonth.getDayOfWeek() == DayOfWeek.SATURDAY || endOfMonth.getDayOfWeek() == DayOfWeek.SUNDAY) {
                            endOfMonth = endOfMonth.minusDays(1);
                          }
                          // 计算当前日期与最后一个工作日的天数差,差值为0的就是目标文档
                          return ChronoUnit.DAYS.between(doc['my_date'].value, endOfMonth);
                        """
                      },
                      "order": "asc"
                    }
                  }
                ],
                "_source": {
                  "includes": ["my_date", "value"]
                }
              }
            }
          }
        }
      }
    }
  }
}

脚本逻辑说明

  • 先定位my_date所在月份的最后一天
  • 从最后一天倒推,跳过周六、周日,得到当月最后一个工作日
  • 计算当前文档日期到该工作日的天数差,差值为0的即为要提取的目标数据
  • 按差值升序排序,取第一条就是对应月份最后一个工作日的记录

方法二:预处理字段(性能优化)

如果数据量较大,实时脚本聚合性能不足,可以在数据写入时提前计算并存储last_workday_of_month字段:

  1. 创建预处理管道:
PUT _ingest/pipeline/add_last_workday
{
  "processors": [
    {
      "script": {
        "source": """
          ZonedDateTime date = ctx.my_date;
          ZonedDateTime endOfMonth = date.withDayOfMonth(date.lengthOfMonth());
          while (endOfMonth.getDayOfWeek() == DayOfWeek.SATURDAY || endOfMonth.getDayOfWeek() == DayOfWeek.SUNDAY) {
            endOfMonth = endOfMonth.minusDays(1);
          }
          ctx.last_workday_of_month = endOfMonth.toInstant().toEpochMilli();
        """
      }
    }
  ]
}
  1. 写入数据时指定管道:
PUT my_index/_doc/1?pipeline=add_last_workday
{
  "my_date": "2024-03-07",
  "wallet": "wallet_1",
  "value": 100
}
  1. 聚合查询时直接按预处理字段分组:
{
  "size": 0,
  "aggs": {
    "group_by_wallet": {
      "terms": {
        "field": "wallet.keyword"
      },
      "aggs": {
        "group_by_last_workday": {
          "terms": {
            "field": "last_workday_of_month",
            "format": "yyyy-MM-dd"
          },
          "aggs": {
            "total_value": {
              "sum": {
                "field": "value"
              }
            }
          }
        }
      }
    }
  }
}

补充:按月份汇总全量数据

如果你的需求是汇总每个wallet当月所有每日value的总和(而非仅取最后一个工作日数据),直接用普通日期直方图聚合即可:

{
  "size": 0,
  "aggs": {
    "group_by_wallet": {
      "terms": {
        "field": "wallet.keyword"
      },
      "aggs": {
        "monthly_total": {
          "date_histogram": {
            "field": "my_date",
            "calendar_interval": "month",
            "format": "yyyy-MM"
          },
          "aggs": {
            "sum_value": {
              "sum": {
                "field": "value"
              }
            }
          }
        }
      }
    }
  }
}

内容的提问来源于stack exchange,提问作者Jaime Yule

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 18:16:25