From 0d8c7bf6a43f3d8118746a5f04e91630ff3ec19b Mon Sep 17 00:00:00 2001 From: stopachka Date: Wed, 10 Dec 2025 13:32:54 -0800 Subject: [PATCH 1/3] roar --- server/src/instant/db/cel_builder.clj | 35 ++++++++- server/src/instant/db/instaql_topic.clj | 51 ++++++++++++- server/test/instant/db/instaql_topic_test.clj | 73 +++++++++++++++++++ 3 files changed, 155 insertions(+), 4 deletions(-) diff --git a/server/src/instant/db/cel_builder.clj b/server/src/instant/db/cel_builder.clj index af41247fef..cbd821bb0c 100644 --- a/server/src/instant/db/cel_builder.clj +++ b/server/src/instant/db/cel_builder.clj @@ -1,5 +1,5 @@ (ns instant.db.cel-builder - (:refer-clojure :exclude [get get-in and or = not=]) + (:refer-clojure :exclude [get get-in and or = not= < <= > >=]) (:import (com.google.protobuf NullValue) (dev.cel.common.ast CelConstant CelExpr CelExprFactory) (dev.cel.parser Operator))) @@ -104,9 +104,40 @@ cel-exprs)))) (defn get-in - "Build nested index: obj[k1][k2][k3]..." ^CelExpr [obj ks] (reduce (fn [^CelExpr acc k] (get acc k)) (->cel-expr obj *factory*) ks)) + +(defn < + ^CelExpr [a b] + (.newGlobalCall *factory* + (.getFunction Operator/LESS) + ^CelExpr/1 + (into-array CelExpr [(->cel-expr a *factory*) + (->cel-expr b *factory*)]))) + +(defn <= + ^CelExpr [a b] + (.newGlobalCall *factory* + (.getFunction Operator/LESS_EQUALS) + ^CelExpr/1 + (into-array CelExpr [(->cel-expr a *factory*) + (->cel-expr b *factory*)]))) + +(defn > + ^CelExpr [a b] + (.newGlobalCall *factory* + (.getFunction Operator/GREATER) + ^CelExpr/1 + (into-array CelExpr [(->cel-expr a *factory*) + (->cel-expr b *factory*)]))) + +(defn >= + ^CelExpr [a b] + (.newGlobalCall *factory* + (.getFunction Operator/GREATER_EQUALS) + ^CelExpr/1 + (into-array CelExpr [(->cel-expr a *factory*) + (->cel-expr b *factory*)]))) diff --git a/server/src/instant/db/instaql_topic.clj b/server/src/instant/db/instaql_topic.clj index d70ee37855..574749cb8e 100644 --- a/server/src/instant/db/instaql_topic.clj +++ b/server/src/instant/db/instaql_topic.clj @@ -25,12 +25,21 @@ ;; --------- ;; form->ast! +(def ^:private comparison-ops #{:$gt :$gte :$lt :$lte}) + +(defn- comparison-op->cel-fn [op] + (case op + :$gt b/> + :$gte b/>= + :$lt b/< + :$lte b/<=)) + (defn- single-cond->cel-expr! [{:keys [etype attrs]} {:keys [cond-data]}] (let [{:keys [path v]} cond-data [v-type v-data] v] (cond - (> (count path) 1) + (clojure.core/> (count path) 1) (throw-not-supported! [:multi-part-path]) (and (= v-type :args-map) (contains? v-data :$isNull)) @@ -51,6 +60,29 @@ (b/= (b/get-in 'entity ["attrs" (str id)]) nil) (b/not= (b/get-in 'entity ["attrs" (str id)]) nil))) + (and (= v-type :args-map) (some comparison-ops (keys v-data))) + (cond+ + :let [label (first path) + rev-attr (attr-model/seek-by-rev-ident-name [etype label] attrs)] + + rev-attr (throw-not-supported! [:reverse-attribute]) + + :let [{:keys [id cardinality] :as fwd-attr} (attr-model/seek-by-fwd-ident-name [etype label] attrs)] + + (not fwd-attr) (throw-not-supported! [:unknown-attribute]) + + (not= :one cardinality) (throw-not-supported! [:cardinality-many]) + + :let [cmp-exprs (keep (fn [op] + (when-let [[_ cmp-val] (get v-data op)] + ((comparison-op->cel-fn op) + (b/get-in 'entity ["attrs" (str id)]) + cmp-val))) + comparison-ops)] + + :else + (apply b/and cmp-exprs)) + (not= v-type :value) (throw-not-supported! [:complex-value-type]) @@ -89,7 +121,7 @@ (let [{:keys [path v]} cond-data [v-type v-data] v] (cond - (> (count path) 1) + (clojure.core/> (count path) 1) (throw-not-supported! [:multi-part-path]) (and (= v-type :args-map) (contains? v-data :$isNull)) @@ -107,6 +139,21 @@ :else nil) + (and (= v-type :args-map) (some comparison-ops (keys v-data))) + (cond+ + :let [label (first path) + rev-attr (attr-model/seek-by-rev-ident-name [etype label] attrs)] + + rev-attr (throw-not-supported! [:reverse-attribute]) + + :let [{:keys [cardinality] :as fwd-attr} (attr-model/seek-by-fwd-ident-name [etype label] attrs)] + + (not fwd-attr) (throw-not-supported! [:unknown-attribute]) + + (not= :one cardinality) (throw-not-supported! [:cardinality-many]) + + :else nil) + (not= v-type :value) (throw-not-supported! [:complex-value-type]) diff --git a/server/test/instant/db/instaql_topic_test.clj b/server/test/instant/db/instaql_topic_test.clj index 8050870165..c6caaefe4b 100644 --- a/server/test/instant/db/instaql_topic_test.clj +++ b/server/test/instant/db/instaql_topic_test.clj @@ -306,3 +306,76 @@ (is (false? (program {:etype "favoriteBook" :attrs {}}))) (is (false? (program {:etype "posts" :attrs {}}))))))) +(deftest comparison-operators + (with-zeneca-app + (fn [app _r] + (let [score-attr-id (random-uuid) + _ (tx/transact! + (aurora/conn-pool :write) + (attr-model/get-by-app-id (:id app)) + (:id app) + [[:add-attr {:id score-attr-id + :forward-identity [(random-uuid) "users" "score"] + :value-type :blob + :cardinality :one + :unique? false + :index? false}]]) + attrs (attr-model/get-by-app-id (:id app))] + + (testing "$gt" + (let [{:keys [program]} (iqt/instaql-topic + {:attrs attrs} + (iq/->forms! attrs {:users {:$ {:where {:score {:$gt 1000}}}}}))] + (is (true? (program {:etype "users" + :attrs {(str score-attr-id) 2000}}))) + (is (false? (program {:etype "users" + :attrs {(str score-attr-id) 1000}}))) + (is (false? (program {:etype "users" + :attrs {(str score-attr-id) 500}}))))) + + (testing "$gte" + (let [{:keys [program]} (iqt/instaql-topic + {:attrs attrs} + (iq/->forms! attrs {:users {:$ {:where {:score {:$gte 1000}}}}}))] + (is (true? (program {:etype "users" + :attrs {(str score-attr-id) 2000}}))) + (is (true? (program {:etype "users" + :attrs {(str score-attr-id) 1000}}))) + (is (false? (program {:etype "users" + :attrs {(str score-attr-id) 500}}))))) + + (testing "$lt" + (let [{:keys [program]} (iqt/instaql-topic + {:attrs attrs} + (iq/->forms! attrs {:users {:$ {:where {:score {:$lt 1000}}}}}))] + (is (true? (program {:etype "users" + :attrs {(str score-attr-id) 500}}))) + (is (false? (program {:etype "users" + :attrs {(str score-attr-id) 1000}}))) + (is (false? (program {:etype "users" + :attrs {(str score-attr-id) 2000}}))))) + + (testing "$lte" + (let [{:keys [program]} (iqt/instaql-topic + {:attrs attrs} + (iq/->forms! attrs {:users {:$ {:where {:score {:$lte 1000}}}}}))] + (is (true? (program {:etype "users" + :attrs {(str score-attr-id) 500}}))) + (is (true? (program {:etype "users" + :attrs {(str score-attr-id) 1000}}))) + (is (false? (program {:etype "users" + :attrs {(str score-attr-id) 2000}}))))) + + (testing "combined $gt and $lt (range)" + (let [{:keys [program]} (iqt/instaql-topic + {:attrs attrs} + (iq/->forms! attrs {:users {:$ {:where {:score {:$gt 100 :$lt 200}}}}}))] + (is (true? (program {:etype "users" + :attrs {(str score-attr-id) 150}}))) + (is (false? (program {:etype "users" + :attrs {(str score-attr-id) 100}}))) + (is (false? (program {:etype "users" + :attrs {(str score-attr-id) 200}}))) + (is (false? (program {:etype "users" + :attrs {(str score-attr-id) 50}}))))))))) + From bf1c9905788ef6582f35b7fab4e4228e033e9b4c Mon Sep 17 00:00:00 2001 From: stopachka Date: Wed, 10 Dec 2025 13:39:21 -0800 Subject: [PATCH 2/3] roar --- server/src/instant/db/instaql_topic.clj | 11 ++++------- server/test/instant/db/instaql_topic_test.clj | 15 +-------------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/server/src/instant/db/instaql_topic.clj b/server/src/instant/db/instaql_topic.clj index 574749cb8e..92d7c30bc4 100644 --- a/server/src/instant/db/instaql_topic.clj +++ b/server/src/instant/db/instaql_topic.clj @@ -73,15 +73,12 @@ (not= :one cardinality) (throw-not-supported! [:cardinality-many]) - :let [cmp-exprs (keep (fn [op] - (when-let [[_ cmp-val] (get v-data op)] - ((comparison-op->cel-fn op) - (b/get-in 'entity ["attrs" (str id)]) - cmp-val))) - comparison-ops)] + :let [[op [_ cmp-val]] (first v-data)] :else - (apply b/and cmp-exprs)) + ((comparison-op->cel-fn op) + (b/get-in 'entity ["attrs" (str id)]) + cmp-val)) (not= v-type :value) (throw-not-supported! [:complex-value-type]) diff --git a/server/test/instant/db/instaql_topic_test.clj b/server/test/instant/db/instaql_topic_test.clj index c6caaefe4b..4427f1b8cd 100644 --- a/server/test/instant/db/instaql_topic_test.clj +++ b/server/test/instant/db/instaql_topic_test.clj @@ -364,18 +364,5 @@ (is (true? (program {:etype "users" :attrs {(str score-attr-id) 1000}}))) (is (false? (program {:etype "users" - :attrs {(str score-attr-id) 2000}}))))) - - (testing "combined $gt and $lt (range)" - (let [{:keys [program]} (iqt/instaql-topic - {:attrs attrs} - (iq/->forms! attrs {:users {:$ {:where {:score {:$gt 100 :$lt 200}}}}}))] - (is (true? (program {:etype "users" - :attrs {(str score-attr-id) 150}}))) - (is (false? (program {:etype "users" - :attrs {(str score-attr-id) 100}}))) - (is (false? (program {:etype "users" - :attrs {(str score-attr-id) 200}}))) - (is (false? (program {:etype "users" - :attrs {(str score-attr-id) 50}}))))))))) + :attrs {(str score-attr-id) 2000}}))))))))) From 2bc44de93c74feb9bdf892d7d0e185d1e610f474 Mon Sep 17 00:00:00 2001 From: stopachka Date: Wed, 10 Dec 2025 13:48:34 -0800 Subject: [PATCH 3/3] roar --- server/src/instant/db/instaql_topic.clj | 116 ++++++------------------ 1 file changed, 29 insertions(+), 87 deletions(-) diff --git a/server/src/instant/db/instaql_topic.clj b/server/src/instant/db/instaql_topic.clj index 92d7c30bc4..57f110d3c9 100644 --- a/server/src/instant/db/instaql_topic.clj +++ b/server/src/instant/db/instaql_topic.clj @@ -34,6 +34,18 @@ :$lt b/< :$lte b/<=)) +(defn- resolve-cardinality-one-fwd-attr! + [etype label attrs] + (cond+ + :let [rev-attr (attr-model/seek-by-rev-ident-name [etype label] attrs)] + rev-attr (throw-not-supported! [:reverse-attribute]) + + :let [{:keys [id cardinality]} (attr-model/seek-by-fwd-ident-name [etype label] attrs)] + (not id) (throw-not-supported! [:unknown-attribute]) + (not= :one cardinality) (throw-not-supported! [:cardinality-many]) + + :else id)) + (defn- single-cond->cel-expr! [{:keys [etype attrs]} {:keys [cond-data]}] (let [{:keys [path v]} cond-data @@ -43,61 +55,24 @@ (throw-not-supported! [:multi-part-path]) (and (= v-type :args-map) (contains? v-data :$isNull)) - (cond+ - :let [label (first path) - rev-attr (attr-model/seek-by-rev-ident-name [etype label] attrs)] - - rev-attr (throw-not-supported! [:reverse-attribute]) - - :let [{:keys [id cardinality] :as fwd-attr} (attr-model/seek-by-fwd-ident-name [etype label] attrs)] - - (not fwd-attr) (throw-not-supported! [:unknown-attribute]) - - (not= :one cardinality) (throw-not-supported! [:cardinality-many]) - - :else - (if (:$isNull v-data) - (b/= (b/get-in 'entity ["attrs" (str id)]) nil) - (b/not= (b/get-in 'entity ["attrs" (str id)]) nil))) + (let [id (resolve-cardinality-one-fwd-attr! etype (first path) attrs)] + (if (:$isNull v-data) + (b/= (b/get-in 'entity ["attrs" (str id)]) nil) + (b/not= (b/get-in 'entity ["attrs" (str id)]) nil))) (and (= v-type :args-map) (some comparison-ops (keys v-data))) - (cond+ - :let [label (first path) - rev-attr (attr-model/seek-by-rev-ident-name [etype label] attrs)] - - rev-attr (throw-not-supported! [:reverse-attribute]) - - :let [{:keys [id cardinality] :as fwd-attr} (attr-model/seek-by-fwd-ident-name [etype label] attrs)] - - (not fwd-attr) (throw-not-supported! [:unknown-attribute]) - - (not= :one cardinality) (throw-not-supported! [:cardinality-many]) - - :let [[op [_ cmp-val]] (first v-data)] - - :else - ((comparison-op->cel-fn op) - (b/get-in 'entity ["attrs" (str id)]) - cmp-val)) + (let [id (resolve-cardinality-one-fwd-attr! etype (first path) attrs) + [op [_ cmp-val]] (first v-data)] + ((comparison-op->cel-fn op) + (b/get-in 'entity ["attrs" (str id)]) + cmp-val)) (not= v-type :value) (throw-not-supported! [:complex-value-type]) :else - (cond+ - :let [label (first path) - rev-attr (attr-model/seek-by-rev-ident-name [etype label] attrs)] - - rev-attr (throw-not-supported! [:reverse-attribute]) - - :let [{:keys [id cardinality] :as fwd-attr} (attr-model/seek-by-fwd-ident-name [etype label] attrs)] - - (not fwd-attr) (throw-not-supported! [:unknown-attribute]) - - (not= :one cardinality) (throw-not-supported! [:cardinality-many]) - - :else - (b/= (b/get-in 'entity ["attrs" (str id)]) v-data))))) + (let [id (resolve-cardinality-one-fwd-attr! etype (first path) attrs)] + (b/= (b/get-in 'entity ["attrs" (str id)]) v-data))))) (defn- where-cond->cel-expr! [ctx {:keys [where-cond]}] @@ -122,52 +97,19 @@ (throw-not-supported! [:multi-part-path]) (and (= v-type :args-map) (contains? v-data :$isNull)) - (cond+ - :let [label (first path) - rev-attr (attr-model/seek-by-rev-ident-name [etype label] attrs)] - - rev-attr (throw-not-supported! [:reverse-attribute]) - - :let [{:keys [cardinality] :as fwd-attr} (attr-model/seek-by-fwd-ident-name [etype label] attrs)] - - (not fwd-attr) (throw-not-supported! [:unknown-attribute]) - - (not= :one cardinality) (throw-not-supported! [:cardinality-many]) - - :else nil) + (do (resolve-cardinality-one-fwd-attr! etype (first path) attrs) + nil) (and (= v-type :args-map) (some comparison-ops (keys v-data))) - (cond+ - :let [label (first path) - rev-attr (attr-model/seek-by-rev-ident-name [etype label] attrs)] - - rev-attr (throw-not-supported! [:reverse-attribute]) - - :let [{:keys [cardinality] :as fwd-attr} (attr-model/seek-by-fwd-ident-name [etype label] attrs)] - - (not fwd-attr) (throw-not-supported! [:unknown-attribute]) - - (not= :one cardinality) (throw-not-supported! [:cardinality-many]) - - :else nil) + (do (resolve-cardinality-one-fwd-attr! etype (first path) attrs) + nil) (not= v-type :value) (throw-not-supported! [:complex-value-type]) :else - (cond+ - :let [label (first path) - rev-attr (attr-model/seek-by-rev-ident-name [etype label] attrs)] - - rev-attr (throw-not-supported! [:reverse-attribute]) - - :let [{:keys [cardinality] :as fwd-attr} (attr-model/seek-by-fwd-ident-name [etype label] attrs)] - - (not fwd-attr) (throw-not-supported! [:unknown-attribute]) - - (not= :one cardinality) (throw-not-supported! [:cardinality-many]) - - :else nil)))) + (do (resolve-cardinality-one-fwd-attr! etype (first path) attrs) + nil)))) (defn- validate-child-form-where-cond! [ctx {:keys [where-cond]}]