#filter #params #parse #odata #pagination #parameters #parser

odata-params

OData v4查询参数解析器,用于处理分页请求

5个版本 (3个破坏性更新)

0.4.0 2024年7月30日
0.3.0 2024年7月28日
0.2.0 2024年6月27日
0.1.1 2024年6月26日
0.1.0 2024年6月25日

#2 in #params

Download history 425/week @ 2024-06-21 49/week @ 2024-06-28 3/week @ 2024-07-05 263/week @ 2024-07-26 45/week @ 2024-08-02

每月308次下载

MIT/Apache

36KB
456

OData v4参数解析库

此库为OData v4 $filter表达式提供解析器。它将这些表达式转换为抽象语法树(AST),以便进行进一步处理和评估。解析器支持广泛的逻辑运算符、比较运算符、函数调用和嵌套表达式,使其非常适合复杂的查询需求。

请注意,这是一个持续开发的项目。下一版本将添加支持和不支持的功能组件的完整列表。

功能

  • 逻辑运算符: and, or, not
  • 比较运算符: eq, ne, gt, lt, ge, le
  • 函数调用: 例如 startswith, endswith, contains, concat
  • 分组: 括号内的嵌套表达式
  • 数据类型: 字符串、数字、布尔值、日期、时间、带时区的日期时间

尚未实现

  • lambda函数: any, all
  • 参数别名: @something
  • has运算符

数据类型

该库在表达式中支持以下数据类型

  • 字符串: 用单引号括起来 'example'
  • 数字: 整数和小数 123, 45.67
  • UUID: UUIDs da820b39-5ad2-4441-b664-c902dbd377d8
  • 布尔值: true, false
  • 时间: ISO 8601格式 HH:MM:SS
  • 日期: ISO 8601格式 YYYY-MM-DD
  • 日期时间:ISO 8601 格式,带有时区 YYYY-MM-DDTHH:MM:SSZ

测试

该库包含一组由AI生成的大部分测试,用于捕获回归。测试文件包含多个示例,以展示如何使用库以及预期输出。

可以使用以下命令运行测试

cargo test

安装

要将此库添加到您的项目中,请将以下内容添加到您的 Cargo.toml

[dependencies]
odata-params = "0.2.0"

或者运行 cargo add odata-params

示例

以下是如何解析简单过滤器表达式的示例

use odata_params::filters::{parse_str, to_query_string};

fn main() {
    // Convert a string query to Expr AST.
    let filter = "name eq 'John' and isActive eq true";
    let result = parse_str(filter).expect("valid filter tree");

    println!("{:#?}", result);

    // Convert Expr AST into a string query.
    let query_string = to_query_string(&result).expect("valid query string");
    println!("{}", query_string);
}

支持的表达式

逻辑运算符

use odata_params::filters::parse_str;

let filter = "name eq 'John' or isActive eq true";
let result = parse_str(filter).expect("valid filter tree");

// Expected Expr structure:
// Expr::Or(
//     Box::new(Expr::Compare(
//         Box::new(Expr::Identifier("name".to_owned())),
//         CompareOperator::Equal,
//         Box::new(Expr::Value(Value::String("John".to_owned()))),
//     )),
//     Box::new(Expr::Compare(
//         Box::new(Expr::Identifier("isActive".to_owned())),
//         CompareOperator::Equal,
//         Box::new(Expr::Value(Value::Bool(true))),
//     )),
// )

比较运算符

use odata_params::filters::parse_str;

let filter = "price lt 99.99";
let result = parse_str(filter).expect("valid filter tree");

// Expected Expr structure:
// Expr::Compare(
//     Box::new(Expr::Identifier("price".to_owned())),
//     CompareOperator::LessThan,
//     Box::new(Expr::Value(Value::Number(BigDecimal::from_str("99.99").unwrap()))),
// )

函数调用

use odata_params::filters::parse_str;

let filter = "endswith(name, 'Smith')";
let result = parse_str(filter).expect("valid filter tree");

// Expected Expr structure:
// Expr::Function(
//     "endswith".to_owned(),
//     vec![
//         Expr::Identifier("name".to_owned()),
//         Expr::Value(Value::String("Smith".to_owned()))
//     ]
// )

高级用法

嵌套分组

use odata_params::filters::parse_str;

let filter = "((name eq 'John' and isActive eq true) or (age gt 30 and age lt 50))";
let result = parse_str(filter).expect("valid filter tree");

// Expected Expr structure:
// Expr::Or(
//     Box::new(Expr::And(
//         Box::new(Expr::Compare(
//             Box::new(Expr::Identifier("name".to_owned())),
//             CompareOperator::Equal,
//             Box::new(Expr::Value(Value::String("John".to_owned()))),
//         )),
//         Box::new(Expr::Compare(
//             Box::new(Expr::Identifier("isActive".to_owned())),
//             CompareOperator::Equal,
//             Box::new(Expr::Value(Value::Bool(true))),
//         )),
//     )),
//     Box::new(Expr::And(
//         Box::new(Expr::Compare(
//             Box::new(Expr::Identifier("age".to_owned())),
//             CompareOperator::GreaterThan,
//             Box::new(Expr::Value(Value::Number(BigDecimal::from_str("30").unwrap()))),
//         )),
//         Box::new(Expr::Compare(
//             Box::new(Expr::Identifier("age".to_owned())),
//             CompareOperator::LessThan,
//             Box::new(Expr::Value(Value::Number(BigDecimal::from_str("50").unwrap()))),
//         )),
//     )),
// )

带有比较的函数

use odata_params::filters::parse_str;

let filter = "concat(concat(city, ', '), country) eq 'Berlin, Germany'";
let result = parse_str(filter).expect("valid filter tree");

// Expected Expr structure:
// Expr::Compare(
//     Box::new(Expr::Function(
//         "concat".to_owned(),
//         vec![
//             Expr::Function(
//                 "concat".to_owned(),
//                 vec![
//                     Expr::Identifier("city".to_owned()),
//                     Expr::Value(Value::String(", ".to_owned()))
//                 ]
//             ),
//             Expr::Identifier("country".to_owned())
//         ]
//     )),
//     CompareOperator::Equal,
//     Box::new(Expr::Value(Value::String("Berlin, Germany".to_owned())))
// )

依赖项

~2.9–4MB
~72K SLoC