Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

transaction numeric, string, datetime, and hash functions #505

Merged
merged 11 commits into from
Jul 12, 2023
22 changes: 11 additions & 11 deletions src/fluree/db/json_ld/transact.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -386,17 +386,17 @@

(defn modify
[db fuel-tracker json-ld {:keys [t] :as _tx-state}]
(go
(let [mdfn (-> json-ld
syntax/coerce-modification
(q-parse/parse-modification db))
error-ch (async/chan)
update-ch (->> (where/search db mdfn fuel-tracker error-ch)
(update/modify db mdfn t fuel-tracker error-ch)
(into-flakeset fuel-tracker))]
(async/alt!
error-ch ([e] e)
update-ch ([flakes] flakes)))))
(let [mdfn (-> json-ld
syntax/coerce-modification
(q-parse/parse-modification db))]
(go
(let [error-ch (async/chan)
update-ch (->> (where/search db mdfn fuel-tracker error-ch)
(update/modify db mdfn t fuel-tracker error-ch)
(into-flakeset fuel-tracker))]
(async/alt!
error-ch ([e] e)
update-ch ([flakes] flakes))))))

(defn flakes->final-db
"Takes final set of proposed staged flakes and turns them into a new db value
Expand Down
236 changes: 198 additions & 38 deletions src/fluree/db/query/exec/eval.cljc
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
(ns fluree.db.query.exec.eval
(:refer-clojure :exclude [compile rand])
(:refer-clojure :exclude [compile rand concat replace])
(:require [fluree.db.query.exec.group :as group]
[fluree.db.query.exec.where :as where]
[fluree.db.util.log :as log]
[clojure.set :as set]
[clojure.string :as str]
[clojure.walk :refer [postwalk]])
#?(:clj (:import (java.time Instant))))
[clojure.walk :refer [postwalk]]
[clojure.math :as math]
[fluree.db.datatype :as datatype]
[fluree.crypto :as crypto]
[fluree.db.constants :as const])
#?(:clj (:import (java.time Instant OffsetDateTime LocalDateTime))))

#?(:clj (set! *warn-on-reflection* true))

(defn sum
[coll]
Expand Down Expand Up @@ -54,13 +60,7 @@
(> n 0) (-> n int)
(< n 0) (-> n int dec)))

(def groupconcat concat)

(defn rand
([coll]
(rand-nth coll))
([n coll]
(vec (repeatedly n #(rand-nth coll)))))
(def groupconcat clojure.core/concat)

(defn sample
[n coll]
Expand Down Expand Up @@ -106,7 +106,8 @@

(defn now
[]
#?(:clj (.toEpochMilli (Instant/now))))
#?(:clj (str (Instant/now))
:cljs (.toISOString (js/Date.))))

(defn strStarts
[s substr]
Expand All @@ -117,16 +118,198 @@
(str/ends-with? s substr))

(defn subStr
[s start end]
(subs s start end))
;; The index of the first character in a string is 1.
([s start]
(subs s (dec start)))
([s start length]
(let [start (dec start)]
(subs s start (min (+ start length)
(count s))))))

(defn strLen
[s]
(count s))

(defn ucase
[s]
(str/upper-case s))

(defn lcase
[s]
(str/lower-case s))

(defn contains
[s substr]
(str/includes? s substr))

(defn strBefore
[s substr]
(let [[before :as split] (str/split s (re-pattern substr))]
(if (> (count split) 1)
before
"")))

(defn strAfter
[s substr]
(let [split (str/split s (re-pattern substr))]
(if (> (count split) 1)
(last split)
"")))

(defn concat
[& strs]
(apply str strs))

(defn regex
[text pattern]
(boolean (re-find (re-pattern pattern) text)))

(defn replace
[s pattern replacement]
(str/replace s (re-pattern pattern) replacement))

(defn rand
[]
(clojure.core/rand))

(defn year
[datetime-string]
(let [datetime (datatype/coerce datetime-string const/$xsd:dateTime)]
#?(:clj (.getYear ^LocalDateTime datetime)
:cljs (.getFullYear datetime))))

(defn month
[datetime-string]
(let [datetime (datatype/coerce datetime-string const/$xsd:dateTime)]
#?(:clj (.getMonthValue ^LocalDateTime datetime)
:cljs (.getMonth datetime))))

(defn day
[datetime-string]
(let [datetime (datatype/coerce datetime-string const/$xsd:dateTime)]
#?(:clj (.getDayOfMonth ^LocalDateTime datetime)
:cljs (.getDate datetime))))

(defn hours
[datetime-string]
(let [datetime (datatype/coerce datetime-string const/$xsd:dateTime)]
#?(:clj (.getHour ^LocalDateTime datetime)
:cljs (.getHours datetime))))

(defn minutes
[datetime-string]
(let [datetime (datatype/coerce datetime-string const/$xsd:dateTime)]
#?(:clj (.getMinute ^LocalDateTime datetime)
:cljs (.getMinutes datetime))))

(defn seconds
[datetime-string]
(let [datetime (datatype/coerce datetime-string const/$xsd:dateTime)]
#?(:clj (.getSecond ^LocalDateTime datetime)
:cljs (.getSeconds datetime))))

(defn tz
[datetime-string]
(let [datetime (datatype/coerce datetime-string const/$xsd:dateTime)]
#?(:clj (.toString (.getOffset ^OffsetDateTime datetime))
:cljs (.getTimeZoneOffset datetime))))

(defn sha256
[x]
(crypto/sha2-256 x))

(defn sha512
[x]
(crypto/sha2-512 x))

(defn uuid
[]
(str "urn:uuid:" (random-uuid)))

(defn struuid
[]
(str (random-uuid)))

(defn isNumeric
[x]
(number? x))

(defn isBlank
[x]
(and (string? x)
(str/starts-with? x "_:")))

(defn sparql-str
[x]
(str x))

(def allowed-scalar-fns
'#{abs && || ! > < >= <= = + - * / quot and bound coalesce if nil?
not not= now or re-find re-pattern strStarts strEnds subStr})
'#{ && || ! > < >= <= = + - * / quot and bound coalesce if nil?
not not= or re-find re-pattern
;; string fns
strStarts strEnds subStr strLen ucase lcase contains strBefore strAfter concat regex replace
;; numeric fns
abs round ceil floor rand
;; datetime fns
now year month day hours minutes seconds tz
;; hash fns
sha256 sha512
;; rdf term fns
uuid struuid isNumeric isBlank str
})

(def allowed-symbols
(set/union allowed-aggregate-fns allowed-scalar-fns))

(def qualified-symbols
'{
! fluree.db.query.exec.eval/!
|| fluree.db.query.exec.eval/||
&& fluree.db.query.exec.eval/&&
abs clojure.core/abs
avg fluree.db.query.exec.eval/avg
bound fluree.db.query.exec.eval/bound
ceil fluree.db.query.exec.eval/ceil
coalesce fluree.db.query.exec.eval/coalesce
concat fluree.db.query.exec.eval/concat
contains fluree.db.query.exec.eval/contains
count fluree.db.query.exec.eval/count-distinct
floor fluree.db.query.exec.eval/floor
groupconcat fluree.db.query.exec.eval/groupconcat
lcase fluree.db.query.exec.eval/lcase
median fluree.db.query.exec.eval/median
now fluree.db.query.exec.eval/now
rand fluree.db.query.exec.eval/rand
regex fluree.db.query.exec.eval/regex
replace fluree.db.query.exec.eval/replace
round clojure.math/round
sample fluree.db.query.exec.eval/sample
stddev fluree.db.query.exec.eval/stddev
strAfter fluree.db.query.exec.eval/strAfter
strBefore fluree.db.query.exec.eval/strBefore
strEnds fluree.db.query.exec.eval/strEnds
strLen fluree.db.query.exec.eval/strLen
strStarts fluree.db.query.exec.eval/strStarts
subStr fluree.db.query.exec.eval/subStr
sum fluree.db.query.exec.eval/sum
ucase fluree.db.query.exec.eval/ucase
variance fluree.db.query.exec.eval/variance
year fluree.db.query.exec.eval/year
month fluree.db.query.exec.eval/month
day fluree.db.query.exec.eval/day
hours fluree.db.query.exec.eval/hours
minutes fluree.db.query.exec.eval/minutes
seconds fluree.db.query.exec.eval/seconds
tz fluree.db.query.exec.eval/tz
sha256 fluree.db.query.exec.eval/sha256
sha512 fluree.db.query.exec.eval/sha512
uuid fluree.db.query.exec.eval/uuid
struuid fluree.db.query.exec.eval/struuid
isNumeric fluree.db.query.exec.eval/isNumeric
isBlank fluree.db.query.exec.eval/isBlank
str fluree.db.query.exec.eval/sparql-str
})

