Skip to content

allow two-step compilation/evaluation #12

Closed

Description

@aschus

As far as I understand, this library is optimized for the use case of evaluating multiple CEL expressions against one and the same context. To also support efficiently evaluating the same CEL expression against multiple contexts, it would make sense to also expose the parsing/compilation step of cel-rust (i.e., the Program class). For example, something like:

filter = CelProgram(expression="person.age >= 18")
adults = [p for p in persons if filter.evaluate(context={"person": p})]

This would also allow exposing the references of the Program, which would be useful to e.g. optimize DB queries to only include the fields that are needed for evaluating some CEL filter expression.

assert CelProgram("age(person)").variable_references == ["person"]
assert CelProgram("age(person)").function_references == ["age"]

I am not sure, though, if this would play well with your type coercion add-on to cel-rust, but I could live with it being unavailable for that use case (see also #11).

Activity

  1. hardbyte commented on Aug 11, 2025

    @hardbyte
    Owner

    Yes I'm very open to that.

    I think upstream is likely to make changes to align closer to the cel-spec to have a binary go/proto serialization e.g. cel-rust/cel-rust#116
    Also see cel-rust/cel-rust#115

    Happy to review a PR otherwise I'll probably wait until upstream have made some changes.

  2. hardbyte commented on Aug 19, 2026

    @hardbyte
    Owner

    This is implemented as of the cel-rust 0.14 work (#37) — cel.compile() returns a reusable Program, and the reference introspection asked for here is available too.

    Verified against main (bc21129, cel 0.14.3):

    import cel
    
    adult = cel.compile("person.age >= 18")
    adult.source                       # 'person.age >= 18'
    [adult.execute({"person": p}) for p in persons]
    
    prog = cel.compile("age(person)")
    prog.variables()                   # ['person']
    prog.functions()                   # ['age']
    prog.references()                  # {'variables': ['person'], 'functions': ['age']}

    Program.execute() accepts either a plain dict or a cel.Context, so the same compiled program can be run against many contexts, and variables()/references() give you the static analysis needed to narrow a DB query to the fields a filter expression actually touches.

    Static analysis notes: names bound by comprehension macros (the x in [1, 2].map(x, x * 2)) show up in variables() because they are identifiers in the AST, and functions() includes CEL operator overload identifiers (e.g. _+_) alongside named functions.

    Closing as completed — please reopen if the reference output doesn't fit your query-optimisation use case.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions