FileNotFoundException when using database function with datomic free - clojure

I am getting a FileNotFoundException when using a database function that requires a namespace. I only get the error when using the persistent datomic free database but not when I'm using the memory database.
(ns test.core
(:use [datomic.api :only [q db] :as d]))
(def uris ["datomic:mem://test"
"datomic:free://localhost:4334/test"])
(map
d/delete-database uris)
(map
d/create-database uris)
(def conns (map d/connect uris))
(defn test-entity []
[{:db/id #db/id[:db.part/db]
:test/test "hello"}])
(def db-function
#db/fn {:lang :clojure
:params [database]
:requires [[test.core :as c]]
:code (c/test-entity)})
(map
#(d/transact % [{:db/id #db/id[:db.part/user]
:db/ident :db-function
:db/fn db-function}])
conns)
(map
#(d/transact % [{:db/id #db/id[:db.part/db]
:db/ident :test/test
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}])
conns)
(comment
(db-function nil)
(d/transact (first conns) [[:db-function]])
(d/transact (second conns) [[:db-function]]))
When you evaluate the first and second line in the comment, it's fine but when you evaluate the third line you get an exception.
Do I need to configure something in datomic so that it can "see" my project?

When you use an in memory database, the transactor runs in the same JVM instance as the peer, hence with the same classpath. But with the free database, the transactor is running in its own JVM instance, and is not aware about namespaces in the peers.
You can add jars to the transactor classpath by putting them in the lib/ folder.

Related

Inconsistency between in (mem) and out (dev,sql,etc) of process Transactor in Datomic:

