Skip to content

Instantly share code, notes, and snippets.

@clifford
Forked from pelle/accounts.clj
Created May 18, 2012 14:32
Show Gist options
  • Save clifford/2725556 to your computer and use it in GitHub Desktop.
Save clifford/2725556 to your computer and use it in GitHub Desktop.
Using database functions in Datomic transactions and annotating transaction history
(use '[datomic.api :only [q db] :as d])
(def uri "datomic:mem://accounts")
;; create database
(d/create-database uri)
;; connect to database
(def conn (d/connect uri))
;; parse schema dtm file
(def schema-tx (read-string (slurp "db/schema.dtm")))
;; submit schema transaction
@(d/transact conn schema-tx)
;; parse seed data dtm file
(def data-tx (read-string (slurp "db/accounts.dtm")))
;; submit seed data transaction
@(d/transact conn data-tx)
(defn accounts []
(d/q '[:find ?c ?n ?b ?count :where [?c :account/name ?n] [?c :account/balance ?b] [?c :account/transaction-count ?count]] (db conn)))
(defn history
([] (q '[:find ?tx ?f ?t ?a ?n ?when :in $ :where [?from :account/name ?f] [?to :account/name ?t] [?tx :ot/from ?from] [?tx :ot/to ?to] [?tx :ot/amount ?a] [?tx :ot/note ?n] [?tx :db/txInstant ?when]] (db conn)))
([acct] (q '[:find ?tx ?f ?t ?a ?n ?when :in $ ?e :where [?from :account/name ?f] [?to :account/name ?t] [?tx :ot/from ?from] [?tx :ot/to ?to] [?tx :ot/amount ?a] [?tx :ot/note ?n] [?tx :db/txInstant ?when] [?e :account/transactions ?tx]] (db conn) acct) ))
(defn transfer [ from to amount note]
(let [txid (datomic.api/tempid :db.part/tx)]
(d/transact conn [[:transfer from to amount]
[:db/add from :account/transactions txid]
[:db/add to :account/transactions txid]
[:inc from :account/transaction-count 1]
[:inc to :account/transaction-count 1]
{:db/id txid, :ot/note note :ot/from from :ot/to to :ot/amount amount}])))
(defn credit [ to amount ]
(d/transact conn [[:credit to amount]]))
(def issuer (first (first (q '[:find ?e :where [?e :account/name "issuer"]] (db conn)))))
(def bob (first (first (q '[:find ?e :where [?e :account/name "bob"]] (db conn)))))
(def alice (first (first (q '[:find ?e :where [?e :account/name "alice"]] (db conn)))))
(prn (accounts))
(transfer issuer alice 77M "Issuance to Alice")
(transfer issuer bob 23M "Issuance to Bob")
(transfer alice bob 7M "Tomatoes")
(println "History")
(prn (history))
;; #<HashSet [[13194139534319 "issuer" "alice" 77M "Issuance to Alice" #inst "2012-05-08T14:35:25.252-00:00"],
;; [13194139534320 "issuer" "bob" 23M "Issuance to Bob" #inst "2012-05-08T14:35:25.256-00:00"],
;; [13194139534321 "alice" "bob" 7M "Tomatoes" #inst "2012-05-08T14:35:25.262-00:00"]]>
(println "Accounts")
(prn (accounts))
;; #<HashSet [[17592186045421 "issuer" -100M 2], [17592186045423 "alice" 70M 2], [17592186045422 "bob" 30M 2]]>
[
{:db/id #db/id[:db.part/user -1000001], :account/name "issuer", :account/balance 0M, :account/max-balance 0M, :account/min-balance -1000M}
{:db/id #db/id[:db.part/user -1000002], :account/name "bob", :account/balance 0M, :account/max-balance 100M, :account/min-balance 0M}
{:db/id #db/id[:db.part/user -1000003], :account/name "alice", :account/balance 0M, :account/max-balance 100M, :account/min-balance 0M}
]
[
;; accounts
{ :db/id #db/id[:db.part/db]
:db/ident :account/name
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/fulltext true
:db/unique :db.unique/value
:db/doc "An account's name"
:db.install/_attribute :db.part/db}
{ :db/id #db/id[:db.part/db]
:db/ident :account/balance
:db/cardinality :db.cardinality/one
:db/valueType :db.type/bigdec
:db/doc "The accounts balance"
:db.install/_attribute :db.part/db}
{ :db/id #db/id[:db.part/db]
:db/ident :account/transaction-count
:db/cardinality :db.cardinality/one
:db/valueType :db.type/long
:db/doc "The amount of transactions made with account"
:db.install/_attribute :db.part/db}
{ :db/id #db/id[:db.part/db]
:db/ident :account/max-balance
:db/cardinality :db.cardinality/one
:db/valueType :db.type/bigdec
:db/doc "The accounts maximum balance"
:db.install/_attribute :db.part/db}
{ :db/id #db/id[:db.part/db]
:db/ident :account/min-balance
:db/cardinality :db.cardinality/one
:db/valueType :db.type/bigdec
:db/doc "The accounts maximum balance"
:db.install/_attribute :db.part/db}
{ :db/id #db/id[:db.part/db]
:db/ident :account/transactions
:db/cardinality :db.cardinality/many
:db/valueType :db.type/ref
:db/doc "The accounts transactions"
:db.install/_attribute :db.part/db}
{ :db/id #db/id[:db.part/db]
:db/ident :ot/note
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/doc "An note about what was transfered"
:db.install/_attribute :db.part/db}
{ :db/id #db/id[:db.part/db]
:db/ident :ot/amount
:db/valueType :db.type/bigdec
:db/cardinality :db.cardinality/one
:db/doc "Amount transacted"
:db.install/_attribute :db.part/db}
{ :db/id #db/id[:db.part/db]
:db/ident :ot/from
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/one
:db/doc "Transferee"
:db.install/_attribute :db.part/db}
{ :db/id #db/id[:db.part/db]
:db/ident :ot/to
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/one
:db/doc "Recipient"
:db.install/_attribute :db.part/db}
{ :db/id #db/id [:db.part/user]
:db/ident :inc
:db/fn #db/fn { :lang "clojure"
:params [db id attr amount]
:code "(let [ e (datomic.api/entity db id)
orig (attr e 0) ]
[[:db/add id attr (+ orig amount) ]])"}}
{ :db/id #db/id [:db.part/user]
:db/ident :credit
:db/fn #db/fn { :lang "clojure"
:params [db id amount]
:code "(let [ e (datomic.api/entity db id)
min-balance (:account/min-balance e 0)
balance (+ (:account/balance e 0) amount) ]
(if (>= balance min-balance)
[[:db/add id :account/balance balance ]]
(throw (Exception. \"Insufficient funds\"))))" }}
{ :db/id #db/id [:db.part/user]
:db/ident :transfer
:db/fn #db/fn { :lang "clojure"
:params [db from to amount]
:code "[[:credit from (- amount)]
[:credit to amount]]"}}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment