Sign in

    queryx

    JSON-queryable filter DSL with foxql SQL builder bridge

    query
    filter
    json
    dsl
    Download zip
    Author
    Version
    0.2.1
    License
    Apache-2.0
    Last updated
    2 months ago
    Downloads
    28

    Dependencies

    #QueryX

    JSON-queryable filter DSL with to/from JSON, eval, and foxql bridge.

    #Setup

    // moon.mod
    import {
    "jaredzhou/queryx@0.1.0",
    }

    // moon.pkg
    import {
    "jaredzhou/queryx",
    }

    #Quick start

    ///|
    let input : Json = {
    "filter": { "age": { "gt": 18.0, "lt": 65.0 }, "name": { "contains": "tom" } },
    "order_by": [{ "age": "desc" }],
    "page": { "after": "", "size": 10.0 },
    }

    ///|
    let query : Query = @json.from_json(input)

    // JSON round-trip

    ///|
    let json = query.to_json()

    #Expr — filter expression tree

    Supports Compare, Between, StrMatch (contains/starts_with/ends_with), And, Or, Not, and Empty.

    let expr = Expr::And([
    Expr::Compare("age", CmpOp::Gt, Value::Int(18L)),
    Expr::StrMatch("name", StrKind::Contains, "tom"),
    ])

    // Evaluate against field bindings
    let values = Map([])
    values.set("age", Value::Int(25L))
    values.set("name", Value::String("tommy"))
    assert_true(expr.eval(values)!)

    #FoxQL bridge

    Convert queryx Expr / Query into foxql SQL via FieldResolver.

    #1. Implement FieldResolver for your table

    ///|
    struct User {
    table : Table
    id : Column[Int]
    name : Column[String]
    age : Column[Int]
    }

    ///|
    impl FieldResolver for User with fn resolve_column(self, field) -> ColumnRef? {
    match field {
    "id" => Some(self.id.to_ref())
    "name" => Some(self.name.to_ref())
    "age" => Some(self.age.to_ref())
    _ => None
    }
    }

    ///|
    impl FieldResolver for User with fn table_name(self) -> String {
    "users"
    }

    #2. Expr → foxql Expr

    ///|
    let expr = Expr::Compare("age", CmpOp::Gt, Value::Int(18L))

    ///|
    let foxql_expr = to_foxql_expr(expr, user)
    // → Expr::Compare(users.age, Gt, Value::Int(18))

    #3. Query → foxql SelectBuilder

    let query : Query = @json.from_json(...)
    let (sql, args) = to_select(query, user).to_sql()
    // WHERE + ORDER BY + LIMIT all handled automatically

    #4. Dynamic schema (SchemaTable)

    let schema = Schema::load([...])
    let users = schema.table("users")
    // SchemaTable implements FieldResolver natively
    let (sql, args) = to_select(query, users).to_sql()

    #Package structure

    queryx/ ├── queryx.mbt # Query, Order, Page types + JSON serde ├── expr.mbt # Expr, Value, CmpOp, StrKind + eval + JSON serde ├── foxql_bridge.mbt # FieldResolver trait, to_foxql_expr, to_select ├── foxql_bridge_test.mbt ├── queryx_test.mbt └── expr_test.mbt

    ToValue

    pub trait ToValue {
    fn to_value(Self) -> Value
    }

    impl ToValue for Bool
    impl ToValue for Int
    impl ToValue for Int64
    impl ToValue for Double
    impl ToValue for String

    QueryError

    pub suberror QueryError {
    EvalError(String)
    } derive(ToJson)

    QueryxError

    pub suberror QueryxError {
    Unsupported(String)
    UnknownField(String)
    EmptyAnd
    EmptyOr
    } derive(
    Debug
    )

    CmpOp

    pub(all) enum CmpOp {
    Eq
    Ne
    Gt
    Lt
    Ge
    Le
    } derive(Eq,
    Debug
    )

    Expr

    pub(all) enum Expr {
    Lit(Value)
    Field(String)
    Compare(String, CmpOp, Value)
    Between(String, Value, Value)
    StrMatch(String, StrKind, String)
    And(Array[Expr])
    Or(Array[Expr])
    Not(Expr)
    Empty
    } derive(Eq,
    Debug
    )

    impl ToJson for Expr

    Expr::eval

    fn Expr::eval(self : Expr, values : Map[String, Value]) -> Bool raise QueryError

    Evaluate the expression against a concrete map of field → value bindings.

    Order

    pub(all) struct Order(String) derive(
    Debug
    )

    Page

    pub(all) struct Page {
    after : String
    size : Int
    } derive(ToJson,
    Debug
    ,
    FromJson
    )

    Query

    pub(all) struct Query {
    filter : Expr
    order_by : Order?
    page : Page?
    set : Json?
    } derive(
    Debug
    )

    StrKind

    pub(all) enum StrKind {
    Contains
    StartsWith
    EndsWith
    } derive(Eq,
    Debug
    )

    Value

    pub(all) enum Value {
    Null
    Bool(Bool)
    Int(Int64)
    Float(Double)
    String(String)
    } derive(Eq,
    Debug
    )

    impl ToValue for Value
    impl ToJson for Value

    to_foxql_expr

    Convert a queryx Expr to a foxql Expr.

    to_foxql_op

    Convert a queryx CmpOp to a foxql Operator.

    to_foxql_value

    fn to_foxql_value(v : Value) ->
    Value

    Convert a queryx Value to a foxql Value.

    to_select

    Convert a queryx Query to a foxql SelectBuilder (WHERE, ORDER BY, LIMIT). The resolver provides both field→column mapping and the table name.