Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions server/src/instant/db/cel_builder.clj
Original file line number Diff line number Diff line change
@@ -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)))
Expand Down Expand Up @@ -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*)])))
100 changes: 43 additions & 57 deletions server/src/instant/db/instaql_topic.clj
Original file line number Diff line number Diff line change
Expand Up @@ -25,50 +25,54 @@
;; ---------
;; 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- 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
[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))
(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)))
(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]}]
Expand All @@ -89,41 +93,23 @@
(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))
(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])
(do (resolve-cardinality-one-fwd-attr! etype (first path) attrs)
nil)

(not= :one cardinality) (throw-not-supported! [:cardinality-many])

:else nil)
(and (= v-type :args-map) (some comparison-ops (keys v-data)))
(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]}]
Expand Down
60 changes: 60 additions & 0 deletions server/test/instant/db/instaql_topic_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,63 @@
(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}})))))))))

Loading