Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save daveliepmann/6043e33093fbcc65423797f4886d4fe7 to your computer and use it in GitHub Desktop.

Select an option

Save daveliepmann/6043e33093fbcc65423797f4886d4fe7 to your computer and use it in GitHub Desktop.
(ns composite
"Demo: rename & re-create a Datomic scheme attribute that is a constituent of a composite tuple (:db/tupleAttrs)."
(:require [datomic.api :as d]))
(def uri "datomic:mem://composite-rename")
(defn fresh-conn []
(d/delete-database uri)
(d/create-database uri)
(d/connect uri))
(def schema
[{:db/ident :source/name
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one}
{:db/ident :foo/bar
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one}
{:db/ident :foo/source-type
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/one}
;; the composite USES :foo/source-type — this is the whole point
{:db/ident :foo/bar+source-type
:db/valueType :db.type/tuple
:db/tupleAttrs [:foo/bar :foo/source-type]
:db/cardinality :db.cardinality/one
:db/unique :db.unique/identity}])
(def seed
[{:db/id "csv" :source/name "CSV"}
{:db/id "api" :source/name "API"}
{:foo/bar "some data" :foo/source-type "csv"}
{:foo/bar "some more data" :foo/source-type "api"}])
(comment
(def conn (fresh-conn))
@(d/transact conn schema)
@(d/transact conn seed)
;; composites populate automatically as constituents are asserted
(d/q '[:find ?bar ?tuple
:where
[?e :foo/bar ?bar]
[?e :foo/bar+source-type ?tuple]]
(d/db conn))
;; => #{["some data" ["some data" <csv-eid>]]
;; ["some more data" ["some more data" <api-eid>]]}
;;; part 1 - rename the constituent ident
@(d/transact conn [{:db/id :foo/source-type
:db/ident :foo/source-type-deprecated}])
;; the attribute eid genuinely carries only the new ident now...
(d/q '[:find ?v :where [:foo/source-type-deprecated :db/ident ?v]]
(d/db conn))
;; => #{[:foo/source-type-deprecated]}
;; ...yet :db/tupleAttrs still reads back the OLD keyword. tupleAttrs stores
;; the keywords literally; renaming a constituent does not rewrite them.
(:db/tupleAttrs (d/entity (d/db conn) :foo/bar+source-type))
;; => [:foo/bar :foo/source-type]
;; consequence: the OLD keyword still resolves to the renamed attribute,
;; because the composite keeps it bound. (A never-used keyword resolves nil.)
(:db/id (d/entity (d/db conn) :foo/source-type)) ; => the attr eid
(:db/id (d/entity (d/db conn) :foo/source-type-deprecated)) ; => the attr eid
(d/entity (d/db conn) :totally/bogus) ; => nil
;;; part 2 — composite maintenance follows the eid, not the keyword!
;; Asserting the constituent under its NEW name still populates the tuple.
@(d/transact conn [{:db/id "ftp" :source/name "FTP"}
{:foo/bar "data via new name"
:foo/source-type-deprecated "ftp"}])
(d/q '[:find ?tuple
:where [?e :foo/bar "data via new name"] [?e :foo/bar+source-type ?tuple]]
(d/db conn))
;; => #{[["data via new name" <ftp-eid>]]} -- tuple built via the renamed attr
;; part 3 — impossible to recreate :foo/source-type as a fresh attribute!
;; The composite still reserves the old keyword, so recreation is REFUSED:
@(d/transact conn [{:db/ident :foo/source-type
:db/valueType :db.type/keyword
:db/cardinality :db.cardinality/one}])
;; => throws :db.error/cannot-retarget-ident
;; "Ident :foo/source-type cannot be used for entity N,
;; already used for :foo/source-type-deprecated"
;; So a composite tuple pins the ORIGINAL name of every constituent.
;;
;; Renaming a constituent frees nothing: you get the new name AND a dead
;; reservation on the old one that you cannot reuse.
;; TODO test with `:db.tuple/discontinued`
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment