-
-
Notifications
You must be signed in to change notification settings - Fork 97
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- schema migration as norm namespace in datahike - Closes #13
- Loading branch information
1 parent
11adbeb
commit c477a5d
Showing
4 changed files
with
216 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
(ns datahike.norm.norm | ||
(:require | ||
[clojure.java.io :as io] | ||
[clojure.string :as string] | ||
[taoensso.timbre :as log] | ||
[datahike.api :as d])) | ||
|
||
(defn attribute-installed? [conn attr] | ||
(some? (d/entity @conn [:db/ident attr]))) | ||
|
||
(defn ensure-norm-attribute! [conn] | ||
(if-not (attribute-installed? conn :tx/norm) | ||
(:db-after (d/transact conn {:tx-data [{:db/ident :tx/norm | ||
:db/valueType :db.type/keyword | ||
:db/cardinality :db.cardinality/one}]})) | ||
@conn)) | ||
|
||
(defn norm-installed? [db norm] | ||
(->> {:query '[:find (count ?t) | ||
:in $ ?tn | ||
:where | ||
[_ :tx/norm ?tn ?t]] | ||
:args [db norm]} | ||
d/q | ||
first | ||
some?)) | ||
|
||
(defn read-norm-files! [norms-folder] | ||
(let [folder (io/file norms-folder)] | ||
(if (.exists folder) | ||
(let [migration-files (file-seq folder) | ||
xf (comp | ||
(filter #(re-find #".edn" (.getPath %))) | ||
(map (fn [migration-file] | ||
(-> (.getPath migration-file) | ||
slurp | ||
read-string | ||
(update :norm (fn [norm] (or norm | ||
(-> (.getName migration-file) | ||
(string/replace #"\.edn" "") | ||
keyword))))))))] | ||
(sort-by :norm (into [] xf migration-files))) | ||
(throw | ||
(ex-info | ||
(format "Norms folder %s does not exist." norms-folder) | ||
{:folder norms-folder}))))) | ||
|
||
(defn neutral-fn [_] []) | ||
|
||
(defn ensure-norms! | ||
([conn] | ||
(ensure-norms! conn (io/resource "migrations"))) | ||
([conn migrations] | ||
(let [db (ensure-norm-attribute! conn) | ||
norm-list (cond | ||
(string? migrations) (read-norm-files! migrations) | ||
(vector? migrations) migrations)] | ||
(log/info "Checking migrations ...") | ||
(doseq [{:keys [norm tx-data tx-fn] | ||
:or {tx-data [] | ||
tx-fn #'neutral-fn}} | ||
norm-list] | ||
(log/info "Checking migration" norm) | ||
(when-not (norm-installed? db norm) | ||
(log/info "Run migration" norm) | ||
(->> (d/transact conn {:tx-data (vec (concat [{:tx/norm norm}] | ||
tx-data | ||
(tx-fn conn)))}) | ||
(log/info "Done"))))))) | ||
|
||
(comment | ||
(d/delete-database {:store {:backend :file | ||
:path "/tmp/file-example"}}) | ||
(d/create-database {:store {:backend :file | ||
:path "/tmp/file-example"}}) | ||
(def conn (d/connect {:store {:backend :file | ||
:path "/tmp/file-example"}})) | ||
(ensure-norms! conn "test/resources") | ||
(def norm-list (read-norm-files! "test/resources")) | ||
(norm-installed? (d/db conn) (:norm (first norm-list))) | ||
(d/transact conn {:tx-data [{:foo "foo"}]})) | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
(ns datahike.norm.norm-test | ||
(:require [clojure.test :refer [deftest is]] | ||
[clojure.string :as s] | ||
[datahike.api :as d] | ||
[datahike.norm :as sut])) | ||
|
||
(defn create-test-db [] | ||
(let [id (apply str | ||
(for [_i (range 8)] | ||
(char (+ (rand 26) 65))))] | ||
(d/create-database {:store {:backend :mem | ||
:id id}}) | ||
(d/connect {:store {:backend :mem | ||
:id id}}))) | ||
|
||
(deftest simple-test | ||
(let [conn (create-test-db) | ||
_ (sut/ensure-norms! conn "test/datahike/norm/resources")] | ||
(is (= #:db{:valueType :db.type/string, :cardinality :db.cardinality/one, :doc "foo", :ident :foo} | ||
(-> (d/schema (d/db conn)) | ||
:foo | ||
(dissoc :db/id)))) | ||
(is (= #:db{:valueType :db.type/string, :cardinality :db.cardinality/one, :doc "Simpsons character name", :ident :character/name} | ||
(-> (d/schema (d/db conn)) | ||
:character/name | ||
(dissoc :db/id)))) | ||
(is (= #:db{:ident :tx/norm, :valueType :db.type/keyword, :cardinality :db.cardinality/one} | ||
(-> (d/schema (d/db conn)) | ||
:tx/norm | ||
(dissoc :db/id)))))) | ||
|
||
(deftest tx-fn-test | ||
(let [conn (create-test-db) | ||
_ (sut/ensure-norms! conn "test/datahike/norm/resources") | ||
_ (d/transact conn {:tx-data [{:foo "upper-case"} | ||
{:foo "Grossbuchstaben"}]}) | ||
test-fn (fn [conn] | ||
(-> (for [[eid value] (d/q '[:find ?e ?v | ||
:where | ||
[?e :foo ?v]] | ||
(d/db conn))] | ||
[:db/add eid | ||
:foo (s/upper-case value)]) | ||
vec)) | ||
test-norm [{:norm :test-norm-1, | ||
:tx-fn test-fn}] | ||
_ (sut/ensure-norms! conn test-norm)] | ||
(is (= #{["GROSSBUCHSTABEN"] ["UPPER-CASE"]} | ||
(d/q '[:find ?v | ||
:where | ||
[_ :foo ?v]] | ||
(d/db conn)))))) | ||
|
||
(deftest tx-and-fn-test | ||
(let [conn (create-test-db) | ||
_ (sut/ensure-norms! conn "test/datahike/norm/resources") | ||
_ (d/transact conn {:tx-data [{:character/name "Homer Simpson"} | ||
{:character/name "Marge Simpson"}]}) | ||
margehomer (d/q '[:find [?e ...] | ||
:where | ||
[?e :character/name]] | ||
(d/db conn)) | ||
tx-data [{:db/doc "Simpsons children reference" | ||
:db/ident :character/child | ||
:db/valueType :db.type/ref | ||
:db/cardinality :db.cardinality/many}] | ||
tx-fn (fn [conn] | ||
(-> (for [[eid] (d/q '[:find ?e | ||
:where | ||
[?e :character/name] | ||
(or-join [?e] | ||
[?e :character/name "Homer Simpson"] | ||
[?e :character/name "Marge Simpson"])] | ||
(d/db conn))] | ||
{:db/id eid | ||
:character/child [{:character/name "Bart Simpson"} | ||
{:character/name "Lisa Simpson"} | ||
{:character/name "Maggie Simpson"}]}) | ||
vec)) | ||
test-norm [{:norm :test-norm-2 | ||
:tx-data tx-data | ||
:tx-fn tx-fn}]] | ||
(sut/ensure-norms! conn test-norm) | ||
(is (= [#:character{:name "Marge Simpson", | ||
:child | ||
[#:character{:name "Bart Simpson"} | ||
#:character{:name "Lisa Simpson"} | ||
#:character{:name "Maggie Simpson"}]} | ||
#:character{:name "Homer Simpson", | ||
:child | ||
[#:character{:name "Bart Simpson"} | ||
#:character{:name "Lisa Simpson"} | ||
#:character{:name "Maggie Simpson"}]}] | ||
(d/pull-many (d/db conn) '[:character/name {:character/child [:character/name]}] margehomer))))) | ||
|
||
(comment | ||
(def conn (create-test-db)) | ||
(sut/ensure-norms! conn "test/resources") | ||
(d/transact conn {:tx-data [{:character/name "Homer Simpson"} | ||
{:character/name "Marge Simpson"}]}) | ||
(def margehomer (-> (d/q '[:find [?e ...] | ||
:where | ||
[?e :character/name]] | ||
(d/db conn)))) | ||
(d/transact conn {:tx-data [{:db/doc "Simpsons children reference" | ||
:db/ident :character/child | ||
:db/valueType :db.type/ref | ||
:db/cardinality :db.cardinality/many}]}) | ||
(d/transact conn (-> (for [[eid] (d/q '[:find ?e | ||
:where | ||
[?e :character/name] | ||
(or-join [?e] | ||
[?e :character/name "Homer Simpson"] | ||
[?e :character/name "Marge Simpson"])] | ||
(d/db conn))] | ||
{:db/id eid | ||
:character/child [{:character/name "Bart Simpson"} | ||
{:character/name "Lisa Simpson"} | ||
{:character/name "Maggie Simpson"}]}) | ||
vec)) | ||
|
||
(d/pull-many (d/db conn) '[:character/name {:character/child [:character/name]}] margehomer)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{:norm :a1-example | ||
:tx-data [{:db/doc "foo" | ||
:db/ident :foo | ||
:db/valueType :db.type/string | ||
:db/cardinality :db.cardinality/one}] | ||
:tx-fn io.replikativ.garantie/neutral-fn} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{:norm :a2-example | ||
:tx-data [{:db/doc "Simpsons character name" | ||
:db/ident :character/name | ||
:db/valueType :db.type/string | ||
:db/cardinality :db.cardinality/one}]} |