This type of use case is harder than it sounds in Kibana for the reasons you found- it requires math across buckets. It's also difficult because of the global time filter which intersects with any buckets. When I have this kind of problem I write a Vega visualization from this template:
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"data": {
"url": {
"index": "metricbeat-*",
"body": {
"aggs": {
"f": {
"filters": {
"filters": {
"current": {
"range": { "@timestamp": { "%timefilter%": true } }
},
"previous": {
"range": {
"@timestamp": {
"%timefilter%": true, "shift": -4, "unit": "week"
}
}
}
}
}
}
},
"size": 0
}
},
"format": {
"type": "json",
"property": "aggregations.f.buckets"
},
}
"transform": [
{
"calculate": "if (datum.previous.doc_count, datum.current.doc_count / datum.previous.doc_count, null)",
"as": "percent_diff"
}
]
"vconcat": [
{
"title": "Percent change from 4 weeks ago"
"width": "container"
height: 40
"mark": "text",
"encoding": {
"text": {
"field": "percent_diff"
"format": "0.2%"
},
"size": { value: 32 },
"align": { "value": "center" },
}
},
{
"title": "Current value"
"width": "container"
"mark": "text",
"encoding": {
"text": {
field: "current.doc_count",
format: ","
},
"fill": { "value": "black" },
"fontWeight": { "value": "bold" },
"align": { "value": "center" },
}
}
],
}