feat(promql): count native histograms in count_values by their Prometheus text (#9855)

I hereby agree to the terms of the [GreptimeDB
CLA](https://github.com/GreptimeTeam/.github/blob/main/CLA.md).

## Refer to a related PR or issue link (optional)

Native operator kernels (#27). Builds on the Go float formatting from
#9840 (merged). Removes the histogram caveat noted in #9854.

## What's changed and what's your intention?

Prometheus `count_values` counts a native histogram sample under the
text `FloatHistogram.String()` writes it as. The native kernel refused
histogram input with `NotImplemented`; it now counts histograms the same
way.

- `go_format::format_float_g`: Go's `%g` / `%v` for a float64 (shortest
digits, exponent below 1e-4 and from 1e6 on).
`format_open_metrics_float` now builds on it, unchanged.
- `histogram_eval::histogram_string`: Go `FloatHistogram.String`. Count
and sum come first, then the non-empty buckets from the lowest up
(negative, zero, positive), each written as `Bucket.String`. Bounds stay
as the bucket index gives them (not clipped by the zero bucket), with
the inclusiveness `baseBucketIterator.at` gives, so custom buckets print
`[-Inf,b0]` first.
- `count_values` keys a histogram by its text, so equal histograms share
a group. Series are validated with `map::validate_grid_series` (from
#9845), which accepts any sample type.

### Tests

- Golden `histogram_eval/string_cases.txt` (17 KB, 47 cases) from
`promql-compat/cmd/narthex-golden-histstring`, Prometheus v3.15.0. Fixed
cases cover the zero bucket at threshold 0 (`[-0,0]`), negative bucket
order, the exponent form, custom bounds, ±Inf and NaN, a zero-length
span, a NaN bucket count, and buckets under a wide zero threshold; 40
random cases follow. Dropping the negative-bucket reversal or the
custom-bucket inclusiveness fails it.
- `count_values` groups two equal histograms together and a float apart.

## PR Checklist
Please convert it to a draft if some of the following conditions are not
met.

- [x] I have written the necessary rustdoc comments.
- [x] I have added the necessary unit tests and integration tests.
- [ ] This PR requires documentation updates.
- [x] API changes are backward compatible.
- [x] Schema or data changes are backward compatible.
- [ ] This PR needs to be backported to release branches, and I have
added the `backport-<target>` labels (e.g. `backport-v1.3` targets
`release/v1.3`).
This commit is contained in:
dennis zhuang
2026-10-22 17:40:50 +08:00
committed by GitHub
7 changed files with 242 additions and 36 deletions
+39 -16
View File
@@ -35,7 +35,8 @@ use datatypes::series::samples::{SampleRef, Series, SeriesSchema};
use crate::go_format::format_float_f;
use crate::grid_series::{GridSeriesBuilder, external};
use crate::map::{drop_empty_labels, validate_float_series, validate_scalar};
use crate::histogram_eval::histogram_string;
use crate::map::{drop_empty_labels, validate_float_series, validate_grid_series, validate_scalar};
use crate::range_eval::quantile;
/// Float aggregations without a parameter.
@@ -277,7 +278,8 @@ pub fn aggregate_quantile(
/// `count_values(label, v)`: at each grid point, the number of samples in each group of series
/// that share a value, with the value written to `label` in Prometheus' `FormatFloat(v, 'f', -1)`
/// form. With `by`, `label` is part of the grouping; with `without`, it is dropped if listed.
/// form, or for a native histogram as its `FloatHistogram.String`. With `by`, `label` is part of
/// the grouping; with `without`, it is dropped if listed.
pub fn count_values(
schema: &Arc<SeriesSchema>,
input: &[Series],
@@ -298,7 +300,11 @@ pub fn count_values(
}
without => without,
};
validate_input(schema, input, checked)?;
// Unlike the other aggregations, this one reads histogram samples too.
validate_grouping(schema, checked)?;
for series in input {
validate_grid_series(schema, series)?;
}
if label.is_empty() {
return Err(DataFusionError::Execution(format!(
"invalid label name {label:?}"
@@ -319,20 +325,26 @@ pub fn count_values(
let mut groups: Counts = Vec::new();
for series in input {
// Equal bits format to the same label value, so each distinct value of a series
// resolves its group once.
let mut group_of_value: HashMap<u64, usize> = HashMap::new();
// resolves its group once. A histogram is keyed by the text it is written as.
let mut group_of_value: HashMap<Value, usize> = HashMap::new();
for (field_index, field) in series.fields.iter().enumerate() {
for ((point, _), sample) in series.time.positions().zip(field.iter()) {
let SampleRef::Float(value) = sample else {
continue;
let key = match sample {
SampleRef::Missing => continue,
SampleRef::Float(value) => Value::Float(value.to_bits()),
SampleRef::Histogram(h) => Value::Histogram(histogram_string(&h)?),
};
let group = match group_of_value.entry(value.to_bits()) {
let group = match group_of_value.entry(key) {
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
// Prometheus writes a float in Go's `'f'` form and a histogram as
// `FloatHistogram.String` does.
let text = match entry.key() {
Value::Float(bits) => format_float_f(f64::from_bits(*bits)),
Value::Histogram(text) => text.clone(),
};
let mut metric = series.metric.clone();
metric
.set(label, Arc::from(format_float_f(value)))
.map_err(external)?;
metric.set(label, Arc::from(text)).map_err(external)?;
let metric = grouping.apply(&metric)?;
let index = *group_index.entry(label_key(&metric)).or_insert_with(|| {
groups.push((metric, BTreeMap::new()));
@@ -370,18 +382,29 @@ fn count_series(schema: &Arc<SeriesSchema>, groups: Counts) -> Result<Vec<Series
Ok(output)
}
/// The value of a `count_values` sample, as it is grouped.
#[derive(PartialEq, Eq, Hash)]
enum Value {
Float(u64),
Histogram(String),
}
/// Schema validation is repeated because Series exposes its fields publicly.
fn validate_input(schema: &SeriesSchema, input: &[Series], grouping: Grouping<'_>) -> Result<()> {
validate_grouping(schema, grouping)?;
for series in input {
validate_float_series(schema, series)?;
}
Ok(())
}
fn validate_grouping(schema: &SeriesSchema, grouping: Grouping<'_>) -> Result<()> {
if schema.eval_grid.is_none() {
return Err(DataFusionError::Execution(
"Aggregate requires an evaluated grid".into(),
));
}
grouping.validate(schema)?;
for series in input {
validate_float_series(schema, series)?;
}
Ok(())
grouping.validate(schema)
}
impl Grouping<'_> {
+45
View File
@@ -1391,3 +1391,48 @@ fn count_values_keeps_neighbours_of_a_power_of_two_apart() {
]
);
}
/// Prometheus counts a histogram sample under the text `FloatHistogram.String` writes it as, so
/// equal histograms share a group and a float beside them has its own.
#[test]
fn count_values_counts_histograms_by_their_text() {
use crate::grid_series::reference::OwnedHistogram;
use crate::grid_series::{GridSample, GridSeriesBuilder};
use crate::histogram_eval::float_histogram::FloatHistogram;
use crate::histogram_eval::histogram_string;
let schema = schema_with_grid_len(&["value"], 1);
let histogram =
OwnedHistogram::parse("0/0/0/4000000000000000/3ff0000000000000/0/1_1/4000000000000000///");
let text = histogram_string(&histogram.view()).unwrap();
let series = |job: &str, sample: GridSample| {
let mut builder = GridSeriesBuilder::try_new(schema.clone()).unwrap();
builder.push_samples(0, &[Some(sample)]).unwrap();
builder
.finish(metric("m", &[("job", job)]))
.unwrap()
.unwrap()
};
let sample = || {
GridSample::Histogram(Box::new(
FloatHistogram::try_from_view(&histogram.view()).unwrap(),
))
};
let input = [
series("a", sample()),
series("b", sample()),
series("c", GridSample::Float(2.0)),
];
let mut counts: Vec<(String, f64)> = count_values(&schema, &input, "v", Grouping::By(&[]))
.unwrap()
.iter()
.map(|series| {
let SampleRef::Float(count) = series.fields.as_slice()[0].sample(0) else {
panic!("a count is a float");
};
(series.metric.get("v").unwrap().to_string(), count)
})
.collect();
counts.sort_by(|a, b| a.0.cmp(&b.0));
assert_eq!(counts, [("2".to_string(), 1.0), (text, 2.0)]);
}
+26 -19
View File
@@ -44,31 +44,38 @@ pub(crate) fn format_open_metrics_float(value: f64) -> String {
if let Some(text) = special(value) {
return text.to_string();
}
let (negative, digits, point) = shortest(value);
// The shortest `'g'` format switches to an exponent below 1e-4 and from 1e6 on.
let exponent = point - 1;
let mut text = if !(-4..6).contains(&exponent) {
let mut text = String::from(if negative { "-" } else { "" });
text.push_str(&digits[..1]);
if digits.len() > 1 {
text.push('.');
text.push_str(&digits[1..]);
}
text.push_str(&format!(
"e{}{:02}",
if exponent < 0 { '-' } else { '+' },
exponent.abs()
));
text
} else {
positional(negative, &digits, point)
};
let mut text = format_float_g(value);
if !text.contains(['e', '.']) {
text.push_str(".0");
}
text
}
/// Go's `strconv.FormatFloat(value, 'g', -1, 64)`, which `%g` and `%v` print a float in: the
/// shortest digits, with an exponent of at least two digits below 1e-4 and from 1e6 on.
pub(crate) fn format_float_g(value: f64) -> String {
if let Some(text) = special(value) {
return text.to_string();
}
let (negative, digits, point) = shortest(value);
let exponent = point - 1;
if (-4..6).contains(&exponent) {
return positional(negative, &digits, point);
}
let mut text = String::from(if negative { "-" } else { "" });
text.push_str(&digits[..1]);
if digits.len() > 1 {
text.push('.');
text.push_str(&digits[1..]);
}
text.push_str(&format!(
"e{}{:02}",
if exponent < 0 { '-' } else { '+' },
exponent.abs()
));
text
}
fn special(value: f64) -> Option<&'static str> {
match value {
f64::INFINITY => Some("+Inf"),
+6
View File
@@ -254,6 +254,12 @@ fn has_histograms(series: &Series) -> bool {
})
}
/// A native histogram as Go's `FloatHistogram.String` writes it, the label value `count_values`
/// gives a histogram sample.
pub(crate) fn histogram_string(h: &HistogramView<'_>) -> Result<String> {
native::go_string(h)
}
/// A function of one native histogram.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NativeHistogramFunction {
+63
View File
@@ -19,6 +19,7 @@ use datafusion::error::{DataFusionError, Result};
use datatypes::series::histogram::{BucketSideView, HistogramView};
use crate::functions::kahan_inc;
use crate::go_format::format_float_g;
use crate::histogram_eval::bounds::EXPONENTIAL_BOUNDS;
pub(super) const CUSTOM_BUCKETS_SCHEMA: i32 = -53;
@@ -28,6 +29,8 @@ pub(super) struct NativeBucket {
lower: f64,
upper: f64,
count: f64,
/// The bucket's index on its side; meaningless for the zero bucket.
index: i32,
}
/// The buckets of `h` from the lowest to the highest, or the reverse: negative buckets, the zero
@@ -57,6 +60,7 @@ pub(super) fn all_buckets(
lower: -h.zero_threshold,
upper: h.zero_threshold,
count: h.zero_count,
index: 0,
});
}
side(h, high, !reverse, out)?;
@@ -107,16 +111,74 @@ fn bucket(h: &HistogramView<'_>, idx: i32, count: f64, positive: bool) -> Result
lower: bound(idx - 1)?,
upper: bound(idx)?,
count,
index: idx,
}
} else {
NativeBucket {
lower: -bound(idx)?,
upper: -bound(idx - 1)?,
count,
index: idx,
}
})
}
/// Go `FloatHistogram.String`, the form `count_values` writes a histogram value in: the count and
/// sum, then the buckets holding observations from the lowest up -- negative, zero, positive --
/// each as `[lower,upper):count` with its bounds' inclusiveness, every number in Go's `%g`.
/// Unlike [`all_buckets`], the buckets keep the bounds their index gives, unclipped by the zero
/// bucket.
pub(super) fn go_string(h: &HistogramView<'_>) -> Result<String> {
let mut text = format!(
"{{count:{}, sum:{}",
format_float_g(h.count),
format_float_g(h.sum)
);
let custom = h.schema == CUSTOM_BUCKETS_SCHEMA;
let mut buckets = Vec::new();
side(h, h.negative, false, &mut buckets)?;
for bucket in buckets.iter().rev().filter(|bucket| bucket.count != 0.0) {
push_bucket(&mut text, bucket, custom);
}
if h.zero_count != 0.0 {
if custom {
return Err(DataFusionError::Execution(
"histograms with custom buckets have no zero bucket".into(),
));
}
text.push_str(&format!(
", [{},{}]:{}",
format_float_g(-h.zero_threshold),
format_float_g(h.zero_threshold),
format_float_g(h.zero_count)
));
}
buckets.clear();
side(h, h.positive, true, &mut buckets)?;
for bucket in buckets.iter().filter(|bucket| bucket.count != 0.0) {
push_bucket(&mut text, bucket, custom);
}
text.push('}');
Ok(text)
}
/// Go `Bucket.String`, with the inclusiveness `baseBucketIterator.at` gives the bucket.
fn push_bucket(text: &mut String, bucket: &NativeBucket, custom: bool) {
let (lower_inclusive, upper_inclusive) = if custom {
(bucket.index == 0, true)
} else {
(bucket.lower < 0.0, bucket.upper > 0.0)
};
text.push_str(&format!(
", {}{},{}{}:{}",
if lower_inclusive { '[' } else { '(' },
format_float_g(bucket.lower),
format_float_g(bucket.upper),
if upper_inclusive { ']' } else { ')' },
format_float_g(bucket.count)
));
}
/// Go `getBound`.
pub(super) fn bound(idx: i32, schema: i32, custom_values: &[f64]) -> Result<f64> {
if schema == CUSTOM_BUCKETS_SCHEMA {
@@ -203,6 +265,7 @@ pub(super) fn quantile(
lower: 0.0,
upper: 0.0,
count: 0.0,
index: 0,
};
let mut count = 0.0;
let mut visited = 0;
@@ -0,0 +1,47 @@
0/0/4000000000000000/4014000000000000/400c000000000000/0/0_3/3ff0000000000000,0,4000000000000000/// {count:5, sum:3.5, [-0,0]:2, (0.5,1]:1, (2,4]:2}
1/3f50624dd2f1a9fc/0/4132d68700000000/be7ad7f29abcaf48/0/20_1/0/-2_2,3_1/3ff0000000000000,4000000000000000,4132d68400000000/ {count:1.234567e+06, sum:-1e-07, [-2.82842712474619,-2):1.234564e+06, [-0.7071067811865475,-0.5):2, [-0.5,-0.35355339059327373):1}
-53/0/0/4018000000000000/4028000000000000/0/0_3/3ff0000000000000,4000000000000000,4008000000000000///3ff0000000000000,4014000000000000 {count:6, sum:12, [-Inf,1]:1, (1,5]:2, (5,+Inf]:3}
-53/0/0/4000000000000000/7ff8000000000001/0/1_2/3fe0000000000000,3ff8000000000000///bff0000000000000,0,3fe0000000000000 {count:2, sum:NaN, (-1,0]:0.5, (0,0.5]:1.5}
3/0/0/7ff0000000000000/fff0000000000000/0///// {count:+Inf, sum:-Inf}
0/0/0/4008000000000000/4000000000000000/0/0_1,0_0,2_1/3ff0000000000000,7ff8000000000001/// {count:3, sum:2, (0.5,1]:1, (4,8]:NaN}
-2/3fe0000000000000/3ff0000000000000/4008000000000000/3ff0000000000000/0/-1_2/3ff0000000000000,3ff0000000000000/// {count:3, sum:1, [-0.5,0.5]:1, (0.00390625,0.0625]:1, (0.0625,1]:1}
3/3fe0000000000000/0/4055001175c76074/40adb352ce0f464f/3/-2_1/0/-19_1,2_4/4042800000000000,3f2aa1d1faf86ea9,0,3f4c431a422a645b,4047800000000000/ {count:84.00106567831409, sum:3801.6617283604933, [-0.3242098886627524,-0.29730177875068026):47, [-0.29730177875068026,-0.2726269331663144):0.0008624914374478864, [-0.25,-0.22925101080116778):0.00020318687664732285, [-0.1927763531759926,-0.17677669529663687):37}
-53/0/0/405bc00000000000/c068dcd4a171fac8/3/0_2/4040800000000000,4053800000000000///3ff0000000000000,4014000000000000,4024000000000000 {count:111, sum:-198.90095588933605, [-Inf,1]:33, (1,5]:78}
0/255bba08cf8c979d/0/0/8000000000000000/1///// {count:0, sum:-0}
5/255bba08cf8c979d/404c000000000000/404c000000000000/40a0bdb814b160c1/3///// {count:56, sum:2142.8595328741653, [-1e-128,1e-128]:56}
-1/255bba08cf8c979d/3f36cdb2e4f0a26e/3f52159408fe4cb1/bf9324164071cb30/3/4_1/3f48c44e9f84482b/// {count:0.0011037774711198207, sum:-0.018692348178272755, [-1e-128,1e-128]:0.0003479539636282229, (64,256]:0.0007558235074915978}
-1/3f847ae147ae147b/0/406308039013db44/40b9864b9433bdfa/3/-9_1,0_4/0,4053a00000000000,4014000000000000,3f3c809eda1bc597,4051300000000000/// {count:152.2504349124739, sum:6534.2952301348505, (3.814697265625e-06,1.52587890625e-05]:78.5, (1.52587890625e-05,6.103515625e-05]:5, (6.103515625e-05,0.000244140625]:0.0004349124738914577, (0.000244140625,0.0009765625]:68.75}
-2/0/0/407a5c05888d260f/40d2aa08d6c06f03/1/-7_1,1_4,1_4/0,3f43aa8dcd5315f5,4053c00000000000,4051800000000000,4055800000000000,4059700000000000,0,4055400000000000,3f4899db6326e870/// {count:421.7513509286518, sum:19112.138107403192, (5.960464477539063e-08,9.5367431640625e-07]:0.0006001655953233308, (9.5367431640625e-07,1.52587890625e-05]:79, (1.52587890625e-05,0.000244140625]:70, (0.000244140625,0.00390625]:86, (0.0625,1]:101.75, (16,256]:85, (256,4096]:0.0007507630564795986}
-4/3fe0000000000000/0/0/0/3///// {count:0, sum:0}
-1/255bba08cf8c979d/4014000000000000/4073bc05b4869dbb/40d03bb730a8c0e4/0/-7_3/3f483e1bc460aff5,4053800000000000,0/-17_4,1_3/0,4044000000000000,3f4566192979cc81,4054700000000000,4043800000000000,402a000000000000,404d800000000000/ {count:315.75139286598613, sum:16622.862344921465, [-9.5367431640625e-07,-2.384185791015625e-07):59, [-2.384185791015625e-07,-5.960464477539063e-08):13, [-5.960464477539063e-08,-1.4901161193847656e-08):39, [-3.725290298461914e-09,-9.313225746154785e-10):81.75, [-9.313225746154785e-10,-2.3283064365386963e-10):0.0006530402051353608, [-2.3283064365386963e-10,-5.820766091346741e-11):40, [-1e-128,1e-128]:5, (1.52587890625e-05,6.103515625e-05]:0.0007398257810158337, (6.103515625e-05,0.000244140625]:78}
0/3f50624dd2f1a9fc/404f000000000000/4076e803fd306cca/40ca9dc9569918a8/0///-4_1,2_2,0_4/403f600000000000,3f4fe983664f7c80,0,405d880000000000,4026000000000000,4056000000000000,404c000000000000/ {count:366.5009738818741, sum:13627.572955262222, [-16,-8):56, [-8,-4):88, [-4,-2):11, [-2,-1):118.125, [-0.5,-0.25):0.0009738818740706728, [-0.0625,-0.03125):31.375, [-0.001,0.001]:62}
1/0/0/406c800000000000/40cb50bfd34524c0/0/3_2,1_1,1_1/404e000000000000,403d000000000000,404e000000000000,4053c00000000000/// {count:228, sum:13985.498634951538, (2,2.82842712474619]:60, (2.82842712474619,4]:29, (5.65685424949238,8]:60, (11.31370849898476,16]:79}
2/0/0/4054480000000000/40b0f29c9a52a00d/0/-13_1/4054480000000000/// {count:81.125, sum:4338.611729778361, (0.08838834764831843,0.1051120519067143]:81.125}
-4/0/3f4b4dbd2c7610e7/40826a048a221018/40a75eb15b3b6ea7/2/-15_3,2_2,2_1/3f3bc20af1adbdf4,4057c00000000000,0,4038000000000000,4018000000000000,3f4b85fd5a261456/-8_4,2_3,1_1/4058100000000000,404b800000000000,4045800000000000,4050800000000000,4050c00000000000,3f1f6b081088c64f,404a800000000000,4055000000000000/ {count:589.2522165928995, sum:2991.346399171121, [-4.294967296e+09,-65536):84, [-1,-1.52587890625e-05):53, [-1.52587890625e-05,-2.3283064365386963e-10):0.0001198505089028631, [-2.3283064365386963e-10,-3.552713678800501e-15):67, [-8.271806125530277e-25,-1.262177448353619e-29):66, [-1.262177448353619e-29,-1.925929944387236e-34):43, [-1.925929944387236e-34,-2.938735877055719e-39):55, [-2.938735877055719e-39,-4.484155085839415e-44):96.25, [-0,0]:0.0008332418155281546, (8.636168555094445e-78,5.659799424266695e-73]:0.00042355315388584104, (5.659799424266695e-73,3.7092061506874214e-68]:95, (1.044048714879764e-53,6.842277657836021e-49]:24, (6.842277657836021e-49,4.484155085839415e-44]:6, (1.925929944387236e-34,1.262177448353619e-29]:0.0008399474211705598}
5/255bba08cf8c979d/0/40770c0000000000/40b5fa8d74c0ae79/1/4_3,0_1/404c800000000000,4043800000000000,404ef00000000000,4056380000000000/-4_1/405e800000000000/ {count:368.75, sum:5626.552562754246, [-0.9170040432046711,-0.8973545375015533):122, (1.0671404006768235,1.0905077326652577]:57, (1.0905077326652577,1.1143867425958924]:39, (1.1143867425958924,1.1387886347566916]:61.875, (1.1387886347566916,1.1637248587775775]:88.875}
5/0/0/4064400710556858/40c19327f5a61c63/3/-2_3,2_2,2_2/3f4015141ed88149,4014000000000000,3f385883050fd155,0,0,4054000000000000,4053400000000000/// {count:162.00086228066607, sum:8998.312184108601, (0.9370838170551498,0.9576032806985735]:0.0004907940144143554, (0.9576032806985735,0.9785720620876999]:5, (0.9785720620876999,1]:0.0003714866516582223, (1.1387886347566916,1.1637248587775775]:80, (1.1637248587775775,1.189207115002721]:77}
0/3f847ae147ae147b/0/4075b203e4a2a823/40b7d2e96b597241/0/-15_3,0_3/4052000000000000,405a480000000000,4058000000000000,3f4d7879838a47e5,4052800000000000,3f0ac9bbd92a4212/// {count:347.1259504655629, sum:6098.911794271845, (1.52587890625e-05,3.0517578125e-05]:72, (3.0517578125e-05,6.103515625e-05]:105.125, (6.103515625e-05,0.0001220703125]:96, (0.0001220703125,0.000244140625]:0.0008993714646570143, (0.000244140625,0.00048828125]:74, (0.00048828125,0.0009765625]:5.1094098258212245e-05}
5/3f847ae147ae147b/4055c00000000000/4055c00000000000/4094695b49a7cbae/2///// {count:87, sum:1306.33914816075, [-0.01,0.01]:87}
-2/3f50624dd2f1a9fc/0/4050000000000000/c074ba1db757b67f/1/4_1,0_1/401c000000000000,404c800000000000/// {count:64, sum:-331.6322549272808, (4096,65536]:7, (65536,1.048576e+06]:57}
0/3fe0000000000000/0/4075ce01dd4d0692/40ce28744fb7f0d2/1/-11_4/4053b80000000000,405e400000000000,4052000000000000,4053400000000000/3_4/3f3dd4d069223133,0,0,0/ {count:348.8754551895919, sum:15440.908682816113, [-8,-4):0.00045518959189718154, (0.000244140625,0.00048828125]:78.875, (0.00048828125,0.0009765625]:121, (0.0009765625,0.001953125]:72, (0.001953125,0.00390625]:77}
0/3fe0000000000000/3f20901594b4379e/406c14063509d9f0/40c9c9a9524a50e8/1/-6_2,1_2,1_1/4041000000000000,4042500000000000,4055c00000000000,3f44b0220295c4b2,4050c00000000000/// {count:224.6257577125939, sum:13203.322823800569, [-0.5,0.5]:0.00012636435926832457, (0.0078125,0.015625]:34, (0.015625,0.03125]:36.625, (0.0625,0.125]:87, (0.125,0.25]:0.0006313482346468528, (0.5,1]:67}
-53/0/0/4024000000000000/4038a4e446df92d7/2/0_2/4024000000000000,0///3ff0000000000000,4014000000000000,4024000000000000 {count:10, sum:24.644108228286168, [-Inf,1]:10}
-1/255bba08cf8c979d/0/4081a80000000000/c0c61d084603aa97/1/-16_4,0_4,1_3/404bd00000000000,403e000000000000,0,4041000000000000,4050800000000000,4046800000000000,403c600000000000,4057800000000000,4052800000000000,4049000000000000,4056000000000000/// {count:565, sum:-11322.064636667534, (5.820766091346741e-11,2.3283064365386963e-10]:55.625, (2.3283064365386963e-10,9.313225746154785e-10]:30, (3.725290298461914e-09,1.4901161193847656e-08]:34, (1.4901161193847656e-08,5.960464477539063e-08]:66, (5.960464477539063e-08,2.384185791015625e-07]:45, (2.384185791015625e-07,9.5367431640625e-07]:28.375, (9.5367431640625e-07,3.814697265625e-06]:94, (1.52587890625e-05,6.103515625e-05]:74, (6.103515625e-05,0.000244140625]:50, (0.000244140625,0.0009765625]:88}
-4/255bba08cf8c979d/402e400000000000/407caa0113ee4a12/40de3cd98f49b46d/1/-3_3,2_4,1_2/403b000000000000,404a000000000000,0,4047800000000000,4028000000000000,0,4010000000000000,3f313ee4a128a361,4037000000000000/-16_4,2_1,2_2/402a000000000000,403c000000000000,4036800000000000,4054c00000000000,4049000000000000,4044000000000000,4045000000000000/ {count:458.62526314813397, sum:30963.399370599935, [-1.262177448353619e-29,-1.925929944387236e-34):42, [-1.925929944387236e-34,-2.938735877055719e-39):40, [-6.842277657836021e-49,-1.044048714879764e-53):50, [-2.4308653429145085e-63,-3.7092061506874214e-68):83, [-3.7092061506874214e-68,-5.659799424266695e-73):22.5, [-5.659799424266695e-73,-8.636168555094445e-78):28, [-8.636168555094445e-78,-1.3177747429038154e-82):13, [-1e-128,1e-128]:15.125, (5.421010862427522e-20,3.552713678800501e-15]:27, (3.552713678800501e-15,2.3283064365386963e-10]:52, (65536,4.294967296e+09]:47, (4.294967296e+09,2.81474976710656e+14]:12, (1.8446744073709552e+19,1.2089258196146292e+24]:4, (7.922816251426434e+28,5.192296858534828e+33]:0.00026314813399835017, (5.192296858534828e+33,3.402823669209385e+38]:23}
5/3f50624dd2f1a9fc/0/0/8000000000000000/3///// {count:0, sum:-0}
8/3f847ae147ae147b/4054800000000000/40716a00d423d86a/c088bf7fb4b025f5/2/-16_1,1_3/4039000000000000,3f2a847b0d4f57f6,4049800000000000,405e280000000000/// {count:278.6252023124895, sum:-791.9373563539108, [-0.01,0.01]:82, (0.9550139751351947,0.9576032806985735]:25, (0.9601996065815236,0.9628029718180622]:0.0002023124895049186, (0.9628029718180622,0.9654133954938133]:51, (0.9654133954938133,0.9680308967461471]:120.625}
-1/255bba08cf8c979d/4055400000000000/4072be0000000000/40a8ecb19995b81f/2/-9_1,2_4/4048000000000000,0,403b600000000000,4053a00000000000,404e800000000000/// {count:299.875, sum:3190.346874884344, [-1e-128,1e-128]:85, (9.5367431640625e-07,3.814697265625e-06]:48, (0.000244140625,0.0009765625]:27.375, (0.0009765625,0.00390625]:78.5, (0.00390625,0.015625]:61}
0/3f847ae147ae147b/404a000000000000/4063e8030e6cf560/4081209123fc57ba/3/-20_1,2_3/4036400000000000,4036000000000000,3f387367ab034118,404f000000000000/-13_2/3ff0000000000000,0/ {count:159.25037308960873, sum:548.0708694185516, [-0.0001220703125,-6.103515625e-05):1, [-0.01,0.01]:52, (4.76837158203125e-07,9.5367431640625e-07]:22.25, (3.814697265625e-06,7.62939453125e-06]:22, (7.62939453125e-06,1.52587890625e-05]:0.00037308960874062177, (1.52587890625e-05,3.0517578125e-05]:62}
-4/3f50624dd2f1a9fc/0/408b9402008af180/c0b46b244e186d84/2/4_3,0_4/0,4010000000000000,404a000000000000,4053200000000000,404a800000000000,4052400000000000,4059000000000000/5_3,2_4,1_4/404e000000000000,4057400000000000,4028000000000000,4054400000000000,4053c00000000000,4055400000000000,4048000000000000,3f5004578c010910,0,404e000000000000,4018000000000000/ {count:882.5009775977087, sum:-5227.141816641557, [-4.9732323640978664e+86,-7.588550360256754e+81):6, [-7.588550360256754e+81,-1.157920892373162e+77):60, [-1.7668470647783843e+72,-2.695994666715064e+67):0.0009775977087121447, [-4.113761393303015e+62,-6.277101735386681e+57):48, [-6.277101735386681e+57,-9.578097130411805e+52):85, [-9.578097130411805e+52,-1.461501637330903e+48):79, [-1.461501637330903e+48,-2.2300745198530623e+43):81, [-5.192296858534828e+33,-7.922816251426434e+28):12, [-7.922816251426434e+28,-1.2089258196146292e+24):93, [-1.2089258196146292e+24,-1.8446744073709552e+19):60, (1.8446744073709552e+19,1.2089258196146292e+24]:4, (1.2089258196146292e+24,7.922816251426434e+28]:52, (7.922816251426434e+28,5.192296858534828e+33]:76.5, (5.192296858534828e+33,3.402823669209385e+38]:53, (3.402823669209385e+38,2.2300745198530623e+43]:73, (2.2300745198530623e+43,1.461501637330903e+48]:100}
-53/0/0/0/0/0/////4000000000000000 {count:0, sum:0}
-1/3fe0000000000000/3f451c23ba20a83c/4064d005d3d0315d/40a5598b12aa98db/1/-17_4,1_3/4056000000000000,4044800000000000,3ff8000000000000,0,4042000000000000,0,3f1198e85aa23b6b/// {count:166.50071135199224, sum:2732.771626788291, [-0.5,0.5]:0.0006442236374415498, (1.4551915228366852e-11,5.820766091346741e-11]:88, (5.820766091346741e-11,2.3283064365386963e-10]:41, (2.3283064365386963e-10,9.313225746154785e-10]:1.5, (1.4901161193847656e-08,5.960464477539063e-08]:36, (2.384185791015625e-07,9.5367431640625e-07]:6.712835480214361e-05}
5/3f50624dd2f1a9fc/0/407da004bdf97b56/40bf8a2ca0dadb2a/3/9_3,0_1,0_2/0,402e000000000000,3f4d33d2909fe965,3f3177f2941fd20e,4010000000000000,0/-1_2,0_4,2_2/4056b00000000000,404f000000000000,404d000000000000,4043000000000000,4056000000000000,4047200000000000,0,4052000000000000/ {count:474.00115773633786, sum:8074.174329451091, [-1.189207115002721,-1.1637248587775775):72, [-1.0905077326652577,-1.0671404006768235):46.25, [-1.0671404006768235,-1.0442737824274138):88, [-1.0442737824274138,-1.0218971486541166):38, [-1.0218971486541166,-1):58, [-1,-0.9785720620876999):62, [-0.9785720620876999,-0.9576032806985735):90.75, (1.2152473599804687,1.241857812073484]:15, (1.241857812073484,1.2690509571917332]:0.0008911874913331395, (1.2690509571917332,1.2968395546510096]:0.0002665488465248008, (1.2968395546510096,1.325236643159741]:4}
-53/0/0/0/0/2/1_1/0///3fe0000000000000,3ff0000000000000,4000000000000000,4014000000000000 {count:0, sum:0}
-1/255bba08cf8c979d/0/0/0/2///// {count:0, sum:0}
8/255bba08cf8c979d/0/0/0/3///// {count:0, sum:0}
-53/0/0/0/0/0/////3ff0000000000000,4014000000000000,4024000000000000 {count:0, sum:0}
-2/255bba08cf8c979d/0/407aec0000000000/c0c5f6b501bf6e81/3/-16_4,1_1,0_3/4047800000000000,0,4050000000000000,4059500000000000,4054400000000000,0,4034200000000000,0/-5_2/405c580000000000,4010000000000000/ {count:430.75, sum:-11245.41411583801, [-1.52587890625e-05,-9.5367431640625e-07):4, [-9.5367431640625e-07,-5.960464477539063e-08):113.375, (3.3881317890172014e-21,5.421010862427522e-20]:47, (8.673617379884035e-19,1.3877787807814457e-17]:64, (1.3877787807814457e-17,2.220446049250313e-16]:101.25, (3.552713678800501e-15,5.684341886080802e-14]:81, (9.094947017729282e-13,1.4551915228366852e-11]:20.125}
-2/0/0/4064b80edc14cbae/40b53ea5f0cc1841/0/-10_1,0_3,0_2/4052000000000000,4050b80000000000,3f4262e9d0820b8b,4030e00000000000,0,0/-10_2,2_2/4024000000000000,3efb4dc57a9c81a3,3f3d19806b2a8e0f,3f49a63afccc53b9/ {count:165.75181392728467, sum:5438.64820552437, [-9.5367431640625e-07,-5.960464477539063e-08):0.0007827556633822542, [-5.960464477539063e-08,-3.725290298461914e-09):0.000444024898721391, [-1.4551915228366852e-11,-9.094947017729282e-13):2.6038927592898807e-05, [-9.094947017729282e-13,-5.684341886080802e-14):10, (5.684341886080802e-14,9.094947017729282e-13]:72, (9.094947017729282e-13,1.4551915228366852e-11]:66.875, (1.4551915228366852e-11,2.3283064365386963e-10]:0.0005611077949797411, (2.3283064365386963e-10,3.725290298461914e-09]:16.875}
-1/3f847ae147ae147b/0/4052b80000000000/4095b92e3d3d165a/1/6_2/4051b80000000000,4010000000000000/// {count:74.875, sum:1390.295155481805, (1024,4096]:70.875, (4096,16384]:4}
1/3fe0000000000000/0/40718a00b3c301d4/c0acbe7f64b2f3a9/1/-11_3/3f2678603a7aeac0,4050380000000000,403f000000000000/-12_3,2_2/0,4052f00000000000,402e000000000000,0,4057800000000000/ {count:280.62517143416085, sum:-3679.2488151476323, [-0.125,-0.08838834764831843):94, [-0.03125,-0.022097086912079608):15, [-0.022097086912079608,-0.015625):75.75, (0.015625,0.022097086912079608]:0.00017143416083860961, (0.022097086912079608,0.03125]:64.875, (0.03125,0.044194173824159216]:31}
3/3f50624dd2f1a9fc/4044400000000000/406290088825a224/40a922533829a9d0/2/4_1,1_2,0_3/0,3f435c1278746a1d,3f3d89082035127a,403d000000000000,4036000000000000,404c800000000000/// {count:148.50104148245111, sum:3217.1625378627577, [-0.001,0.001]:40.5, (1.5422108254079407,1.6817928305074288]:0.0005908098400227466, (1.6817928305074288,1.8340080864093422]:0.00045067261108425844, (1.8340080864093422,2]:29, (2,2.1810154653305154]:22, (2.1810154653305154,2.378414230005442]:57}
+16 -1
View File
@@ -25,7 +25,7 @@ use datatypes::series::time::{EvalGrid, GridRange, GridSpec, TimeAxis};
use super::float_histogram::{AddOutcome, FloatHistogram};
use super::{
HistogramAggregate, HistogramFunction, NativeHistogramFunction, aggregate_histograms,
evaluate_histograms, histogram_quantiles, map_native_histogram,
evaluate_histograms, histogram_quantiles, histogram_string, map_native_histogram,
};
use crate::grid_series::merge_same_labelset;
use crate::grid_series::reference::{
@@ -862,3 +862,18 @@ fn native_histogram_functions_keep_the_runs_of_a_sparse_series() {
);
}
}
/// Go `FloatHistogram.String` over fixed and random histograms (`narthex-golden-histstring`):
/// bucket order, inclusiveness, empty buckets skipped, the zero bucket, custom bounds and `%g`.
#[test]
fn histogram_string_matches_prometheus() {
for line in include_str!("string_cases.txt").lines() {
let (encoded, expected) = line.split_once('\t').expect("tab-separated case");
let histogram = OwnedHistogram::parse(encoded);
assert_eq!(
histogram_string(&histogram.view()).unwrap(),
expected,
"{encoded}"
);
}
}