You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

在Elasticsearch中计算订单发货前平均等待时间的技术方案咨询

计算Elasticsearch中订单发货前的平均等待时间的可行方案

首先得明确咱们的核心需求:从order_history数组里提取每个订单从初始下单状态首次进入发货前状态的时间差,再计算所有订单的这个差值的平均值。结合你的订单数据,我给出两种可行方案:


方案一:直接使用嵌套聚合实时计算

这个方案不需要修改原始数据,完全通过Elasticsearch的聚合能力实时计算结果。假设你的订单索引中order_historynested类型(如果不是,需要先修改映射为nested,否则数组聚合会出问题),具体DSL如下:

{
  "size": 0,
  "aggs": {
    "group_by_order": {
      "terms": {
        "field": "id",
        "size": 10000  // 根据你的订单总数调整,确保覆盖所有订单;数据量极大时建议用composite聚合分页
      },
      "aggs": {
        "traverse_history": {
          "nested": {
            "path": "order_history"
          },
          "aggs": {
            // 筛选初始状态(比如你的数据里status_id=3是下单状态)的最早时间
            "initial_order_time": {
              "filter": {
                "term": {
                  "order_history.status_id": 3
                }
              },
              "aggs": {
                "first_initial_time": {
                  "min": {
                    "field": "order_history.created_at"
                  }
                }
              }
            },
            // 筛选发货前状态(比如status_id=6是准备发货状态)的最早时间
            "pre_shipment_start_time": {
              "filter": {
                "term": {
                  "order_history.status_id": 6
                }
              },
              "aggs": {
                "first_pre_ship_time": {
                  "min": {
                    "field": "order_history.created_at"
                  }
                }
              }
            },
            // 计算单个订单的等待时间(转换为小时,可自行调整单位)
            "single_order_wait_time": {
              "bucket_script": {
                "buckets_path": {
                  "initial": "initial_order_time>first_initial_time",
                  "preShip": "pre_shipment_start_time>first_pre_ship_time"
                },
                "script": "(params.preShip.getTime() - params.initial.getTime()) / (1000 * 60 * 60)"
              }
            }
          }
        },
        // 过滤掉没有有效时间差的订单(比如缺少初始/发货前状态的)
        "filter_valid_orders": {
          "bucket_selector": {
            "buckets_path": {
              "waitTime": "traverse_history>single_order_wait_time"
            },
            "script": "params.waitTime != null && params.waitTime > 0"
          }
        }
      }
    },
    // 计算所有有效订单的平均等待时间
    "average_wait_time": {
      "avg_bucket": {
        "buckets_path": "group_by_order>traverse_history>single_order_wait_time"
      }
    }
  }
}

关键说明:

  • 替换status_id:根据你的业务实际,把36换成对应的初始状态、发货前状态的ID。
  • 时间单位调整:脚本里的(1000 * 60 * 60)是把毫秒转换成小时,换成(1000 * 60)就是分钟,直接用1000就是秒。
  • 数据量适配:如果订单数超过size设置的值,建议改用composite聚合来分页处理,避免内存溢出。

方案二:预处理字段,简化后续查询

如果需要频繁查询这个平均值,或者数据量极大,先预处理每个订单的等待时间字段会更高效:

步骤1:给每个订单添加wait_time_before_shipment字段

_update_by_query批量计算并写入字段:

POST /your_order_index/_update_by_query
{
  "script": {
    "source": """
      def initialTime = null;
      def preShipTime = null;
      // 遍历订单历史,找到初始状态和首次发货前状态的时间
      for (def historyItem : ctx._source.order_history) {
        // 记录初始状态的最早时间
        if (historyItem.status_id == 3 && (initialTime == null || historyItem.created_at.getTime() < initialTime.getTime())) {
          initialTime = historyItem.created_at;
        }
        // 记录首次进入发货前状态的时间
        if (historyItem.status_id == 6 && (preShipTime == null || historyItem.created_at.getTime() < preShipTime.getTime())) {
          preShipTime = historyItem.created_at;
        }
      }
      // 计算并写入等待时间(单位:小时)
      if (initialTime != null && preShipTime != null) {
        ctx._source.wait_time_before_shipment = (preShipTime.getTime() - initialTime.getTime()) / (1000 * 60 * 60);
      } else {
        ctx._source.wait_time_before_shipment = null;
      }
    """,
    "lang": "painless"
  }
}

步骤2:直接查询平均值

之后查询就非常简单了:

{
  "size": 0,
  "aggs": {
    "average_wait_time": {
      "avg": {
        "field": "wait_time_before_shipment",
        "missing": 0  // 可以忽略缺失值,或者设置为0,根据需求调整
      }
    }
  }
}

额外注意事项:

  1. 字段类型检查:确保order_history.created_at是Elasticsearch的date类型,否则脚本中的getTime()会报错。
  2. 时区处理:如果业务需要考虑时区,需要在时间计算时添加时区偏移,比如在Painless脚本中转换时间时指定时区。
  3. 状态逻辑确认:如果你的业务中发货前的状态不止一个,或者初始状态有多个,需要调整筛选条件(比如用terms代替term)。

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

火山引擎 最新活动