(defn variable?
[sym]
(and (symbol? sym)
Expand All @@ -153,29 +336,6 @@
symbols
(filter variable?)))

(def qualified-symbols
'{abs fluree.db.query.exec.eval/abs
avg fluree.db.query.exec.eval/avg
bound fluree.db.query.exec.eval/bound
ceil fluree.db.query.exec.eval/ceil
coalesce fluree.db.query.exec.eval/coalesce
count fluree.db.query.exec.eval/count-distinct
floor fluree.db.query.exec.eval/floor
groupconcat fluree.db.query.exec.eval/groupconcat
median fluree.db.query.exec.eval/median
now fluree.db.query.exec.eval/now
rand fluree.db.query.exec.eval/rand
sample fluree.db.query.exec.eval/sample
stddev fluree.db.query.exec.eval/stddev
strStarts fluree.db.query.exec.eval/strStarts
strEnds fluree.db.query.exec.eval/strEnds
subStr fluree.db.query.exec.eval/subStr
sum fluree.db.query.exec.eval/sum
variance fluree.db.query.exec.eval/variance
! fluree.db.query.exec.eval/!
&& fluree.db.query.exec.eval/&&
|| fluree.db.query.exec.eval/||})

(defn qualify
[sym allow-aggregates?]
(let [allowed-fns (if allow-aggregates?
Expand Down
11 changes: 4 additions & 7 deletions src/fluree/db/query/exec/update.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,13 @@
p (::where/val p-mch)
o (::where/val o-mch)
dt (::where/datatype o-mch)]
(when (and s p o dt)
(when (and (some? s) (some? p) (some? o) (some? dt))
(let [s* (if-not (number? s)
(<? (dbproto/-subid db s true))
s)]
[(flake/create s* p o dt t true nil)]))) ; wrap created flake in
; a vector so the
; output of this
; function has the same
; shape as the retract
; functions
;; wrap created flake in a vector so the output of this function has the
;; same shape as the retract functions
[(flake/create s* p o dt t true nil)])))
(catch* e
(log/error e "Error inserting new triple")
(>! error-ch e)))))
Expand Down
2 changes: 1 addition & 1 deletion src/fluree/db/query/json_ld/response.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@
(not (#{:list :set} (-> context (get p-iri) :container))))
(first acc)
acc))))]
(if v
(if (some? v)
(recur r (assoc acc p-iri v))
(recur r acc)))
(if reverse
Expand Down
4 changes: 2 additions & 2 deletions test/fluree/db/query/fql_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@
:where [[?s :schema/age ?age]
{:bind {?decadesOld (quot ?age 10)}}
[?s :schema/name ?name]
{:bind {?firstLetterOfName (subStr ?name 0 1)}}]
{:bind {?firstLetterOfName (subStr ?name 1 1)}}]
:order-by ?firstLetterOfName}
res @(fluree/query db q)]
(is (= [["A" "Alice" 5]
Expand All @@ -159,7 +159,7 @@
(let [q '{:select [?firstLetterOfName ?name ?canVote]
:where [[?s :schema/age ?age]
[?s :schema/name ?name]
{:bind {?firstLetterOfName (subStr ?name 0 1)
{:bind {?firstLetterOfName (subStr ?name 1 1)
?canVote (>= ?age 18)}}]
:order-by ?name}
res @(fluree/query db q)]
Expand Down
Loading