Skip to content

Commit 73af3eb

Browse files
committed
add method to prune intermediate agg results
1 parent 6b8bd7b commit 73af3eb

3 files changed

Lines changed: 508 additions & 1 deletion

File tree

src/aggregation/agg_tests.rs

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use serde_json::Value;
33
use crate::aggregation::agg_req::{Aggregation, Aggregations};
44
use crate::aggregation::agg_result::AggregationResults;
55
use crate::aggregation::collector::AggregationCollector;
6-
use crate::aggregation::intermediate_agg_result::IntermediateAggregationResults;
6+
use crate::aggregation::intermediate_agg_result::{
7+
IntermediateAggregationResult, IntermediateAggregationResults, IntermediateBucketResult,
8+
IntermediateKey,
9+
};
710
use crate::aggregation::tests::{get_test_index_2_segments, get_test_index_from_values_and_terms};
811
use crate::aggregation::DistributedAggregationCollector;
912
use crate::docset::COLLECT_BLOCK_BUFFER_LEN;
@@ -1479,3 +1482,132 @@ fn test_aggregation_field_validation_helper() {
14791482
crate::aggregation::agg_req::validate_aggregation_fields_exist(&agg_req, segment_reader);
14801483
assert!(result.is_ok());
14811484
}
1485+
1486+
// "a" has low scores (p50 ≈ 1.5), "b" has high scores (p50 ≈ 99.5).
1487+
// With order by p50 desc and segment_size=1, the segment collector should retain only "b".
1488+
#[test]
1489+
fn test_percentile_order_segment_level() -> crate::Result<()> {
1490+
let index = get_test_index_from_values_and_terms(
1491+
false,
1492+
&[vec![
1493+
(1.0, "a".to_string()),
1494+
(2.0, "a".to_string()),
1495+
(99.0, "b".to_string()),
1496+
(100.0, "b".to_string()),
1497+
]],
1498+
)?;
1499+
1500+
let agg_req: Aggregations = serde_json::from_value(serde_json::json!({
1501+
"my_terms": {
1502+
"terms": {
1503+
"field": "string_id",
1504+
"size": 1,
1505+
"segment_size": 1,
1506+
"order": { "my_pct.50": "desc" }
1507+
},
1508+
"aggs": {
1509+
"my_pct": { "percentiles": { "field": "score_f64", "percents": [50] } }
1510+
}
1511+
}
1512+
}))
1513+
.unwrap();
1514+
1515+
let collector = DistributedAggregationCollector::from_aggs(agg_req, Default::default());
1516+
let reader = index.reader()?;
1517+
let intermediate = reader.searcher().search(&AllQuery, &collector)?;
1518+
1519+
let IntermediateAggregationResult::Bucket(IntermediateBucketResult::Terms { buckets }) =
1520+
intermediate.aggs_res.get("my_terms").unwrap()
1521+
else {
1522+
panic!("expected terms bucket");
1523+
};
1524+
assert_eq!(
1525+
buckets.entries.len(),
1526+
1,
1527+
"segment_size=1 should retain only one bucket"
1528+
);
1529+
assert!(
1530+
buckets
1531+
.entries
1532+
.contains_key(&IntermediateKey::Str("b".to_string())),
1533+
"\"b\" (higher p50) should survive, not \"a\""
1534+
);
1535+
assert!(
1536+
buckets.sum_other_doc_count > 0,
1537+
"pruned docs should be accounted for"
1538+
);
1539+
1540+
Ok(())
1541+
}
1542+
1543+
// Same setup with two segments. Both terms survive segment-level pruning (segment_size=2).
1544+
// After merging, prune_intermediate_results with size=1 should keep only "b".
1545+
#[test]
1546+
fn test_percentile_order_prune_intermediate() -> crate::Result<()> {
1547+
let index = get_test_index_from_values_and_terms(
1548+
false,
1549+
&[
1550+
vec![
1551+
(1.0, "a".to_string()),
1552+
(2.0, "a".to_string()),
1553+
(99.0, "b".to_string()),
1554+
(100.0, "b".to_string()),
1555+
],
1556+
vec![(3.0, "a".to_string()), (98.0, "b".to_string())],
1557+
],
1558+
)?;
1559+
1560+
let agg_req: Aggregations = serde_json::from_value(serde_json::json!({
1561+
"my_terms": {
1562+
"terms": {
1563+
"field": "string_id",
1564+
"size": 1,
1565+
"segment_size": 2,
1566+
"order": { "my_pct.50": "desc" }
1567+
},
1568+
"aggs": {
1569+
"my_pct": { "percentiles": { "field": "score_f64", "percents": [50] } }
1570+
}
1571+
}
1572+
}))
1573+
.unwrap();
1574+
1575+
let collector = DistributedAggregationCollector::from_aggs(agg_req.clone(), Default::default());
1576+
let reader = index.reader()?;
1577+
let mut intermediate = reader.searcher().search(&AllQuery, &collector)?;
1578+
1579+
// Both terms should have survived segment-level pruning (segment_size=2).
1580+
{
1581+
let IntermediateAggregationResult::Bucket(IntermediateBucketResult::Terms { buckets }) =
1582+
intermediate.aggs_res.get("my_terms").unwrap()
1583+
else {
1584+
panic!("expected terms bucket");
1585+
};
1586+
assert_eq!(
1587+
buckets.entries.len(),
1588+
2,
1589+
"both terms should survive segment-level pruning"
1590+
);
1591+
}
1592+
1593+
intermediate.prune_intermediate_results(&agg_req, false)?;
1594+
1595+
let IntermediateAggregationResult::Bucket(IntermediateBucketResult::Terms { buckets }) =
1596+
intermediate.aggs_res.get("my_terms").unwrap()
1597+
else {
1598+
panic!("expected terms bucket");
1599+
};
1600+
assert_eq!(
1601+
buckets.entries.len(),
1602+
1,
1603+
"size=1 should retain only one bucket"
1604+
);
1605+
assert!(
1606+
buckets
1607+
.entries
1608+
.contains_key(&IntermediateKey::Str("b".to_string())),
1609+
"\"b\" (higher p50) should survive, not \"a\""
1610+
);
1611+
1612+
Ok(())
1613+
}

0 commit comments

Comments
 (0)