Skip to content

Instantly share code, notes, and snippets.

@jcf
Forked from a2ndrade/gist:5654999
Created January 22, 2014 15:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcf/8560161 to your computer and use it in GitHub Desktop.
Save jcf/8560161 to your computer and use it in GitHub Desktop.
(require '[datomic.api :as d])
(def uri "datomic:mem://test")
(d/create-database uri)
(def conn (d/connect uri))
(d/transact conn [{:db/id #db/id [:db.part/db]
:db/ident :enum/ns
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "Enum's namespace. Help enforce fk constraints on :db.type/ref enum references"
:db.install/_attribute :db.part/db}])
(d/transact conn [{:db/id #db/id[:db.part/db]
:db/ident :community/name
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/fulltext true
:db/doc "A community's name"
:db.install/_attribute :db.part/db}
{:db/id #db/id[:db.part/db]
:db/ident :community/orgtype
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/one
:db/doc "A community orgtype enum value"
:db.install/_attribute :db.part/db
;; used by the DB function below to enforce the fk constraint
:enum/ns "community.orgtype"}])
(d/transact conn [[:db/add #db/id[:db.part/user] :db/ident :community.orgtype/community]
[:db/add #db/id[:db.part/user] :db/ident :community.orgtype/commercial]
[:db/add #db/id[:db.part/user] :db/ident :community.orgtype/nonprofit]
[:db/add #db/id[:db.part/user] :db/ident :community.orgtype/personal]
;; not a orgtype enum value
[:db/add #db/id[:db.part/user] :db/ident :community.type/email-list]])
;; defines an DB function
(def enum-fk-entity
"Defines a database function (itself an entity) that enforces a foreign key constraint
on :db.type/ref attributes that reference enum values. Those attributes must add a
:enum/ns attribute holding the namespace of the enum idents, e.g:
[:add-fk community-id :community/orgtype :community.orgtype/personal]
is equivalent to
[:db/add community-id :community/orgtype :community.orgtype/personal]
but with a foreign-key constraint enforced by the database."
[{:db/id #db/id [:db.part/user]
:db/ident :add-fk
:db/fn (d/function
'{:lang :clojure
:params [db e a v]
:code (let [;; get value's symbolic keyword
ident (if (keyword? v) v (d/ident db v))
;; get enum's constraint
aent (d/entity db a)
aname (:db/ident aent)
enum-ns (:enum/ns aent)
;; find all possible enum idents
allowed (if (nil? enum-ns)
(throw (Exception. (str "Cannot check fk constraint. " aname " has no :enum/ns attribute")))
(d/q '[:find ?ident
:in $ ?enum-ns
:where
[_ :db/ident ?ident]
[(namespace ?ident) ?ns]
[(= ?enum-ns ?ns)]]
db enum-ns))]
;; enforce constraint
(if (contains? allowed [ident])
[[:db/add e a v]]
(throw (Exception. (str v " is not one of " allowed)))))})}])
(d/transact conn enum-fk-entity)
;; will succeed
(d/transact conn [[:db/add #db/id [:db.part/user -100] :community/name "15th Ave Community"]
[:add-fk #db/id [:db.part/user -100] :community/orgtype :community.orgtype/personal]])
;; will fail
(d/transact conn [[:db/add #db/id [:db.part/user -100] :community/name "15th Ave Community"]
[:add-fk #db/id [:db.part/user -100] :community/orgtype :community.type/email-list]])
;; java.lang.Exception: :community.type/email-list is not one of
;; [[:community.orgtype/community], [:community.orgtype/commercial],
;; [:community.orgtype/personal], [:community.orgtype/nonprofit]]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment