Skip to content

Instantly share code, notes, and snippets.

@jeroenvandijk
Last active January 23, 2020 10:58
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jeroenvandijk/85eb96d68c31c4e48f99 to your computer and use it in GitHub Desktop.
Save jeroenvandijk/85eb96d68c31c4e48f99 to your computer and use it in GitHub Desktop.
(A) method to dump a datomic database schema
(ns datomic.schema-dump
(:require
[datomic.api :as d]
[clojure.pprint]))
(defmethod clojure.pprint/simple-dispatch datomic.db.DbId [v] (pr v))
(defmethod clojure.pprint/simple-dispatch datomic.function.Function [v] (pr v))
(defn database-url [name]
(str "datomic:mem://" name))
(defn unique-database-url []
(database-url (d/squuid)))
(defn unique-empty-conn []
(let [db-url (unique-database-url)]
(d/create-database db-url)
(d/connect db-url)))
(defn- partition-ident [db eid]
(:db/ident (d/entity db (d/part eid))))
(defn not-datomic-entity [ident]
(or
(= ident :fressian/tag)
(let [ns (namespace ident)]
(or (nil? ns)
(not (re-matches #"db(?:[\./].+)?" ns))))))
;; With inspiration from http://aan.io/dumping-datomic-schema/
(defn dump-schema [db]
(let [part-order (zipmap [:db.part/db :db.part/tx :db.part/user] (range))]
(->>
(d/q '[:find [?e ...]
:where
[?e :db/ident ?v]
[(datomic.schema-dump/not-datomic-entity ?v)]]
db)
(group-by (fn [eid] (partition-ident db eid)))
(sort-by (fn [[part _]]
(str (part-order part (name part)))))
(mapv (fn [[part eids]]
[part
(let [tmp-id-idx (atom 0)]
(->> (sort eids)
(mapv
(fn [eid]
(let [ent (d/entity db eid)]
(cond->
(select-keys ent [:db/ident
:db/valueType
:db/cardinality
:db/doc
:db/isComponent
:db/fulltext
:db/unique
:db/fn])
true (assoc :db/id (d/tempid part (swap! tmp-id-idx dec)))
(:db/valueType ent) (assoc :db.install/_attribute :db.part/db)
(:db/fn ent) (assoc-in [:db/fn :fnref] nil)
))
))))])))))
(defn write-schema-file [schema-target-file db]
(let [schema (dump-schema db)]
(spit schema-target-file
(with-out-str
(binding [*print-length* nil]
(clojure.pprint/pprint schema))))))
(comment
(write-schema-file "resources/db/schema.edn" my-db)
)
(defn read-schema-file [file]
(try
(clojure.edn/read-string
{:readers {'db/id #'datomic.db/id-literal
'db/fn (fn [x]
(assoc (datomic.function/construct x) :fnref nil))
}}
(slurp file))
(catch Exception e
(throw (ex-info (str "Error while reading " (str file) ", " (.getMessage e)) {:file file})))))
(defn schema-from-file
([]
(schema-from-file (clojure.java.io/resource "db/schema.edn")))
([schema-file]
(read-schema-file schema-file)))
(defn load-from-schema []
(let [conn (unique-empty-conn)
grouped-txns (schema-from-file)]
(doseq [[part txns] grouped-txns]
(when-not (#{:db.part/db :db.part/tx :db.part/user} part)
@(d/transact conn [{:db/id (d/tempid :db.part/db)
:db.install/_partition :db.part/db
:db/ident part}]))
@(d/transact conn txns))
conn))
(ns datomic.schema-test
(:require [midje.sweet :refer :all]
[adgoji.datomic.schema :as schema]
[datomic.api :as d]))
;; NOTE: schema/unique-database-conn returns a new database connection
;; where all migrations have been applied.
;; To detect problems with dumping:
(fact "schema dumps are the same each time"
(let [schema-1 (schema/dump-schema (d/db (schema/unique-database-conn)))
schema-2 (schema/dump-schema (d/db (schema/unique-database-conn)))
schema-diff (clojure.data/diff schema-1 schema-2)]
(butlast schema-diff) => [nil nil]))
(fact "Latest schema has been saved"
(let [saved-schema (schema/schema-from-file)
extracted-schema (schema/dump-schema (d/db (schema/unique-database-conn)))
schema-diff (clojure.data/diff saved-schema extracted-schema)]
(= saved-schema extracted-schema) => true
(butlast schema-diff) => [nil nil]))
(fact "Schema can be loaded"
(let [loaded-schema (schema/dump-schema (d/db (schema/load-from-schema)))
extracted-schema (schema/dump-schema (d/db (schema/unique-database-conn)))
schema-diff (clojure.data/diff loaded-schema extracted-schema)]
(= loaded-schema extracted-schema) => true
(butlast schema-diff) => [nil nil]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment