Fork me on GitHub
#datomic
<
2021-08-26
>
emccue18:08:44

If I wanted to get all the transactions associated with an entity, what would that look like?

emccue18:08:54

(defn all-transactions-for-entity [connection entity]
  (d/q {:query '[:find  (pull ?transaction [*])
                 :in    $ ?e
                 :where [?e _ _ ?transaction]]
        :args [(d/db connection) (:db/id entity)]}))

emccue18:08:20

right now this is my code but it is only returning 1 transaction

favila18:08:36

What are you expecting? 1 is a possible answer

favila18:08:08

Could you be more precise about what you mean by “associated with an entity”?

emccue18:08:14

I have this function, which inserts an entity and returns all the stuff about it

emccue18:08:20

(defn insert-and-return [db entity]
  (let [temp-id          (str (UUID/randomUUID))
        entity-to-insert (update entity :db/id (fn [id]
                                                 (if (nil? id)
                                                   temp-id
                                                   id)))
        transact-result  (transact db
                                   {:tx-data [entity-to-insert]})]
    (d/pull (:db-after transact-result)
            '[*]
            (or (:db/id entity) ((:tempids transact-result) temp-id)))))

emccue18:08:34

(def e (insert-and-return connection {:payment-request/price-cents 123}))
=> #'l.datomic/e
e
=> {:db/id 83562883711071, :payment-request/price-cents 123}

emccue18:08:39

then i update this entity

emccue18:08:15

(def e2 (insert-and-return connection (assoc e :payment-request/price-cents 2345234)))
=> #'l.datomic/e2
e2
=> {:db/id 83562883711071, :payment-request/price-cents 2345234}

emccue18:08:36

so there should be two transactions associated with that :db/id

ghadi18:08:53

unrelated to your question, always pass the db to a query function, not a connection

ghadi18:08:30

if you pass the db , you can make a complicated report/query by asking several questions of the same db value

ghadi18:08:17

(by making several function that all take a db , the call them all with the same db arg)

favila18:08:45

I think what you mean is “a datom with a matching E was in a TX” If so, then use a history db instead of a normal db and your query should work. The db you use has only currently-asserted datoms in it.

favila18:08:04

But to reiterate what Ghadi said, passing “connection” around is an antipatern

emccue18:08:12

okay, so what is a history db

favila18:08:26

(d/history db) => A database with all datoms in it

emccue18:08:59

(defn all-transactions-for-entity [db entity]
  (d/q {:query '[:find  (pull ?transaction [*])
                 :in    $ ?e
                 :where [?e _ _ ?transaction]]
        :args [db (:db/id entity)]}))

emccue18:08:04

okay so i updated it to take the db

emccue18:08:19

(all-transactions-for-entity (d/history (d/db connection)) e2)
Execution error (IllegalStateException) at datomic.core.pull/pull* (pull.clj:364).
Can't pull from history

favila18:08:46

ah, I forgot about that. You will need to pass two dbs, and make a decision about the moment-in-time value of the tx you pull

favila18:08:18

(defn all-transactions-for-entity [db entity]
  (d/q {:query '[:find  (pull ?transaction [*])
                 :in    $ $h ?e
                 :where [$h ?e _ _ ?transaction]]
        :args [db (d/history db) (:db/id entity)]}))

emccue18:08:19

okay that did it, but i do not understand what is happening

emccue18:08:45

okay so i understand that (d/db ...) gets me a logical snapshot of the db

favila18:08:56

You were querying all datoms in a history db (i.e., including retractions) and collecting their TX; then you were projecting the tx entities into maps at the moment-in-time of db

emccue18:08:17

so i cannot use pull in the history db?

emccue18:08:39

because I would be getting retractions as well as current values?

ghadi18:08:51

pull projects an entity at a particular point in time (db), and a history db includes all points in time

emccue18:08:20

okay - so what if i wanted to see the current value of the entity at the time of each one of these transactions

favila18:08:58

Use as-of on the database

emccue18:08:36

okay small extension to that - what if i wanted to get all entities affected by a transaction

favila18:08:26

It’s a different index

hden13:08:44

> what if i wanted to get all entities affected by a transaction d/tx-range is the index to go. For queries, this might work as well. (see concerns below↓)

[:find ?e
 :in $ ?tx
 :where [?e _ _ ?tx]]

favila13:08:33

That will be a full scan of the entire index. I’m not sure datomic will even let you issue that query

Ivar Refsdal18:08:34

Hi. Is there any simple way to count the total number of datoms in a database?

ghadi18:08:49

on-prem or cloud?

ghadi18:08:27

not sure if the db-stats function exists in on-prem

ghadi18:08:34

but it does in cloud

ghadi18:08:43

should be O(1)

favila20:08:13

it does not. The transactor reports metrics “Datoms” and “IndexDatoms”. https://docs.datomic.com/on-prem/operation/monitoring.html

favila20:08:25

that’s the only way I know of to get a quick count in on-prem