-
-
Notifications
You must be signed in to change notification settings - Fork 143
Updates for counts API #1347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Updates for counts API #1347
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,6 +47,8 @@ use tokio::runtime::Runtime; | |
use self::error::ExecuteError; | ||
use self::stream_schema_provider::GlobalSchemaProvider; | ||
pub use self::stream_schema_provider::PartialTimeFilter; | ||
use crate::alerts::alerts_utils::get_filter_string; | ||
use crate::alerts::Conditions; | ||
use crate::catalog::column::{Int64Type, TypedStatistics}; | ||
use crate::catalog::manifest::Manifest; | ||
use crate::catalog::snapshot::Snapshot; | ||
|
@@ -297,7 +299,7 @@ impl Query { | |
} | ||
|
||
/// Record of counts for a given time bin. | ||
#[derive(Debug, Serialize, Clone)] | ||
#[derive(Debug, Serialize, Clone, Deserialize)] | ||
pub struct CountsRecord { | ||
/// Start time of the bin | ||
pub start_time: String, | ||
|
@@ -312,6 +314,15 @@ struct TimeBounds { | |
end: DateTime<Utc>, | ||
} | ||
|
||
#[derive(Debug, Deserialize, Clone)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct CountConditions { | ||
/// Optional conditions for filters | ||
pub conditions: Option<Conditions>, | ||
/// GroupBy columns | ||
pub group_by: Option<Vec<String>>, | ||
} | ||
|
||
/// Request for counts, received from API/SQL query. | ||
#[derive(Debug, Deserialize, Clone)] | ||
#[serde(rename_all = "camelCase")] | ||
|
@@ -324,6 +335,8 @@ pub struct CountsRequest { | |
pub end_time: String, | ||
/// Number of bins to divide the time range into | ||
pub num_bins: u64, | ||
/// Conditions | ||
pub conditions: Option<CountConditions>, | ||
} | ||
|
||
impl CountsRequest { | ||
|
@@ -429,6 +442,34 @@ impl CountsRequest { | |
|
||
bounds | ||
} | ||
|
||
/// This function will get executed only if self.conditions is some | ||
pub async fn get_df_sql(&self) -> Result<String, QueryError> { | ||
let count_conditions = self.conditions.as_ref().unwrap(); | ||
|
||
let time_range = TimeRange::parse_human_time(&self.start_time, &self.end_time)?; | ||
|
||
let dur = time_range.end.signed_duration_since(time_range.start); | ||
|
||
let date_bin = if dur.num_minutes() <= 60 * 10 { | ||
// date_bin 1 minute | ||
format!("CAST(DATE_BIN('1 minute', {}.p_timestamp, TIMESTAMP '2025-01-01T00:00:00.000Z') AS TEXT) as start_time, DATE_BIN('1 minute', p_timestamp, TIMESTAMP '2025-01-01T00:00:00.000Z') + INTERVAL '1 minute' as end_time", self.stream) | ||
} else if dur.num_minutes() > 60 * 10 && dur.num_minutes() < 60 * 240 { | ||
// date_bin 1 hour | ||
String::from("CAST(DATE_BIN('1 hour', p_timestamp, TIMESTAMP '2025-01-01T00:00:00.000Z') AS TEXT) as start_time, DATE_BIN('1 hour', p_timestamp, TIMESTAMP '2025-01-01T00:00:00.000Z') + INTERVAL '1 hour' as end_time") | ||
} else { | ||
// date_bin 1 day | ||
String::from("CAST(DATE_BIN('1 day', p_timestamp, TIMESTAMP '2025-01-01T00:00:00.000Z') AS TEXT) as start_time, DATE_BIN('1 day', p_timestamp, TIMESTAMP '2025-01-01T00:00:00.000Z') + INTERVAL '1 day' as end_time") | ||
}; | ||
|
||
let query = if let Some(conditions) = &count_conditions.conditions { | ||
let f = get_filter_string(conditions).map_err(QueryError::CustomError)?; | ||
format!("SELECT {date_bin}, COUNT(*) as count FROM {} WHERE {} GROUP BY end_time,start_time ORDER BY end_time",self.stream,f) | ||
} else { | ||
format!("SELECT {date_bin}, COUNT(*) as count FROM {} GROUP BY end_time,start_time ORDER BY end_time",self.stream) | ||
}; | ||
Ok(query) | ||
} | ||
Comment on lines
+447
to
+472
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion
Issues:
- let date_bin = if dur.num_minutes() <= 60 * 10 {
- // date_bin 1 minute
- format!("CAST(DATE_BIN('1 minute', {}.p_timestamp, TIMESTAMP '2025-01-01T00:00:00.000Z') AS TEXT) as start_time, DATE_BIN('1 minute', p_timestamp, TIMESTAMP '2025-01-01T00:00:00.000Z') + INTERVAL '1 minute' as end_time", self.stream)
+ let ts_col = r#""p_timestamp""#;
+ let anchor = "TIMESTAMP '1970-01-01T00:00:00Z'";
+ let date_bin = if dur.num_minutes() <= 600 {
+ format!(
+ "CAST(DATE_BIN('1 minute', {ts_col}, {anchor}) AS TEXT) AS start_time, \
+ DATE_BIN('1 minute', {ts_col}, {anchor}) + INTERVAL '1 minute' AS end_time"
+ )
} else if dur.num_minutes() < 60 * 240 {
// date_bin 1 hour
- String::from("CAST(DATE_BIN('1 hour', p_timestamp, TIMESTAMP '2025-01-01T00:00:00.000Z') AS TEXT) as start_time, DATE_BIN('1 hour', p_timestamp, TIMESTAMP '2025-01-01T00:00:00.000Z') + INTERVAL '1 hour' as end_time")
+ format!(
+ "CAST(DATE_BIN('1 hour', {ts_col}, {anchor}) AS TEXT) AS start_time, \
+ DATE_BIN('1 hour', {ts_col}, {anchor}) + INTERVAL '1 hour' AS end_time"
+ )
} else {
// date_bin 1 day
- String::from("CAST(DATE_BIN('1 day', p_timestamp, TIMESTAMP '2025-01-01T00:00:00.000Z') AS TEXT) as start_time, DATE_BIN('1 day', p_timestamp, TIMESTAMP '2025-01-01T00:00:00.000Z') + INTERVAL '1 day' as end_time")
+ format!(
+ "CAST(DATE_BIN('1 day', {ts_col}, {anchor}) AS TEXT) AS start_time, \
+ DATE_BIN('1 day', {ts_col}, {anchor}) + INTERVAL '1 day' AS end_time"
+ )
};
- let query = if let Some(conditions) = &count_conditions.conditions {
- let f = get_filter_string(conditions).map_err(QueryError::CustomError)?;
- format!("SELECT {date_bin}, COUNT(*) as count FROM {} WHERE {} GROUP BY end_time,start_time ORDER BY end_time",self.stream,f)
- } else {
- format!("SELECT {date_bin}, COUNT(*) as count FROM {} GROUP BY end_time,start_time ORDER BY end_time",self.stream)
- };
+ let base = format!(
+ "SELECT {date_bin}, COUNT(*) AS count FROM \"{}\"",
+ self.stream
+ );
+
+ let base = if let Some(cond) = &count_conditions.conditions {
+ let f = get_filter_string(cond).map_err(QueryError::CustomError)?;
+ format!("{base} WHERE {f}")
+ } else {
+ base
+ };
+
+ // add GROUP BY clauses
+ let mut group_cols = vec!["end_time", "start_time"];
+ if let Some(extra) = &count_conditions.group_by {
+ group_cols.extend(extra.iter().map(|c| c.as_str()));
+ }
+ let group_by = group_cols.join(",");
+ let query = format!("{base} GROUP BY {group_by} ORDER BY end_time");
+
Ok(query)
} This resolves the panic path, honours additional
🤖 Prompt for AI Agents
|
||
} | ||
|
||
/// Response for the counts API | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
get_filter_string
omits identifier quoting & OR-support – may break queries and is inconsistent withget_filter_expr
condition.column
).• This loses the double-quote casing that
match_alert_operator
applies and will silently fail for mixed-case columns or reserved keywords.AND
path is accepted;OR
and single-condition (None
) cases return an error, whereas the expression-based siblingget_filter_expr
handles them gracefully.This keeps behaviour aligned with
get_filter_expr
, avoids casing pitfalls and closes a basic SQL-injection surface.📝 Committable suggestion
🤖 Prompt for AI Agents