Search code examples
pathjmespathjmesa

Jmespath calculate average value


Using jmespath I want to find the average value from the response. First I need to check if the response is empty or not. If the response is not empty then find the average of points. I'm trying using this expression "[].points[].value.doubleValue | avg(@)" but the expression fails when the response is empty.

[
  {
    "metric": {
      "labels": {
        "instance_name": "crpsmokes-f4a35bd07"
      },
      "type": "compute.googleapis.com/instance/cpu/utilization"
    },
    "resource": {
      "type": "gce_instance",
      "labels": {
        "instance_id": "1507883580111256528",
        "project_id": "rightscale.com:resat-prem",
        "zone": "asia-east1-a"
      }
    },
    "metricKind": "GAUGE",
    "valueType": "DOUBLE",
    "points": [
      {
        "interval": {
          "startTime": "2020-08-24T22:44:00Z",
          "endTime": "2020-08-24T22:44:00Z"
        },
        "value": {
          "doubleValue": 0.00096042153551181288
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:43:00Z",
          "endTime": "2020-08-24T22:43:00Z"
        },
        "value": {
          "doubleValue": 0.0019398753629962092
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:42:00Z",
          "endTime": "2020-08-24T22:42:00Z"
        },
        "value": {
          "doubleValue": 0.0019628626377153086
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:41:00Z",
          "endTime": "2020-08-24T22:41:00Z"
        },
        "value": {
          "doubleValue": 0.0019210491946092627
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:40:00Z",
          "endTime": "2020-08-24T22:40:00Z"
        },
        "value": {
          "doubleValue": 0.0018963341116292535
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:39:00Z",
          "endTime": "2020-08-24T22:39:00Z"
        },
        "value": {
          "doubleValue": 0.0019406727388149156
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:38:00Z",
          "endTime": "2020-08-24T22:38:00Z"
        },
        "value": {
          "doubleValue": 0.0019643314070845481
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:37:00Z",
          "endTime": "2020-08-24T22:37:00Z"
        },
        "value": {
          "doubleValue": 0.0019477523878447774
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:36:00Z",
          "endTime": "2020-08-24T22:36:00Z"
        },
        "value": {
          "doubleValue": 0.0019031642638339008
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:35:00Z",
          "endTime": "2020-08-24T22:35:00Z"
        },
        "value": {
          "doubleValue": 0.0019058478564064766
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:34:00Z",
          "endTime": "2020-08-24T22:34:00Z"
        },
        "value": {
          "doubleValue": 0.0019202072242573347
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:33:00Z",
          "endTime": "2020-08-24T22:33:00Z"
        },
        "value": {
          "doubleValue": 0.0019225147371779864
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:32:00Z",
          "endTime": "2020-08-24T22:32:00Z"
        },
        "value": {
          "doubleValue": 0.0019468938226940699
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:31:00Z",
          "endTime": "2020-08-24T22:31:00Z"
        },
        "value": {
          "doubleValue": 0.0018969617509197196
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:30:00Z",
          "endTime": "2020-08-24T22:30:00Z"
        },
        "value": {
          "doubleValue": 0.0019062680485175084
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:29:00Z",
          "endTime": "2020-08-24T22:29:00Z"
        },
        "value": {
          "doubleValue": 0.0019370667695042699
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:28:00Z",
          "endTime": "2020-08-24T22:28:00Z"
        },
        "value": {
          "doubleValue": 0.0020045164017271112
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:27:00Z",
          "endTime": "2020-08-24T22:27:00Z"
        },
        "value": {
          "doubleValue": 0.0020103660806600012
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:26:00Z",
          "endTime": "2020-08-24T22:26:00Z"
        },
        "value": {
          "doubleValue": 0.0019040336136034359
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:25:00Z",
          "endTime": "2020-08-24T22:25:00Z"
        },
        "value": {
          "doubleValue": 0.0019056174446023513
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:24:00Z",
          "endTime": "2020-08-24T22:24:00Z"
        },
        "value": {
          "doubleValue": 0.0019463043436514244
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:23:00Z",
          "endTime": "2020-08-24T22:23:00Z"
        },
        "value": {
          "doubleValue": 0.0019467681385854972
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:22:00Z",
          "endTime": "2020-08-24T22:22:00Z"
        },
        "value": {
          "doubleValue": 0.0019057447090541284
        }
      },
      {
        "interval": {
          "startTime": "2020-08-24T22:21:00Z",
          "endTime": "2020-08-24T22:21:00Z"
        },
        "value": {
          "doubleValue": 0.0018988823662930087
        }
      }]
  }
  ]


Solution

  • The avg function will generate an error if it receives anything that isn't a list consisting of one or more numbers, and only of numbers. There are three particular cases that you might want to handle:

    1. The list is empty. E.g. there were no metrics or the metrics had no points in them.
    2. The list contains non-numbers. E.g. there are null values in value or value.doubleValue of a point.
    3. There is no list. E.g. the entire response was null.

    I am going to assume for simplicity that case 1 is the only one of these that is considered normal, but you could extend this approach to cover the other cases too if you need to.

    Here's a query that should result in null instead of an error when there are no items in the list:

    [].points[].value.doubleValue | (@ != `[]`) && avg(@) || `null`
    

    In general, we can use the expression A && B || C to mean "if A is true, then use the value of B, otherwise use the value of C", so long as B will not be a false value when A is a truth-like value. (Note that unlike some other languages, in JMESPath zero is not considered a false value.) In our expression, @ != `[]` is true when the list has values and false when it is empty. So when it's true the right side of the && will be evaluated to calculate the average, and when it's false the && will short-circuit and simply be false without attempting to calculate the average. Finally the || `null` will keep the value of the average calculation but replace false with null.