Skip to content

Instantly share code, notes, and snippets.

@claj
Created July 17, 2012 23:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save claj/3132782 to your computer and use it in GitHub Desktop.
Save claj/3132782 to your computer and use it in GitHub Desktop.
Generate datom :db.type/ref s with fn
(use '[datomic.api :only [db q] :as d])
;;LOOK IN THE BOTTOM FOR CORRECT USE OF DATOMIC :)
;;not supplied:
;;* connecting to a db named "conn"
;; (checkout awesome Jonas Edlunds gist
;; for inspiration: https://gist.github.com/3122363 )
;;* a schema that fit's the following person-datoms
;; Problem:
;;it's a bit quirky to generate references by functions to transact them to datoms
;;since it's an instant literal it seems to be evaluated when created by a function
(defn refsymbol
"given a reference long, this generates a correct, non-evaluated reference"
[ref]
(read-string (apply str "#db/id[:db.part/user " ref "]")))
;;that is
(refsymbol 12345) => #db/id[:db.part/user 12345] ;; all the way into datomic!
;;12345 should not be a random number, it's usually found by a query like
;;(q '[:find ?id :where [?id :company/name "GoodCorp Inc."]] (db conn))
;;=>#<HashSet [[17592186045497]]> given GoodCorp Inc.-datom exists in the database
;;this is a hash nested two times (given we only have one GoodCorp Inc.-datom)
;;this first ref-number can be picked out by (first (first (q ...)))
;;or (ffirst (q ...)) for short
;;btw, good intro to shape of datoms here:
;;http://pelle.github.com/Datomic/2012/07/08/thinking-in-datomic/
(def goodcorp-refno (ffirst (q '[:find ?id :where [?id :company/name "GoodCorp Inc."]] (db conn))))
;;which can also be automated like this:
(defn company-refno [company-name]
(ffirst (q `[:find ?ref :where [?ref :company/name ~company-name]] (db conn))))
;;=>17592186045497
;;armed with a ref-number we can make datoms with functions like this:
(defn add-new-person
"lets say we want to connect a person with a name to a company with a reference"
[name ref-long]
(let [correct-ref (refsymbol ref-long)]
`[{:db/id ~#db/id[:db.part/user] :person/name ~name :person/company ~correct-ref}]))
;;` means "don't evaluate the following form"
;;~x means "lookup the symbol x and insert it here anyway `"
(add-new-person "Bob Smith" 17592186045497)
or
(add-new-person "Bob Smith" goodcorp-refno)
or
(add-new-person "Bob Smith" (company-refno "GoodCorp Inc.")) ;;hairball warning!
;;which creates a record
[{:person/company #db/id[:db.part/user 17592186045497],
:person/name "Bob Smith",
:db/id #db/id[:db.part/user -1000416]}]
;;that could be added to a database with:
(d/transact conn (add-new-person "Bob Smith" 17592186045497))
;;if you want to use (map add-new-person [["Bob Smith" 17592186045497] ["Ben Andersen" 17592186043525]])
;;you have to remove the [vector] holding the {map} in add-new-person like
(defn add-new-person2
[name ref-long]
(let [correct-ref (refsymbol ref-long)]
`{:db/id ~#db/id[:db.part/user] ;;no vector outside the map!
:person/name ~name
:person/company ~correct-ref}))
;;now:
(d/transact conn (map add-new-person2 [["Bob Smith" 17592186045497] ["Ben Andersen" 17592186043525]]))
;;(with-datomic-awesomeness (success!))
;;some things helped me much:
;;jonas edlunds getting-stared gist
;;https://gist.github.com/3122363
;;esp. check out how elegant the ids are generated
;;pelle braendgaard on datom concepts
;;http://pelle.github.com/Datomic/2012/07/08/thinking-in-datomic/
;;more on transact-stuff:
;;http://pelle.github.com/Datomic/2012/07/09/transactions-as-entities/
;;a banking app example, also from mr braendgaard
;;https://gist.github.com/2635666
;;also checkout the /samples/seattle/getting-started.clj
;;and the .dtm files in the same folder (everything in datomic distribution)
;;and the attribute stuff at the Datomic reference:
;;http://www.datomic.com/company/resources/schema
;; OR DO IT RIGHT (thank you Stuart Halloway, Datomic team)
(1) If you need to programmatically create a tempid, there is no need to do weird reader tricks. Just use the Peer.tempid (Java) or api/tempid (Clojure) function.
(2) The entity id returned by a query is a complete id. You do not need to plug it back into an id literal to use it:
(defn add-new-person
[name ref-long]
[{:db/id (d/tempid :db.part/user)
:person/name name
:person/company ref-long}]))
Note also that backquote is not necessary.
(3) The return value of add-new-person above is shaped like transaction data. When you remove the vector to facilitate calling map, you make your function less general (now you require that it return only a single piece of transaction data). Better to leave the return value flexible and call mapcat instead of map.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment