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 {
@@ -244,7 +244,7 @@ impl From<(u64, Unit)> for Duration {
/// Specify time offset for the queries
/// Specify time offset for the queries
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy)]
pub struct Offset(i64, Unit);
pub struct Offset(pub i64, pub Unit);
impl Display for Offset {
impl Display for Offset {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -269,6 +269,71 @@ impl Display for Scalar {
@@ -269,6 +269,71 @@ impl Display for Scalar {
impl Sealed for Scalar {}
impl Sealed for Scalar {}
impl ops::Operable 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
/// 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
/// This also allows combining complex queries, especially when a function is generic over a single type
@@ -339,7 +404,7 @@ impl Display for RawExpr {
@@ -339,7 +404,7 @@ impl Display for RawExpr {
/// Additional/extended query builder API functionality
/// Additional/extended query builder API functionality
///
///
/// This trait has a blanket implementation for all types that implement [`Operable`]
/// 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
/// 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`
/// 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 {
@@ -348,7 +413,9 @@ pub trait QueryExt {
/// While the `String` type could have been allowed to be part of the builder API directly,
/// 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
/// 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.
/// 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
/// 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.
/// 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 {
@@ -368,22 +435,25 @@ pub trait QueryExt {
/// > idea of what's happening when writing/reading the complex queries.
/// > idea of what's happening when writing/reading the complex queries.
fn then<R>(self, func: impl FnOnce(Self) -> R) -> R
fn then<R>(self, func: impl FnOnce(Self) -> R) -> R
where
where
Self: Sized;
Self: Sized,
}
{
func(self)
impl<T: Operable> QueryExt for T {
fn to_expr(&self) -> RawExpr {
RawExpr(self.to_string())
}
}
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
where
Self: Sized,
Self: Sized,
{
{
func(self)
SubQry::new(self, dur)
}
}
}
}
 
impl<T: Operable> QueryExt for T {}
 
#[cfg(test)]
#[cfg(test)]
mod tests {
mod tests {
use super::*;
use super::*;
@@ -407,4 +477,16 @@ mod tests {
@@ -407,4 +477,16 @@ mod tests {
metric
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