diff --git a/graph/src/schema/api.rs b/graph/src/schema/api.rs index db179b049d2..7fe29806a3f 100644 --- a/graph/src/schema/api.rs +++ b/graph/src/schema/api.rs @@ -485,6 +485,12 @@ fn add_types_for_aggregation_types( input_schema: &InputSchema, ) -> Result<(), APISchemaError> { for (name, agg_type) in input_schema.aggregation_types() { + // Combine regular fields and aggregate fields for ordering + let mut all_fields = agg_type.fields.to_vec(); + for agg in agg_type.aggregates.iter() { + all_fields.push(agg.as_agg_field()); + } + add_order_by_type(&mut api.document, name, &all_fields)?; add_aggregation_filter_type(api, name, agg_type)?; } Ok(()) @@ -678,13 +684,25 @@ impl FilterOps { s::Type::NamedType("OrderDirection".to_string()), ), ], - FilterOps::Aggregation => vec![input_value( - "interval", - "", - s::Type::NonNullType(Box::new(s::Type::NamedType( - "Aggregation_interval".to_string(), - ))), - )], + FilterOps::Aggregation => vec![ + input_value( + "interval", + "", + s::Type::NonNullType(Box::new(s::Type::NamedType( + "Aggregation_interval".to_string(), + ))), + ), + input_value( + "orderBy", + "", + s::Type::NamedType(format!("{}_orderBy", type_name)), + ), + input_value( + "orderDirection", + "", + s::Type::NamedType("OrderDirection".to_string()), + ), + ], }; let mut args = vec![skip, first]; diff --git a/graph/src/schema/input/mod.rs b/graph/src/schema/input/mod.rs index 5ff8ddcda2f..634930c5731 100644 --- a/graph/src/schema/input/mod.rs +++ b/graph/src/schema/input/mod.rs @@ -824,7 +824,7 @@ impl Aggregate { /// The field needed for the finalised aggregation for hourly/daily /// values - fn as_agg_field(&self) -> Field { + pub fn as_agg_field(&self) -> Field { Field { name: self.name.clone(), field_type: self.field_type.clone(), diff --git a/graphql/src/store/prefetch.rs b/graphql/src/store/prefetch.rs index de97b243227..33f0b67452b 100644 --- a/graphql/src/store/prefetch.rs +++ b/graphql/src/store/prefetch.rs @@ -713,8 +713,8 @@ impl<'a> Loader<'a> { // that causes unnecessary work in the database query.order = EntityOrder::Unordered; } - // Aggregations are always ordered by (timestamp, id) - if child_type.is_aggregation() { + // Apply default timestamp ordering for aggregations if no custom order is specified + if child_type.is_aggregation() && matches!(query.order, EntityOrder::Default) { let ts = child_type.field(kw::TIMESTAMP).unwrap(); query.order = EntityOrder::Descending(ts.name.to_string(), ts.value_type); }