user299709
user299709

Reputation: 5422

datomic: transact function not really writing to database

I'm running the dev db in datomic-free

However, I thought that it would write something to the database but when I do a pull, it's not there. this is my peer.clj

(ns dank.peer
  (:require [datomic.api :as d :refer (q)]
            [clojure.pprint :as pp]))

; Name the database as a uri
(def uri "datomic:mem://dank")

; Read the schema and seed data as strings
(def schema-tx (read-string (slurp "resources/dank/schema.edn")))
(def data-tx (read-string (slurp "resources/dank/seed-data.edn")))

; Initialize the db with above vars
(defn init-db
  []
  (when (d/create-database uri)
    (let
      [conn (d/connect uri)]
      @(d/transact conn schema-tx)
      @(d/transact conn data-tx))))

(init-db)

(def conn (d/connect uri))
(def db (d/db conn))

(defn create
  "Creates a new note"
  [notes]
    @(d/transact
      conn
      [{:db/id #db/id[:db.part/user]
        :item/author "default"
        :item/notes notes}]))

(defn get-notes
  [author]
    (d/pull-many db '[*]
      (flatten (into []
       (q '[:find ?e
            :in $ ?author
           :where
           [?e :item/author ?author]]
          db
          author
         )))))

(create "some note") ----> shows the whole transaction data structure with before and after.

(get-notes "default") ---> [] it is empty!

Am I missing something here?

Upvotes: 3

Views: 259

Answers (2)

Marshall
Marshall

Reputation: 596

mavbozo is correct in his response that when you call your get-notes function you are using a value of the database from before you transacted your new note.

If you were to call (def db (d/db conn)) again prior to get-notes you will see the new data in the database.

However, it's generally preferred to avoid dealing with connections or databases as global variables, as it prevents function behaviors from being determined strictly from their parameters. As you noticed, the behavior of get-notes depends on the state of the db variable in your global state. As a rule of thumb, it's better to pass a database value to functions that query or pull from the database and pass a connection to functions that transact against a database or monitor the state of a database over time (see http://docs.datomic.com/best-practices.html#consistent-db-value-for-unit-of-work for more discussion of individual values of a Datomic database).

In this case, your get-notes function could be something like:

(defn get-notes
  [db author]
    (d/q '[:find (pull ?e [*])  
           :in $ ?author
           :where
           [?e :item/author ?author]]
          db
          author))

Which you can then invoke via:

(get-notes (d/db conn) "default")

To query the most current version of the database. This approach also has the advantage that you can call get-notes with any database value (i.e. the most recent, one from a given time in the past, or a history db).

Similarly, I would recommend changing your create function to take a connection instead of using the globally defined conn.

As far as managing your connection, again it is best to avoid using a global variable whenever possible. Some preferred options might be to put the connection into an application state map or an atom that is passed throughout your program as appropriate. Another good option that is used frequently is to leverage Stuart Sierra's component library (https://github.com/stuartsierra/component) to manage the lifecycle of the connection.

Upvotes: 6

mavbozo
mavbozo

Reputation: 1261

t0, t1, t2 denote increasing time t where t0 precedes t1, t1 precedes t2

(d/db conn) returns current snapshot of datomic database when the call is made.

let's say in t0 you called (def db (d/db conn)). At time t0 the notes are empty. the db here is a snapshot of db time t0.

then you create something during time t1 using (create "some note"). At time t1, the notes at least contain 1 note.

then during time t2 you call (get-notes "default"). At time t2, the notes at least contain 1 note.

the problem is, the db inside get-notes still refers to database snapshot during time t0 that is the db you retrieve using (def db (d/db conn)) at t0. At time t0, there are still no notes. That's why get-notes return empty.

when you query a datomic database, you must specify which db you want to query: the db as of now, yesterday db, db from 1 hour ago, etc. Of course, it's quite easy to get db as of now, just call (d/db conn).

I find it useful to make db explicit for query functions such as changing your get-notes function declaration parameter to (defn get-notes [db author] ....).

Upvotes: 4

Related Questions