Skip to content

Commit c8d76ef

Browse files
committed
agg: support to deserialize f64 from string
1 parent 1223a87 commit c8d76ef

File tree

13 files changed

+230
-24
lines changed

13 files changed

+230
-24
lines changed

src/aggregation/bucket/histogram/date_histogram.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use serde::{Deserialize, Serialize};
22

33
use super::{HistogramAggregation, HistogramBounds};
4-
use crate::aggregation::AggregationError;
4+
use crate::aggregation::*;
55

66
/// DateHistogramAggregation is similar to `HistogramAggregation`, but it can only be used with date
77
/// type.

src/aggregation/bucket/histogram/histogram.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::aggregation::intermediate_agg_result::{
2020
use crate::aggregation::segment_agg_result::{
2121
build_segment_agg_collector, AggregationLimits, SegmentAggregationCollector,
2222
};
23-
use crate::aggregation::{f64_from_fastfield_u64, format_date};
23+
use crate::aggregation::*;
2424
use crate::TantivyError;
2525

2626
/// Histogram is a bucket aggregation, where buckets are created dynamically for given `interval`.
@@ -73,6 +73,7 @@ pub struct HistogramAggregation {
7373
pub field: String,
7474
/// The interval to chunk your data range. Each bucket spans a value range of [0..interval).
7575
/// Must be a positive value.
76+
#[serde(deserialize_with = "deserialize_f64")]
7677
pub interval: f64,
7778
/// Intervals implicitly defines an absolute grid of buckets `[interval * k, interval * (k +
7879
/// 1))`.
@@ -85,6 +86,7 @@ pub struct HistogramAggregation {
8586
/// fall into the buckets with the key 0 and 10.
8687
/// With offset 5 and interval 10, they would both fall into the bucket with they key 5 and the
8788
/// range [5..15)
89+
#[serde(default, deserialize_with = "deserialize_option_f64")]
8890
pub offset: Option<f64>,
8991
/// The minimum number of documents in a bucket to be returned. Defaults to 0.
9092
pub min_doc_count: Option<u64>,

src/aggregation/bucket/range.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ use crate::aggregation::intermediate_agg_result::{
1414
use crate::aggregation::segment_agg_result::{
1515
build_segment_agg_collector, SegmentAggregationCollector,
1616
};
17-
use crate::aggregation::{
18-
f64_from_fastfield_u64, f64_to_fastfield_u64, format_date, Key, SerializedKey,
19-
};
17+
use crate::aggregation::*;
2018
use crate::TantivyError;
2119

2220
/// Provide user-defined buckets to aggregate on.
@@ -72,11 +70,19 @@ pub struct RangeAggregationRange {
7270
pub key: Option<String>,
7371
/// The from range value, which is inclusive in the range.
7472
/// `None` equals to an open ended interval.
75-
#[serde(skip_serializing_if = "Option::is_none", default)]
73+
#[serde(
74+
skip_serializing_if = "Option::is_none",
75+
default,
76+
deserialize_with = "deserialize_option_f64"
77+
)]
7678
pub from: Option<f64>,
7779
/// The to range value, which is not inclusive in the range.
7880
/// `None` equals to an open ended interval.
79-
#[serde(skip_serializing_if = "Option::is_none", default)]
81+
#[serde(
82+
skip_serializing_if = "Option::is_none",
83+
default,
84+
deserialize_with = "deserialize_option_f64"
85+
)]
8086
pub to: Option<f64>,
8187
}
8288

src/aggregation/metric/average.rs

+62-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use std::fmt::Debug;
22

33
use serde::{Deserialize, Serialize};
44

5-
use super::{IntermediateStats, SegmentStatsCollector};
5+
use super::*;
6+
use crate::aggregation::*;
67

78
/// A single-value metric aggregation that computes the average of numeric values that are
89
/// extracted from the aggregated documents.
@@ -24,7 +25,7 @@ pub struct AverageAggregation {
2425
/// By default they will be ignored but it is also possible to treat them as if they had a
2526
/// value. Examples in JSON format:
2627
/// { "field": "my_numbers", "missing": "10.0" }
27-
#[serde(default)]
28+
#[serde(default, deserialize_with = "deserialize_option_f64")]
2829
pub missing: Option<f64>,
2930
}
3031

