Skip to content
Snippets Groups Projects

Resolve "Subquery support"

Merged Maaz Ahmed requested to merge 43-subquery-support into main
2 files
+ 108
12
Compare changes
  • Side-by-side
  • Inline
Files
2
+ 93
11
@@ -244,7 +244,7 @@ impl From<(u64, Unit)> for Duration {
/// Specify time offset for the queries
#[derive(Debug, Clone, Copy)]
pub struct Offset(i64, Unit);
pub struct Offset(pub i64, pub Unit);
impl Display for Offset {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -269,6 +269,71 @@ impl Display for Scalar {
impl Sealed for Scalar {}
impl ops::Operable for Scalar {}
/// A subquery that lets you make a ranged query on an instant query with an optional resolution/set and offset
///
/// This type can also be constructed using the [`subquery`] method provided by the [`QueryExt`] trait.
#[derive(Clone, Debug)]
pub struct SubQry<E: Operable> {
expr: E,
dur: (u64, Unit),
res: Option<(u64, Unit)>,
off: Option<Offset>,
}
impl<E: Operable> SubQry<E> {
/// Create a subquery using an instant query
pub fn new(expr: E, duration: Duration) -> Self {
Self {
expr,
dur: (duration.0, duration.1),
res: None,
off: None,
}
}
/// Set the resolution/step for the subquery
pub fn resolution(mut self, res: Duration) -> Self {
self.res.replace((res.0, res.1));
self
}
/// Set the subquery's offset duration
pub fn offset(mut self, off: Offset) -> Self {
self.off.replace(off);
self
}
}
impl<E: Operable> Display for SubQry<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"({expr})[{v}{unit}",
expr = self.expr,
v = self.dur.0,
unit = self.dur.1
)?;
if let Some(res) = self.res.as_ref() {
write!(f, ":{v}{unit}", v = res.0, unit = res.1)?;
}
f.write_str("]")?;
if let Some(off) = self.off.as_ref() {
write!(f, " {off}")?;
}
Ok(())
}
}
impl<E: Operable> Sealed for SubQry<E> {}
impl<E: Operable> ops::Operable for SubQry<E> {}
impl<E: Operable> IntoQuery for SubQry<E> {
type Target = String;
fn into_query(self) -> Self::Target {
self.to_string()
}
}
/// Raw Expression type that allows raw strings to be used in the builder API where necessary
///
/// This also allows combining complex queries, especially when a function is generic over a single type
@@ -339,7 +404,7 @@ impl Display for RawExpr {
/// Additional/extended query builder API functionality
///
/// This trait has a blanket implementation for all types that implement [`Operable`]
pub trait QueryExt {
pub trait QueryExt: Operable {
/// Convert a query builder API type into a [`RawExpr`] type
///
/// This is useful when a function takes a sequence (array/vector/iterator) of a generic type `T: Operable`
@@ -348,7 +413,9 @@ pub trait QueryExt {
/// While the `String` type could have been allowed to be part of the builder API directly,
/// it is by design that a user must explicitly turn a string into a [`RawExpr`] saying that
/// they're opting in to use a raw string in the query builder API.
fn to_expr(&self) -> RawExpr;
fn to_expr(&self) -> RawExpr {
RawExpr(self.to_string())
}
/// Call a function or closure on `Self` to transform it into another type
///
/// This is similar to the `Iterator's` `map` method, except this is eagerly evaluated and operates on a single item.
@@ -368,22 +435,25 @@ pub trait QueryExt {
/// > idea of what's happening when writing/reading the complex queries.
fn then<R>(self, func: impl FnOnce(Self) -> R) -> R
where
Self: Sized;
}
impl<T: Operable> QueryExt for T {
fn to_expr(&self) -> RawExpr {
RawExpr(self.to_string())
Self: Sized,
{
func(self)
}
fn then<R>(self, func: impl FnOnce(Self) -> R) -> R
/// Turn the current query expression into a subquery using the provided range
///
/// Optional resolution and offset can be set for the subquery using the provided methods of the
/// returned type.
fn subquery(self, dur: Duration) -> SubQry<Self>
where
Self: Sized,
{
func(self)
SubQry::new(self, dur)
}
}
impl<T: Operable> QueryExt for T {}
#[cfg(test)]
mod tests {
use super::*;
@@ -407,4 +477,16 @@ mod tests {
metric
)
}
#[test]
fn subquery() {
let qry = Metric::new("metric").subquery(Duration(30, Unit::Day));
assert_eq!(qry.to_string(), "(metric)[30d]");
let qry = qry.resolution(Duration(5, Unit::Min));
assert_eq!(qry.to_string(), "(metric)[30d:5m]");
let qry = qry.offset(Offset(1, Unit::Day));
assert_eq!(qry.to_string(), "(metric)[30d:5m] offset 1d");
}
}
Loading