|  | 
| 16 | 16 |  * | 
| 17 | 17 |  */ | 
| 18 | 18 | 
 | 
|  | 19 | +use std::fmt::Display; | 
|  | 20 | + | 
| 19 | 21 | use arrow_array::{Float64Array, Int64Array, RecordBatch}; | 
| 20 | 22 | use datafusion::{ | 
| 21 | 23 |     functions_aggregate::{ | 
| @@ -345,67 +347,91 @@ pub fn get_filter_string(where_clause: &Conditions) -> Result<String, String> { | 
| 345 | 347 |             LogicalOperator::And => { | 
| 346 | 348 |                 let mut exprs = vec![]; | 
| 347 | 349 |                 for condition in &where_clause.condition_config { | 
| 348 |  | -                    let value = NumberOrString::from_string(condition.value.to_string()); | 
| 349 |  | -                    match value { | 
| 350 |  | -                        NumberOrString::Number(val) => exprs.push(format!( | 
| 351 |  | -                            "{} {} {}", | 
| 352 |  | -                            condition.column, condition.operator, val | 
| 353 |  | -                        )), | 
| 354 |  | -                        NumberOrString::String(val) => exprs.push(format!( | 
| 355 |  | -                            "{} {} '{}'", | 
| 356 |  | -                            condition.column, condition.operator, val | 
| 357 |  | -                        )), | 
|  | 350 | +                    if let Some(value) = &condition.value { | 
|  | 351 | +                        // ad-hoc error check in case value is some and operator is either `is null` or `is not null` | 
|  | 352 | +                        if condition.operator.eq(&WhereConfigOperator::IsNull) | 
|  | 353 | +                            || condition.operator.eq(&WhereConfigOperator::IsNotNull) | 
|  | 354 | +                        { | 
|  | 355 | +                            return Err("value must be null when operator is either `is null` or `is not null`" | 
|  | 356 | +                                .into()); | 
|  | 357 | +                        } | 
|  | 358 | +                        let value = NumberOrString::from_string(value.to_owned()); | 
|  | 359 | +                        match value { | 
|  | 360 | +                            NumberOrString::Number(val) => exprs.push(format!( | 
|  | 361 | +                                "\"{}\" {} {}", | 
|  | 362 | +                                condition.column, condition.operator, val | 
|  | 363 | +                            )), | 
|  | 364 | +                            NumberOrString::String(val) => exprs.push(format!( | 
|  | 365 | +                                "\"{}\" {} '{}'", | 
|  | 366 | +                                condition.column, | 
|  | 367 | +                                condition.operator, | 
|  | 368 | +                                val.replace('\'', "''") | 
|  | 369 | +                            )), | 
|  | 370 | +                        } | 
|  | 371 | +                    } else { | 
|  | 372 | +                        exprs.push(format!("\"{}\" {}", condition.column, condition.operator)) | 
| 358 | 373 |                     } | 
| 359 | 374 |                 } | 
| 360 | 375 | 
 | 
| 361 | 376 |                 Ok(exprs.join(" AND ")) | 
| 362 | 377 |             } | 
| 363 |  | -            _ => Err(String::from("Invalid option 'or'")), | 
|  | 378 | +            _ => Err(String::from("Invalid option 'or', only 'and' is supported")), | 
| 364 | 379 |         }, | 
| 365 |  | -        _ => Err(String::from("Invalid option 'null'")), | 
|  | 380 | +        _ => Err(String::from( | 
|  | 381 | +            "Invalid option 'null', only 'and' is supported", | 
|  | 382 | +        )), | 
| 366 | 383 |     } | 
| 367 | 384 | } | 
| 368 | 385 | 
 | 
| 369 | 386 | fn match_alert_operator(expr: &ConditionConfig) -> Expr { | 
| 370 | 387 |     // the form accepts value as a string | 
| 371 | 388 |     // if it can be parsed as a number, then parse it | 
| 372 | 389 |     // else keep it as a string | 
| 373 |  | -    let value = NumberOrString::from_string(expr.value.clone()); | 
| 374 |  | - | 
| 375 |  | -    // for maintaining column case | 
| 376 |  | -    let column = format!(r#""{}""#, expr.column); | 
| 377 |  | -    match expr.operator { | 
| 378 |  | -        WhereConfigOperator::Equal => col(column).eq(lit(value)), | 
| 379 |  | -        WhereConfigOperator::NotEqual => col(column).not_eq(lit(value)), | 
| 380 |  | -        WhereConfigOperator::LessThan => col(column).lt(lit(value)), | 
| 381 |  | -        WhereConfigOperator::GreaterThan => col(column).gt(lit(value)), | 
| 382 |  | -        WhereConfigOperator::LessThanOrEqual => col(column).lt_eq(lit(value)), | 
| 383 |  | -        WhereConfigOperator::GreaterThanOrEqual => col(column).gt_eq(lit(value)), | 
| 384 |  | -        WhereConfigOperator::IsNull => col(column).is_null(), | 
| 385 |  | -        WhereConfigOperator::IsNotNull => col(column).is_not_null(), | 
| 386 |  | -        WhereConfigOperator::ILike => col(column).ilike(lit(&expr.value)), | 
| 387 |  | -        WhereConfigOperator::Contains => col(column).like(lit(&expr.value)), | 
| 388 |  | -        WhereConfigOperator::BeginsWith => Expr::BinaryExpr(BinaryExpr::new( | 
| 389 |  | -            Box::new(col(column)), | 
| 390 |  | -            Operator::RegexIMatch, | 
| 391 |  | -            Box::new(lit(format!("^{}", expr.value))), | 
| 392 |  | -        )), | 
| 393 |  | -        WhereConfigOperator::EndsWith => Expr::BinaryExpr(BinaryExpr::new( | 
| 394 |  | -            Box::new(col(column)), | 
| 395 |  | -            Operator::RegexIMatch, | 
| 396 |  | -            Box::new(lit(format!("{}$", expr.value))), | 
| 397 |  | -        )), | 
| 398 |  | -        WhereConfigOperator::DoesNotContain => col(column).not_ilike(lit(&expr.value)), | 
| 399 |  | -        WhereConfigOperator::DoesNotBeginWith => Expr::BinaryExpr(BinaryExpr::new( | 
| 400 |  | -            Box::new(col(column)), | 
| 401 |  | -            Operator::RegexNotIMatch, | 
| 402 |  | -            Box::new(lit(format!("^{}", expr.value))), | 
| 403 |  | -        )), | 
| 404 |  | -        WhereConfigOperator::DoesNotEndWith => Expr::BinaryExpr(BinaryExpr::new( | 
| 405 |  | -            Box::new(col(column)), | 
| 406 |  | -            Operator::RegexNotIMatch, | 
| 407 |  | -            Box::new(lit(format!("{}$", expr.value))), | 
| 408 |  | -        )), | 
|  | 390 | +    if let Some(value) = &expr.value { | 
|  | 391 | +        let value = NumberOrString::from_string(value.clone()); | 
|  | 392 | + | 
|  | 393 | +        // for maintaining column case | 
|  | 394 | +        let column = format!(r#""{}""#, expr.column); | 
|  | 395 | +        match expr.operator { | 
|  | 396 | +            WhereConfigOperator::Equal => col(column).eq(lit(value)), | 
|  | 397 | +            WhereConfigOperator::NotEqual => col(column).not_eq(lit(value)), | 
|  | 398 | +            WhereConfigOperator::LessThan => col(column).lt(lit(value)), | 
|  | 399 | +            WhereConfigOperator::GreaterThan => col(column).gt(lit(value)), | 
|  | 400 | +            WhereConfigOperator::LessThanOrEqual => col(column).lt_eq(lit(value)), | 
|  | 401 | +            WhereConfigOperator::GreaterThanOrEqual => col(column).gt_eq(lit(value)), | 
|  | 402 | +            WhereConfigOperator::ILike => col(column).ilike(lit(value)), | 
|  | 403 | +            WhereConfigOperator::Contains => col(column).like(lit(value)), | 
|  | 404 | +            WhereConfigOperator::BeginsWith => Expr::BinaryExpr(BinaryExpr::new( | 
|  | 405 | +                Box::new(col(column)), | 
|  | 406 | +                Operator::RegexIMatch, | 
|  | 407 | +                Box::new(lit(format!("^{}", value))), | 
|  | 408 | +            )), | 
|  | 409 | +            WhereConfigOperator::EndsWith => Expr::BinaryExpr(BinaryExpr::new( | 
|  | 410 | +                Box::new(col(column)), | 
|  | 411 | +                Operator::RegexIMatch, | 
|  | 412 | +                Box::new(lit(format!("{}$", value))), | 
|  | 413 | +            )), | 
|  | 414 | +            WhereConfigOperator::DoesNotContain => col(column).not_ilike(lit(value)), | 
|  | 415 | +            WhereConfigOperator::DoesNotBeginWith => Expr::BinaryExpr(BinaryExpr::new( | 
|  | 416 | +                Box::new(col(column)), | 
|  | 417 | +                Operator::RegexNotIMatch, | 
|  | 418 | +                Box::new(lit(format!("^{}", value))), | 
|  | 419 | +            )), | 
|  | 420 | +            WhereConfigOperator::DoesNotEndWith => Expr::BinaryExpr(BinaryExpr::new( | 
|  | 421 | +                Box::new(col(column)), | 
|  | 422 | +                Operator::RegexNotIMatch, | 
|  | 423 | +                Box::new(lit(format!("{}$", value))), | 
|  | 424 | +            )), | 
|  | 425 | +            _ => unreachable!("value must not be null for operators other than `is null` and `is not null`. Should've been caught in validation") | 
|  | 426 | +        } | 
|  | 427 | +    } else { | 
|  | 428 | +        // for maintaining column case | 
|  | 429 | +        let column = format!(r#""{}""#, expr.column); | 
|  | 430 | +        match expr.operator { | 
|  | 431 | +            WhereConfigOperator::IsNull => col(column).is_null(), | 
|  | 432 | +            WhereConfigOperator::IsNotNull => col(column).is_not_null(), | 
|  | 433 | +            _ => unreachable!("value must be null for `is null` and `is not null`. Should've been caught in validation") | 
|  | 434 | +        } | 
| 409 | 435 |     } | 
| 410 | 436 | } | 
| 411 | 437 | 
 | 
| @@ -444,3 +470,12 @@ impl NumberOrString { | 
| 444 | 470 |         } | 
| 445 | 471 |     } | 
| 446 | 472 | } | 
|  | 473 | + | 
|  | 474 | +impl Display for NumberOrString { | 
|  | 475 | +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | 
|  | 476 | +        match self { | 
|  | 477 | +            NumberOrString::Number(v) => write!(f, "{v}"), | 
|  | 478 | +            NumberOrString::String(v) => write!(f, "{v}"), | 
|  | 479 | +        } | 
|  | 480 | +    } | 
|  | 481 | +} | 
0 commit comments