@@ -65,3 +66,62 @@ impl IntermediateAverage {
6566
self.stats.finalize().avg
6667
}
6768
}
69+
70+
#[cfg(test)]
71+
mod tests {
72+
use super::*;
73+
74+
#[test]
75+
fn deserialization_with_missing_test1() {
76+
let json = r#"{
77+
"field": "score",
78+
"missing": "10.0"
79+
}"#;
80+
let avg: AverageAggregation = serde_json::from_str(json).unwrap();
81+
assert_eq!(avg.field, "score");
82+
assert_eq!(avg.missing, Some(10.0));
83+
// no dot
84+
let json = r#"{
85+
"field": "score",
86+
"missing": "10"
87+
}"#;
88+
let avg: AverageAggregation = serde_json::from_str(json).unwrap();
89+
assert_eq!(avg.field, "score");
90+
assert_eq!(avg.missing, Some(10.0));
91+
92+
// from value
93+
let avg: AverageAggregation = serde_json::from_value(json!({
94+
"field": "score_f64",
95+
"missing": 10u64,
96+
}))
97+
.unwrap();
98+
assert_eq!(avg.missing, Some(10.0));
99+
// from value
100+
let avg: AverageAggregation = serde_json::from_value(json!({
101+
"field": "score_f64",
102+
"missing": 10u32,
103+
}))
104+
.unwrap();
105+
assert_eq!(avg.missing, Some(10.0));
106+
let avg: AverageAggregation = serde_json::from_value(json!({
107+
"field": "score_f64",
108+
"missing": 10i8,
109+
}))
110+
.unwrap();
111+
assert_eq!(avg.missing, Some(10.0));
112+
}
113+
114+
#[test]
115+
fn deserialization_with_missing_test_fail() {
116+
let json = r#"{
117+
"field": "score",
118+
"missing": "a"
119+
}"#;
120+
let avg: Result<AverageAggregation, _> = serde_json::from_str(json);
121+
assert!(avg.is_err());
122+
assert!(avg
123+
.unwrap_err()
124+
.to_string()
125+
.contains("Failed to parse f64 from string: \"a\""));
126+
}
127+
}

src/aggregation/metric/count.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use std::fmt::Debug;
22

33
use serde::{Deserialize, Serialize};
44

5-
use super::{IntermediateStats, SegmentStatsCollector};
5+
use super::*;
6+
use crate::aggregation::*;
67

78
/// A single-value metric aggregation that counts the number of values that are
89
/// extracted from the aggregated documents.
@@ -24,7 +25,7 @@ pub struct CountAggregation {
2425
/// By default they will be ignored but it is also possible to treat them as if they had a
2526
/// value. Examples in JSON format:
2627
/// { "field": "my_numbers", "missing": "10.0" }
27-
#[serde(default)]
28+
#[serde(default, deserialize_with = "deserialize_option_f64")]
2829
pub missing: Option<f64>,
2930
}
3031

src/aggregation/metric/max.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use std::fmt::Debug;
22

33
use serde::{Deserialize, Serialize};
44

5-
use super::{IntermediateStats, SegmentStatsCollector};
5+
use super::*;
6+
use crate::aggregation::*;
67

78
/// A single-value metric aggregation that computes the maximum of numeric values that are
89
/// extracted from the aggregated documents.
@@ -24,7 +25,7 @@ pub struct MaxAggregation {
2425
/// By default they will be ignored but it is also possible to treat them as if they had a
2526
/// value. Examples in JSON format:
2627
/// { "field": "my_numbers", "missing": "10.0" }
27-
#[serde(default)]
28+
#[serde(default, deserialize_with = "deserialize_option_f64")]
2829
pub missing: Option<f64>,
2930
}
3031

src/aggregation/metric/min.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use std::fmt::Debug;
22

33
use serde::{Deserialize, Serialize};
44

5-
use super::{IntermediateStats, SegmentStatsCollector};
5+
use super::*;
6+
use crate::aggregation::*;
67

78
/// A single-value metric aggregation that computes the minimum of numeric values that are
89
/// extracted from the aggregated documents.
@@ -24,7 +25,7 @@ pub struct MinAggregation {
2425
/// By default they will be ignored but it is also possible to treat them as if they had a
2526
/// value. Examples in JSON format:
2627
/// { "field": "my_numbers", "missing": "10.0" }
27-
#[serde(default)]
28+
#[serde(default, deserialize_with = "deserialize_option_f64")]
2829
pub missing: Option<f64>,
2930
}
3031

src/aggregation/metric/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ mod percentiles;
2424
mod stats;
2525
mod sum;
2626
mod top_hits;
27+
2728
pub use average::*;
2829
pub use count::*;
2930
pub use max::*;