I've discovered an inconsistency between the way db.type/instant values are handled in the in (mem) and out (dev,sql...) of process transactor.
When using the in process mem transactor the underlying type of a db.type/instant value is preserved when transacting and retrieving. For example, (type-printer uri) below shows that a transacted java.util.TimeStamp is retrieved as a java.sql.TimeStamp. However in the out of process transactors it is retrieved as a java.util.Date.
In most cases this inconsistency does not cause problems, however when dealing with bad Date implementations it can cause all sorts of problems. In my case the fact that java.sql.TimeStamp does not have a symmetric equals method, meant that a bug in my code only appeared when I ran it in an out of process transactor...
Obviously Datomic does not have any control over external implementations of java.util.Date, so I think the 'Date sanitisation' approach of the out of process transactors is correct.
Really all I want is a sanity check before I raise it as an issue with the Datomic guys so if someone could take a look and see if you agree it would be greatly appreciated.
Thanks,
Matt.
(require '[datomic.api :as d])
(import '(java.sql Timestamp))
(defn type-printer [uri]
"Prints the type of a transacted java.sql.TimeStamp when retrieved from the databse"
(d/create-database uri)
(let [ts-att-schema {:db/id (d/tempid :db.part/db)
:db/ident :entity/ts
:db/valueType :db.type/instant
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}
ts (Timestamp. 1000000000000)
entity {:db/id (d/tempid :db.part/user)
:entity/ts ts}
conn (d/connect uri)]
;transact schema and then entity with associated TimeStamp value
#(d/transact conn [ts-att-schema])
#(d/transact conn [entity])
(let [type (->> (d/db conn)
(d/q '[:find ?ts :where[?e :entity/ts ?ts]])
(first)
(first)
(type)
)]
(println "java.sql.TimeStamp is retrived as type" type))))
;TimeStamp type is preserved when being transacted and then retrieved
(type-printer "datomic:mem://test-db")
;TimeStamp is sanitised in the database and retrieved as a java.util.Date. This case obviously relies on a
;dev transactor running on localhost:4334. Have only verified for dev and sql transactors.
(type-printer "datomic:dev://localhost:4334/test-db")

How to keep track of the entity id for the stuff that was just added to the db?

On a html-post page a user can input various fields and hit submit,
My router.clj code looks like
(POST "/postGO" [ post-title post-input post-tags :as request ]
(def email (get-in request [:session :ze-auth-email]))
;; connect to datomic and write in the request
(dbm/add-ze-post post-title post-input post-tags email) ;; db insert
{:status 200,
:body "successfully added the post to the database",
:headers {"Content-Type" "text/plain"}}) ;;generic return page
It works well, but I want to redirect the user afterwards to a page that can show them their uploaded post. To do this, it would be very helpful to have the eid of the entity just transacted.
;; code from my dbm file for playing directly with the database
;; id est: the db transact code
(defn add-ze-blurb [title, content, tags, useremail]
(d/transact conn [{:db/id (d/tempid :db.part/user),
:post/title title,
:post/content content,
:post/tag tags,
:author/email useremail}]))
Is there any way to have datomic return the eid as soon as something is added to the DB successfully, or should I use another query right afterward to make sure it's there?
For simple cases, simply deref the future returned by d/transact and use :tx-data. Full example:
(require '[datomic.api :refer [db q] :as d])
(def uri "datomic:mem://test")
(d/create-database uri)
(def conn (d/connect uri))
(def schema [{:db/id (d/tempid :db.part/db)
:db/ident :foo
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}])
#(d/transact conn schema)
(def tempid (d/tempid :db.part/user))
(def tx [{:db/id tempid :foo "bar"}])
(def result #(d/transact conn tx)) ;;
(def eid (:e (second (:tx-data result))))
(assert (= "bar" (:foo (d/entity (db conn) eid))))
You can alternatively use d/resolve-tempid:
(def eid (d/resolve-tempid (db conn) (:tempids result) tempid))
(assert (= "bar" (:foo (d/entity (db conn) eid))))
As described in the Datomic documentation, after the deref the transaction will have been applied by the transactor and the data returned reflects the new value of the database.

How do I build a transaction that has references to a variable number of entities?

I'm getting into datomic and still don't grok it. How do I build a transaction that has references to a variable number of entities?
For example this creates a transaction with a child entity and a family entity with a child attribute that references the new child entity:
(defn insert-child [id child]
{:db/id #db/id id
:child/first-name (:first-name child)
:child/middle-name (:middle-name child)
:child/last-name (:last-name child)
:child/date-of-birth {:date-of-birth child}})
(defn insert-family [id]
(let [child-id #db/id[:db.part/user]]
(vector
(insert-child child-id
{:first-name "Richard"
:middle-name "M"
:last-name "Stallman"})
{:db/id id
:family/child child-id})))
(insert-family #db/id[:db.part/user])
=> [{:db/id #db/id[:db.part/user -1000012],
:child/first-name "Richard",
:child/middle-name "M",
:child/last-name "Stallman",
:child/date-of-birth nil}
{:db/id #db/id[:db.part/user -1000013],
:family/child #db/id[:db.part/user -1000012]}]
Notice I used let for child-id. I'm not sure how to write this such that I can map over insert-child while having a family entity that references each one.
I thought about using iterate over #db/id[:db.part/user] and the number of children then mapping over both the result of iterate and a vector of children. Seems kind of convoluted and #db/id[:db.part/user] isn't a function to iterate over to begin with.
Instead of using the macro form #db/id[:db.part/user] which is meant for EDN files and data literals, you should use d/tempid.
You could do something like this (using simplified child entities):
(ns family-tx
(:require [datomic.api :refer [q db] :as d]))
(def uri "datomic:mem://testfamily")
(d/delete-database uri)
(d/create-database uri)
(def conn (d/connect uri))
(def schema [
{:db/id (d/tempid :db.part/db)
:db/ident :first-name
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}
{:db/id (d/tempid :db.part/db)
:db/ident :last-name
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}
{:db/id (d/tempid :db.part/db)
:db/ident :family/child
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/many
:db.install/_attribute :db.part/db}
])
#(d/transact conn schema)
(defn make-family-tx [kids]
(let [kids-tx (map #(into {:db/id (d/tempid :db.part/user)} %) kids)
kids-id (map :db/id kids-tx)]
(conj kids-tx {:db/id (d/tempid :db.part/user)
:family/child kids-id})))
(def kids [{:first-name "Billy" :last-name "Bob"}
{:first-name "Jim" :last-name "Beau"}
{:first-name "Junior" :last-name "Bacon"}])
#(d/transact conn (make-family-tx kids))
There are a few strategies for this discussed in the Transactions docs as well (see the "Identifying Entities" section).

Clojure Agent Parellel HTTP IllegalStateException and await-for

I am working through the example of making parallel http requests in Clojure,
http://lethain.com/a-couple-of-clojure-agent-examples/
In particular
(ns parallel-fetch
(:import [java.io InputStream InputStreamReader BufferedReader]
[java.net URL HttpURLConnection]))
(defn get-url [url]
(let [conn (.openConnection (URL. url))]
(.setRequestMethod conn "GET")
(.connect conn)
(with-open [stream (BufferedReader.
(InputStreamReader. (.getInputStream conn)))]
(.toString (reduce #(.append %1 %2)
(StringBuffer.) (line-seq stream))))))
(defn get-urls [urls]
(let [agents (doall (map #(agent %) urls))]
(doseq [agent agents] (send-off agent get-url))
(apply await-for 5000 agents)
(doall (map #(deref %) agents))))
(prn (get-urls '("http://lethain.com" "http://willarson.com")))
When I run this in the
IllegalStateException await-for in transaction
What does this mean and how do I fix it?
Taking the comment on the question into account:
A transaction is being set up in the process of loading your namespace, and since it has a call to get-urls at the top-level, the await-for happens in that transaction and throws the exception.
The best way to fix that is to put the prn / get-urls form inside a function and only call it once the namespace is loaded. (If you wanted to run this code as a standalone app, with lein run or java -jar on an überjar, you'd put a call to that function inside -main.)
Incidentally, the transaction is set up when you use :reload-all, but not without it. (See the private functions load-lib, which checks for the presence of :reload-all and decides to use the private function load-all if it's there, and load-all itself, which is where the transaction is set up. Here's a link to the 1.5.1 source.)

"No reader function" error using Datomic in Light Table

When I eval this code in lighttable:
(ns app.core
(:require [datomic.api :refer [q] :as d]
:reload-all))
(defn add-person
[conn id]
(d/transact conn [{:db/id #db/id[:db.part/user -1000001]
:person/id id}]))
I get:
clojure.lang.ExceptionInfo: No reader function for tag id
core.clj:4327 clojure.core/ex-info
Anyone knows what is going on?
This tutorial is attributed to stuart halloway and Bobby Calderwood:
(use :reload 'datomic.samples.repl)
(easy!)
(def conn (scratch-conn))
;; in data, use data literals for tempids
(def tx-data [{:db/id #db/id[:db.part/user]
:db/doc "Example 1"}])
(transact conn tx-data)
;; in code, call tempid to create tempids
(let [id (tempid :db.part/user)
doc "Example 2"]
(transact conn [{:db/id id :db/doc doc}]))
;; same argument applies to functions:
;; use #db/fn literals in data
;; use Peer.function or d/function in code
;; broken, uses db/fn literal in code
(transact conn [{:db/id #db/id [:db.part/user]
:db/ident :hello
:db/fn #db/fn {:lang "clojure"
:params []
:code '(println :hello)}}])
;; corrected: used d/function to construct function
(transact conn [{:db/id (d/tempid :db.part/user)
:db/ident :hello
:db/fn (d/function {:lang "clojure"
:params []
:code '(println :hello)})}])
(d/invoke (db conn) :hello)
Source: https://github.com/Datomic/day-of-datomic/blob/master/samples/literals_vs_code.clj
It looks like there's an issue with trying to set :person/id. After the #db/id[:db.part/user -1000001] part, you've got a temporary id for adding data.
You should be able to start setting attributes for the entity using things like things like :person/name name.
If you're trying to create a "public id" type of thing, this blog post may be helpful.
It's a problem in nREPL. The way I solved this is to start the REPL at the command line with:
lein repl
This will start a process that you can connect to from LightTable or Emacs. It will print information like:
nREPL server started on port 51395 on host 127.0.0.1
^^^^^
Now in LightTable, Add a Connection -> Clojure Remote -> 127.0.0.1:XXXXX
The XXXXX should equal the port printed out by lein repl.
If you're in Emacs, cider has the same issue. Follow the same steps of starting lein repl, then use M-x cider-connect (it's default keybinding is C-c M-c).