<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><id>urn:augustl-com:feed</id><updated>2019-06-11T00:00:00.000+02:00</updated><title type="text">Datomic</title><link href="http://augustl.com/atom.xml" rel="self" /><entry><title>Datomic: Querying for the history of an entity (v2)</title><updated>2019-06-11T00:00:00.000+02:00</updated><author><name>August Lilleaas</name></author><link href="http://augustl.com/blog/2019/datomic_querying_for_history_of_entity_v2/" /><id>urn:augustl-com:feed:post::blog:2019:datomic_querying_for_history_of_entity_v2</id><content type="html">&lt;p&gt;This is the updated and rewritten version of an &lt;a href=&quot;https://augustl.com/blog/2013/querying_datomic_for_history_of_entity/&quot;&gt;old classic&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Intro: how is this even possible?&lt;/h2&gt;
&lt;p&gt;Datomic makes it easy to implement an &amp;quot;audit log&amp;quot; of changes over time to anything in your system.&lt;/p&gt;
&lt;p&gt;You &lt;em&gt;could&lt;/em&gt; implement your own. But when you have Datomic, you are one abstraction level above other databases. And there are &lt;a href=&quot;https://augustl.com/blog/2018/datomic_look_at_all_the_things_i_am_not_doing/&quot;&gt;many ways to get that wrong&lt;/a&gt;. So you don&apos;t have to think of this when modelling your data. The power is already there. You just have to know how to query the data out of Datomic.&lt;/p&gt;
&lt;h2&gt;The history database&lt;/h2&gt;
&lt;p&gt;Normally, you transact facts into your database, and your schema determines what the &amp;quot;current view&amp;quot; of the database is like.&lt;/p&gt;
&lt;p&gt;Let&apos;s say we have the following history of transactions:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; The three transactions that we write to the database&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; tx 1&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/add&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user/email&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;august@augustl.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/add&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user/password&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;hash-here&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; tx 2&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/add&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user/subscribed-to-newsletter?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; tx 3&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/add&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user/email&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;august@kodemaker.no&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We pull out a view of the database:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; Set up&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;datomic.api&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-db&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-eid&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;123654&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;;; The assumed entity ID created above (`[:db/add 1 ...]`)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you query &lt;code&gt;my-db&lt;/code&gt;, you&apos;ll see a &amp;quot;current view&amp;quot; of the data, without any history. If you ask the db for the &lt;code&gt;:user/email&lt;/code&gt; of user `1`` you&apos;ll get:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; Just &amp;quot;latest data&amp;quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/q&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?email&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?eid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:where&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?eid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user/email&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-eid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; =&amp;gt; returns&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;august@kodemaker.no&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, you can also ask for the &amp;quot;history&amp;quot; db. If you ask &lt;em&gt;that&lt;/em&gt; for the value of the &lt;code&gt;:user/email&lt;/code&gt; of user `1`` you&apos;ll get:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; All historical data, just like that!&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-history-db&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/history&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/q&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?email&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?eid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:where&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?eid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user/email&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?email&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-history-db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-eid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; =&amp;gt; returns&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;august@augustl.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;august@kodemaker.no&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &amp;quot;history&amp;quot; db is a special view of the database that doesn&apos;t filter out previously added data.&lt;/p&gt;
&lt;h2&gt;Inspect the history&lt;/h2&gt;
&lt;p&gt;All Datomic facts contains information about which transaction the fact was added in. This is particularly useful for querying the history database.&lt;/p&gt;
&lt;p&gt;This query will return a list of all changes made to the entity`, with information about the transaction in where it was added, the attribute in question, and whether that attribute was added or removed in that transaction.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; All changes made to entity my-eid&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;res&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/q&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?tx&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?attr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?added&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;:in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?e&lt;/span&gt;
      &lt;span class=&quot;ss&quot;&gt;:where&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?attr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?tx&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?added&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/history&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;my-eid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; =&amp;gt; returns&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13194139534318&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;63&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13194139534313&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;63&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13194139534317&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13194139534313&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13194139534318&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;63&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This data structure is pretty bare metal. What follows are some examples of how to use it.&lt;/p&gt;
&lt;h2&gt;Making sense of the daa&lt;/h2&gt;
&lt;p&gt;We don&apos;t really do anything here other than restructuring the data to make it look more intuitive.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; More prettier&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;txs&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;res&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;group-by&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tx&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;attr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;added&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:attr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;attr&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:added?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;added&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]))&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sort-by &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; =&amp;gt; returns&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13194139534313&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:attr&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;63&lt;/span&gt;, &lt;span class=&quot;ss&quot;&gt;:added?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:attr&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;, &lt;span class=&quot;ss&quot;&gt;:added?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})]&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13194139534317&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:attr&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;65&lt;/span&gt;, &lt;span class=&quot;ss&quot;&gt;:added?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})]&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13194139534318&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:attr&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;63&lt;/span&gt;, &lt;span class=&quot;ss&quot;&gt;:added?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:attr&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;63&lt;/span&gt;, &lt;span class=&quot;ss&quot;&gt;:added?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we se the three transactions we did above. The first transaction contains the two &amp;quot;added&amp;quot; items, the email and the password. The second transaction contains the added newsletter subscription. The third transaction contains the removed old e-mail and the added new e-mail.&lt;/p&gt;
&lt;p&gt;You can use this query power to do lots of neat things.&lt;/p&gt;
&lt;p&gt;It also exposes an interesting detail about Datomic internals. Attributes themselves (&lt;code&gt;?attr&lt;/code&gt;) are &lt;em&gt;also&lt;/em&gt; represented entities in the Datomic database, and can be queried fully. As well as the transactions themselves, of course.&lt;/p&gt;
&lt;h2&gt;Generate a list of entities before/after, whith a timestamp&lt;/h2&gt;
&lt;p&gt;Our raw data is not that interesting, so let&apos;s do something about that.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; List of changes, with the full entity as of before and after the change was made&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;res&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tx-eid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tx&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/entity&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tx-eid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
           &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timestamp&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/txInstant&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;ss&quot;&gt;:ent-before&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/entity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/as-of&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-db&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;dec &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/tx-&amp;gt;t&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tx-eid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-eid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;ss&quot;&gt;:ent-after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/entity&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/as-of&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tx-eid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-eid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))})))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&apos;s what we get back&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; =&amp;gt; returns&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; The first TX&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;inst&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;2019-06-11T13:53:09.055-00:00&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;ss&quot;&gt;:ent-before&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:id&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;17592186045418&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;,
  &lt;span class=&quot;ss&quot;&gt;:ent-after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/id&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;17592186045418&lt;/span&gt;, 
              &lt;span class=&quot;ss&quot;&gt;:user/email&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;august@augustl.com&amp;quot;&lt;/span&gt;, 
              &lt;span class=&quot;ss&quot;&gt;:user/password&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;hash-123-abc&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; The second TX&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;inst&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;2019-06-11T13:54:12.693-00:00&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;ss&quot;&gt;:ent-before&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/id&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;17592186045418&lt;/span&gt;, 
               &lt;span class=&quot;ss&quot;&gt;:user/email&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;august@augustl.com&amp;quot;&lt;/span&gt;, 
               &lt;span class=&quot;ss&quot;&gt;:user/password&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;hash-123-abc&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;,
  &lt;span class=&quot;ss&quot;&gt;:ent-after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/id&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;17592186045418&lt;/span&gt;,
              &lt;span class=&quot;ss&quot;&gt;:user/email&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;august@augustl.com&amp;quot;&lt;/span&gt;,
              &lt;span class=&quot;ss&quot;&gt;:user/password&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;hash-123-abc&amp;quot;&lt;/span&gt;,
              &lt;span class=&quot;ss&quot;&gt;:user/subscribed-to-newsletter?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; The third TX&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;inst&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;2019-06-11T13:54:22.530-00:00&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;ss&quot;&gt;:ent-before&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/id&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;17592186045418&lt;/span&gt;,
               &lt;span class=&quot;ss&quot;&gt;:user/email&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;august@augustl.com&amp;quot;&lt;/span&gt;,
               &lt;span class=&quot;ss&quot;&gt;:user/password&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;hash-123-abc&amp;quot;&lt;/span&gt;,
               &lt;span class=&quot;ss&quot;&gt;:user/subscribed-to-newsletter?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;,
  &lt;span class=&quot;ss&quot;&gt;:ent-after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/id&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;17592186045418&lt;/span&gt;,
              &lt;span class=&quot;ss&quot;&gt;:user/email&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;august@kodemaker.no&amp;quot;&lt;/span&gt;,
              &lt;span class=&quot;ss&quot;&gt;:user/password&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;hash-123-abc&amp;quot;&lt;/span&gt;,
              &lt;span class=&quot;ss&quot;&gt;:user/subscribed-to-newsletter?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a couple of intersting to note here.&lt;/p&gt;
&lt;p&gt;Note how we get what seems like a full entity with an id from &lt;code&gt;:ent-before&lt;/code&gt; in the first tx. This is because the Datomic &lt;code&gt;(d/entity)&lt;/code&gt; API does not actually check whether any facts exists for an entity before generating an &lt;code&gt;EntityMap&lt;/code&gt; data structure.&lt;/p&gt;
&lt;p&gt;The essential data structure here is just a sorted list of TX ids. We can safely sort on transaction ID, since the transaction is a monotone sequence.&lt;/p&gt;
&lt;p&gt;We also use a special trick. We have a list of transactions, but how do we get the entity as of right before that transaction? We use &lt;code&gt;(d/tx-&amp;gt;t)&lt;/code&gt; to get the &amp;quot;T&amp;quot; value of the tx, i.e. its internal monotone sequence number that we can pass to &amp;quot;as-of&amp;quot;. If we decrement that number, we&apos;ll get the transaction before tx. This is guaranteed by Datomic.&lt;/p&gt;
&lt;p&gt;This is a slighly more practical version of the &amp;quot;real&amp;quot; way, which would be to map over pairs of transaction A+B, B+C, C+D etc.&lt;/p&gt;
&lt;h2&gt;Generate a list of entities with the timestamp, and a list of attributes that changed, and to which value&lt;/h2&gt;
&lt;p&gt;This is pretty simple when we have our existing query over the history db.&lt;/p&gt;
&lt;p&gt;To make things slightly more interesting, let&apos;s add a fourth transaction, that doesn&apos;t update anything, but just deletes an attribute. Let&apos;s assume that the password expired or something like that, so we retract the hash from the database.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; Add some more data - retract (delete/remove/wipe) an attribute&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; tx 4&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/retract&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user/password&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;hash-123-abc&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; Pull out reference to latest db&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-db&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-conn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that if we didn&apos;t re-assign my-db, we&apos;d still get the old view without transaction 4, even though we use the history db. The history db is still an immutable reference to a point in time, it&apos;s just that it contains all of the history up until that point, not just the data directly visible in the index in the normal db view.&lt;/p&gt;
&lt;p&gt;Now, let&apos;s generate the data we need.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; Complete diff of changes made over time to an entity&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/q&lt;/span&gt;
       &lt;span class=&quot;o&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?tx&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?attr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?val&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?added&lt;/span&gt;
         &lt;span class=&quot;ss&quot;&gt;:in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?e&lt;/span&gt;
         &lt;span class=&quot;ss&quot;&gt;:where&lt;/span&gt;
         &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?attr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?val&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?tx&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?added&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
       &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/history&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
       &lt;span class=&quot;nv&quot;&gt;my-eid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;group-by&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tx&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tx&lt;/span&gt;
             &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;v&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;group-by&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tx&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;attr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;val &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;added&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;attr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;changes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
                         &lt;span class=&quot;p&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/ident&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tx&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;attr&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;val &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;added?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:val&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;val &lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:added?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;added?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
                               &lt;span class=&quot;nv&quot;&gt;changes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])))]))&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sort-by &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tx&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;tx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we look at the data, we can see that we have everything we need in order to generate a useful GUI.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; =&amp;gt; returns&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;;; tx 1&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13194139534313&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user/password&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:val&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;hash-123-abc&amp;quot;&lt;/span&gt;, &lt;span class=&quot;ss&quot;&gt;:added?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})]&lt;/span&gt; 
   &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user/email&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:val&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;august@augustl.com&amp;quot;&lt;/span&gt;, &lt;span class=&quot;ss&quot;&gt;:added?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})])]&lt;/span&gt;
  
 &lt;span class=&quot;c1&quot;&gt;;; tx 2&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13194139534317&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user/subscribed-to-newsletter?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:val&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;, &lt;span class=&quot;ss&quot;&gt;:added?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})])]&lt;/span&gt;
 
 &lt;span class=&quot;c1&quot;&gt;;; tx 3&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13194139534318&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user/email&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:val&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;august@kodemaker.no&amp;quot;&lt;/span&gt;, &lt;span class=&quot;ss&quot;&gt;:added?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                 &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:val&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;august@augustl.com&amp;quot;&lt;/span&gt;, &lt;span class=&quot;ss&quot;&gt;:added?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})])]&lt;/span&gt;
  
 &lt;span class=&quot;c1&quot;&gt;;; tx 4&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;13194139534319&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user/email&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:val&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;august@kodemaker.no&amp;quot;&lt;/span&gt;, &lt;span class=&quot;ss&quot;&gt;:added?&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})])])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Look at all the things I&apos;m not doing&lt;/h2&gt;
&lt;p&gt;If you don&apos;t use Datomic, you can always implement this yourself. And figure out all the wrong ways to do it before you end up with something useful (and performant).&lt;/p&gt;
&lt;p&gt;But what if you get this requirement when your app is already up and running in production?&lt;/p&gt;
&lt;p&gt;Well, a buddy of mine has done just that. They did &lt;em&gt;not&lt;/em&gt; have an audit log requirement up front. But since this is not an up-front decision you have to make, you can be completely oblivious to this, and just add it later, in the forms of queries.&lt;/p&gt;
&lt;p&gt;Check it out! It starts around 24:00.&lt;/p&gt;
&lt;iframe src=&quot;https://player.vimeo.com/video/234054197?color=ff9933&amp;portrait=0&quot; width=&quot;640&quot; height=&quot;360&quot; frameborder=&quot;0&quot; allow=&quot;autoplay; fullscreen&quot; allowfullscreen&gt;&lt;/iframe&gt;
</content></entry><entry><title>Datomic: Look at all the things I&apos;m not doing! (Cont.)</title><updated>2018-04-21T00:00:00.000+02:00</updated><author><name>August Lilleaas</name></author><link href="http://augustl.com/blog/2018/datomic_look_at_all_the_things_i_am_not_doing_cont/" /><id>urn:augustl-com:feed:post::blog:2018:datomic_look_at_all_the_things_i_am_not_doing_cont</id><content type="html">&lt;p&gt;In &lt;a href=&quot;http://augustl.com/blog/2018/datomic_look_at_all_the_things_i_am_not_doing/&quot;&gt;a previous blog post&lt;/a&gt; I listed a bunch of things I&apos;m &lt;em&gt;not&lt;/em&gt; doing when I&apos;m using Datomic.&lt;/p&gt;
&lt;p&gt;This is a continuation of that post, where I list even more things that I&apos;m not doing, based on feedback I got on the previous post.&lt;/p&gt;
&lt;h2&gt;Look at all the query sit-ups I&apos;m not doing&lt;/h2&gt;
&lt;p&gt;Thanks, &lt;a href=&quot;https://twitter.com/newresa/status/987660036682993664&quot;&gt;@newresa&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;In a traditional database, the query engine runs on the database server.&lt;/p&gt;
&lt;p&gt;Well, duh. Of course. That&apos;s how databases work.&lt;/p&gt;
&lt;p&gt;Or do they?&lt;/p&gt;
&lt;p&gt;With Datomic, the query engine is embedded into the client.&lt;/p&gt;
&lt;p&gt;This is &lt;em&gt;not&lt;/em&gt; to say that Datomic does something like &lt;code&gt;SELECT * FROM *&lt;/code&gt; and iterates a bunch of maps and has a bunch of stupid if-statements and regexps to query your data. It&apos;s a proper, real query engine, based on the Datalog language, that converts a query into efficient index access and so on and so forth.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This concept is an unbelievably good idea that I will try to blog about in detail later.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There are a few rules that you thought were rules that aren&apos;t rules anymore with this architecture.&lt;/p&gt;
&lt;p&gt;Fundamentally, the data structures in Datomic allows multiple queries to run simultaneously, without synchronization on the database server, or between the clients that are running queries. This means that queries are trivial to scale horizontally - you just add another instance of your app, and it&apos;ll run more queries in isolation, with no effect on the performance of other queries. The only bottleneck here is fetching data from storage and into the client. You can put memcached between storage and your apps to help alleviate this.&lt;/p&gt;
&lt;p&gt;In traditional databases, we&apos;ve learned to avoid N+1 queries. To list projects and count the todo items for each project, we do NOT run one query to list the projects, and then one more query for each project to count the todos.&lt;/p&gt;
&lt;p&gt;Why? This is not because queries are slow. Fundamentally the same amount of work has to be done by the query engine. But there will be a round-trip cost from sending the query to the database and getting the response back. This is just fundamental networking.&lt;/p&gt;
&lt;p&gt;This is not the case with Datomic. The query engine is &lt;em&gt;right there&lt;/em&gt;, in your application.&lt;/p&gt;
&lt;p&gt;If you think this sounds crazy, it helps to remember that &lt;em&gt;someone&lt;/em&gt; has to have the working set in memory. Your database server can&apos;t seek on disk when you run a query, that would just be way too slow. So why does this &lt;em&gt;have&lt;/em&gt; to happen on the database server? Well, it hasn&apos;t. Not when you use Datomic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;So you end up designing much simpler queries with Datomic, since you don&apos;t have to express your queries as one huge statement&lt;/strong&gt;. You &lt;em&gt;can&lt;/em&gt; actually get the list of projects, and then count the number of todos for each project directly in the loop.&lt;/p&gt;
&lt;p&gt;This might sound crazy. But it isn&apos;t.&lt;/p&gt;
&lt;h2&gt;Look at all the application level caching I&apos;m not doing&lt;/h2&gt;
&lt;p&gt;On a related note, you don&apos;t need to cache the result of database queries at the application level when you use Datomic.&lt;/p&gt;
&lt;p&gt;Kind of.&lt;/p&gt;
&lt;p&gt;Application level caching is a common pattern to use, where you cache the result of queries. If you cache because the query is slow, Datomic can&apos;t help you. Slow queries are slow, even if the query engine is embedded in your application. However, if you&apos;re caching in order to avoid round-trips on read-heavy and cacheable queries, Datomic can help.&lt;/p&gt;
&lt;p&gt;It should be self evident why Datomic is different here. The query engine and your working set is already in your application. So there&apos;s no need to cache what&apos;s already cached.&lt;/p&gt;
&lt;p&gt;Thanks, &lt;a href=&quot;https://twitter.com/val_waeselynck/status/987402104804466690&quot;&gt;val_waeselynck&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;Look at all the audit logging I&apos;m not doing&lt;/h2&gt;
&lt;p&gt;Thanks again, &lt;a href=&quot;https://twitter.com/val_waeselynck/status/987402104804466690&quot;&gt;val_waeselynck&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Your domain has entities. In traditional databases, you typically map an entity to a row in a table or a document in a collection.&lt;/p&gt;
&lt;p&gt;You want to be able to know how this entity changed over time. Good luck, and try to not pick one of the horrible ways of doing this. Because there are many of those.&lt;/p&gt;
&lt;p&gt;In order to avoid having versioned entities actually being a part of your core domain, maybe you just add a separate audit log table where you dump json blobs of each version of your entity. This can end up being many different levels of slow.&lt;/p&gt;
&lt;p&gt;With Datomic, this is just a query away.&lt;/p&gt;
&lt;p&gt;I wrote a separate blog post about &lt;a href=&quot;http://augustl.com/blog/2013/querying_datomic_for_history_of_entity/&quot;&gt;querying for the history of an entity&lt;/a&gt;. This is something you can just &lt;em&gt;do&lt;/em&gt;. You don&apos;t have to plan for it or adjust your domain to support it or add audit log tables or anything.&lt;/p&gt;
&lt;h2&gt;Look at all the change detection I&apos;m not doing&lt;/h2&gt;
&lt;p&gt;Datomic has a queue you can consume from where you get all the changes that&apos;s happening provided lazily to you in a queue. This is built right in to the Database API. You can create many of these queues, and create filters on them so you only get information about specific attributes, and so on.&lt;/p&gt;
&lt;p&gt;There&apos;s a more general point to be made here. The reason Datomic is even able to do this, is because it is able to provide all its data structures lazily, because all the views you get of data in Datomic are immutable.&lt;/p&gt;
&lt;p&gt;This allows you to do things like implement SSE listeners. And you can also do this in a completely generic fashion. Typically, you have to either deliberately separate the concerns between writing to the database and sending out SSE events, or you end up having SSE change code right next to the code you have for writing data. With Datomic, you can create a completely isolated section of your code, and all it has to know about is the connection object to talk to the Database. Then you set up your Datomic change listener queue and fire away.&lt;/p&gt;
&lt;p&gt;Additionally, you don&apos;t have to figure out yourself what specifically it was that changed. The level of granularity in Datomic are facts, which refers to a single attribute of your entities in your domain. So you don&apos;t have to do things like figuring out which of the attributes changed by comparing the previous and current version of your row or document to each other.&lt;/p&gt;
&lt;p&gt;Thanks again, again, &lt;a href=&quot;https://twitter.com/val_waeselynck/status/987402104804466690&quot;&gt;val_waeselynck&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;Look at all the database mocking/stubbing I&apos;m not doing&lt;/h2&gt;
&lt;p&gt;The query engine in Datomic is embedded in your app.&lt;/p&gt;
&lt;p&gt;Internally, the query engine works on two data structures: The index trees, and a log of transactions (raw transaction data) that has not yet been merged into the tree.&lt;/p&gt;
&lt;p&gt;Datomic calls this a &amp;quot;live merge&amp;quot;, and it&apos;s borrowed from Google&apos;s Bigtable. It can&apos;t just merge new data into the index trees immediately, because that merge is an expensive operation and it would dramatically lower write throughput.&lt;/p&gt;
&lt;p&gt;Thankfully, it has been found that it&apos;s very efficient to do a &amp;quot;live&amp;quot; combination of the latest version of the index trees, and the raw transaction data that has not yet been merged into the tree. This is partly because the amount of data in the unmerged log is relatively low, since it &lt;em&gt;is&lt;/em&gt; merged in, only periodically, not immediately after the transaction commits.&lt;/p&gt;
&lt;p&gt;(Note: Obviously, the transaction log is persisted to disk as well, alongside the index trees.)&lt;/p&gt;
&lt;p&gt;This means that one of the things the query engine can do, is to have a view of a database, take a list of raw transactions, and run queries as if those transactions are present in the database.&lt;/p&gt;
&lt;p&gt;This &lt;em&gt;also&lt;/em&gt; means that  Datomic can very easily do this without actually writing to the transaction log, but instead keeping a in-memory list of &amp;quot;fake&amp;quot; transactions.&lt;/p&gt;
&lt;p&gt;The only difference is that it&apos;s in-memory. The actual query engine performs the same operations.&lt;/p&gt;
&lt;p&gt;This is very useful for writing tests. You can create an in-memory Datomic database, call the code that generates your transactions, use the &lt;code&gt;with&lt;/code&gt;API to add these transactions to the &amp;quot;fake&amp;quot; transaction log, and run queries against it.&lt;/p&gt;
&lt;p&gt;So you are able to test writes against your database &lt;strong&gt;without any mocking or stubbing at all&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Yes, you read that right.&lt;/p&gt;
&lt;p&gt;It&apos;ll be a unit test, not an integration test, but still. Pretty cool.&lt;/p&gt;
&lt;p&gt;Thanks again, again, again, &lt;a href=&quot;https://twitter.com/val_waeselynck/status/987402104804466690&quot;&gt;val_waeselynck&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;Look at all the up-front schema planning I&apos;m ~~not~~ doing&lt;/h2&gt;
&lt;p&gt;Wait, what? Isn&apos;t this about what I&apos;m &lt;em&gt;not&lt;/em&gt; doing?&lt;/p&gt;
&lt;p&gt;In my previous post, I said the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You create structure when you read, not when you write. Therefore, you get to come up with new ways of thinking about your data arbitrarily, after the fact. This is just a logical conclusion of having all the decisions about structure being put in the query engine, and not the write API. Contrary to traditional databases, where so much structure is defined up-front when you insert the data.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.reddit.com/r/Clojure/comments/8dqes8/datomic_look_at_all_the_things_im_not_doing/dxpc1kb/&quot;&gt;/u/dustingetz&lt;/a&gt; on Reddit had some very interesting insight regarding this statement.&lt;/p&gt;
&lt;p&gt;This is a Datomic schema made by Rich Hickey himself, for Codeq, the Datomic layer on top of git:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.s3.amazonaws.com/downloads/Datomic/codeq/codeq.pdf&quot;&gt;https://github.s3.amazonaws.com/downloads/Datomic/codeq/codeq.pdf&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That sure looks like a lot of up-front planning of schemas to me.&lt;/p&gt;
&lt;p&gt;I&apos;m not sure how to reconcile this yet. I think I&apos;m right in asserting that you create structure when you read, not when you write, in Datomic. At least more so than in other databases. But I believe that /u/dustingetz is also right. Hopefully this will culminate into a new blog post some time in the future.&lt;/p&gt;
</content></entry><entry><title>Datomic: Look at all the things I&apos;m not doing!</title><updated>2018-04-20T00:00:00.000+02:00</updated><author><name>August Lilleaas</name></author><link href="http://augustl.com/blog/2018/datomic_look_at_all_the_things_i_am_not_doing/" /><id>urn:augustl-com:feed:post::blog:2018:datomic_look_at_all_the_things_i_am_not_doing</id><content type="html">&lt;div class=&quot;update&quot;&gt;
&lt;p&gt;UPDATE: This post has a follow-up: &lt;a href=&quot;http://augustl.com/blog/2018/datomic_look_at_all_the_things_i_am_not_doing_cont/&quot;&gt;http://augustl.com/blog/2018/datomic_look_at_all_the_things_i_am_not_doing_cont/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Original post follows.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This is the video that got me into programming:&lt;/p&gt;
&lt;p&gt;&lt;small&gt;&lt;em&gt;(This embed should take you directly to 01:52, where it&apos;s all at)&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/Gzj723LkRJY?start=112&quot; frameborder=&quot;0&quot; allow=&quot;autoplay; encrypted-media&quot; allowfullscreen&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p&gt;It&apos;s DHH&apos;s epic first Ruby on Rails demo from 2005. His unique selling point (or one of them, at least) was &amp;quot;look at all the things I&apos;m not doing&amp;quot;&lt;/p&gt;
&lt;p&gt;At the time, this was ground breaking in its implementation and accessibility. &amp;quot;Convention over configuration&amp;quot; became a thing.&lt;/p&gt;
&lt;p&gt;However, a portion of the developer community now seems to agree that hiding all the complexities of large CRUD apps has its trade offs, especially when it comes to large and long running projects. You end up in a situation where you have to be as knowledgeable about the conventions as if you wrote the code yourself.&lt;/p&gt;
&lt;p&gt;Whoops!&lt;/p&gt;
&lt;p&gt;Any sufficiently advanced technology is indistinguishable from magic. But is magic always bad? Do we like magic, as long as it hits the sweet spot where it is able to remain hidden from us?&lt;/p&gt;
&lt;p&gt;Why do Clojure devs despise the magic in Ruby on Rails, but love the magic of auto-promoting persistent data structures?&lt;/p&gt;
&lt;p&gt;Let&apos;s explore all the things you&apos;re &lt;em&gt;not&lt;/em&gt; doing when you&apos;re using Datomic.&lt;/p&gt;
&lt;h2&gt;Look at all the data modeling I&apos;m not doing&lt;/h2&gt;
&lt;p&gt;Datomic has made a decision for you. When inserting data, everything is completely and utterly flat, with no structure.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/add&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;-1&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:person/name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;August Lilleaas&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/add&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;-1&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:person/age&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is &lt;em&gt;it&lt;/em&gt;. All you have for writing data are these facts. A fact consists of:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/add&lt;/span&gt; 
  &lt;span class=&quot;mi&quot;&gt;-1&lt;/span&gt;                 &lt;span class=&quot;c1&quot;&gt;;; Entity (the id of the thing we&amp;#39;re talking about)&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:person/name&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;;; Attribute (defines the type and some other properties)&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;&amp;quot;August Lilleaas&amp;quot;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;;; Value (self evident)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  
  &lt;span class=&quot;c1&quot;&gt;;; No table, no nesting, no depth, no ...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Any structure comes later, from queries and reads.&lt;/p&gt;
&lt;p&gt;Datomic isn&apos;t &lt;em&gt;free&lt;/em&gt; of data modeling. You have to choose if an attribute has a cardinality of &lt;em&gt;one&lt;/em&gt; or &lt;em&gt;many&lt;/em&gt;. You need to define the type of your attributes. But the API you get for writing data is extremely limited. There&apos;s no structured schema.&lt;/p&gt;
&lt;p&gt;So, essentially: &lt;strong&gt;Look at all the up front decisions I&apos;m not making about data modeling&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This has one &lt;em&gt;enormous&lt;/em&gt; benefit. You create structure when you read, not when you write. Therefore, you get to come up with new ways of thinking about your data &lt;em&gt;arbitrarily, after the fact&lt;/em&gt;. This is just a logical conclusion of having all the decisions about structure being put in the query engine, and not the write API. Contrary to traditional databases, where so much structure is defined up-front when you insert the data.&lt;/p&gt;
&lt;h2&gt;Look at all the time modeling I&apos;m not doing&lt;/h2&gt;
&lt;p&gt;So you write these facts into the database, and there&apos;s almost no structure when you write.&lt;/p&gt;
&lt;p&gt;There are 50 ways to get time modeling wrong in a traditional database, and a few ways to get it right. And even when you got it right, maybe your specific implementation of time management didn&apos;t cut it so you need to update it. And maybe you didn&apos;t add time management to your &lt;em&gt;entire&lt;/em&gt; domain, just the crucial parts.&lt;/p&gt;
&lt;p&gt;Given Datomic&apos;s extremely simple write API, it is able to implement time management for you, without &lt;em&gt;any&lt;/em&gt; additional cost of data modeling or query hatching on your part.&lt;/p&gt;
&lt;p&gt;So, essentially: &lt;strong&gt;Look at all the up front decisions I&apos;m not making about time modeling&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It&apos;s difficult to talk about Datomic without at one point mentioning the time model. The writer is single threaded. You get a completely serialized time-line of your writes, without any special coordination or re-ordering happening. There&apos;s no such thing in Datomic as &amp;quot;read this thing, write that thing, read this other thing&amp;quot; and have that be inconsistent in any way shape or form. There&apos;s no such thing as &amp;quot;isolation level&amp;quot; (alternatively, Datomic is isolation level 9000), and transactions can&apos;t see what happens in other transactions. This is fundamentally solved by having only one transaction being processed at a time.&lt;/p&gt;
&lt;p&gt;When you query Datomic, you query against what Datomic calls a &amp;quot;database&amp;quot;. This is essentially a view of a point in time of the time-line of writes that have occurred so far. This view is completely immutable, and requires no synchronization with the writer process because Datomic is an append-only database with append-only semantics.&lt;/p&gt;
&lt;p&gt;Are you thinking what I&apos;m thinking?&lt;/p&gt;
&lt;p&gt;Did you notice the detail there that makes Datomic amazing?&lt;/p&gt;
&lt;p&gt;You ask Datomic for a &amp;quot;database&amp;quot;, and you get an immutable reference to the value of that database at that point in time. You get to hold onto this value forever, no matter how many writes that occur in the future.&lt;/p&gt;
&lt;p&gt;Getting it yet?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;You get to hold onto a view of the database that&apos;s immutable, no matter what happens to it in the future.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This means that it&apos;s extremely cheap for Datomic to let you get a view of a &lt;em&gt;previous state of the database&lt;/em&gt;. In fact, internally, the API is exactly the same. You get a reference to the database data structure, with a T value (a reference or id to a point in time in the transaction time-line), and all your queries will ensure that you get the data as of that T value.&lt;/p&gt;
&lt;p&gt;So it&apos;s really cheap to just set the T value to an earlier point in time, say two weeks ago, or whatever, and get a &lt;em&gt;completely immutable and consistent view of the entire database as of two weeks ago&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Woha.&lt;/p&gt;
&lt;h2&gt;Look at all the consistency management I&apos;m not doing&lt;/h2&gt;
&lt;p&gt;In that vein, the consistency model in Datomic is also extremely simple. As mentioned above, the writes happen in a single thread, writing one transaction at a time. This is isolation level 9000. There&apos;s no extra management to be done on your part to ensure that you use the correct isolation level or that you do things in the correct order to get the expected result or any of that. Datomic is consistent, period. There&apos;s no way to screw that up by misunderstandings or bugs made by the developer that uses Datomic in their app.&lt;/p&gt;
&lt;p&gt;The only trade-off here is that you need to resort to stored procedures, VoltDB style, when you want to do anything other than setting a value, deleting (retracting) a value, and doing a few built-in operations like compare-and-swap and identity based upsert. If you want to do things like &amp;quot;increment by 10&amp;quot; or &amp;quot;take N from account X and put it into account Y&amp;quot;, you need to express this as a transaction function (Datomic&apos;s name for stored procedures), written in Clojure or Java, and ship it off to the transactor (Datomic&apos;s name for the database server).&lt;/p&gt;
&lt;h2&gt;Look at all the time management I&apos;m not doing&lt;/h2&gt;
&lt;p&gt;This is a bit of a minor point, but I still like it. It is an example of the many things that are suddenly fundamentally solved when you have a database that manages time for you.&lt;/p&gt;
&lt;p&gt;Exhibit A: Pagination. Pagination has the issue of being a non-atomic operation. You load the first 50 items. Then you go forward, and load the next 50 items. But in between those two fetches of items, the database might have changed. With Datomic, you can have the T value be a factor in your pagination. You can use this to get a consistent view of the database, so that page two is actually page two. You can also run queries to see if your data set has changed since the provided T value, and inform the user. &amp;quot;Click here to update your view of the data to the most recent changes&amp;quot;.&lt;/p&gt;
&lt;p&gt;Exhibit B: Return values. Let&apos;s say you have a method that takes a bunch of arguments and inserts a new thing into your database. What should it return? Maybe just the ID, so we don&apos;t force all users of the API to wait for us to reconstruct a view of the thing that was just inserted. With Datomic, however, this is a non-issue. You simply return the ID and a reference to the T-value as of after your insert (which is something Datomic provides automatically). Then it is up to the consumer to do what it wants with the T-value. And this is another example of the extreme agility you get with Datomic. Because time is managed for you, you get to make &lt;em&gt;a whole lot of decisions later on, since you don&apos;t have to worry about the rug getting pulled from underneath your feet&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Look at all the event sourcing I&apos;m not doing&lt;/h2&gt;
&lt;p&gt;This is a big one.&lt;/p&gt;
&lt;p&gt;If you&apos;re familiar with event sourcing, you might wonder what the big deal is. &amp;quot;One single threaded time-line of writes&amp;quot; is exactly what event sourcing is?&lt;/p&gt;
&lt;p&gt;What does Datomic have that event sourcing doesn&apos;t?&lt;/p&gt;
&lt;p&gt;Answer: An absence of hand written views.&lt;/p&gt;
&lt;p&gt;With event sourcing, you have raw data in your stream of events. This data is useless until you create a view of it. A view is typically a left fold reducing function that gets the previous state and the next event in the list passed to it, and returns a new and updated state.&lt;/p&gt;
&lt;p&gt;&lt;img width=&quot;600&quot; src=&quot;/static/posts/datomic_look_at_all_the_things_i_am_not_doing/event_sourcing.jpg&quot;&gt;&lt;/p&gt;
&lt;p&gt;This means that you have to decide which views you want to have. And that if there&apos;s a bug in your view code, you have to traverse the entire time-line again to build up the state.&lt;/p&gt;
&lt;p&gt;With Datomic, you &lt;em&gt;have&lt;/em&gt; the stream of events (facts/transactions), but there is one crucial but fundamentally important difference:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;You don&apos;t have to decide what your views are up-front&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So, essentially: &lt;strong&gt;Look at all the up front decisions I&apos;m not making about which views to have&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img width=&quot;600&quot; src=&quot;/static/posts/datomic_look_at_all_the_things_i_am_not_doing/event_sourcing_datomic.jpg&quot;&gt;&lt;/p&gt;
&lt;p&gt;Again, Datomic provides us with extreme agility. Because the data we write is completely flat, but is &lt;em&gt;not&lt;/em&gt; arbitrary data but a well known structure (facts), and Datomic have generic indices to query these facts, and because Datomic keeps these indices up to date when we add data to our system, you get the benefits of event sourcing (time travel, a consistent time-line, all your historical data easily traversable), without having to worry about creating views for your data. The views are already there, in form of Datomic&apos;s query engine, its built-in time travel features, and efficient indices of historical data.&lt;/p&gt;
&lt;h2&gt;Look at all the file writing I&apos;m not doing&lt;/h2&gt;
&lt;p&gt;(Implementation detail)&lt;/p&gt;
&lt;p&gt;Internally, Datomic doesn&apos;t write to the file system directly. It writes to existing storage engines and databases. You can set up Datomic to write to any JDBC database (mysql, postgres, h2, oracle, db/2, ...), dynamodb, riak + zookeper, and a few other combinations.&lt;/p&gt;
&lt;p&gt;This means that Datomic doesn&apos;t have to implement its own code for properly writing to disks, which is a huge pain to get right. And it means that if you already use postgres, you don&apos;t have to worry about backup routines and operations and what not, other than running the Datomic transactor itself. You can just use your existing routines for postgres operations.&lt;/p&gt;
&lt;h2&gt;Look at all the database concept reinvention I&apos;m not doing&lt;/h2&gt;
&lt;p&gt;(Implementation detail)&lt;/p&gt;
&lt;p&gt;Datomic doesn&apos;t actually invent a whole lot from scratch.&lt;/p&gt;
&lt;p&gt;I&apos;ve already mentioned VoltDB. VoltDB also writes in a single thread, after research revealed that &lt;a href=&quot;http://nms.csail.mit.edu/~stavros/pubs/OLTP_sigmod08.pdf&quot;&gt;multi-threaded RDBMS databases spend about 75% of their time doing coordination and only 25% of their time actually doing reads and writes&lt;/a&gt;. There&apos;s this instinct that single threaded processes can&apos;t possibly be efficient. But you&apos;d be surprised how much efficiency you can get out of a thread that spends close to 100% of its time doing useful work without much time spent on coordination between multiple threads.&lt;/p&gt;
&lt;p&gt;There&apos;s also a concept borrowed from Google&apos;s Big Table. The indices in Datomic aren&apos;t updated live for every write, that would be way too much overhead. So a query in Datomic runs against the latest index + a list of transactions that are written to a transaction log but not yet merged into the index data structures. Then, Datomic will periodically take the unmerged transaction log and merge it into the main index. Big Table does the same, after Google learned that live-merging an index of &lt;em&gt;the entire internet&lt;/em&gt; was maybe not going to work.&lt;/p&gt;
&lt;h2&gt;Look at all the .... ok, I&apos;m done&lt;/h2&gt;
&lt;p&gt;In my experience, Datomic is indistinguishable from magic, in a good way. It&apos;s one level of abstraction above traditional databases, but manages to hide those abstractions since you at no point worry about postgres or zookeeper internals when you make use of Datomic on top of the existing storage engines. You get a whole lot of stuff for free, without Datomic getting in your way.&lt;/p&gt;
&lt;p&gt;And to me, the most important point is all of the things you&apos;re &lt;em&gt;not&lt;/em&gt; doing and all of the decisions you are &lt;em&gt;not&lt;/em&gt; making up front, but rather in an agile fashion as your application grows.&lt;/p&gt;
&lt;h2&gt;Update: even more things I&apos;m not doing&lt;/h2&gt;
&lt;p&gt;I wrote a follow-up, with even more things I&apos;m not doing:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://augustl.com/blog/2018/datomic_look_at_all_the_things_i_am_not_doing_cont/&quot;&gt;http://augustl.com/blog/2018/datomic_look_at_all_the_things_i_am_not_doing_cont/&lt;/a&gt;&lt;/p&gt;
</content></entry><entry><title>Datomic: Traditional databases only spend about 25% of their time actually writing to and reading from the database</title><updated>2018-04-20T00:00:00.000+02:00</updated><author><name>August Lilleaas</name></author><link href="http://augustl.com/blog/2018/datomic_traditional_databases_only_spend_25_percent_of_their_time_actually_writing_to_and_reading_from_the_database/" /><id>urn:augustl-com:feed:post::blog:2018:datomic_traditional_databases_only_spend_25_percent_of_their_time_actually_writing_to_and_reading_from_the_database</id><content type="html">&lt;p&gt;This post is about how Datomic (and Volt DB, and a few other databases) are different from traditional databases.&lt;/p&gt;
&lt;p&gt;It turns out that OLTP databases &lt;a href=&quot;http://nms.csail.mit.edu/~stavros/pubs/OLTP_sigmod08.pdf&quot;&gt;spend 75% of their time coordinating reads and writes&lt;/a&gt;. That means that 75% of the time, the database isn&apos;t actually running queries and writing data, but is instead figuring out safe ways to allow multiple concurrent clients to perform reads and writes in parallel.&lt;/p&gt;
&lt;p&gt;Volt DB figured out that with modern hardware, you can get much better performance if you instead partition your dataset across N cores, and have those cores do uncoordinated reads and writes to their respective partitions, and then when you &lt;em&gt;do&lt;/em&gt; need consistency across partitions, the coordinator can step in.&lt;/p&gt;
&lt;p&gt;Datomic is completely single threaded when it comes to writes. (And infinitely multi threaded, without coordination, when it comes to reads.)&lt;/p&gt;
&lt;p&gt;It turns out that even on today&apos;s multi threaded hardware, having a single core do useful work close to 100% of the time, is actually quite efficient.&lt;/p&gt;
&lt;p&gt;So when you hear that Datomic has a single threaded writer, maybe that&apos;s not such a bad idea after all. You get unparalleled consistency (pun intended), on top of a writer that might actually be performing better than what your gut feeling tells you.&lt;/p&gt;
</content></entry><entry><title>Datomic: The most innovative DB you&apos;ve never heard of</title><updated>2016-01-20T00:00:00.000+01:00</updated><author><name>August Lilleaas</name></author><link href="http://augustl.com/blog/2016/datomic_the_most_innovative_db_youve_never_heard_of/" /><id>urn:augustl-com:feed:post::blog:2016:datomic_the_most_innovative_db_youve_never_heard_of</id><content type="html">&lt;p&gt;Once upon a time (late 2011, to be exact), a small group of programmers got blown away by &lt;a href=&quot;http://www.infoq.com/presentations/Simple-Made-Easy&quot;&gt;Rich Hickey&apos;s talk Simple Made Easy&lt;/a&gt;. This group of programmers became passionately obsessed with the programming language Hickey created: Clojure. When Hickey then introduced Datomic a year later, &lt;em&gt;reconstructive jaw surgery&lt;/em&gt; became a meme in the Clojure community.&lt;/p&gt;

&lt;p&gt;Why did our jaws suddenly become so useless? And why hasn&apos;t Datomic become an A-level option in the database space?&lt;/p&gt;

&lt;p&gt;
    &lt;img src=&quot;/static/posts/datomic_never_heard_of/simple_made_easy.png&quot; width=&quot;300px&quot;&gt;
    &lt;img src=&quot;/static/posts/datomic_never_heard_of/woah.gif&quot;&gt;
&lt;/p&gt;

&lt;p&gt;Let&apos;s investigate.&lt;/p&gt;



&lt;h2&gt;Datomic stores the actual data directly in (multiple) indexes&lt;/em&gt;&lt;/h2&gt;

&lt;p&gt;Datomic doesn&apos;t have the data in one place and the indexes elsewhere. In Datomic, data and indexes are the same thing, and data is stored directly in the index. This is called a &lt;em&gt;covering index&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Not only that, the data is stored &lt;em&gt;multiple&lt;/em&gt; times, once for each index, of which there are four and only four.&lt;/p&gt;



&lt;h2&gt;Datomic only stores its data in other databases&lt;/h2&gt;

&lt;p&gt;Datomic does not write directly to the file system. Instead, you can use a number of different databases as a storage backend for Datomic: Any SQL JDBC database, CouchBase, Riak, Cassandra, DynamoDB, Infinispan, and a &quot;dev&quot; mode that writes to a file backed H2 database.&lt;/p&gt;



&lt;h2&gt;Datomic has a completely different API for reads vs. writes&lt;/h2&gt;

&lt;p&gt;In most databases, you use the same API for reading and writing. Typically in the form of a DSL, like SQL, Cypher in Neo4j, the JavaScript based MongoDB API, and so on.&lt;/p&gt;

&lt;p&gt;In Datomic, you use a &quot;transactor&quot; API for writes and datalog for reads.&lt;/p&gt;

&lt;p&gt;Reads and writes have completely different performance characteristics, semantics, APIs, operational details - you name it.&lt;/p&gt;



&lt;h2&gt;Datomic is single threaded&lt;/h2&gt;

&lt;p&gt;To be specific, &lt;strong&gt;writes&lt;/strong&gt; are single threaded.&lt;/p&gt;

&lt;p&gt;Note that &lt;strong&gt;reads&lt;/strong&gt; are however multi-threaded. As I already mentioned, reads and writes have completely separate characteristics in Datomic.&lt;/p&gt;


&lt;p&gt;It turns out that traditional multi threaded databases &lt;a href=&quot;http://nms.csail.mit.edu/~stavros/pubs/OLTP_sigmod08.pdf&quot;&gt;only spend about 25% of their time actually writing to and reading from the database&lt;/a&gt;. The rest of the time is spent coordinating multiple clients reading and writing in parallell.&lt;/p&gt;

&lt;p&gt;So a single threaded writer, like Datomic, is able to completely saturate a CPU core for writes, spending 100% of the time doing actual useful writes.&lt;/p&gt;

&lt;p&gt;This also means that you get what Stuart Halloway calls isolation level 9000. A single thread doesn&apos;t have concurrency problems.&lt;/p&gt;


&lt;h2&gt;Datomic runs queries on &lt;em&gt;the client&lt;/em&gt;, not on the server&lt;/h2&gt;

&lt;p&gt;This tends to seriously creep out most experienced programmers. Including myself when I first heard of Datomic.&lt;/p&gt;

&lt;p&gt;First, keep in mind that if you want your reads to be fast, you have to keep the working dataset in RAM on the database server anyway.&lt;/p&gt;

&lt;p&gt;Second, realize that this means you can infinitely scale your reads to as many clients as you want.&lt;/p&gt;

&lt;p&gt;This doesn&apos;t mean that the entire dataset has to fit in memory, by the way, only the part that you actively query. And, as mentioned, for speed, you want the entire active dataset in RAM anyway. So why does this absolutely have to happen on the database server?&lt;/p&gt;

&lt;p&gt;The Datomic server is not concerned with reads whatsoever! You can run the nastiest long running query you can think of and not affect anyone but yourself.&lt;/p&gt;

&lt;p&gt;Remember how Datomic doesn&apos;t write to its own storage, but stores data in other databases? As an extension of that, when a client (or &lt;em&gt;peer&lt;/em&gt; in Datomic lingo) needs to fetch some data from storage to run a query, the Datomic server is not involved in any way at all! The client instead connects directly to the underlying storage engine. Furthermore, you can put memcached between you and storage, so you don&apos;t even have to talk directly to the storage engine for reads.&lt;/p&gt;



&lt;h2&gt;Datomic has no round-trip open long running transactions&lt;/h2&gt;

&lt;p&gt;In Datomic, you can&apos;t open a transaction, do some reads, some writes, some more reads, and then commit. You have to create a complete transaction statement and send it off whole sale to the server.&lt;/p&gt;

&lt;p&gt;This goes back to what I&apos;ve already mentioned, reads and writes are completely separated.&lt;/p&gt;

&lt;p&gt;Fear not: It is completely doable to write transactions that say &quot;increment existing value by 10&quot; and &quot;replace whatever list that might exist with this new list&quot; consistently. It&apos;s just that you have to write this in a complete statement that you send off to the Datomic server, so that Datomic can put your transaction on a queue and run it when it&apos;s ready (previous writes have finished).&lt;/p&gt;

&lt;p&gt;Sending off a transaction like this is what makes writes so efficient. The Datomic server requires no coordination to perform writes, all it needs to do is to read the next transaction off of the queue and execute it.&lt;/p&gt;


&lt;h2&gt;Datomic treats the database as one huge immutable map&lt;/h2&gt;

&lt;p&gt;Or, well, not a &lt;em&gt;map&lt;/em&gt;, it&apos;s actually a sorted set implemented as a persistent red-black tree traversed with a O(log n) binary search. And I&apos;m actually not 100% sure about the red-black tree part. It&apos;s a proprietary database, so self taught programmers like myself have no chance of grokking the details completely.&lt;/p&gt;

&lt;p&gt;To run a query, you ask the Database connection &quot;give me the current database&quot;. What you get back, is a single data structure, a &quot;db&quot;, that represents your entire database, consistently, at a point in time.&lt;/p&gt;

&lt;p&gt;As mentioned, queries are executed on the client, not the server. This &quot;entire database value&quot; is of course lazy, backed by a LRU cache, so you only actually pull in the subset that you operate on.&lt;/p&gt;



&lt;h2&gt;Datomic has time travel built in&lt;/h2&gt;

&lt;p&gt;You can ask the database about its current value and get a immutable value back, representing the entire database.&lt;/p&gt;

&lt;p&gt;Then, you can say &quot;give me the database as of &amp;lt;timestamp&amp;gt;&quot;. What you get back, is another immutable value, representing your entire database, &lt;em&gt;at a previous point in time&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This is one of those things that is so utterly sexy and useful and absolutely a killer feature that you&apos;ll end up loving so much you&apos;ll have problems using other databases.&lt;/p&gt;

&lt;p&gt;At no point can you say &quot;damn, I wonder what actually happened yesterday at 22:30 when nobody was in the office&quot;. With Datomic, all you need to do is to ask &quot;give me the database as of 22:30 yesterday&quot;. Zomg!&lt;/p&gt;

&lt;p&gt;I can&apos;t praise this feature enough. And honestly, it&apos;s difficult to explain how awesome it is until you&apos;ve become familiar with it yourself. It&apos;s one of those things that seems &lt;em&gt;so&lt;/em&gt; fundamental, and there&apos;s no going back.&lt;/p&gt;

&lt;p&gt;Wait, you actually &lt;em&gt;consistently delete data en masse from your customers database&lt;/em&gt;? Are you &lt;em&gt;crazy&lt;/em&gt;?&lt;/p&gt;



&lt;h2&gt;Datomic has &quot;what if&quot; for all database operations&lt;/h2&gt;

&lt;p&gt;As I&apos;ve mentioned, a write is represented as a single operation that you ship off to the Datomic server.&lt;/p&gt;

&lt;p&gt;When you have one of those writes available, you can also ask Datomic for a database (the current one, or any previous version) and say &quot;give me a database as if this transaction/write was applied&quot;.&lt;/p&gt;

&lt;p&gt;This is also kind of difficult to adequately communicate so that you&apos;ll grasp how completely awesome this is&lt;/p&gt;

&lt;p&gt;Since all your queries run against a &quot;db&quot;, an immutable value representing your entire database, your codebase typically ends up having a lot of methods/functions that run their business logic by getting a &quot;db&quot; passed in.&lt;/p&gt;

&lt;p&gt;So let&apos;s say you have a method like that for generating a report, or maybe running some analytics and doing some number crunching.&lt;/p&gt;

&lt;p&gt;With Datomic, you can use &lt;em&gt;the exact same function&lt;/em&gt; and run it against a &quot;what if&quot; database - it doesn&apos;t care if the database value is a &quot;real&quot; one from the Datomic server or a &quot;what if&quot; one generated locally!&lt;/p&gt;




&lt;h2&gt;Datomic lets you query the history of your database&lt;/h2&gt;

&lt;p&gt;I won&apos;t go into details, since this can get quite technical, but I&apos;ve &lt;a href=&quot;http://augustl.com/blog/2013/querying_datomic_for_history_of_entity/&quot;&gt;written a separate article about this subject&lt;/a&gt; which goes into detail how this works.&lt;/p&gt;


&lt;h2&gt;Datomic has actually invented very little from scratch&lt;/h2&gt;

&lt;p&gt;Covering indices are nothing new, you can do that with plain old SQL databases. VoltDB is also single threaded and has a similar queue based transaction API without round-trips. I haven&apos;t talked about Datomic&apos;s periodic merging and live merging of the transaction log, but that is borrowed from Google BigTable.&lt;/p&gt;


&lt;h2&gt;That&apos;s nice. So why the heck are so few people using Datomic?&lt;/h2&gt;

&lt;p&gt;I think there are a number of reasons.&lt;/p&gt;

&lt;p&gt;Datomic is a proprietary database. I&apos;ve used it in production without paying a dime. There is a free version of Datomic and it works just fine for startups and smaller apps without a gazillion users. But developers generally find proprietary systems off-putting, so it has some difficulty in gaining traction because of that.&lt;/p&gt;

&lt;p&gt;Datomic is made by Cognitect, and I suspect that they want to stay small. I suspect that they have a few 100s of Datomic customers, and that&apos;s more than enough for them to make Datomic a sustainable business. It is actually possible for companies to &lt;em&gt;not&lt;/em&gt; want to grow - I know, I&apos;m an owner and stakeholder in such a company, &lt;a href=&quot;http://kodemaker.no&quot;&gt;Kodemaker&lt;/a&gt; (in fact, all of us are owners and stakeholders, that&apos;s the model).&lt;/p&gt;

&lt;p&gt;But most importantly, I think: Datomic solves a lot of problems I didn&apos;t know I had, until I learned Datomic.&lt;/p&gt;

&lt;p&gt;I didn&apos;t learn Datomic because its promises of time travel or separation of reads and writes and what not. I learned Datomic because I was passionate about Clojure, and was pretty open to whatever Rich Hickey created. So I toyed with it, wrote some dummy stuff, and quickly began to love to hate other databases. A lot of the problems I had at work with our RDBMS actually weren&apos;t fundamental problems! And now, at least once or twice a week, I encounter a problem that Datomic would have solved at a fundamental level.&lt;/p&gt;

&lt;p&gt;I blogged some about Datomic a couple of years ago, and this post (i hope) marks the first of many blog posts about Datomic I&apos;ll write in 2016. I have no problems calling myself a Datomic evangelist. I would quite frankly like to see an open source alternative, or at least &lt;em&gt;one&lt;/em&gt; other database similar to Datomic, and for now the best way of achieving that (short if making one) is to evangelize Datomic and make developers around the world aware of the problems you don&apos;t have to solve yourself.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;Here&apos;s to an ACID future where databases are immutable values and time travel is for free!&lt;/p&gt;</content></entry><entry><title>Datomic: Direct index lookup</title><updated>2013-07-17T00:00:00.000+02:00</updated><author><name>August Lilleaas</name></author><link href="http://augustl.com/blog/2013/datomic_direct_index_lookup/" /><id>urn:augustl-com:feed:post::blog:2013:datomic_direct_index_lookup</id><content type="html">&lt;p&gt;A couple of years ago, I read about someone whose MySQL performance bottleneck was SQL parsing. Most of the queries were straightforward primary key lookups, so the author wrote code that called InnoDB (MySQL&apos;s storage engine) directly and removed the bottleneck. I thought this was really cool, and I feel like I&apos;ve reached a small milestone today for doing the same thing myself, only in Datomic.&lt;/p&gt;

&lt;p&gt;In our system, we have a querying API that lists &lt;em&gt;everything&lt;/em&gt; if an empty query is passed. This means we need to ask datomic for potentially 1000s of entities, and also make it happen in a reasonable amount of time. In the future, we we&apos;ll probably be more clever and lazily load stuff, but I wanted to see if there was a quick fix, since lazy loading in the UI takes a lot of work to get right.&lt;/p&gt;

&lt;p&gt;Here&apos;s the old code:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;datomic.api&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;public-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/entity&lt;/span&gt;
      &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ffirst&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/q&lt;/span&gt;
          &lt;span class=&quot;o&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?attendant&lt;/span&gt;
            &lt;span class=&quot;ss&quot;&gt;:in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?pub-id&lt;/span&gt;
            &lt;span class=&quot;ss&quot;&gt;:where&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?attendant&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:attendant/public-id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?pub-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
          &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;
          &lt;span class=&quot;nv&quot;&gt;pub-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))))&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;public-ids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The attribute &lt;code class=&quot;highlight&quot;&gt;:attendant/public-id&lt;/code&gt; is a string attribute where we insert Datomic squuids. We only expose these IDs to the outside world, the Datomic entity IDs stay internal to Datomic.&lt;/p&gt;

&lt;p&gt;In the extreme cases, 2600 such public IDs is what we mapped over, resulting in 2600 queries being performed. This took about &lt;strong&gt;1 second&lt;/strong&gt;, and was not acceptable. The solution is to not query at all, but just look up the value in the Datomic index. This ended up taking about &lt;strong&gt;0.1 seconds&lt;/strong&gt;, so a ten-fold performance increase. Not bad at all! Here&apos;s the updated code:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; Yields exactly the same output as the above query&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;public-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/entity&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:e&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;first &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/datoms&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:avet&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:attendant/public-id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;public-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))))&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;public-ids&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Datomic has multiple indexes, and all of them are full copies of the data. So looking things up in an index is the only thing you need to do in order to access the data, and this is what the Datomic query engine does under the hood anyway, since all your data in the database is available to you as an immutable (and lazy) value.&lt;/p&gt;

&lt;p&gt;The code to &lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;nv&quot;&gt;d/datoms&lt;/span&gt;&lt;/code&gt; flows as following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Pass the datomic db whose indexes we want to look up.&lt;/li&gt;
  &lt;li&gt;Pass the index to use. We want the &lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;ss&quot;&gt;:avet&lt;/span&gt;&lt;/code&gt; index. (Attribute, value, entity, transaction)
    &lt;ul&gt;&lt;li&gt;The following arguments are the &lt;em&gt;components&lt;/em&gt; to look up in the index.&lt;/li&gt;&lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;The first entry in &lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;ss&quot;&gt;:avet&lt;/span&gt;&lt;/code&gt; is &lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;ss&quot;&gt;:a&lt;/span&gt;&lt;/code&gt;. So we pass &lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;ss&quot;&gt;:attendant/public-id&lt;/span&gt;&lt;/code&gt;, the attribute we want to look up.&lt;/li&gt;
  &lt;li&gt;The second entry in &lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;ss&quot;&gt;:avet&lt;/span&gt;&lt;/code&gt; is &lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;ss&quot;&gt;:v&lt;/span&gt;&lt;/code&gt;. So we pass &lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;nv&quot;&gt;public-id&lt;/span&gt;&lt;/code&gt;, the value we want to look up.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This will give us a raw seq of facts. Since we know that the attribute &lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;ss&quot;&gt;:attendant/public-id&lt;/span&gt;&lt;/code&gt; is set to unique, we can just get the &lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt; one.&lt;/p&gt;

&lt;p&gt;Aside: We could also just have passed one component, the attribute, and gotten back a sequence of facts for all facts for that attribute.&lt;/p&gt;

&lt;p&gt;When we have our fact, we extract the entity ID from it. The fact is map-like, so all we need to do is &lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;fact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then we proceed as normal, and look up the full aggregated entity just as we do with the entity IDs we get from Datomic query results.&lt;/p&gt;

&lt;p&gt;So there you go! Again, we should probably be more clever and lazy load stuff, but we just wanted a quick fix, and a ten-fold performance increase is more than good enough for now.&lt;/p&gt;</content></entry><entry><title>Ordering of &lt;code&gt;:db.cardinality/many&lt;/code&gt; attributes in Datomic</title><updated>2013-05-04T00:00:00.000+02:00</updated><author><name>August Lilleaas</name></author><link href="http://augustl.com/blog/2013/ordering_cardinality_many_in_datomic/" /><id>urn:augustl-com:feed:post::blog:2013:ordering_cardinality_many_in_datomic</id><content type="html">&lt;p&gt;In this post, I assume you already know about Datomic, how to use it, and the difference of the two settings for &lt;code class=&quot;highlight&quot;&gt;:db/cardinality&lt;/code&gt; on an attribute.&lt;/p&gt;

&lt;h2&gt;Tree structures and ordering&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlight&quot;&gt;:db/cardinality.many&lt;/code&gt; values are sets. One of the big differences between Datomic and in a typical RDBMS, is that Datomic uses tree structures for pretty much everything. In a RDBMS you typically get insertion order when querying without specifying what to sort on, but Datomic can&apos;t do that since a tree structure isn&apos;t ordered in the same way. &lt;/p&gt;

&lt;p&gt;Your business logic might require ordering, though, so to achieve this you need to add a &lt;code class=&quot;highlight&quot;&gt;:position&lt;/code&gt; attribute to your entities. So the question becomes: how to we consistently and safely manage this &lt;code class=&quot;highlight&quot;&gt;:position&lt;/code&gt; attribute in a way that ensures we don&apos;t get inconsistent values in a multi-peer world?&lt;/p&gt;

&lt;h2&gt;Transaction functions to the rescue&lt;/h2&gt;

&lt;p&gt;In any situation where multi-peer consistensy is required, a &lt;a href=&quot;http://docs.datomic.com/database-functions.html&quot;&gt;transaction function&lt;/a&gt; is the solution. Most of the developers I&apos;ve spoken to that aren&apos;t familiar with Datomic despises the idea of storing and executing code on the database server. But since I&apos;m in Clojure land in my system, and the transactor is too, it doesn&apos;t really feel as foreign as PLSQL might do, and since Datomic is free of time complexities, maintaining and synchronizing changes in the transaction functions is pretty much hassle free.&lt;/p&gt;

&lt;p&gt;Here&apos;s an example of a transaction entry that adds a transaction function for ordering to the transactor.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db/id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db.part/user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:db/ident&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:append-position-in-scope&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:db/doc&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Atomically adds to the end of a list of sorted cardinality/many lists&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:db/fn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db/fn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:lang&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;clojure&amp;quot;&lt;/span&gt;
                 &lt;span class=&quot;ss&quot;&gt;:params&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scope-id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scope-attr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;new-id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pos-attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                 &lt;span class=&quot;ss&quot;&gt;:code&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;children &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;scope-attr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;datomic.api/entity&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scope-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                             &lt;span class=&quot;nv&quot;&gt;highest-pos&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;reduce max &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;map &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;pos-attr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))]&lt;/span&gt;
                         &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/add&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;new-id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;pos-attr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;inc &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;highest-pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]])}}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The entities at work here is a &amp;quot;todo list&amp;quot; that has a &lt;code class=&quot;highlight&quot;&gt;:db/cardinality.many&lt;/code&gt; attribute called &lt;code class=&quot;highlight&quot;&gt;:todolist/items&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlight&quot;&gt;children&lt;/code&gt; essentially translates to &lt;code class=&quot;highlight&quot;&gt;(:todolist/items todolist-entity)&lt;/code&gt;, and then we calculate the highest &lt;code class=&quot;highlight&quot;&gt;:position&lt;/code&gt;, &lt;code class=&quot;highlight&quot;&gt;pos-attr&lt;/code&gt;, currently in the list and increments that by one.&lt;/p&gt;

&lt;p&gt;On the peer side, we can now create a transaction for adding a new todo item and setting its position to be at the end of the list.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:todoitem/text&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;This needs to be done&amp;quot;&lt;/span&gt;
 &lt;span class=&quot;ss&quot;&gt;:db/id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;new-todo-item-tempid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:append-position-in-scope&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;todolist-eid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:todolist/items&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;new-todo-item-tempid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Since the Datomic transactor ensures consistency, the transaction function is guaranteed to expand to a &lt;code class=&quot;highlight&quot;&gt;:position&lt;/code&gt; value of the currently highest position in the list plus one. There&apos;s absolutely no risk of race conditions here.&lt;/p&gt;

&lt;h2&gt;Filling gaps&lt;/h2&gt;

&lt;p&gt;The other function I have for working with sorted sets is a function that I use when I delete an individual item, to fill in the gaps.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db/id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db.part/user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:db/ident&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:reset-position-in-scope&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:db/doc&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Goes through existing positions and sequentializes them&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;ss&quot;&gt;:db/fn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db/fn&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:lang&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;clojure&amp;quot;&lt;/span&gt;
                 &lt;span class=&quot;ss&quot;&gt;:params&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scope-id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scope-attr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;retracted-eid&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;sorted-attr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                 &lt;span class=&quot;ss&quot;&gt;:code&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;let &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;children &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;scope-attr&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;datomic.api/entity&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scope-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
                             &lt;span class=&quot;nv&quot;&gt;sorted-children&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sort-by &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sorted-attr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                             &lt;span class=&quot;nv&quot;&gt;without-retracted&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;filter &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;not= &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;retracted-eid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                                                       &lt;span class=&quot;nv&quot;&gt;sorted-children&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;
                         &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map-indexed&lt;/span&gt;
                          &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;idx&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;entity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                           &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:db/add&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;scope-id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;sorted-attr&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])))}}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When a positioned entity is deleted, it would normally leave a gap in the position attribute. This function goes through all of what&apos;s left and sets the positions to 0 and up, with zero gaps. Note that this creates facts for _all_ the remaining entities. It could probably be improved to only state facts for the items from retracted-eid and below.&lt;/p&gt;

&lt;p&gt;Since this is always used when an entity is retracted, we need to pass in the entity that is being retracted as well, so we can remove it from the set. The transaction gets the database as of before the transaction commits, so even if we have a retract entity command in the transaction, this function won&apos;t know. So we manually remove that from the list.&lt;/p&gt;</content></entry><entry><title>Finding by ID (primary key) in Datomic</title><updated>2013-03-10T00:00:00.000+01:00</updated><author><name>August Lilleaas</name></author><link href="http://augustl.com/blog/2013/find_by_id_in_datomic/" /><id>urn:augustl-com:feed:post::blog:2013:find_by_id_in_datomic</id><content type="html">&lt;p&gt;Datomic has no essential type for entities, so how do you &lt;strong&gt;find the user with id X&lt;/strong&gt; with Datomic?&lt;/p&gt;

&lt;h2&gt;Finding by internal Datomic entity ID&lt;/h2&gt;

&lt;p&gt;All entities in datomic gets the attribute &lt;code class=&quot;highlight&quot;&gt;:db/id&lt;/code&gt;, which is Datomic&apos;s own primary key attribute. You might be surprised that &lt;code class=&quot;highlight&quot;&gt;(d/entity db 123)&lt;/code&gt; for an entity id that doesn&apos;t exists doesn&apos;t return nil.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;&amp;#39;datomic.api&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:as&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;&amp;#39;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-datomic-connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;non-existing-id&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;123123&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/entity&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;non-existing-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; Returns an actual entity&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; Even if there&amp;#39;s no entity with id 123123&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To actually find an entity by ID, you need to run a query.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; Assuming an entity with id 123 exists&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/q&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?eid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?eid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:where&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?eid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; HashSet [[123]]&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/q&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?eid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?eid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:where&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?eid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;non-existing-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; HashSet [[]]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Also, just for fun, we can create a reproducible example that doesn&apos;t depend on having an actual Datomic database around, since Datomic is able to query any data strucutre that consists of a list of lists.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; This is what an actual Datomic DB actually looks like.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;def &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;my-mock-db&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;123&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Test&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;123&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:email&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;test@test.com&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;456&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Foo&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;456&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:email&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Hello&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]])&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; A &amp;quot;normal&amp;quot; query, for good measure.&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/q&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?person&lt;/span&gt;
       &lt;span class=&quot;ss&quot;&gt;:in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?name&lt;/span&gt;
       &lt;span class=&quot;ss&quot;&gt;:where&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?person&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
     &lt;span class=&quot;nv&quot;&gt;my-mock-db&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Test&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; HashSet [[123]]&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/q&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?eid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?eid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:where&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?eid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-mock-db&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; HashSet [[123]]&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/q&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?eid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?eid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:where&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?eid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;my-mock-db&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;999&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;;; HashSet [[]]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;However, how do we know that we get an entity of the type we want? Datomic entities doesn&apos;t actually have a type, they&apos;re just entities with attributes, so we&apos;re at a loss.&lt;/p&gt;

&lt;h2&gt;Using manual &quot;public ID&quot; attributes&lt;/h2&gt;

&lt;p&gt;I like to have a public ID field per entity type in my system. Again, Datomic entities has no type, so this is all on us, Datomic doesn&apos;t care.&lt;/p&gt;

&lt;p&gt;I use a string type that&apos;s set to unique. Setting it to unique is obviously important. You could also use the built-in UUID type if you want to, but I find it more convenient to just work with strings.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;&lt;span class=&quot;c1&quot;&gt;;; Inserting data&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/transact&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;my-datomic-conn&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:person/name&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;Test&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:person/email&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;test@test.com&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:person/public-id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;java.util.UUID/randomUUID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;ss&quot;&gt;:db/id&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/tempid&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:db.part/user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}])&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;;; ...&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;d/q&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:find&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?person&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:in&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?pub-id&lt;/span&gt;
   &lt;span class=&quot;ss&quot;&gt;:where&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;?person&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:person/public-id&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;?pub-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
 &lt;span class=&quot;nv&quot;&gt;db&lt;/span&gt;
 &lt;span class=&quot;nv&quot;&gt;person-id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You get an entity type system for free. If you use a Datomic ID, how do you query for people entities only for &lt;tt&gt;GET /people/123&lt;/tt&gt;?&lt;/li&gt;
  &lt;li&gt;Datomic entity IDs stay internal, meaning you don&apos;t have to worry about possible entity ID changes when you import/export the database.&lt;/li&gt;
  &lt;li&gt;You don&apos;t have to convert from a string type to a numeric type when you get the entity ID from a URL. Not a very big deal, but since it&apos;s a very common use case I&apos;d say it counts.&lt;/li&gt;
&lt;/ul&gt;</content></entry><entry><title>Querying Datomic for the history of an entity</title><updated>2013-01-20T00:00:00.000+01:00</updated><author><name>August Lilleaas</name></author><link href="http://augustl.com/blog/2013/querying_datomic_for_history_of_entity/" /><id>urn:augustl-com:feed:post::blog:2013:querying_datomic_for_history_of_entity</id><content type="html">&lt;div class=&quot;update&quot;&gt;
    &lt;p&gt;This post has been rewritten and updated.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;https://augustl.com/blog/2019/datomic_querying_for_history_of_entity_v2/&quot;&gt;https://augustl.com/blog/2019/datomic_querying_for_history_of_entity_v2/&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;div id=&quot;original_post&quot; style=&quot;position: relative;&quot;&gt;

&lt;p&gt;The main reason for choosing Datomic for our project was our requirement to have a page that lists all the changes that ever occurred to a specific entity. This turned out to be a bit more tricky than I anticipated, but after I finally wrapped my head around the basics, the solution ended up being quite elegant.&lt;/p&gt;

&lt;p&gt;The first step is to get a hold of all the transactions related to the entity in question. This is done with a query on &lt;code class=&quot;highlight&quot;&gt;(datomic.api/history db)&lt;/code&gt;, a special version of the database containing the full history of assertions and retractions.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;(:require [&amp;#39;datomic.api :as &amp;#39;d])

(d/q
 &amp;#39;[:find ?e ?a ?v ?tx ?added
   :in $ ?e
   :where
   [?e ?a ?v ?tx ?added]]
  (datomic.api/history db)
  123123)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is the generic query to find them all. This query returns the datoms in the history for the specified entity only.&lt;/p&gt;

&lt;p&gt;My actual query was a bit different though. I had the entity ID, and needed to find all datoms that touched a referred attribute for that entity. So simply finding all transactions for my entity would not suffice. Here&apos;s a short summary of my schema:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;;; Attendants

 {:db/id #db/id[:db.part/db]
  :db/ident :attendant/public-id
  :db/valueType :db.type/string
  :db/cardinality :db.cardinality/one
  :db.install/_attribute :db.part/db}

 {:db/id #db/id[:db.part/db]
  :db/ident :attendant/attributes
  :db/valueType :db.type/ref
  :db/cardinality :db.cardinality/many
  :db.install/_attribute :db.part/db}

 ;; Attendant attributes

 {:db/id #db/id[:db.part/db]
  :db/ident :attendant-attr/value
  :db/valueType :db.type/string
  :db/cardinality :db.cardinality/one
  :db.install/_attribute :db.part/db}

 {:db/id #db/id[:db.part/db]
  :db/ident :attendant-attr/schema-attr
  :db/valueType :db.type/ref
  :db/cardinality :db.cardinality/one
  :db.install/_attribute :db.part/db}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The entity I&apos;m tracking is the attendant, but the attributes of an attendant isn&apos;t stored directly on the attendant entity. The attributes are a reference, in order to join the attribute values to the schema-attr for the event the attendant belongs to. The user of the system is able to specify, per event, the attributes that are available to an attendant, so this join maintains data integrity.&lt;/p&gt;

&lt;p&gt;Here&apos;s the actual query we run:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;(d/q
 &amp;#39;[:find ?tx ?attr ?added
   :in $ ?attendant
   :where
   [?attendant :attendant/attributes ?attr]
   [?attr _ _ ?tx ?added]]
  (d/history db)
  123123)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This gets all the transactions that touches the attribute &lt;code class=&quot;highlight&quot;&gt;:attendant/attributes&lt;/code&gt; for the specified attendant.&lt;/p&gt;

&lt;p&gt;And this is where the fun begins.&lt;/p&gt;

&lt;p&gt;The next step is to take the tuples of entity IDs we get from the query, and turn it into a full changeset of entities we can use to display the list of changes to the user. Essentially, we want to return a list of maps, where each map contains the timestamp of the change, and the actual list of changes. We also need to handle all the cases: an attribute being created for the first time, and attribute changing from something to something else, and an attribute being removed entirely. As a word of caution, this means we have to be careful when we create the actual transactions for our attendant, and only create transactions for the attributes that actually change, and make sure we retract facts when fields are blanked out. What we&apos;re about to do wouldn&apos;t be possible if the blanking out of a field simply blanked out the attribute in the database. We need to have a clean transaction history.&lt;/p&gt;

&lt;p&gt;We extract the entities for the changeset with a &lt;code class=&quot;highlight&quot;&gt;map&lt;/code&gt; over the result of the query above.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;highlight&quot;&gt;(map
 (fn [[tx attr added]]
   (let [tx-db (d/as-of db tx)]
     {:tx (d/entity tx-db tx)
      :attr (if added
              (d/entity tx-db attr)
              (d/entity (d/as-of db (dec (d/tx-t tx))) attr))
      :added added}))
 result-of-transaction-query)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It&apos;s important to make sure you use the database as of the transaction inside the map. If you just use &lt;code class=&quot;highlight&quot;&gt;db&lt;/code&gt; as of now (specifically, the same db as you used when querying for all the transactions just before), you&apos;ll get the entities as of &lt;em&gt;now&lt;/em&gt;, which you don&apos;t want. So in the map, we make sure to always use &lt;code class=&quot;highlight&quot;&gt;(db/as-of db tx)&lt;/code&gt; when we retrieve entities, so we get the entity as of that transaction. This &quot;small&quot; detail caused me lots of pain and trouble when implementing this feature, so make sure you get it right.&lt;/p&gt;

&lt;p&gt;I haven&apos;t explained what &lt;code class=&quot;highlight&quot;&gt;added&lt;/code&gt; actually means. In the history db we queried for transactions, &lt;code class=&quot;highlight&quot;&gt;?added&lt;/code&gt; is a boolean that tells us whether the datom was an assertion or a retraction.&lt;/p&gt;

&lt;p&gt;If the datom was an &lt;strong&gt;assertion&lt;/strong&gt; to the database, the entity is just the entity as of the transaction.&lt;/p&gt;

&lt;p&gt;If the datom was a &lt;strong&gt;retraction&lt;/strong&gt;, the entity as of the transaction will (perhaps surprisingly) not have any attributes other than the entity id. When we show the details of the change to the user, we want to show what the old values for the entity were, so we need to know what the entity looked like prior to deletion. Thankfully, Datomic doesn&apos;t delete any old data, even when we retract entities from the database. So to get the values, we simply get the entity as of the transaction &lt;em&gt;before&lt;/em&gt; the transaction where the entity was retracted. &lt;code class=&quot;highlight&quot;&gt;d/tx-t&lt;/code&gt; is used to turn our transaction entity id into a timestamp, and &lt;code class=&quot;highlight&quot;&gt;dec&lt;/code&gt; decrements it by one. So our call to &lt;code class=&quot;highlight&quot;&gt;d/as-of&lt;/code&gt; gets the instant just before the transaction, leaving us with the database as it was the moment before our transaction occurred.&lt;/p&gt;

&lt;p&gt;Note: I&apos;m not entirely sure how safe this is; it&apos;s probably better to somehow get the transaction entity ID from before. For all I know, there was a large amount of writes going on at the time of our transaction, so the &lt;code class=&quot;highlight&quot;&gt;dec&lt;/code&gt; of the transaction instant might be inaccurate.&lt;/p&gt;

&lt;p&gt;Phew! We now have a list of &lt;code class=&quot;highlight&quot;&gt;{:tx tx-entity :attr attr-entity :added true/false}&lt;/code&gt;. I&apos;ll leave it as an exercise to the reader to morph this list into something you can show to your users. Essentially, you need to group by &lt;code class=&quot;highlight&quot;&gt;:tx&lt;/code&gt;, sort by the transactions &lt;code class=&quot;highlight&quot;&gt;(:db/txInstant tx)&lt;/code&gt; (all transactions automatically gets a timestamp associated with them by Datomic), and traverse the data to figure out which combination of &lt;code class=&quot;highlight&quot;&gt;added&lt;/code&gt; you have in order to determine whether the transaction added, updated or removed the value.&lt;/p&gt;

&lt;p&gt;We&apos;re really happy with Datomic so far. We haven&apos;t hit production yet, so I&apos;m sure this query will be tweaked even further (we already know we want to add more stuff, such as tracking the user that made the change). But the fact that this feature is just a query away is very convenient indeed.&lt;/p&gt;


&lt;div id=&quot;original_post_fade&quot; class=&quot;original-post-fade&quot;&gt;&lt;/div&gt;
&lt;div id=&quot;original_post_button&quot; class=&quot;original-post-button&quot;&gt;
    &lt;div class=&quot;original-post-wrapper&quot;&gt;
        &lt;a href=&quot;#&quot; id=&quot;original_post_button_real&quot; class=&quot;original-post-button-link&quot;&gt;Show full article&lt;/a&gt;
        &lt;div style=&quot;width: 20px&quot;&gt;&lt;/div&gt;
        &lt;a href=&quot;https://augustl.com/blog/2019/datomic_querying_for_history_of_entity_v2/&quot; class=&quot;original-post-button-link&quot;&gt;Go to updated article&lt;/a&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;script&gt;
    var op = document.getElementById(&quot;original_post&quot;)
    op.style.height = &quot;200px&quot;
    op.style.overflow = &quot;hidden&quot;

    var opf = document.getElementById(&quot;original_post_fade&quot;)
    opf.style.display = &quot;block&quot;

    var opb = document.getElementById(&quot;original_post_button&quot;)
    opb.style.display = &quot;block&quot;

    var opbr = document.getElementById(&quot;original_post_button_real&quot;)
    opbr.addEventListener(&quot;click&quot;, function (e) {
        e.preventDefault()
        opf.parentNode.removeChild(opf)
        opb.parentNode.removeChild(opb)
        op.style.height = &quot;auto&quot;
        op.style.overflow = &quot;auto&quot;
    })
&lt;/script&gt;</content></entry></feed>