src/aggregation/metric/percentiles.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::aggregation::intermediate_agg_result::{
1111
IntermediateAggregationResult, IntermediateAggregationResults, IntermediateMetricResult,
1212
};
1313
use crate::aggregation::segment_agg_result::SegmentAggregationCollector;
14-
use crate::aggregation::{f64_from_fastfield_u64, f64_to_fastfield_u64, AggregationError};
14+
use crate::aggregation::*;
1515
use crate::{DocId, TantivyError};
1616

1717
/// # Percentiles
@@ -84,7 +84,11 @@ pub struct PercentilesAggregationReq {
8484
/// By default they will be ignored but it is also possible to treat them as if they had a
8585
/// value. Examples in JSON format:
8686
/// { "field": "my_numbers", "missing": "10.0" }
87-
#[serde(skip_serializing_if = "Option::is_none", default)]
87+
#[serde(
88+
skip_serializing_if = "Option::is_none",
89+
default,
90+
deserialize_with = "deserialize_option_f64"
91+
)]
8892
pub missing: Option<f64>,
8993
}
9094
fn default_percentiles() -> &'static [f64] {

src/aggregation/metric/stats.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::aggregation::intermediate_agg_result::{
99
IntermediateAggregationResult, IntermediateAggregationResults, IntermediateMetricResult,
1010
};
1111
use crate::aggregation::segment_agg_result::SegmentAggregationCollector;
12-
use crate::aggregation::{f64_from_fastfield_u64, f64_to_fastfield_u64};
12+
use crate::aggregation::*;
1313
use crate::{DocId, TantivyError};
1414

1515
/// A multi-value metric aggregation that computes a collection of statistics on numeric values that
@@ -33,7 +33,7 @@ pub struct StatsAggregation {
3333
/// By default they will be ignored but it is also possible to treat them as if they had a
3434
/// value. Examples in JSON format:
3535
/// { "field": "my_numbers", "missing": "10.0" }
36-
#[serde(default)]
36+
#[serde(default, deserialize_with = "deserialize_option_f64")]
3737
pub missing: Option<f64>,
3838
}
3939

@@ -580,6 +580,30 @@ mod tests {
580580
})
581581
);
582582

583+
// From string
584+
let agg_req: Aggregations = serde_json::from_value(json!({
585+
"my_stats": {
586+
"stats": {
587+
"field": "json.partially_empty",
588+
"missing": "0.0"
589+
},
590+
}
591+
}))
592+
.unwrap();
593+
594+
let res = exec_request_with_query(agg_req, &index, None)?;
595+
596+
assert_eq!(
597+
res["my_stats"],
598+
json!({
599+
"avg": 2.5,
600+
"count": 4,
601+
"max": 10.0,
602+
"min": 0.0,
603+
"sum": 10.0
604+
})
605+
);
606+
583607
Ok(())
584608
}
585609

src/aggregation/metric/sum.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use std::fmt::Debug;
22

33
use serde::{Deserialize, Serialize};
44

5-
use super::{IntermediateStats, SegmentStatsCollector};
5+
use super::*;
6+
use crate::aggregation::*;
67

78
/// A single-value metric aggregation that sums up numeric values that are
89
/// extracted from the aggregated documents.
@@ -24,7 +25,7 @@ pub struct SumAggregation {
2425
/// By default they will be ignored but it is also possible to treat them as if they had a
2526
/// value. Examples in JSON format:
2627
/// { "field": "my_numbers", "missing": "10.0" }
27-
#[serde(default)]
28+
#[serde(default, deserialize_with = "deserialize_option_f64")]
2829
pub missing: Option<f64>,
2930
}
3031

src/aggregation/metric/top_hits.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ impl RetrievalFields {
151151
return Ok(vec![field.to_owned()]);
152152
}
153153

154-
let pattern = globbed_string_to_regex(&field)?;
154+
let pattern = globbed_string_to_regex(field)?;
155155
let fields = reader
156156
.iter_columns()?
157157
.map(|(name, _)| {
@@ -270,7 +270,9 @@ impl Serialize for KeyOrder {
270270

271271
impl<'de> Deserialize<'de> for KeyOrder {
272272
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
273-
where D: Deserializer<'de> {
273+
where
274+
D: Deserializer<'de>,
275+
{
274276
let mut k_o = <HashMap<String, Order>>::deserialize(deserializer)?.into_iter();
275277
let (k, v) = k_o.next().ok_or(serde::de::Error::custom(
276278
"Expected exactly one key-value pair in KeyOrder, found none",

0 commit comments

Comments
 (0)