From f4abb7cb6202ce70eab915eeade1cf578d114969 Mon Sep 17 00:00:00 2001 From: Matthew Davidson Date: Thu, 11 Apr 2024 22:56:48 +0700 Subject: [PATCH] feat: Add CONDITIONED BY * EXCEPT capability --- resources/inferenceql/query/base.bnf | 7 +++-- src/inferenceql/query/scalar.cljc | 46 +++++++++++++++++++--------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/resources/inferenceql/query/base.bnf b/resources/inferenceql/query/base.bnf index 6cf3c78..748bd68 100644 --- a/resources/inferenceql/query/base.bnf +++ b/resources/inferenceql/query/base.bnf @@ -80,8 +80,8 @@ select-list ::= select-star-clause / selection (ws? ',' ws? selection)* / aggregation (ws? ',' ws? aggregation)* -select-star-clause ::= star (ws? select-except-clause)? star ::= '*' +select-star-clause ::= star (ws? select-except-clause)? select-except-clause ::= #'(?i)EXCEPT' ws? '(' ws? identifier-list ws? ')' selection ::= (scalar-expr | aggregation) (ws alias-clause)? @@ -225,7 +225,10 @@ density-event-and ::= density-event-1 (ws #'(?i)AND' ws density-event-1)+ density-event-group ::= '(' ws? density-event ws? ')' -conditioned-by-expr ::= model-expr ws #'(?i)CONDITIONED' ws #'(?i)BY' ws ('*' | density-event) +conditioned-by-expr ::= model-expr ws #'(?i)CONDITIONED' ws #'(?i)BY' ws (conditioned-by-star-clause | density-event) + ::= star (ws? conditioned-by-except-clause)? +conditioned-by-except-clause ::= #'(?i)EXCEPT' ws? (<'('> ws? model-var-list ws? <')'> | model-var-list) + incorporate-expr ::= #'(?i)INCORPORATE' ws relation-expr ws #'(?i)INTO' ws model-expr diff --git a/src/inferenceql/query/scalar.cljc b/src/inferenceql/query/scalar.cljc index a3a8594..b9dd4c4 100644 --- a/src/inferenceql/query/scalar.cljc +++ b/src/inferenceql/query/scalar.cljc @@ -56,7 +56,7 @@ [:distribution-event-and left _and right] [:and (plan left) (plan right)] [:distribution-event-binop (variable :guard (tree/tag-pred :variable)) [:binop s] (scalar :guard (tree/tag-pred :scalar-expr))] [(keyword s) (plan variable) (plan scalar)] - [:distribution-event-binop (scalar :guard (tree/tag-pred :scalar-expr)) [:binop s] (variable :guard (tree/tag-pred :variable))] [(keyword s) (plan variable) (plan scalar)] + [:distribution-event-binop (scalar :guard (tree/tag-pred :scalar-expr)) [:binop s] (variable :guard (tree/tag-pred :variable))] [(keyword s) (plan variable) (plan scalar)] [:distribution-event-group "(" child ")"] (plan child) @@ -64,7 +64,7 @@ [:density-event-and & children] (into {} (comp (filter tree/branch?) (map plan)) children) [:density-event-eq (variable :guard (tree/tag-pred :variable)) _= (scalar :guard (tree/tag-pred :scalar-expr))] {(plan variable) (plan scalar)} - [:density-event-eq (scalar :guard (tree/tag-pred :scalar-expr)) _= (variable :guard (tree/tag-pred :variable))] {(plan variable) (plan scalar)} + [:density-event-eq (scalar :guard (tree/tag-pred :scalar-expr)) _= (variable :guard (tree/tag-pred :variable))] {(plan variable) (plan scalar)} [:density-event-group "(" child ")"] (plan child) @@ -81,14 +81,19 @@ (let [query-plan (requiring-resolve 'inferenceql.query.plan/plan)] `(~'iql/eval-relation-plan (~'quote ~(query-plan relation))))]) - [:conditioned-by-expr model _conditioned _by "*"] `(~'iql/condition-all ~(plan model)) - [:conditioned-by-expr model _conditioned _by event] `(~'iql/condition ~(plan model) ~(plan event)) + [:conditioned-by-expr model _conditioned _by [:star _]] `(~'iql/condition-all ~(plan model)) + [:conditioned-by-expr model _conditioned _by [:star _] [:conditioned-by-except-clause & except-children]] + `(~'iql/condition-all-except ~(plan model) ~(plan (into [:conditioned-by-except-clause] except-children))) + [:conditioned-by-expr model _conditioned _by child] `(~'iql/condition ~(plan model) ~(plan child)) + [:conditioned-by-except-clause _except model-var-list] (plan model-var-list) + [:constrained-by-expr model _constrained _by event] `(~'iql/constrain ~(plan model) ~(plan event)) + [:value child] (literal/read child) [:variable _var child] (id-node->str child) - [:variable-list & variables] (map plan variables) + [:variable-list & variables] (into [] (comp (filter tree/branch?) (map plan)) variables) ; remove commas [:identifier child] (plan child) [:delimited-symbol s] (list 'iql/safe-get 'iql-bindings s) @@ -138,14 +143,26 @@ (gpm/condition conditions)))))) (defn condition-all - [model bindings] - (let [conditions (reduce (fn [conditions variable] - (cond-> conditions - (contains? bindings variable) - (assoc variable (get bindings variable)))) - {} - (map str (gpm/variables model)))] - (condition model conditions))) + "Retrieves all variables from the model and conditions them based on the + value found in the bindings, which includes the current tuple/row. + + The 3-arity version takes an additional coll of vars to exclude." + ([model bindings] + (condition-all model #{} bindings)) + ([model exclusions bindings] + (let [exclusions (set exclusions) + condition-vars (into [] + (comp + (map name) + (filter (complement exclusions))) + (gpm/variables model)) + conditions (reduce (fn [conditions variable] + (cond-> conditions + (contains? bindings variable) + (assoc variable (get bindings variable)))) + {} + condition-vars)] + (condition model conditions)))) (defn operation? "Given an event form, returns `true` if the form is an operation." @@ -273,7 +290,8 @@ #?@(:clj ['eval-relation-plan (let [eval (requiring-resolve 'inferenceql.query.plan/eval)] #(generative-table/generative-table (eval % env bindings)))]) - #?@(:clj ['condition-all #(condition-all % bindings)]) + #?@(:clj ['condition-all #(condition-all % bindings) + 'condition-all-except #(condition-all %1 %2 bindings)]) 'condition condition 'constrain constrain 'mutual-info mutual-info