<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <generator>Hugo -- gohugo.io</generator>
  <link href="https://joshuadavey.com/" rel="alternate" type="text/html"/>
  <link href="https://joshuadavey.com/atom.xml" rel="self" type="application/atom+xml"/>
  <updated>2023-06-22T13:00:00+00:00</updated>
  <id>https://joshuadavey.com/</id>
  <title type="html">Joshua Davey</title>
  <author>
    <name>Joshua Davey</name>
  </author>
  <entry>
    <title type="html">Social Offboarding</title>
    <link href="https://joshuadavey.com/2023/06/22/social-offboarding/" rel="alternate" type="text/html" title="Social Offboarding"/>
    <published>2023-06-22T13:00:00+00:00</published>
    <updated>2023-06-22T13:00:00+00:00</updated>
    <id>https://joshuadavey.com/2023/06/22/social-offboarding/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2023/06/22/social-offboarding/"><![CDATA[<p>Few people would dispute the importance of onboarding new members of a
team. Getting to know the other people you&rsquo;ll be working with is vital
to becoming part of a team. This is often done a number of ways, but
at the very least involves introductions and informal get-to-know-you
icebreakers. Depending on the position, it can also involve training
sessions, one-on-one conversations, happy hour meetups or other
social events. Even when the stated topic is about &ldquo;work&rdquo; stuff, good
managers allow and even encourage informal chatting.</p>
<p>If a team is a machine, then socializing is the grease that keeps it
moving fluidly.</p>
<p>I&rsquo;ve remotely for 8 years, for 5 different companies. The kind of
social onboarding that I mention above has been a part of each
position, even when it happens organically. Given how important
socializing is to team health, it should not come as a surprise that
the places that have emphasized social onboarding have also been the
ones that I started contributing to most quickly.</p>
<p>Bringing a new person into a team changes that team fundamentally,
and the healthiest teams I&rsquo;ve been a part of seem to understand that,
emphasizing this social onboarding in ways both subtle and overt.</p>
<p>When a person leaves a company, be it because of downsizing, accepting
an offer at another company, or even just an internal reorganization,
the same priority to the social is not always given. Indeed, even as
projects are handed off, and any HR offboarding procedure is executed
carefully, few teams give adequate attention the <em>social</em> offboarding.</p>
<p>One the most helpful traditions that I&rsquo;ve come to embrace when someone
leaves a team has been a &ldquo;social offboarding&rdquo; call with the team.
Given that I&rsquo;ve been on remote teams, this has looked like an informal
Zoom call and has been a great way to say goodbye, talk about &ldquo;what&rsquo;s
next&rdquo;, and gain some sense of closure. And when I&rsquo;ve been the one
leaving, it has served as a nice palette cleanser amid all of the HR
paperwork and other formalized processes necessary when one leaves a
company.</p>
<h2 id="how-does-this-get-set-up">
  How does this get set up?
  <a class="heading-link" href="#how-does-this-get-set-up">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Depending on the circumstances that a person is leaving, it might just
be a normal Zoom (or whatever) meeting that you make and invite all
the teammates to. But if access to internal tools has been revoked,
this may involve treating the leaving employee like an external
entity, and invite them to a meeting as such. At the very least,
ensuring that you have personal contact information makes this a
possibility.</p>
<h2 id="who-should-come">
  Who should come?
  <a class="heading-link" href="#who-should-come">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>This has usually involved anyone that I worked with every day. If you
are involved in a daily standup meeting, for example, that might be a
good place to start with the &ldquo;guest list&rdquo;. Not everyone feels
comfortable being candid with higher-ups, so use your discretion on
who the team and employee would feel comfortable with.</p>
<p>Of course, you can always as the person leaving who they would <em>like</em>
to have on the call.</p>
<h2 id="what-do-we-say">
  What do we say?
  <a class="heading-link" href="#what-do-we-say">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Given that this is usually an informal conversation, the folks on the
call can discuss whatever makes sense. However, as a guideline, I&rsquo;d
caution against discussing the terms of the end of the employment or
other topics that could get you in hot water. If the person leaving is
going to another position, they might give some details about what
drew them to the new place, but steer clear of why they are leaving
this team.</p>
<p>This might be a time to reminisce about projects people worked on
together, accomplishments you are proud of, or just re-tell funny
stories. Again, this should be an informal call.</p>
<p>If, however, you are hoping for more structure, here&rsquo;s an approach
I&rsquo;ve taken at several places when I&rsquo;ve left: I offer positive
affirmations about the specific people on the call. I&rsquo;ll address each
person individually, naming the things I appreciate about them as
coworkers or what I may miss about not being on their team anymore.</p>
<p>Here&rsquo;s an illustration using completely made up people and positions:</p>
<pre><code>Alice, I've learned so much from your deep knowledge of Bells, and
your work with Whistles has been inspiring. The team is so lucky
to have you, and I'll miss learning from you.

Bob, you were the first person that reached out to me when I was
new here, and you've never ceased encouraging me even while I
learned the ropes. You've been a great teammate, and I'll miss
having you in my corner.

Charlotte, thank you for trusting me enough to experiment on the
Widget project, even though it was clearly in your area of
expertise. You helped me when I got stuck, and I'm so appreciative.
</code></pre>
<p>People, in general, seem to like when you compliment them, and even
more so when you&rsquo;re able to call out specifics. Every time I&rsquo;ve used
this format, the people on the call have been quite receptive. It&rsquo;s
been a helpful, perhaps even therapeutic, way to spend the last few
minutes of time with these people as teammates.</p>
<p>And when someone else has been the one leaving, this kind of call has
been helpful to me and the rest of the team to have a sense of
closure, and we leave the call with positive vibes.</p>
]]></content>
  </entry>
  <entry>
    <title type="html">Introducting clj-pgcopy</title>
    <link href="https://joshuadavey.com/2019/12/01/introducing-clj-pgcopy/" rel="alternate" type="text/html" title="Introducting clj-pgcopy"/>
    <published>2019-12-01T00:00:00+00:00</published>
    <updated>2019-12-01T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2019/12/01/introducing-clj-pgcopy/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2019/12/01/introducing-clj-pgcopy/"><![CDATA[<p>On a recent project, I was working on an ETL process, where
the transform step happened in Clojure. After all of the data
had been shaped the way we wanted, we imported the data into a
Postgresql database. As with our typical pattern, we used the
<code>clojure.java.jdbc/insert-multi!</code> function to insert rows in batches.
But the performance for the database insertion wasn&rsquo;t quite what we
were hoping for.</p>
<p>Although the overhead from <code>org.clojure.jdbc</code> is negligible in most
scenarios, and certainly in virtually all CRUD-like workflows, it can
become significant with batch insertions at scale: all of the work of
converting data types to String-y parameters adds up.</p>
<p>Having previously used the <code>COPY</code> syntax for importing textual
data like CSVs, I found that Postgresql&rsquo;s <code>COPY</code> also has a binary
variant that allows direct insertion into tables. Because the binary
on-the-wire representation of Postgresql&rsquo;s data types is virtually
the same as the storage representation, there is significantly less
overhead for importing using <code>COPY ... FROM STDIN WITH BINARY</code>.</p>
<p>So, there are immediately two benefits to using the binary <code>COPY</code> approach:</p>
<ol>
<li>The binary on-the-wire representation weighs less than its textual,
<code>INSERT</code>-based counterpart.</li>
<li>The Postgres server has less work to do to extract data from a
binary <code>COPY</code> than it does with <code>INSERT</code> statement because the
binary representation so closely mirrors the internal storage
representation.</li>
</ol>
<p>There may be other benefits I&rsquo;m unaware of, but those two things alone
made me curious if there was a way to get the <code>COPY</code> goodness with
idiomatic Clojure.</p>
<p>Both main postgresql jdbc drivers, <a href="https://jdbc.postgresql.org/"  class="external-link" target="_blank" rel="noopener">https://jdbc.postgresql.org/</a> and
<a href="https://impossibl.github.io/pgjdbc-ng/"  class="external-link" target="_blank" rel="noopener">https://impossibl.github.io/pgjdbc-ng/</a>, support putting a connection
into COPY mode, after which data can be streamed into or out of
postgresql. However, both drivers don&rsquo;t do anything to help you get
the data into the raw format that postgresql expects. For the text
(default) format, that&rsquo;s basically a tab-delimited or CSV payload.</p>
<p>The binary COPY format, however, is a different beast entirely. Each
postgresql data type has a very specific binary representation. Again,
while the postgresql jdbc drivers provide a way to stream in or stream
out COPY data, the data within those streams is left entirely up to
you. The best library I found to do this kind of serialization was
<a href="https://github.com/bytefish/PgBulkInsert"  class="external-link" target="_blank" rel="noopener">PgBulkInsert</a>, which works with JDBC, but has a very Java-centric
API.</p>
<p>That&rsquo;s where <a href="https://github.com/jgdavey/clj-pgcopy"  class="external-link" target="_blank" rel="noopener">clj-pgcopy</a> comes in. It maps common data types to
their binary represenation, with the hope that using the binary COPY
style of importing data is as easy to use as clojure.java.jdbc
methods. The primary API of clj-pgcopy is
<code>clj-pgcopy.core/copy-into!</code>, which aims to be drop-in compatible with
most places where <code>clojure.java.jdbc/insert-multi!</code> is being used.</p>
<h2 id="example-usage">
  Example usage
  <a class="heading-link" href="#example-usage">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>For example, let&rsquo;s say that we have a table of product listings,
looking like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#00a8c8">create</span> <span style="color:#00a8c8">table</span> <span style="color:#111">inventory</span> <span style="color:#111">(</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">guid</span> <span style="color:#111">uuid</span> <span style="color:#00a8c8">primary</span> <span style="color:#00a8c8">key</span><span style="color:#111">,</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">created_at</span> <span style="color:#111">timestamptz</span> <span style="color:#00a8c8">not</span> <span style="color:#00a8c8">null</span><span style="color:#111">,</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">active</span> <span style="color:#111">boolean</span> <span style="color:#00a8c8">not</span> <span style="color:#00a8c8">null</span> <span style="color:#00a8c8">default</span> <span style="color:#00a8c8">false</span><span style="color:#111">,</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">price</span> <span style="color:#111">decimal</span><span style="color:#111">(</span><span style="color:#ae81ff">8</span><span style="color:#111">,</span><span style="color:#ae81ff">2</span><span style="color:#111">),</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">average_rating</span> <span style="color:#111">float4</span>
</span></span><span style="display:flex;"><span><span style="color:#111">);</span>
</span></span></code></pre></div><p>And some data we&rsquo;d like to import that looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">def </span><span style="color:#111">data</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">[{</span><span style="color:#d88200">:guid</span> <span style="color:#f92672">#</span><span style="color:#111">uuid</span> <span style="color:#d88200">&#34;d44c2977-0a9f-4d12-88d2-7d85e07ce1e2&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#d88200">:created_at</span> <span style="color:#f92672">#</span><span style="color:#111">inst</span> <span style="color:#d88200">&#34;2019-12-01T23:37:33.701-00:00&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#d88200">:active</span> <span style="color:#111">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#d88200">:price</span> <span style="color:#ae81ff">998.49</span><span style="color:#111">M</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#d88200">:average_rating</span> <span style="color:#ae81ff">3.3</span><span style="color:#111">}</span>
</span></span><span style="display:flex;"><span>   <span style="color:#111">{</span><span style="color:#d88200">:guid</span> <span style="color:#f92672">#</span><span style="color:#111">uuid</span> <span style="color:#d88200">&#34;220603d4-c1b9-4ea4-b5f4-c61a38e9f515&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#d88200">:created_at</span> <span style="color:#f92672">#</span><span style="color:#111">inst</span> <span style="color:#d88200">&#34;2019-12-01T16:22:35.826-00:00&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#d88200">:active</span> <span style="color:#111">false</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#d88200">:price</span> <span style="color:#ae81ff">847.90</span><span style="color:#111">M</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#d88200">:average_rating</span> <span style="color:#ae81ff">2.1</span><span style="color:#111">}])</span>
</span></span></code></pre></div><p>A typical way to import data with <code>clojure.java.jdbc/insert-multi!</code> would look like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">let </span><span style="color:#111">[</span><span style="color:#111">cols</span> <span style="color:#111">[</span><span style="color:#d88200">:guid</span> <span style="color:#d88200">:created_at</span> <span style="color:#d88200">:active</span> <span style="color:#d88200">:price</span> <span style="color:#d88200">:average_rating</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">-&gt;tuple</span> <span style="color:#111">(</span><span style="color:#111">apply </span><span style="color:#111">juxt</span> <span style="color:#111">cols</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#75af00">jdbc/with-db-connection</span> <span style="color:#111">[</span><span style="color:#111">conn</span> <span style="color:#111">conn-spec</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">(</span><span style="color:#75af00">jdbc/insert-multi!</span> <span style="color:#111">conn</span> <span style="color:#d88200">:inventory</span> <span style="color:#111">cols</span> <span style="color:#111">(</span><span style="color:#111">map </span><span style="color:#111">-&gt;tuple</span> <span style="color:#111">data</span><span style="color:#111">))))</span>
</span></span></code></pre></div><p>To use <code>clj-pgcopy</code>, the only thing that needs to change is adding a
<code>require</code>, and changing the callsite:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#75af00">require</span> <span style="color:#f92672">&#39;</span><span style="color:#111">[</span><span style="color:#111">clj-pgcopy.core</span> <span style="color:#d88200">:as</span> <span style="color:#111">copy</span><span style="color:#111">])</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">let </span><span style="color:#111">[</span><span style="color:#111">cols</span> <span style="color:#111">[</span><span style="color:#d88200">:guid</span> <span style="color:#d88200">:created_at</span> <span style="color:#d88200">:active</span> <span style="color:#d88200">:price</span> <span style="color:#d88200">:average_rating</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">-&gt;tuple</span> <span style="color:#111">(</span><span style="color:#111">apply </span><span style="color:#111">juxt</span> <span style="color:#111">cols</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#75af00">jdbc/with-db-connection</span> <span style="color:#111">[</span><span style="color:#111">conn</span> <span style="color:#111">conn-spec</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">(</span><span style="color:#75af00">copy/copy-into!</span> <span style="color:#111">(</span><span style="color:#d88200">:connection</span> <span style="color:#111">conn</span><span style="color:#111">)</span> <span style="color:#d88200">:inventory</span> <span style="color:#111">cols</span> <span style="color:#111">(</span><span style="color:#111">map </span><span style="color:#111">-&gt;tuple</span> <span style="color:#111">data</span><span style="color:#111">))))</span>
</span></span></code></pre></div><p>Please note that <code>copy-into!</code> expects a &ldquo;raw&rdquo; JDBC connection, not a
Clojure map wrapping one, like <code>clojure.java.jdbc</code> uses.</p>
<h2 id="performance">
  Performance
  <a class="heading-link" href="#performance">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Using the same table as our above example, I did some non-definitive benchmarking. For most typical use-cases, <code>clj-pgcopy</code> should be a little more than twice as fast as <code>insert-multi</code>:</p>
<table>
  <thead>
      <tr>
          <th>tuples</th>
          <th>batch size</th>
          <th>insert-multi</th>
          <th>clj-pgcopy</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>10000</td>
          <td>100</td>
          <td>218.4 ms</td>
          <td>107.2 ms</td>
      </tr>
      <tr>
          <td>10000</td>
          <td>500</td>
          <td>205.2 ms</td>
          <td>90.35 ms</td>
      </tr>
      <tr>
          <td>50000</td>
          <td>100</td>
          <td>1.030 sec</td>
          <td>422.3 ms</td>
      </tr>
      <tr>
          <td>50000</td>
          <td>500</td>
          <td>1.272 sec</td>
          <td>382.3 ms</td>
      </tr>
      <tr>
          <td>100000</td>
          <td>100</td>
          <td>2.051 sec</td>
          <td>1.005 sec</td>
      </tr>
  </tbody>
</table>
<p>For measurement methodology, or to run your own benchmarks, see the
<a href="https://github.com/jgdavey/clj-pgcopy/blob/master/bench/clj_pgcopy/benchmark.clj"  class="external-link" target="_blank" rel="noopener">benchmark</a>
namespace of the <a href="https://github.com/jgdavey/clj-pgcopy"  class="external-link" target="_blank" rel="noopener">clj-pgcopy</a> repository.</p>
<h2 id="type-mappings">
  Type Mappings
  <a class="heading-link" href="#type-mappings">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Out of the box, clj-pgcopy supports many data types, with what I
consider reasonable default mappings.</p>
<h3 id="basic-type-mapping">
  Basic type mapping
  <a class="heading-link" href="#basic-type-mapping">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<table>
  <thead>
      <tr>
          <th>JVM type</th>
          <th>Postgres type</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Short</td>
          <td>int2 (aka smallint)</td>
      </tr>
      <tr>
          <td>Integer</td>
          <td>int4 (aka integer)</td>
      </tr>
      <tr>
          <td>Long</td>
          <td>int8 (aka bigint)</td>
      </tr>
      <tr>
          <td>Float</td>
          <td>float4 (aka real)</td>
      </tr>
      <tr>
          <td>Double</td>
          <td>float8 (aka double presicion)</td>
      </tr>
      <tr>
          <td>BigDecimal</td>
          <td>numeric/decimal</td>
      </tr>
      <tr>
          <td>Boolean</td>
          <td>boolean</td>
      </tr>
      <tr>
          <td>String</td>
          <td>text/varchar/char</td>
      </tr>
      <tr>
          <td>java.util.UUID</td>
          <td>uuid</td>
      </tr>
  </tbody>
</table>
<h3 id="date-related-mappings">
  Date-related mappings
  <a class="heading-link" href="#date-related-mappings">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<table>
  <thead>
      <tr>
          <th>JVM type</th>
          <th>Postgres type</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>java.sql.Date</td>
          <td>date</td>
      </tr>
      <tr>
          <td>java.time.LocalDate</td>
          <td>date</td>
      </tr>
      <tr>
          <td>java.util.Date</td>
          <td>timestamp[tz]</td>
      </tr>
      <tr>
          <td>java.sql.Timestamp</td>
          <td>timestamp[tz]</td>
      </tr>
      <tr>
          <td>java.time.Instant</td>
          <td>timestamp[tz]</td>
      </tr>
      <tr>
          <td>java.time.ZonedDateTime</td>
          <td>timestamp[tz]</td>
      </tr>
      <tr>
          <td>java.time.OffsetDatetime</td>
          <td>timestamp[tz]</td>
      </tr>
      <tr>
          <td>org.postgres.util.PGInterval</td>
          <td>interval</td>
      </tr>
  </tbody>
</table>
<h3 id="geometric-mappings">
  Geometric mappings
  <a class="heading-link" href="#geometric-mappings">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<table>
  <thead>
      <tr>
          <th>JVM type</th>
          <th>Postgres type</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>org.postgres.geometric.PGpoint</td>
          <td>point</td>
      </tr>
      <tr>
          <td>org.postgres.geometric.PGline</td>
          <td>line</td>
      </tr>
      <tr>
          <td>org.postgres.geometric.PGpath</td>
          <td>path</td>
      </tr>
      <tr>
          <td>org.postgres.geometric.PGbox</td>
          <td>box</td>
      </tr>
      <tr>
          <td>org.postgres.geometric.PGcircle</td>
          <td>circle</td>
      </tr>
      <tr>
          <td>org.postgres.geometric.PGpolygon</td>
          <td>polygon</td>
      </tr>
  </tbody>
</table>
<h3 id="arrays">
  Arrays
  <a class="heading-link" href="#arrays">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Impemented for the following JVM-typed arrays for:</p>
<table>
  <thead>
      <tr>
          <th>JVM type</th>
          <th>Postgres type</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>int[]</td>
          <td>int4[] (aka integer[])</td>
      </tr>
      <tr>
          <td>long[]</td>
          <td>int8[] (aka bigint[])</td>
      </tr>
      <tr>
          <td>float[]</td>
          <td>float4[] (aka real[])</td>
      </tr>
      <tr>
          <td>double[]</td>
          <td>float8[] (aka double precision[])</td>
      </tr>
      <tr>
          <td>byte[]</td>
          <td>bytea</td>
      </tr>
      <tr>
          <td>String[]</td>
          <td>text[] (or varchar)</td>
      </tr>
      <tr>
          <td>java.util.UUID[]</td>
          <td>uuid[]</td>
      </tr>
  </tbody>
</table>
<p>Currently, only 1-dimensional Postgres arrays are supported.</p>
<h3 id="jsonb">
  jsonb
  <a class="heading-link" href="#jsonb">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Things that are String-like, or serialized in string form, should work
using the String -&gt; text mapping. An exception is the <code>jsonb</code> postgres
type, because the binary format requires a version signifier. Wrapping
a JSON string in a <code>clj-pgcopy.core/JsonB</code> handles that.</p>
<p>Note that this library does not serialize to JSON, it simply wraps a
valid JSON string such that it can actually be used.</p>
<h2 id="what-about-my-weird-type">
  What about my weird type?
  <a class="heading-link" href="#what-about-my-weird-type">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>These type mappings are implented using a clojure protocol, namely
<code>clj-pgcopy.core/IPGBinaryWrite</code>. In order to add support for another
type, just extend that protocol with an implementation. You can and
should use the implementation of other types in the library for
guidance on doing so.</p>
<h2 id="conclusion">
  Conclusion
  <a class="heading-link" href="#conclusion">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Go forth and import!</p>
]]></content>
  </entry>
  <entry>
    <title type="html">Single-Use Tokens in Datomic</title>
    <link href="https://joshuadavey.com/2016/09/30/single-use-tokens-in-datomic/" rel="alternate" type="text/html" title="Single-Use Tokens in Datomic"/>
    <published>2016-09-30T00:00:00+00:00</published>
    <updated>2016-09-30T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2016/09/30/single-use-tokens-in-datomic/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2016/09/30/single-use-tokens-in-datomic/"><![CDATA[<p>Single-use tokens have a variety of security applications. Whether it&rsquo;s
a password-reset token, or capturing a financial transaction, there are
times when something should happen exactly once, atomically. Once a
token is used, it is invalidated.</p>
<p>Doing things exactly once, atomically, is relatively straightforward in
traditional ACID transactional databases: within a transaction, you find
an entity (row) by the token, perform any updates to that entity, and
finally invalidate the token (often by deleting or nullifying it). <sup id="a1"><a href="#f1" >1</a></sup></p>
<p>But how can we accomplish some like this in Datomic?</p>
<h2 id="the-worlds-worst-singles-network">
  The World&rsquo;s Worst Singles Network
  <a class="heading-link" href="#the-worlds-worst-singles-network">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<blockquote>
<p>Welcome to WWSN! We&rsquo;re so excited you&rsquo;re here! On WWSN, you can sign up,
sign in, and reset your password. It&rsquo;s so simple!</p>
</blockquote>
<p>Let&rsquo;s say we have a really simple schema. A user has an email address
and a bcrypted password:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">[{</span><span style="color:#d88200">:db/id</span> <span style="color:#f92672">#</span><span style="color:#111">db/id</span> <span style="color:#111">[</span><span style="color:#d88200">:db.part/db</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/ident</span> <span style="color:#d88200">:user/email</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/valueType</span> <span style="color:#d88200">:db.type/string</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/unique</span> <span style="color:#d88200">:db.unique/identity</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/cardinality</span> <span style="color:#d88200">:db.cardinality/one</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db.install/_attribute</span> <span style="color:#d88200">:db.part/db</span><span style="color:#111">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#111">{</span><span style="color:#d88200">:db/id</span> <span style="color:#f92672">#</span><span style="color:#111">db/id</span> <span style="color:#111">[</span><span style="color:#d88200">:db.part/db</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/ident</span> <span style="color:#d88200">:user/crypted-password</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/valueType</span> <span style="color:#d88200">:db.type/string</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/cardinality</span> <span style="color:#d88200">:db.cardinality/one</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db.install/_attribute</span> <span style="color:#d88200">:db.part/db</span><span style="color:#111">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#111">{</span><span style="color:#d88200">:db/id</span> <span style="color:#f92672">#</span><span style="color:#111">db/id</span> <span style="color:#111">[</span><span style="color:#d88200">:db.part/db</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/ident</span> <span style="color:#d88200">:user/single-use-token</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/valueType</span> <span style="color:#d88200">:db.type/string</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/unique</span> <span style="color:#d88200">:db.unique/value</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/cardinality</span> <span style="color:#d88200">:db.cardinality/one</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db.install/_attribute</span> <span style="color:#d88200">:db.part/db</span><span style="color:#111">}]</span>
</span></span></code></pre></div><p>Some initial data might be added like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#75af00">d/transact</span> <span style="color:#111">conn</span> <span style="color:#111">[{</span><span style="color:#d88200">:db/id</span> <span style="color:#111">(</span><span style="color:#75af00">d/tempid</span> <span style="color:#d88200">:db.part/user</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>                   <span style="color:#d88200">:user/email</span> <span style="color:#d88200">&#34;jim@example.com&#34;</span>
</span></span><span style="display:flex;"><span>                   <span style="color:#d88200">:user/crypted-password</span> <span style="color:#111">(</span><span style="color:#75af00">crypt</span> <span style="color:#d88200">&#34;jello4stapler&#34;</span><span style="color:#111">)}</span>
</span></span><span style="display:flex;"><span>                  <span style="color:#111">{</span><span style="color:#d88200">:db/id</span> <span style="color:#111">(</span><span style="color:#75af00">d/tempid</span> <span style="color:#d88200">:db.part/user</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>                   <span style="color:#d88200">:user/email</span> <span style="color:#d88200">&#34;pam@example.com&#34;</span>
</span></span><span style="display:flex;"><span>                   <span style="color:#d88200">:user/crypted-password</span> <span style="color:#111">(</span><span style="color:#75af00">crypt</span> <span style="color:#d88200">&#34;art4evah&#34;</span><span style="color:#111">)}])</span>
</span></span></code></pre></div><p>Later, one of the users wants to reset their password, so we generate a
password reset token and persist it:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#75af00">d/transact</span> <span style="color:#111">conn</span> <span style="color:#111">[{</span><span style="color:#d88200">:db/id</span> <span style="color:#111">(</span><span style="color:#75af00">d/tempid</span> <span style="color:#d88200">:db.part/user</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>                   <span style="color:#d88200">:user/email</span> <span style="color:#d88200">&#34;jim@example.com&#34;</span>
</span></span><span style="display:flex;"><span>                   <span style="color:#d88200">:user/single-use-token</span> <span style="color:#111">(</span><span style="color:#75af00">generate-secure-random</span><span style="color:#111">)}])</span>
</span></span></code></pre></div><h2 id="an-initial-approach">
  An initial approach
  <a class="heading-link" href="#an-initial-approach">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>After receiving an email, they follow the link that includes the
single-use token in the URL. When they submit their new password, we
look up the user by that token and update them accordingly:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">let </span><span style="color:#111">[</span><span style="color:#111">db</span> <span style="color:#111">(</span><span style="color:#75af00">d/db</span> <span style="color:#111">conn</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">token</span> <span style="color:#111">(</span><span style="color:#d88200">:token</span> <span style="color:#111">params</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">e</span> <span style="color:#111">(</span><span style="color:#75af00">d/q</span> <span style="color:#f92672">&#39;</span><span style="color:#111">[</span><span style="color:#d88200">:find</span> <span style="color:#111">?e</span> <span style="color:#111">.</span>
</span></span><span style="display:flex;"><span>               <span style="color:#d88200">:in</span> <span style="color:#111">$</span> <span style="color:#111">?token</span>
</span></span><span style="display:flex;"><span>               <span style="color:#d88200">:where</span> <span style="color:#111">[</span><span style="color:#111">?e</span> <span style="color:#d88200">:user/single-use-token</span> <span style="color:#111">?token</span><span style="color:#111">]]</span>
</span></span><span style="display:flex;"><span>              <span style="color:#111">db</span> <span style="color:#111">token</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#00a8c8">if </span><span style="color:#111">e</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">(</span><span style="color:#75af00">d/transact</span> <span style="color:#111">conn</span> <span style="color:#111">[[</span><span style="color:#d88200">:db/add</span> <span style="color:#111">e</span> <span style="color:#d88200">:user/crypted-password</span> <span style="color:#111">new-password</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>                      <span style="color:#111">[</span><span style="color:#d88200">:db/retract</span> <span style="color:#111">e</span> <span style="color:#d88200">:user/single-use-token</span> <span style="color:#111">token</span><span style="color:#111">]])))</span>
</span></span></code></pre></div><p>But there&rsquo;s a problem with this solution. Even though the new password
is asserted in the same transaction that the token is invalidated, the
opportunity for concurrency problems between threads and/or peers still
exists.</p>
<p>Here&rsquo;s why. In between the time that <code>e</code> is first found by its token
and its new facts are transacted, somebody else could have already used
(and deleted) the token. In other words, even though all writes are
transactional, the reads are not. In practice this is rarely, if ever,
a problem. <code>(d/db conn)</code> will return the most recent version of the
database that the peer can get.</p>
<h2 id="croutons-are-just-stale-bread">
  Croutons are just stale bread
  <a class="heading-link" href="#croutons-are-just-stale-bread">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Let&rsquo;s illustrate this possibility of a stale database introducing a bug:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">def </span><span style="color:#111">stale-db</span> <span style="color:#111">(</span><span style="color:#75af00">d/db</span> <span style="color:#111">conn</span><span style="color:#111">))</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">let </span><span style="color:#111">[{</span><span style="color:#d88200">:keys</span> <span style="color:#111">[</span><span style="color:#111">token</span> <span style="color:#111">new-password</span><span style="color:#111">]}</span> <span style="color:#111">params</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">e</span> <span style="color:#111">(</span><span style="color:#75af00">d/q</span> <span style="color:#f92672">&#39;</span><span style="color:#111">[</span><span style="color:#d88200">:find</span> <span style="color:#111">?e</span> <span style="color:#111">.</span>
</span></span><span style="display:flex;"><span>               <span style="color:#d88200">:in</span> <span style="color:#111">$</span> <span style="color:#111">?token</span>
</span></span><span style="display:flex;"><span>               <span style="color:#d88200">:where</span> <span style="color:#111">[</span><span style="color:#111">?e</span> <span style="color:#d88200">:user/single-use-token</span> <span style="color:#111">?token</span><span style="color:#111">]]</span>
</span></span><span style="display:flex;"><span>              <span style="color:#111">stale-db</span> <span style="color:#111">token</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#00a8c8">if </span><span style="color:#111">e</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">(</span><span style="color:#75af00">d/transact</span> <span style="color:#111">conn</span> <span style="color:#111">[[</span><span style="color:#d88200">:db/add</span> <span style="color:#111">e</span> <span style="color:#d88200">:user/crypted-password</span> <span style="color:#111">new-password</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>                      <span style="color:#111">[</span><span style="color:#d88200">:db/retract</span> <span style="color:#111">e</span> <span style="color:#d88200">:user/single-use-token</span> <span style="color:#111">token</span><span style="color:#111">]])))</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">let </span><span style="color:#111">[</span><span style="color:#111">token</span> <span style="color:#111">(</span><span style="color:#d88200">:token</span> <span style="color:#111">params</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">;; this token still exists because we&#39;re using and &#34;old&#34; db value</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">e</span> <span style="color:#111">(</span><span style="color:#75af00">d/q</span> <span style="color:#f92672">&#39;</span><span style="color:#111">[</span><span style="color:#d88200">:find</span> <span style="color:#111">?e</span> <span style="color:#111">.</span>
</span></span><span style="display:flex;"><span>               <span style="color:#d88200">:in</span> <span style="color:#111">$</span> <span style="color:#111">?token</span>
</span></span><span style="display:flex;"><span>               <span style="color:#d88200">:where</span> <span style="color:#111">[</span><span style="color:#111">?e</span> <span style="color:#d88200">:user/single-use-token</span> <span style="color:#111">?token</span><span style="color:#111">]]</span>
</span></span><span style="display:flex;"><span>              <span style="color:#111">stale-db</span> <span style="color:#111">token</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#00a8c8">if </span><span style="color:#111">e</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">(</span><span style="color:#75af00">d/transact</span> <span style="color:#111">conn</span> <span style="color:#111">[[</span><span style="color:#d88200">:db/add</span> <span style="color:#111">e</span> <span style="color:#d88200">:user/crypted-password</span> <span style="color:#d88200">&#34;somethingelse&#34;</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>                      <span style="color:#111">[</span><span style="color:#d88200">:db/retract</span> <span style="color:#111">e</span> <span style="color:#d88200">:user/single-use-token</span> <span style="color:#111">token</span><span style="color:#111">]])))</span>
</span></span></code></pre></div><p>The user entity <code>e</code> is found both times because the database value is
immutable. So, the transactions will both succeed.</p>
<p>The astute reader may have noticed something. I said all writes are
transactional, but in the second block of code, we&rsquo;re retracting a value
that&rsquo;s already been retracted. Something must be broken!</p>
<p>Nothing&rsquo;s broken. This tripped me up at first, but it turns out that
retractions work just like assertions with regard to <em>redundancy
elimination</em>.</p>
<h2 id="the-department-of-redundancy-department">
  The Department of Redundancy Department
  <a class="heading-link" href="#the-department-of-redundancy-department">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>From the Datomic <a href="https://docs.datomic.com/transactions.html#redundancy-elimination"  class="external-link" target="_blank" rel="noopener">documentation on transactions</a>:</p>
<blockquote>
<p>Redundancy Elimination</p>
<p>A datom is redundant with the current value of the database if
there is a matching datom that differs only by transaction id. If a
transaction would produce redundant datoms, those datoms are filtered
out, and do not appear a second time in either the indexes or the
transaction log.</p>
</blockquote>
<p>In other words, Datomic is eliminating the redundant retraction: we&rsquo;ve
already retracted the token, so the effective datoms of the transaction
only include the <code>[:db/add ...]</code> of the new password. In this particular
use case, retractions cannot be used to safeguard us from using a token
more than once.</p>
<h2 id="transaction-functions-to-the-rescue">
  Transaction functions to the rescue
  <a class="heading-link" href="#transaction-functions-to-the-rescue">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Datomic&rsquo;s got us covered. I mentioned before that all writes are
transactional, and reads are not. That&rsquo;s actually only true on the
peers. The transactor itself is guaranteed to always have access to the
most recent database value at any time. Among other things, this is what
enables <a href="https://docs.datomic.com/transactions.html#built-in-transaction-functions"  class="external-link" target="_blank" rel="noopener">built-in database functions</a> like <code>:db.fn/cas</code> to work.</p>
<p>Within a transaction, a database function is used in place of a
<code>:db/add</code> or <code>:db/retract</code>. When the transactor sees a transactor
function, it invokes it and splices the result into the rest of the
transaction. Also, a database function always receives the most recent
<code>db</code> value as it&rsquo;s first argument. Because you have access to the whole
of the Datomic API, you can leverage this <code>db</code> value to do all sorts of
things.</p>
<p>Let&rsquo;s transact the following new schema info into our database:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">[{</span><span style="color:#d88200">:db/id</span> <span style="color:#f92672">#</span><span style="color:#111">db/id</span> <span style="color:#111">[</span><span style="color:#d88200">:db.part/db</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/ident</span> <span style="color:#d88200">:db.fn/set-with-token</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/doc</span> <span style="color:#d88200">&#34;Look up entity by token, set attr and value, and retract token&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/fn</span> <span style="color:#f92672">#</span><span style="color:#111">db/fn</span> <span style="color:#111">{</span><span style="color:#d88200">:lang</span> <span style="color:#d88200">&#34;clojure&#34;</span>
</span></span><span style="display:flex;"><span>                 <span style="color:#d88200">:params</span> <span style="color:#111">[</span><span style="color:#111">db</span> <span style="color:#111">token-attr</span> <span style="color:#111">token-value</span> <span style="color:#111">attr</span> <span style="color:#111">value</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>                 <span style="color:#d88200">:code</span> <span style="color:#111">(</span><span style="color:#00a8c8">let </span><span style="color:#111">[</span><span style="color:#111">e</span> <span style="color:#111">(</span><span style="color:#75af00">datomic.api/q</span> <span style="color:#f92672">&#39;</span><span style="color:#111">[</span><span style="color:#d88200">:find</span> <span style="color:#111">?e</span> <span style="color:#111">.</span>
</span></span><span style="display:flex;"><span>                                                <span style="color:#d88200">:in</span> <span style="color:#111">$</span> <span style="color:#111">?ta</span> <span style="color:#111">?tv</span>
</span></span><span style="display:flex;"><span>                                                <span style="color:#d88200">:where</span>
</span></span><span style="display:flex;"><span>                                                <span style="color:#111">[</span><span style="color:#111">?e</span> <span style="color:#111">?ta</span> <span style="color:#111">?tv</span><span style="color:#111">]]</span>
</span></span><span style="display:flex;"><span>                                              <span style="color:#111">db</span> <span style="color:#111">token-attr</span> <span style="color:#111">token-value</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>                         <span style="color:#111">(</span><span style="color:#00a8c8">if </span><span style="color:#111">e</span>
</span></span><span style="display:flex;"><span>                           <span style="color:#111">[[</span><span style="color:#d88200">:db/add</span> <span style="color:#111">e</span> <span style="color:#111">attr</span> <span style="color:#111">value</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>                            <span style="color:#111">[</span><span style="color:#d88200">:db/retract</span> <span style="color:#111">e</span> <span style="color:#111">token-attr</span> <span style="color:#111">token-value</span><span style="color:#111">]]</span>
</span></span><span style="display:flex;"><span>                           <span style="color:#111">(</span><span style="color:#75af00">throw</span> <span style="color:#111">(</span><span style="color:#75af00">ex-info</span> <span style="color:#d88200">&#34;No entity with that token exists&#34;</span>
</span></span><span style="display:flex;"><span>                                           <span style="color:#111">{</span><span style="color:#111">token-attr</span> <span style="color:#111">token-value</span><span style="color:#111">}))))}}]</span>
</span></span></code></pre></div><p>This function is more generic than our immediate use-case, but I prefer
to parameterize attributes as well as values in database functions. It
allows us to re-use the database function for other token fields, and
won&rsquo;t have to be updated in the schema if we ever change the name our
token attribute.</p>
<p>Here&rsquo;s how we use this shiny new function:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">let </span><span style="color:#111">[{</span><span style="color:#d88200">:keys</span> <span style="color:#111">[</span><span style="color:#111">token</span> <span style="color:#111">new-password</span><span style="color:#111">]}</span> <span style="color:#111">params</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#75af00">d/transact</span> <span style="color:#111">conn</span> <span style="color:#111">[[</span><span style="color:#d88200">:db.fn/set-with-token</span> <span style="color:#d88200">:user/single-use-token</span> <span style="color:#111">token</span>
</span></span><span style="display:flex;"><span>                                           <span style="color:#d88200">:user/crypted-password</span> <span style="color:#111">new-password</span><span style="color:#111">]]))</span>
</span></span></code></pre></div><p>When we transact this data, the transactor invokes our function using
the most recent database value. In other words, we are making the lookup
portion serializable with the rest of the operations.</p>
<p>If we run this transaction a second time, we&rsquo;ll get the error message.</p>
<h2 id="in-summary">
  In Summary
  <a class="heading-link" href="#in-summary">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>This kind of transaction atomicity is made possible by Datomic&rsquo;s
single-writer design. Other database systems (e.g. SQL) have to employ
very complicated isolation patterns like MVVC to allow multiple writers
while keeping data integrity guarantees. Datomic side-steps those problems by
using a single writer, paired with immutable history.</p>
<p>The catch, as we have seen, is that read-dependent writes will require
the use of database functions to maintain atomicity. Of course, database
functions have uses outside of concurrency contexts. And, as a bonus,
they can be loaded and invoked on the client as well.</p>
<p>For more information on database functions, see <a href="https://docs.datomic.com/database-functions.html"  class="external-link" target="_blank" rel="noopener">the
docs</a>, <a href="https://www.youtube.com/watch?v=8fY687k7DMA"  class="external-link" target="_blank" rel="noopener">watch
the video</a>,
or see [the Day of Datomic
examples](<a href="https://github.com/Datomic/day-of-datomic/blob/master/tutorial"  class="external-link" target="_blank" rel="noopener">https://github.com/Datomic/day-of-datomic/blob/master/tutorial</a>
/data_functions.clj). You can also view my <a href="https://gist.github.com/jgdavey/f40731172553724fc86ce270776c7932"  class="external-link" target="_blank" rel="noopener">scratch.clj</a> file that I
used to build up the code examples here.</p>
<p><sup id="f1">1</sup> However, even in SQL setups there are potential
pitfalls. Because of the potential for multiple writers, care must be
taken to satisfy the &ldquo;exactly once&rdquo; requirement. Where possible, a SQL
client should use a single statement to find, update, and nullify a
token. When a single statement isn&rsquo;t possible, the use of row-level
locks can be used. Or, better yet, wrap everything in a transaction with
serializable isolation level.</p>
]]></content>
  </entry>
  <entry>
    <title type="html">Bulk imports with Datomic</title>
    <link href="https://joshuadavey.com/2015/09/17/bulk-imports-with-datomic/" rel="alternate" type="text/html" title="Bulk imports with Datomic"/>
    <published>2015-09-17T00:00:00+00:00</published>
    <updated>2015-09-17T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2015/09/17/bulk-imports-with-datomic/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2015/09/17/bulk-imports-with-datomic/"><![CDATA[<p><em>This is a repost. You can find the original <a href="https://hashrocket.com/blog/posts/bulk-imports-with-datomic" title="Bulk imports with Datomic"  class="external-link" target="_blank" rel="noopener">here</a></em></p>
<p>I&rsquo;ve been really happy with Datomic, but doing an initial bulk import
wasn&rsquo;t as familiar as SQL dump/restore. Here are some things that I&rsquo;ve
learned from doing several imports.</p>
<h2 id="use-coreasync">
  Use core.async
  <a class="heading-link" href="#use-coreasync">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The Datomic transactor handles concurrency by transacting datoms
serially, but that doesn&rsquo;t mean it isn&rsquo;t fast! In my experience, the bottleneck is actually in the
reshaping of data and formatting transactions. I
use <code>core.async</code> to parallelize just about everything in the import
pipeline.</p>
<p>One example of how I&rsquo;ve leveraged <code>core.async</code> for
import jobs can be found in my <a href="https://github.com/jgdavey/kevin/blob/master/src/kevin/loader.clj"  class="external-link" target="_blank" rel="noopener">Kevin Bacon project
repository</a>.</p>
<h2 id="run-the-import-locally">
  Run the import locally
  <a class="heading-link" href="#run-the-import-locally">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>I use DynamoDB as my storage backend in production. I used to try to
run my import tasks directly to the production transactor/storage.
Lately, though, I&rsquo;ve found it really helpful to run my import tasks to
a locally-running transactor and the <code>dev</code> storage backend.</p>
<p>Running an import locally means I don&rsquo;t have to worry about networking, which speeds the whole process up quite a bit; also, it give me a much more freedom to iterate on the database
design itself. (I rarely get an import correct the first time.) And
in the case of DynamoDB, I save some money, as I don&rsquo;t have to have my
&ldquo;write throughput&rdquo; cranked way up for as long.</p>
<h2 id="clean-up-the-local-database">
  Clean up the local database
  <a class="heading-link" href="#clean-up-the-local-database">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Bulk imports create some garbage, so manually reindexing before backing
up is advantageous. Here&rsquo;s what a REPL session looks like:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">def </span><span style="color:#111">conn</span> <span style="color:#111">(</span><span style="color:#75af00">d/connect</span> <span style="color:#d88200">&#34;datomic:dev://localhost:4334/database-name&#34;</span><span style="color:#111">))</span>
</span></span><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#75af00">d/request-index</span> <span style="color:#111">conn</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#75af00">-&gt;&gt;</span> <span style="color:#111">conn</span> <span style="color:#111">d/db</span> <span style="color:#111">d/basis-t</span> <span style="color:#111">(</span><span style="color:#75af00">d/sync-index</span> <span style="color:#111">conn</span><span style="color:#111">)</span> <span style="color:#111">deref</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">;; blocks until done indexing</span>
</span></span><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#75af00">d/gc-storage</span> <span style="color:#111">conn</span> <span style="color:#111">(</span><span style="color:#75af00">java.util.Date.</span><span style="color:#111">))</span>
</span></span></code></pre></div><p>For more information on why this cleanup
is important, see <a href="https://docs.datomic.com/capacity.html#sec-2-4"  class="external-link" target="_blank" rel="noopener">the relevant Datomic
documentation</a>.</p>
<h2 id="use-backuprestore">
  Use backup/restore
  <a class="heading-link" href="#use-backuprestore">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Once everything looks good on the local production database, I use
Datomic&rsquo;s builtin backup/restore facilities to send the database
up to production. Assuming you&rsquo;ve already deployed a production transactor and provisioned DynamoDB storage, here&rsquo;s the process I follow:</p>
<ol>
<li>Run the <code>datomic backup-db</code> command against the local import.</li>
<li>Crank my &ldquo;write throughput&rdquo; on DynamoDB <em>way</em> up (on the order of 1000).</li>
<li>Run the <code>datomic restore-db</code> command from the backup folder to the
remote database.</li>
<li>Turn the &ldquo;write throughput&rdquo; back down to whatever
value I plan to use for ongoing use (see <a href="https://docs.datomic.com/capacity.html#sec-5"  class="external-link" target="_blank" rel="noopener">the Datomic
documentation</a> for more
information).</li>
</ol>
<p>The heart of almost every business is its data. Datomic is a great
choice for business data, in part because it treats <em>all</em> data as
important: nothing is overwritten. New things are learned, but the old
facts are not replaced. And knowing how to get your data into Datomic
is half the battle.</p>
<p>Go forth and import!</p>
]]></content>
  </entry>
  <entry>
    <title type="html">Boot &#43; Middleman: The ClojureScript development environment of my dreams</title>
    <link href="https://joshuadavey.com/2015/06/04/boot-middleman/" rel="alternate" type="text/html" title="Boot &#43; Middleman: The ClojureScript development environment of my dreams"/>
    <published>2015-06-04T00:00:00+00:00</published>
    <updated>2015-06-04T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2015/06/04/boot-middleman/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2015/06/04/boot-middleman/"><![CDATA[<p><em>This is a repost. You can find the original <a href="https://hashrocket.com/blog/posts/the-front-end-development-environment-of-my-dreams" title="Boot &#43; Middleman: The ClojureScript development environment of my dreams"  class="external-link" target="_blank" rel="noopener">here</a></em></p>
<p>I&rsquo;m getting closer to the frontend development environment of my dreams. The
combination of editor integration, live browser reload, and not having to
manually run commands over and over is time-saving and a pleasure to
work with.</p>
<p>At Hashrocket, designers and developers work very closely
together. Visual design and markup is handled by our
designers, who create &ldquo;stubbed out&rdquo; templates in <a href="https://hashrocket.com/blog/posts/managing-design-handoffs-with-the-ui-controller"  class="external-link" target="_blank" rel="noopener">the UI
directory</a>.
It&rsquo;s a process that works very well for us, and
allows us to iteratively add features to an application.</p>
<p>This process has served us very well in Rails using a <a href="https://hashrocket.com/blog/posts/managing-design-handoffs-with-the-ui-controller"  class="external-link" target="_blank" rel="noopener">UI
controller</a>,
available only in development mode.</p>
<p>I&rsquo;ve been using ClojureScript a lot lately, particularly with Om, and
have missed that directory of collaboration. After all, the designers
at Hashrocket have a proclivity for HAML and SASS.</p>
<p>In the past, I&rsquo;ve set up a separate repository using <a href="https://middlemanapp.com"  class="external-link" target="_blank" rel="noopener">Middleman</a> to
handle markup and styles, using <code>middleman build</code>, copying the generated
CSS files, and eyeballing the generated markup to ensure it matched the
Om component&rsquo;s markup. Aside from being tedious, it&rsquo;s really easy to
get out of sync with a manual process like this. The static resource
generation should be a part of our build process.</p>
<p>Enter <a href="https://boot-clj.com"  class="external-link" target="_blank" rel="noopener">boot</a>.</p>
<h3 id="using-boot-for-clojurescript-development">
  Using Boot for ClojureScript development
  <a class="heading-link" href="#using-boot-for-clojurescript-development">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>If you&rsquo;re new to the Clojure world, you may have heard of <a href="https://leiningen.org"  class="external-link" target="_blank" rel="noopener">Leiningen</a>,
which is the de facto dependency management and build tool for
Clojure/Script. Boot is similar to Leiningen, but adds the ability to
compose tasks to create build pipelines. This composability, along with
some really smart architectural decisions, is what makes boot a great
choice for the problem at hand.</p>
<p>Adzerk&rsquo;s <a href="https://github.com/adzerk-oss/boot-cljs-example"  class="external-link" target="_blank" rel="noopener">example repo</a>
is a great way to get started with ClojureScript and boot. Of particular
note is the <code>build.boot</code> file. It demonstrates how one can build
up a <code>dev</code> task that watches a directory for changes, rebuilding
ClojureScript sources, and notifying the browser to reload the code. It
includes the setup necessary for source maps, a development server,
and the browser-connected REPL. But what I want to add to that pot
of awesome is the ability to compile HAML and SASS as a part of the
pipeline.</p>
<h3 id="boot-middleman-gluing-everything-together">
  boot-middleman: Gluing Everything Together
  <a class="heading-link" href="#boot-middleman-gluing-everything-together">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>I had an epiphany one night after working on this problem for a while: I
can just use Middleman. After all, boot and the ClojureScript compiler
run on the JVM, and JRuby is easily embeddable. After a short bit, I
came up with <a href="https://github.com/jgdavey/boot-middleman"  class="external-link" target="_blank" rel="noopener">boot-middleman</a>, the glue I needed to build HAML/SASS as
a part of my build process.</p>
<p>It assumes a subdirectory is a Middleman app (<code>assets</code> by default). This
works nicely because my designer pals can collaborate with me without
having to use the JVM at all. They just run <code>middleman</code> in the <code>assets</code>
subdirectory and work as normal.</p>
<p>See the boot-middleman README for instructions on setting up.</p>
<p>I used this workflow to create a minesweeper clone, <a href="https://github.com/jgdavey/minesweeper"  class="external-link" target="_blank" rel="noopener">the
source</a> of which is on GitHub.
Just clone and run <code>boot dev</code>.</p>
<h3 id="the-workflow-in-action">
  The Workflow in Action
  <a class="heading-link" href="#the-workflow-in-action">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>To see the workflow in action, check out the following video. It
demonstrates how editing front-end files do not require a manual browser
refresh to see the effects.</p>
<iframe src="https://player.vimeo.com/video/128494099" width="600" height="380" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
]]></content>
  </entry>
  <entry>
    <title type="html">Screencast: Clojure &#43; vim basics</title>
    <link href="https://joshuadavey.com/2014/06/13/screencast-clojure-and-vim-basics/" rel="alternate" type="text/html" title="Screencast: Clojure &#43; vim basics"/>
    <published>2014-06-13T00:00:00+00:00</published>
    <updated>2014-06-13T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2014/06/13/screencast-clojure-and-vim-basics/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2014/06/13/screencast-clojure-and-vim-basics/"><![CDATA[<p>In this screencast I cover how to do basic evaluation and get Clojure
documentation from within vim. I&rsquo;m also including the transcript below.</p>
<iframe src="//player.vimeo.com/video/98052766" width="100%" height="280" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
<p>Vim is a powerful text editor. Clojure is a powerful programming
language. While its been possible to edit Clojure code in vim for years,
the toolchain has improved greatly over the past year. Today we&rsquo;re going
to see how we can integrate vim with our Clojure REPL environment.</p>
<h1 id="life-without-integration">
  Life without integration
  <a class="heading-link" href="#life-without-integration">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h1>
<p>In a shell session, let&rsquo;s fire up a Clojure REPL. I&rsquo;m going to use <code>lein repl</code> to do this. In another shell session, let&rsquo;s start vim and edit a
clojure file.</p>
<p>As I edit my file, I can copy code from the editor, switch to the window
with the REPL in it, and paste that code in. This works, but it&rsquo;s an
awkward, slow process. REPLs are supposed to be all about fast feedback.
We can do better than copy and paste.</p>
<h1 id="plugins">
  Plugins
  <a class="heading-link" href="#plugins">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h1>
<p>Before we get started, we should get the some basic plugins for clojure
development. Using your preferred vim plugin manager, add these plugins:</p>
<pre><code>guns/vim-clojure-static
tpope/fireplace.vim
</code></pre>
<h1 id="setup">
  Setup
  <a class="heading-link" href="#setup">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h1>
<p>After you&rsquo;ve installed the necessary Vim plugins, enter a project
directory. For example, if you have a leiningen project, cd into the
directory. In one shell session, fire up a REPL with <code>lein repl</code>. In
another shell session, cd that that folder once again, and then open
vim.</p>
<p>Fireplace is able to detect when you are in the same directory as an
active REPL, and will attempt to automatically connect for you. This
process is transparent, but should be obvious once we attempt to to send
a command to the connected REPL.</p>
<h1 id="evaluation">
  Evaluation
  <a class="heading-link" href="#evaluation">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h1>
<p>The most basic fireplace command is <code>:Eval</code>. <code>:Eval</code> takes an arbitrary
clojure expression, sends it off to the REPL, and prints the result
for you. For example, we could run <code>:Eval (+ 1 1)</code>, and we would, as
expected, see <code>2</code> printed out. This emulates typing at REPL prompt
directly, but there&rsquo;s much more we can do with our REPL-connected vim
session.</p>
<p>Let&rsquo;s stay with <code>:Eval</code> for a bit longer. <code>:Eval</code> without any arguments
will send eval and print the outermost form on the current line. For
example, let&rsquo;s look at a simple expression.</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#111">map inc </span><span style="color:#111">[</span><span style="color:#ae81ff">1</span> <span style="color:#ae81ff">2</span> <span style="color:#ae81ff">3</span><span style="color:#111">])</span>
</span></span></code></pre></div><p>When we have our cursor on this line and type <code>:Eval</code> with no arguments,
we&rsquo;ll see <code>(2 3 4)</code> printed back.</p>
<p><code>:Eval</code>, as with many vim commands, can also take a range. So,
<code>:1,3Eval</code> would evaluate all of lines 1 through 3. All of the normal
special ranges work here, such as <code>%</code> for the entire file, and <code>'&lt;,'&gt;</code>
for the current selection in visual mode.</p>
<p><code>:Eval</code> works well, but there&rsquo;s a quicker way to get feedback. <code>cp</code> is
the normal mode mapping for doing a simple eval and print. By default,
<code>cp</code> expects a motion. The form that I use most though is <code>cpp</code>, which
will eval and print the innermost form from the cursor&rsquo;s current
position.</p>
<p>To demonstrate what this means, let&rsquo;s look at that expression again.</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#111">map inc </span><span style="color:#111">[</span><span style="color:#ae81ff">1</span> <span style="color:#ae81ff">2</span> <span style="color:#ae81ff">3</span><span style="color:#111">])</span>
</span></span></code></pre></div><p>When our cursor is on the <code>m</code> of <code>map</code>, and we type <code>cpp</code>, we&rsquo;ll see
<code>(2 3 4)</code>, just as when we did the plain <code>:Eval</code>. But if we move our
cursor inside the vector and type <code>cpp</code> again, we&rsquo;ll see that inner form
evaluated.</p>
<p>Something unique to fireplace is its concept of a quasi-REPL. This is a
cousin of the <code>cp</code> mappings, but with an intermediate editing window. To
demonstrate this, let&rsquo;s consider the following example.</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#75af00">-&gt;&gt;</span> <span style="color:#111">[</span><span style="color:#ae81ff">1</span> <span style="color:#ae81ff">2</span> <span style="color:#ae81ff">3</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">(</span><span style="color:#111">map </span><span style="color:#111">str</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">reverse</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">(</span><span style="color:#75af00">mapv</span> <span style="color:#111">dec</span><span style="color:#111">))</span>
</span></span></code></pre></div><p>In this trivial example, we want to reverse a sequence and decrement
each number. There&rsquo;s a bug in here, but it&rsquo;s in the middle of the
thread-through macro. We could just edit the line directly and
eval/print using <code>cpp</code>, but there&rsquo;s another way to do one-off iterative
development like this.</p>
<p>Type <code>cqc</code> in normal mode. A commandline window will open. This is very
much like a normal vim buffer, with a few notable exceptions:</p>
<ol>
<li>It cannot be modified or saved</li>
<li>Pressing <code>Enter</code> in normal mode sends the current line to the REPL
for eval-ing.</li>
<li>As you run commands, they are added to this buffer.</li>
</ol>
<p>tpope calls this the &ldquo;quasi-repl&rdquo;, and indeed that is the mnemonic for
the mapping itself: <code>cq</code> is the &ldquo;Clojure Quasi-REPL&rdquo;.</p>
<p>While we&rsquo;re in this special window, let&rsquo;s type the following, and hit
enter:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#111">map str </span><span style="color:#111">[</span><span style="color:#ae81ff">1</span> <span style="color:#ae81ff">2</span> <span style="color:#ae81ff">3</span><span style="color:#111">])</span>
</span></span></code></pre></div><p>Immediately, we can see the issue. Converting each number to a string
prevents <code>dec</code> from working later on.</p>
<p>Having to type the whole line again isn&rsquo;t always convenient. For those
cases, there&rsquo;s <code>cqq</code>, which is like <code>cqc</code> except that it pre-populates
the command window with the innermost form under the cursor. We can
see this in action by putting our cursor near the beginning of the
thread-through macro, and typing <code>cqq</code>.</p>
<p>You can think of <code>cqq</code> as being very similar to <code>cpp</code>, but with a chance
to edit the line or lines before sending it off to the REPL.</p>
<h1 id="documentation">
  Documentation
  <a class="heading-link" href="#documentation">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h1>
<p>One of the great things about Clojure is that documentation is a
first-class citizen, and builtin functions have documentation attached
to them. With a standard REPL, we can use the <code>doc</code> function to get the
signature and documentation for a given function.</p>
<p>With fireplace, we get this with the <code>:Doc</code> command, and it works just
like <code>doc</code>. To see the documentation for <code>map</code>, for example, type <code>:Doc map</code>. We immediately see the documentation for the map command printed.</p>
<p>There&rsquo;s an even shorter way to look up documentation for a function.
When your cursor is on a word, you can press <code>K</code>, that is <code>Shift</code> and
<code>K</code>. We can try this again with the <code>map</code> function by placing our cursor
on the function itself, and pressing <code>K</code>.</p>
<p>We can also use the <code>:Source</code> command to show the source for a function.
When we do this with <code>map</code>, we see the source code for <code>map</code> from
<code>clojure.core</code>.</p>
]]></content>
  </entry>
  <entry>
    <title type="html">Using Datomic as a Graph Database</title>
    <link href="https://joshuadavey.com/2014/04/10/using-datomic-as-a-graph-database/" rel="alternate" type="text/html" title="Using Datomic as a Graph Database"/>
    <published>2014-04-10T00:00:00+00:00</published>
    <updated>2014-04-10T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2014/04/10/using-datomic-as-a-graph-database/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2014/04/10/using-datomic-as-a-graph-database/"><![CDATA[<p><em>This is a repost. You can find the original <a href="https://hashrocket.com/blog/posts/using-datomic-as-a-graph-database" title="Using Datomic as a Graph Database"  class="external-link" target="_blank" rel="noopener">here</a></em></p>
<p>Datomic is a database that changes the way that you think about databases. It
also happens to be effective at modeling graph data and was a great fit for
performing graph traversal in a recent project I built.</p>
<p>I started out building a six degress of Kevin Bacon project using
<a href="https://www.neo4j.org/"  class="external-link" target="_blank" rel="noopener">Neo4j</a>, a popular open-source graph database. It worked very well
for actors that were a few hops away, but finding paths between actors
with more than 5 hops proved problematic.  The cypher query language
gave me little visibility into the graph algorithms actually being
executed. I wanted more.</p>
<p>Despite not being explicitly labeled as such, Datomic proved to be an effective
graph database. Its ability to arbitrarily traverse datoms, when paired with
the appropriate graph searching algorithm, solved my problem elegantly. This
technique ended up being fast as well.</p>
<p>Quick aside: this post assumes a cursory understanding of Datomic. I won&rsquo;t cover
the basics, but the <a href="https://docs.datomic.com/tutorial.html"  class="external-link" target="_blank" rel="noopener">official tutorial</a> will help you get started.</p>
<h2 id="6-degrees-kevin--cool-6-degrees-kelvin--cold">
  6 Degrees Kevin == Cool; 6 Degrees Kelvin == Cold
  <a class="heading-link" href="#6-degrees-kevin--cool-6-degrees-kelvin--cold">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The problem domain should be fairly familiar: <a href="https://en.wikipedia.org/wiki/Six_Degrees_of_Kevin_Bacon"  class="external-link" target="_blank" rel="noopener">the 6 degrees of Kevin
Bacon</a>. I wanted to create an app where you could pick an
actor and find out what their Bacon Number was. That is, given an actor,
I wanted to answer the question &ldquo;how many degrees of separation is there
between that actor and Kevin Bacon?&rdquo;</p>
<p>Using <a href="https://www.imdb.com/interfaces"  class="external-link" target="_blank" rel="noopener">information freely available from IMDb</a>, I developed the following
schema:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">[</span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; movies</span>
</span></span><span style="display:flex;"><span> <span style="color:#111">{</span><span style="color:#d88200">:db/id</span> <span style="color:#f92672">#</span><span style="color:#111">db/id</span><span style="color:#111">[</span><span style="color:#d88200">:db.part/db</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/ident</span> <span style="color:#d88200">:movie/title</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/valueType</span> <span style="color:#d88200">:db.type/string</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/cardinality</span> <span style="color:#d88200">:db.cardinality/one</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/fulltext</span> <span style="color:#111">true</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/unique</span> <span style="color:#d88200">:db.unique/identity</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/doc</span> <span style="color:#d88200">&#34;A movie&#39;s title (upsertable)&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db.install/_attribute</span> <span style="color:#d88200">:db.part/db</span><span style="color:#111">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#111">{</span><span style="color:#d88200">:db/id</span> <span style="color:#f92672">#</span><span style="color:#111">db/id</span><span style="color:#111">[</span><span style="color:#d88200">:db.part/db</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/ident</span> <span style="color:#d88200">:movie/year</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/valueType</span> <span style="color:#d88200">:db.type/long</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/cardinality</span> <span style="color:#d88200">:db.cardinality/one</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/doc</span> <span style="color:#d88200">&#34;A movie&#39;s release year&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db.install/_attribute</span> <span style="color:#d88200">:db.part/db</span><span style="color:#111">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">;; actors</span>
</span></span><span style="display:flex;"><span> <span style="color:#111">{</span><span style="color:#d88200">:db/id</span> <span style="color:#f92672">#</span><span style="color:#111">db/id</span><span style="color:#111">[</span><span style="color:#d88200">:db.part/db</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/ident</span> <span style="color:#d88200">:person/name</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/valueType</span> <span style="color:#d88200">:db.type/string</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/cardinality</span> <span style="color:#d88200">:db.cardinality/one</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/fulltext</span> <span style="color:#111">true</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/unique</span> <span style="color:#d88200">:db.unique/identity</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/doc</span> <span style="color:#d88200">&#34;A person&#39;s name (upsertable)&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db.install/_attribute</span> <span style="color:#d88200">:db.part/db</span><span style="color:#111">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#111">{</span><span style="color:#d88200">:db/id</span> <span style="color:#f92672">#</span><span style="color:#111">db/id</span><span style="color:#111">[</span><span style="color:#d88200">:db.part/db</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/ident</span> <span style="color:#d88200">:actor/movies</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/valueType</span> <span style="color:#d88200">:db.type/ref</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/cardinality</span> <span style="color:#d88200">:db.cardinality/many</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db/doc</span> <span style="color:#d88200">&#34;An actor&#39;s ref to a movie&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">:db.install/_attribute</span> <span style="color:#d88200">:db.part/db</span><span style="color:#111">}</span>
</span></span><span style="display:flex;"><span> <span style="color:#111">]</span>
</span></span></code></pre></div><p>In a nutshell, movies have titles and years. Actors have names and movies.
The &ldquo;relationship&rdquo; of actors to movies is many-to-many, so I&rsquo;ve declared the
<code>:actor/movies</code> attribute as having a cardinality of many.</p>
<h2 id="using-datalog-queries">
  Using datalog queries
  <a class="heading-link" href="#using-datalog-queries">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Using datalog and <code>datomic.api/q</code>, we can make graph-like queries fairly easily.
Because the <code>:where</code> clauses of a datalog query form an implicit join, we can
join from our starting point to our ending point with relative ease.</p>
<p>As an example, what if we wanted to know the shortest path or paths from Kevin
Bacon to Jon Belushi? Let&rsquo;s use a query to find out:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#75af00">require</span> <span style="color:#f92672">&#39;</span><span style="color:#111">[</span><span style="color:#111">datomic.api</span> <span style="color:#d88200">:as</span> <span style="color:#111">d</span> <span style="color:#d88200">:refer</span> <span style="color:#111">[</span><span style="color:#111">q</span> <span style="color:#111">db</span><span style="color:#111">]])</span>
</span></span><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">def </span><span style="color:#111">conn</span> <span style="color:#111">(</span><span style="color:#75af00">d/connect</span> <span style="color:#111">...</span><span style="color:#111">))</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#75af00">q</span> <span style="color:#f92672">&#39;</span><span style="color:#111">[</span><span style="color:#d88200">:find</span> <span style="color:#111">?start</span> <span style="color:#111">?title</span> <span style="color:#111">?end</span>
</span></span><span style="display:flex;"><span>      <span style="color:#d88200">:in</span> <span style="color:#111">$</span> <span style="color:#111">?start</span> <span style="color:#111">?end</span>
</span></span><span style="display:flex;"><span>      <span style="color:#d88200">:where</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">[</span><span style="color:#111">?a1</span> <span style="color:#d88200">:actor/name</span> <span style="color:#111">?start</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">[</span><span style="color:#111">?a2</span> <span style="color:#d88200">:actor/name</span> <span style="color:#111">?end</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">[</span><span style="color:#111">?a1</span> <span style="color:#d88200">:actor/movies</span> <span style="color:#111">?m</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">[</span><span style="color:#111">?a2</span> <span style="color:#d88200">:actor/movies</span> <span style="color:#111">?m</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">[</span><span style="color:#111">?m</span> <span style="color:#d88200">:movie/title</span> <span style="color:#111">?title</span><span style="color:#111">]]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">(</span><span style="color:#75af00">db</span> <span style="color:#111">conn</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#d88200">&#34;Bacon, Kevin (I)&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#d88200">&#34;Belushi, John&#34;</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">;=&gt; #{[&#34;Bacon, Kevin (I)&#34; &#34;Animal House (1978)&#34; &#34;Belushi, John&#34;]}</span>
</span></span></code></pre></div><p>That is fine when actors have worked together in a movie (a Bacon Number of 1),
but doesn&rsquo;t help us solve Bacon numbers when there are 2 or more movies between
the actors. We could add more where clauses to join over two movies, but that
isn&rsquo;t sustainable. The queries would quickly become too long to reason about.
This is a prime opportunity to use Datomic&rsquo;s rules.</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">def </span><span style="color:#111">acted-with-rules</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#39;</span><span style="color:#111">[[(</span><span style="color:#75af00">acted-with</span> <span style="color:#111">?e1</span> <span style="color:#111">?e2</span> <span style="color:#111">?path</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">[</span><span style="color:#111">?e1</span> <span style="color:#d88200">:actor/movies</span> <span style="color:#111">?m</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">[</span><span style="color:#111">?e2</span> <span style="color:#d88200">:actor/movies</span> <span style="color:#111">?m</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">[(</span><span style="color:#75af00">!=</span> <span style="color:#111">?e1</span> <span style="color:#111">?e2</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">[(</span><span style="color:#111">vector </span><span style="color:#111">?e1</span> <span style="color:#111">?m</span> <span style="color:#111">?e2</span><span style="color:#111">)</span> <span style="color:#111">?path</span><span style="color:#111">]]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">[(</span><span style="color:#75af00">acted-with-1</span> <span style="color:#111">?e1</span> <span style="color:#111">?e2</span> <span style="color:#111">?path</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">(</span><span style="color:#75af00">acted-with</span> <span style="color:#111">?e1</span> <span style="color:#111">?e2</span> <span style="color:#111">?path</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">[(</span><span style="color:#75af00">acted-with-2</span> <span style="color:#111">?e1</span> <span style="color:#111">?e2</span> <span style="color:#111">?path</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">(</span><span style="color:#75af00">acted-with</span> <span style="color:#111">?e1</span> <span style="color:#111">?x</span> <span style="color:#111">?pp</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">(</span><span style="color:#75af00">acted-with</span> <span style="color:#111">?x</span> <span style="color:#111">?e2</span> <span style="color:#111">?p2</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">[(</span><span style="color:#111">butlast </span><span style="color:#111">?pp</span><span style="color:#111">)</span> <span style="color:#111">?p1</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">[(</span><span style="color:#111">concat </span><span style="color:#111">?p1</span> <span style="color:#111">?p2</span><span style="color:#111">)</span> <span style="color:#111">?path</span><span style="color:#111">]]])</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#75af00">q</span> <span style="color:#f92672">&#39;</span><span style="color:#111">[</span><span style="color:#d88200">:find</span> <span style="color:#111">?path</span>
</span></span><span style="display:flex;"><span>     <span style="color:#d88200">:in</span> <span style="color:#111">$</span> <span style="color:#111">%</span> <span style="color:#111">?start</span> <span style="color:#111">?end</span>
</span></span><span style="display:flex;"><span>     <span style="color:#d88200">:where</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">[</span><span style="color:#111">?a1</span> <span style="color:#d88200">:actor/name</span> <span style="color:#111">?start</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">[</span><span style="color:#111">?a2</span> <span style="color:#d88200">:actor/name</span> <span style="color:#111">?end</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>     <span style="color:#111">(</span><span style="color:#75af00">acted-with-2</span> <span style="color:#111">?a1</span> <span style="color:#111">?a2</span> <span style="color:#111">?path</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">(</span><span style="color:#75af00">db</span> <span style="color:#111">conn</span><span style="color:#111">)</span> <span style="color:#111">acted-with-rules</span> <span style="color:#d88200">&#34;Bieber, Justin&#34;</span> <span style="color:#d88200">&#34;Bacon, Kevin (I)&#34;</span><span style="color:#111">))</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">;=&gt; #{[(17592186887476 17592186434418 17592187362817 17592186339273 17592186838882)] [(17592186887476 17592186434418 17592188400376 17592186529535 17592186838882)] [(17592186887476 17592186434418 17592187854963 17592186529535 17592186838882)] [(17592186887476 17592186434418 17592186926035 17592186302397 17592186838882)]}</span>
</span></span></code></pre></div><p>This time we get back a collection of paths with entity ids. We can easily
transform these ids by mapping them into entities and getting the name or title,
using a function like the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">defn </span><span style="color:#111">actor-or-movie-name</span> <span style="color:#111">[</span><span style="color:#111">db</span> <span style="color:#111">eid</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#00a8c8">let </span><span style="color:#111">[</span><span style="color:#111">ent</span> <span style="color:#111">(</span><span style="color:#75af00">d/entity</span> <span style="color:#111">db</span> <span style="color:#111">eid</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">(</span><span style="color:#111">or </span><span style="color:#111">(</span><span style="color:#d88200">:movie/title</span> <span style="color:#111">ent</span><span style="color:#111">)</span> <span style="color:#111">(</span><span style="color:#d88200">:person/name</span> <span style="color:#111">ent</span><span style="color:#111">))))</span>
</span></span></code></pre></div><p>So, putting the query together with the above function, we get:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">let </span><span style="color:#111">[</span><span style="color:#111">d</span> <span style="color:#111">(</span><span style="color:#75af00">db</span> <span style="color:#111">conn</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">name </span><span style="color:#111">(</span><span style="color:#111">partial </span><span style="color:#111">actor-or-movie-name</span> <span style="color:#111">d</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#75af00">-&gt;&gt;</span> <span style="color:#111">(</span><span style="color:#75af00">q</span> <span style="color:#f92672">&#39;</span><span style="color:#111">[</span><span style="color:#d88200">:find</span> <span style="color:#111">?path</span>
</span></span><span style="display:flex;"><span>            <span style="color:#d88200">:in</span> <span style="color:#111">$</span> <span style="color:#111">%</span> <span style="color:#111">?start</span> <span style="color:#111">?end</span>
</span></span><span style="display:flex;"><span>            <span style="color:#d88200">:where</span>
</span></span><span style="display:flex;"><span>            <span style="color:#111">[</span><span style="color:#111">?a1</span> <span style="color:#d88200">:actor/name</span> <span style="color:#111">?start</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>            <span style="color:#111">[</span><span style="color:#111">?a2</span> <span style="color:#d88200">:actor/name</span> <span style="color:#111">?end</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>            <span style="color:#111">(</span><span style="color:#75af00">acted-with-2</span> <span style="color:#111">?a1</span> <span style="color:#111">?a2</span> <span style="color:#111">?path</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>          <span style="color:#111">d</span> <span style="color:#111">acted-with-rules</span> <span style="color:#d88200">&#34;Bieber, Justin&#34;</span> <span style="color:#d88200">&#34;Bacon, Kevin (I)&#34;</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>       <span style="color:#111">(</span><span style="color:#111">map </span><span style="color:#111">first</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>       <span style="color:#111">(</span><span style="color:#111">map </span><span style="color:#111">(</span><span style="color:#111">partial </span><span style="color:#111">mapv</span> <span style="color:#111">name</span><span style="color:#111">))))</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">;=&gt; ([&#34;Bieber, Justin&#34; &#34;Men in Black 3 (2012)&#34; &#34;Jones, Tommy Lee&#34; &#34;JFK (1991)&#34; &#34;Bacon, Kevin (I)&#34;] [&#34;Bieber, Justin&#34; &#34;Men in Black 3 (2012)&#34; &#34;Howard, Rosemary (II)&#34; &#34;R.I.P.D. (2013)&#34; &#34;Bacon, Kevin (I)&#34;] [&#34;Bieber, Justin&#34; &#34;Men in Black 3 (2012)&#34; &#34;Segal, Tobias&#34; &#34;R.I.P.D. (2013)&#34; &#34;Bacon, Kevin (I)&#34;] [&#34;Bieber, Justin&#34; &#34;Men in Black 3 (2012)&#34; &#34;Brolin, Josh&#34; &#34;Hollow Man (2000)&#34; &#34;Bacon, Kevin (I)&#34;])</span>
</span></span></code></pre></div><p>The rules above are defined statically, but they are simply clojure data
structures: it would be trivial to generate those rules to an arbitrary
depth. For an example of doing just that, see the <a href="https://github.com/Datomic/mbrainz-sample/blob/master/src/clj/datomic/samples/mbrainz/rules.clj"  class="external-link" target="_blank" rel="noopener">Datomic mbrainz
sample</a>.</p>
<h2 id="low-level-traversal-for-better-performance">
  Low-level traversal for better performance
  <a class="heading-link" href="#low-level-traversal-for-better-performance">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Having to know the depth at which to traverse the graph is cumbersome. Datomic
has a distinct advantage of being able to treat your data as local, even if its
permanent storage lives somewhere else. That means that we can bring our own
functions to the problem and execute locally, rather than on a database server.
We can leverage Datomic&rsquo;s <a href="https://docs.datomic.com/clojure/#datomic.api/datoms"  class="external-link" target="_blank" rel="noopener"><code>datoms</code> function</a> to search the graph using
our own graph-searching algorithm, rather than relying on the query engine.</p>
<p>Our IMDb actor data is essentially a dense unweighted graph. Because of its
density, a <a href="https://en.wikipedia.org/wiki/Bidirectional_search"  class="external-link" target="_blank" rel="noopener">bidirectional</a> <a href="https://en.wikipedia.org/wiki/Breadth-first_search"  class="external-link" target="_blank" rel="noopener">breadth-first search</a> is probably the most
efficient alogrithm for finding the shortest paths from one point to another. A
generic bidirectional BFS returning all shortest paths might look like this.</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">defn </span><span style="color:#111">paths</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">&#34;Returns a lazy seq of all non-looping path vectors starting with
</span></span></span><span style="display:flex;"><span><span style="color:#d88200">  [&lt;start-node&gt;]&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">[</span><span style="color:#111">nodes-fn</span> <span style="color:#111">path</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#00a8c8">let </span><span style="color:#111">[</span><span style="color:#111">this-node</span> <span style="color:#111">(</span><span style="color:#111">peek </span><span style="color:#111">path</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">(</span><span style="color:#75af00">-&gt;&gt;</span> <span style="color:#111">(</span><span style="color:#75af00">nodes-fn</span> <span style="color:#111">this-node</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>         <span style="color:#111">(</span><span style="color:#111">filter </span><span style="color:#f92672">#</span><span style="color:#111">(</span><span style="color:#111">not-any? </span><span style="color:#111">(</span><span style="color:#00a8c8">fn </span><span style="color:#111">[</span><span style="color:#111">edge</span><span style="color:#111">]</span> <span style="color:#111">(</span><span style="color:#111">= </span><span style="color:#111">edge</span> <span style="color:#111">[</span><span style="color:#111">this-node</span> <span style="color:#111">%</span><span style="color:#111">]))</span>
</span></span><span style="display:flex;"><span>                            <span style="color:#111">(</span><span style="color:#75af00">partition</span> <span style="color:#ae81ff">2</span> <span style="color:#ae81ff">1</span> <span style="color:#111">path</span><span style="color:#111">)))</span>
</span></span><span style="display:flex;"><span>         <span style="color:#111">(</span><span style="color:#111">mapcat </span><span style="color:#f92672">#</span><span style="color:#111">(</span><span style="color:#75af00">paths</span> <span style="color:#111">nodes-fn</span> <span style="color:#111">(</span><span style="color:#111">conj path </span><span style="color:#111">%</span><span style="color:#111">)))</span>
</span></span><span style="display:flex;"><span>         <span style="color:#111">(</span><span style="color:#111">cons </span><span style="color:#111">path</span><span style="color:#111">))))</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">defn </span><span style="color:#111">trace-paths</span> <span style="color:#111">[</span><span style="color:#111">m</span> <span style="color:#111">start</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#111">remove </span><span style="color:#f92672">#</span><span style="color:#111">(</span><span style="color:#75af00">m</span> <span style="color:#111">(</span><span style="color:#111">peek </span><span style="color:#111">%</span><span style="color:#111">))</span> <span style="color:#111">(</span><span style="color:#75af00">paths</span> <span style="color:#111">m</span> <span style="color:#111">[</span><span style="color:#111">start</span><span style="color:#111">])))</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">defn- </span><span style="color:#111">find-paths</span> <span style="color:#111">[</span><span style="color:#111">from-map</span> <span style="color:#111">to-map</span> <span style="color:#111">matches</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#111">for </span><span style="color:#111">[</span><span style="color:#111">n</span> <span style="color:#111">matches</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">from</span> <span style="color:#111">(</span><span style="color:#111">map reverse </span><span style="color:#111">(</span><span style="color:#75af00">trace-paths</span> <span style="color:#111">from-map</span> <span style="color:#111">n</span><span style="color:#111">))</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">to</span> <span style="color:#111">(</span><span style="color:#111">map rest </span><span style="color:#111">(</span><span style="color:#75af00">trace-paths</span> <span style="color:#111">to-map</span> <span style="color:#111">n</span><span style="color:#111">))]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">(</span><span style="color:#75af00">vec</span> <span style="color:#111">(</span><span style="color:#111">concat </span><span style="color:#111">from</span> <span style="color:#111">to</span><span style="color:#111">))))</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">defn- </span><span style="color:#111">neighbor-pairs</span> <span style="color:#111">[</span><span style="color:#111">neighbors</span> <span style="color:#111">q</span> <span style="color:#111">coll</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#111">for </span><span style="color:#111">[</span><span style="color:#111">node </span><span style="color:#111">q</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">nbr</span> <span style="color:#111">(</span><span style="color:#75af00">neighbors</span> <span style="color:#111">node</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#d88200">:when</span> <span style="color:#111">(</span><span style="color:#111">not </span><span style="color:#111">(</span><span style="color:#111">contains? </span><span style="color:#111">coll</span> <span style="color:#111">nbr</span><span style="color:#111">))]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">[</span><span style="color:#111">nbr</span> <span style="color:#111">node</span><span style="color:#111">]))</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">defn </span><span style="color:#111">bidirectional-bfs</span> <span style="color:#111">[</span><span style="color:#111">start</span> <span style="color:#111">end</span> <span style="color:#111">neighbors</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#00a8c8">let </span><span style="color:#111">[</span><span style="color:#111">find-pairs</span> <span style="color:#111">(</span><span style="color:#111">partial </span><span style="color:#111">neighbor-pairs</span> <span style="color:#111">neighbors</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">overlaps</span> <span style="color:#111">(</span><span style="color:#00a8c8">fn </span><span style="color:#111">[</span><span style="color:#111">coll</span> <span style="color:#111">q</span><span style="color:#111">]</span> <span style="color:#111">(</span><span style="color:#111">seq </span><span style="color:#111">(</span><span style="color:#111">filter </span><span style="color:#f92672">#</span><span style="color:#111">(</span><span style="color:#111">contains? </span><span style="color:#111">coll</span> <span style="color:#111">%</span><span style="color:#111">)</span> <span style="color:#111">q</span><span style="color:#111">)))</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">map-set-pairs</span> <span style="color:#111">(</span><span style="color:#00a8c8">fn </span><span style="color:#111">[</span><span style="color:#111">map </span><span style="color:#111">pairs</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>                        <span style="color:#111">(</span><span style="color:#75af00">persistent!</span> <span style="color:#111">(</span><span style="color:#111">reduce </span><span style="color:#111">(</span><span style="color:#00a8c8">fn </span><span style="color:#111">[</span><span style="color:#111">map </span><span style="color:#111">[</span><span style="color:#111">key </span><span style="color:#111">val</span><span style="color:#111">]]</span>
</span></span><span style="display:flex;"><span>                                  <span style="color:#111">(</span><span style="color:#75af00">assoc!</span> <span style="color:#111">map key </span><span style="color:#111">(</span><span style="color:#111">conj </span><span style="color:#111">(</span><span style="color:#111">get map key </span><span style="color:#f92672">#</span><span style="color:#111">{})</span> <span style="color:#111">val</span><span style="color:#111">)))</span>
</span></span><span style="display:flex;"><span>                                <span style="color:#111">(</span><span style="color:#75af00">transient</span> <span style="color:#111">map</span><span style="color:#111">)</span> <span style="color:#111">pairs</span><span style="color:#111">)))]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">(</span><span style="color:#00a8c8">loop </span><span style="color:#111">[</span><span style="color:#111">preds</span> <span style="color:#111">{</span><span style="color:#111">start</span> <span style="color:#111">nil</span><span style="color:#111">}</span> <span style="color:#75715e">; map of outgoing nodes to where they came from</span>
</span></span><span style="display:flex;"><span>           <span style="color:#111">succs</span> <span style="color:#111">{</span><span style="color:#111">end</span> <span style="color:#111">nil</span><span style="color:#111">}</span>   <span style="color:#75715e">; map of incoming nodes to where they came from</span>
</span></span><span style="display:flex;"><span>           <span style="color:#111">q1</span> <span style="color:#111">(</span><span style="color:#111">list </span><span style="color:#111">start</span><span style="color:#111">)</span>   <span style="color:#75715e">; queue of outgoing things to check</span>
</span></span><span style="display:flex;"><span>           <span style="color:#111">q2</span> <span style="color:#111">(</span><span style="color:#111">list </span><span style="color:#111">end</span><span style="color:#111">)]</span>    <span style="color:#75715e">; queue of incoming things to check</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">(</span><span style="color:#111">when </span><span style="color:#111">(</span><span style="color:#111">and </span><span style="color:#111">(</span><span style="color:#111">seq </span><span style="color:#111">q1</span><span style="color:#111">)</span> <span style="color:#111">(</span><span style="color:#111">seq </span><span style="color:#111">q2</span><span style="color:#111">))</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">(</span><span style="color:#00a8c8">if </span><span style="color:#111">(</span><span style="color:#111">&lt;= </span><span style="color:#111">(</span><span style="color:#111">count </span><span style="color:#111">q1</span><span style="color:#111">)</span> <span style="color:#111">(</span><span style="color:#111">count </span><span style="color:#111">q2</span><span style="color:#111">))</span>
</span></span><span style="display:flex;"><span>          <span style="color:#111">(</span><span style="color:#00a8c8">let </span><span style="color:#111">[</span><span style="color:#111">pairs</span> <span style="color:#111">(</span><span style="color:#75af00">find-pairs</span> <span style="color:#111">q1</span> <span style="color:#111">preds</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>                <span style="color:#111">preds</span> <span style="color:#111">(</span><span style="color:#75af00">map-set-pairs</span> <span style="color:#111">preds</span> <span style="color:#111">pairs</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>                <span style="color:#111">q1</span> <span style="color:#111">(</span><span style="color:#111">map first </span><span style="color:#111">pairs</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>            <span style="color:#111">(</span><span style="color:#111">if-let </span><span style="color:#111">[</span><span style="color:#111">all</span> <span style="color:#111">(</span><span style="color:#75af00">overlaps</span> <span style="color:#111">succs</span> <span style="color:#111">q1</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>              <span style="color:#111">(</span><span style="color:#75af00">find-paths</span> <span style="color:#111">preds</span> <span style="color:#111">succs</span> <span style="color:#111">(</span><span style="color:#111">set </span><span style="color:#111">all</span><span style="color:#111">))</span>
</span></span><span style="display:flex;"><span>              <span style="color:#111">(</span><span style="color:#75af00">recur</span> <span style="color:#111">preds</span> <span style="color:#111">succs</span> <span style="color:#111">q1</span> <span style="color:#111">q2</span><span style="color:#111">)))</span>
</span></span><span style="display:flex;"><span>          <span style="color:#111">(</span><span style="color:#00a8c8">let </span><span style="color:#111">[</span><span style="color:#111">pairs</span> <span style="color:#111">(</span><span style="color:#75af00">find-pairs</span> <span style="color:#111">q2</span> <span style="color:#111">succs</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>                <span style="color:#111">succs</span> <span style="color:#111">(</span><span style="color:#75af00">map-set-pairs</span> <span style="color:#111">succs</span> <span style="color:#111">pairs</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>                <span style="color:#111">q2</span> <span style="color:#111">(</span><span style="color:#111">map first </span><span style="color:#111">pairs</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>            <span style="color:#111">(</span><span style="color:#111">if-let </span><span style="color:#111">[</span><span style="color:#111">all</span> <span style="color:#111">(</span><span style="color:#75af00">overlaps</span> <span style="color:#111">preds</span> <span style="color:#111">q2</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>              <span style="color:#111">(</span><span style="color:#75af00">find-paths</span> <span style="color:#111">preds</span> <span style="color:#111">succs</span> <span style="color:#111">(</span><span style="color:#111">set </span><span style="color:#111">all</span><span style="color:#111">))</span>
</span></span><span style="display:flex;"><span>              <span style="color:#111">(</span><span style="color:#75af00">recur</span> <span style="color:#111">preds</span> <span style="color:#111">succs</span> <span style="color:#111">q1</span> <span style="color:#111">q2</span><span style="color:#111">))))))))</span>
</span></span></code></pre></div><p>There&rsquo;s a lot of code here, including some optimizations and helper functions.
The important function here is <code>bidirectional-bfs</code>. I won&rsquo;t explain the details
of the algorithm, but at a high level, it takes in a start and end node and a
function to be called on any node to get it&rsquo;s &ldquo;neighbors&rdquo;.</p>
<p>This is a generic, pure function, agnostic of Datomic or our data. In fact, I used
a simple map as the &ldquo;graph&rdquo; while developing this:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">def </span><span style="color:#111">graph</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">{</span><span style="color:#d88200">:a</span> <span style="color:#111">[</span><span style="color:#d88200">:b</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>   <span style="color:#d88200">:b</span> <span style="color:#111">[</span><span style="color:#d88200">:a</span> <span style="color:#d88200">:c</span> <span style="color:#d88200">:d</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>   <span style="color:#d88200">:c</span> <span style="color:#111">[</span><span style="color:#d88200">:b</span> <span style="color:#d88200">:e</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>   <span style="color:#d88200">:d</span> <span style="color:#111">[</span><span style="color:#d88200">:b</span> <span style="color:#d88200">:c</span> <span style="color:#d88200">:e</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>   <span style="color:#d88200">:e</span> <span style="color:#111">[</span><span style="color:#d88200">:c</span> <span style="color:#d88200">:d</span> <span style="color:#d88200">:f</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>   <span style="color:#d88200">:f</span> <span style="color:#111">[]})</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#75af00">bidirectional-bfs</span> <span style="color:#d88200">:a</span> <span style="color:#d88200">:e</span> <span style="color:#111">graph</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">;=&gt; [[:a :b :c :e] [:a :b :d :e]]</span>
</span></span></code></pre></div><p>To use this generic algorithm with our database, we need a <code>neighbors</code> function.
Depending on whether a node is an &ldquo;actor&rdquo; or a &ldquo;movie&rdquo;, we need to return its
appropriate counterpart. A naive &ldquo;or&rdquo; condition is actually good enough here:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">defn </span><span style="color:#111">movie-actors</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">&#34;Given a Datomic database value and a movie id,
</span></span></span><span style="display:flex;"><span><span style="color:#d88200">  returns ids for actors in that movie.&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">[</span><span style="color:#111">db</span> <span style="color:#111">eid</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#111">map </span><span style="color:#d88200">:e</span> <span style="color:#111">(</span><span style="color:#75af00">d/datoms</span> <span style="color:#111">db</span> <span style="color:#d88200">:vaet</span> <span style="color:#111">eid</span> <span style="color:#d88200">:actor/movies</span><span style="color:#111">)))</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">defn </span><span style="color:#111">actor-movies</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">&#34;Given a Datomic database value and an actor id,
</span></span></span><span style="display:flex;"><span><span style="color:#d88200">  returns ids for movies that actor was in.&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">[</span><span style="color:#111">db</span> <span style="color:#111">eid</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#111">map </span><span style="color:#d88200">:v</span> <span style="color:#111">(</span><span style="color:#75af00">d/datoms</span> <span style="color:#111">db</span> <span style="color:#d88200">:eavt</span> <span style="color:#111">eid</span> <span style="color:#d88200">:actor/movies</span><span style="color:#111">)))</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">defn </span><span style="color:#111">neighbors</span>
</span></span><span style="display:flex;"><span>  <span style="color:#d88200">&#34;db is database value
</span></span></span><span style="display:flex;"><span><span style="color:#d88200">  eid is an actor or movie eid&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">[</span><span style="color:#111">db</span> <span style="color:#111">eid</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#111">or </span><span style="color:#111">(</span><span style="color:#111">seq </span><span style="color:#111">(</span><span style="color:#75af00">actor-movies</span> <span style="color:#111">db</span> <span style="color:#111">eid</span><span style="color:#111">))</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">(</span><span style="color:#111">seq </span><span style="color:#111">(</span><span style="color:#75af00">movie-actors</span> <span style="color:#111">db</span> <span style="color:#111">eid</span><span style="color:#111">))))</span>
</span></span></code></pre></div><p>Gluing everything together is a simple matter of partial application:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">defn </span><span style="color:#111">find-id-paths</span> <span style="color:#111">[</span><span style="color:#111">db</span> <span style="color:#111">source</span> <span style="color:#111">target</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#75af00">bidirectional-bfs</span> <span style="color:#111">source</span> <span style="color:#111">target</span> <span style="color:#111">(</span><span style="color:#111">partial </span><span style="color:#111">neighbors</span> <span style="color:#111">db</span><span style="color:#111">)))</span>
</span></span></code></pre></div><p>Given a source entity id and a target entity id, this will return all shortest
paths (ids), much like the query example above. From there, we could map them
to Datomic entities, get their names, or sort the paths using a domain-specific
heuristic. Plugging in the previous example, we might do something like the
following:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-clojure" data-lang="clojure"><span style="display:flex;"><span><span style="color:#111">(</span><span style="color:#00a8c8">let </span><span style="color:#111">[</span><span style="color:#111">d</span> <span style="color:#111">(</span><span style="color:#75af00">db</span> <span style="color:#111">conn</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">d</span> <span style="color:#111">(</span><span style="color:#75af00">d/filter</span> <span style="color:#111">d</span> <span style="color:#111">(</span><span style="color:#75af00">without-documentaries</span> <span style="color:#111">d</span><span style="color:#111">))</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">biebs</span> <span style="color:#111">(</span><span style="color:#75af00">d/entid</span> <span style="color:#111">d</span> <span style="color:#111">[</span><span style="color:#d88200">:actor/name</span> <span style="color:#d88200">&#34;Bieber, Justin&#34;</span><span style="color:#111">])</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">bacon</span> <span style="color:#111">(</span><span style="color:#75af00">d/entid</span> <span style="color:#111">d</span> <span style="color:#111">[</span><span style="color:#d88200">:actor/name</span> <span style="color:#d88200">&#34;Bacon, Kevin (I)&#34;</span><span style="color:#111">])</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">name </span><span style="color:#111">(</span><span style="color:#111">partial </span><span style="color:#111">actor-or-movie-name</span> <span style="color:#111">d</span><span style="color:#111">)]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">(</span><span style="color:#111">map </span><span style="color:#111">(</span><span style="color:#111">partial </span><span style="color:#111">mapv</span> <span style="color:#111">name</span><span style="color:#111">)</span> <span style="color:#111">(</span><span style="color:#75af00">find-id-paths</span> <span style="color:#111">d</span> <span style="color:#111">biebs</span> <span style="color:#111">bacon</span><span style="color:#111">)))</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">;=&gt; ([&#34;Bieber, Justin&#34; &#34;Men in Black 3 (2012)&#34; &#34;Jones, Tommy Lee&#34; &#34;JFK (1991)&#34; &#34;Bacon, Kevin (I)&#34;] [&#34;Bieber, Justin&#34; &#34;Men in Black 3 (2012)&#34; &#34;Segal, Tobias&#34; &#34;R.I.P.D. (2013)&#34; &#34;Bacon, Kevin (I)&#34;] [&#34;Bieber, Justin&#34; &#34;Men in Black 3 (2012)&#34; &#34;Brolin, Josh&#34; &#34;Hollow Man (2000)&#34; &#34;Bacon, Kevin (I)&#34;] [&#34;Bieber, Justin&#34; &#34;Men in Black 3 (2012)&#34; &#34;Howard, Rosemary (II)&#34; &#34;R.I.P.D. (2013)&#34; &#34;Bacon, Kevin (I)&#34;])</span>
</span></span></code></pre></div><p>This returns the same set of paths as the query method did. However, this
version has the advantage of going to an arbitrary depth.</p>
<p>This is just one example of graph searching with Datomic. Different kinds of
problems and domains could use other algorithms. The idea, though, is that
generic graph searching functions can be used directly, since the data is
effectively local to the peer machine.</p>
<p>For more Clojure implementations of generic graph searching algorithms,
<a href="https://github.com/aysylu/loom"  class="external-link" target="_blank" rel="noopener">loom&rsquo;s</a> <a href="https://github.com/aysylu/loom/blob/master/src/loom/alg_generic.clj"  class="external-link" target="_blank" rel="noopener">alg_generic</a> namespace is a great starting point.</p>
<h2 id="performance">
  Performance
  <a class="heading-link" href="#performance">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>I&rsquo;m using the above ideas and functions on IMDB&rsquo;s dataset to power
the project. Once the peer&rsquo;s index caches are warmed, the performance
is quite good: most searches I&rsquo;ve performed between well-known actors complete
in under a second, and in many cases, under 100 ms. I never got results that
good with Neo4j&rsquo;s cypher query language.</p>
<h2 id="source">
  Source
  <a class="heading-link" href="#source">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The code in this post is based on the <a href="https://github.com/jgdavey/kevin"  class="external-link" target="_blank" rel="noopener">source</a>.</p>
]]></content>
  </entry>
  <entry>
    <title type="html">Deferring constraints in PostgreSQL</title>
    <link href="https://joshuadavey.com/2013/08/22/deferring-database-constraints/" rel="alternate" type="text/html" title="Deferring constraints in PostgreSQL"/>
    <published>2013-08-22T00:00:00+00:00</published>
    <updated>2013-08-22T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2013/08/22/deferring-database-constraints/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2013/08/22/deferring-database-constraints/"><![CDATA[<p><em>This is a repost. You can find the original <a href="https://hashrocket.com/blog/posts/deferring-database-constraints" title="Deferring constraints in PostgreSQL"  class="external-link" target="_blank" rel="noopener">here</a></em></p>
<p>Database constraints are essential to ensuring data integrity, and you
should use them. Allowing them to be deferrable during transactions
makes them even more convenient. A common scenario in which the database
can help us is in a sortable list implementation. This post outlines
the how and why of deferring database constraints, using a sortable list
domain as an example.</p>
<h1 id="modeling-lists-of-sortable-items">
  Modeling lists of sortable items
  <a class="heading-link" href="#modeling-lists-of-sortable-items">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h1>
<p>Imagine that you have an application with multiple lists. Each list has
items that can be reordered with a drag-and-drop interaction. This can
be modelled in a fairly straightforward manner.</p>
<p>Each list <code>has_many</code> list items, which are ordered by the <code>position</code>
column. Each list&rsquo;s items have a position beginning with 1 and
incrementing with each subsequent item.</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#75715e"># app/models/list.rb</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">List</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">has_many</span> <span style="color:#d88200">:items</span><span style="color:#111">,</span> <span style="color:#f92672">-&gt;</span> <span style="color:#111">{</span> <span style="color:#111">order</span> <span style="color:#d88200">:position</span> <span style="color:#111">},</span> <span style="color:#d88200">class_name</span><span style="color:#111">:</span> <span style="color:#d88200">&#34;ListItem&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">validates_presence_of</span> <span style="color:#d88200">:name</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#75715e"># app/models/list_item.rb</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">ListItem</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">belongs_to</span> <span style="color:#d88200">:list</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">validates_presence_of</span> <span style="color:#d88200">:name</span><span style="color:#111">,</span> <span style="color:#d88200">:list</span><span style="color:#111">,</span> <span style="color:#d88200">:position</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">before_validation</span> <span style="color:#d88200">:ensure_position</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">self</span><span style="color:#f92672">.</span><span style="color:#75af00">update_positions</span><span style="color:#111">(</span><span style="color:#111">ids</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">ids</span><span style="color:#f92672">.</span><span style="color:#111">each_with_index</span> <span style="color:#00a8c8">do</span> <span style="color:#f92672">|</span><span style="color:#111">id</span><span style="color:#111">,</span> <span style="color:#111">index</span><span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">where</span><span style="color:#111">(</span><span style="color:#111">id</span><span style="color:#111">:</span> <span style="color:#111">id</span><span style="color:#111">)</span><span style="color:#f92672">.</span><span style="color:#111">update_all</span><span style="color:#111">(</span><span style="color:#d88200">position</span><span style="color:#111">:</span> <span style="color:#111">index</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">private</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">ensure_position</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">self</span><span style="color:#f92672">.</span><span style="color:#111">position</span> <span style="color:#f92672">||=</span> <span style="color:#111">self</span><span style="color:#f92672">.</span><span style="color:#111">class</span><span style="color:#f92672">.</span><span style="color:#111">where</span><span style="color:#111">(</span><span style="color:#d88200">list_id</span><span style="color:#111">:</span> <span style="color:#111">list_id</span><span style="color:#111">)</span><span style="color:#f92672">.</span><span style="color:#111">maximum</span><span style="color:#111">(</span><span style="color:#d88200">:position</span><span style="color:#111">)</span><span style="color:#f92672">.</span><span style="color:#111">to_i</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>A couple things are worth noting about the <code>ListItem</code> class. Firstly, we
have <code>update_positions</code>, a class method that accepts an array of ids and
updates each. This method will be called in a <code>sort</code> controller action
as such:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">ItemsController</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ApplicationController</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">expose</span><span style="color:#111">(</span><span style="color:#d88200">:list</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">sort</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># list item ids is an ordered array of ids</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">list</span><span style="color:#f92672">.</span><span style="color:#111">items</span><span style="color:#f92672">.</span><span style="color:#111">update_positions</span><span style="color:#111">(</span><span style="color:#111">params</span><span style="color:#f92672">[</span><span style="color:#d88200">:list_item_ids</span><span style="color:#f92672">]</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">head</span> <span style="color:#d88200">:ok</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>Secondly, new items don&rsquo;t necessarily know what position they should
have, so we put list items that don&rsquo;t have <code>position</code> at the end of
their respective list, just before validating that the position is
present.</p>
<p>Here are the migrations that we used to create the models&rsquo; database
tables:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">CreateLists</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Migration</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">change</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">create_table</span> <span style="color:#d88200">:lists</span> <span style="color:#00a8c8">do</span> <span style="color:#f92672">|</span><span style="color:#111">t</span><span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">t</span><span style="color:#f92672">.</span><span style="color:#111">string</span> <span style="color:#d88200">:name</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">t</span><span style="color:#f92672">.</span><span style="color:#111">timestamps</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">CreateListItems</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Migration</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">change</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">create_table</span> <span style="color:#d88200">:list_items</span> <span style="color:#00a8c8">do</span> <span style="color:#f92672">|</span><span style="color:#111">t</span><span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">t</span><span style="color:#f92672">.</span><span style="color:#111">belongs_to</span> <span style="color:#d88200">:list</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">t</span><span style="color:#f92672">.</span><span style="color:#111">integer</span> <span style="color:#d88200">:position</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">t</span><span style="color:#f92672">.</span><span style="color:#111">string</span> <span style="color:#d88200">:name</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">t</span><span style="color:#f92672">.</span><span style="color:#111">timestamps</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>Notice anything missing? If you said database constraints, you&rsquo;re
correct! Our application is enforcing presence for most attributes, but
our corresponding columns are missing <code>NOT NULL</code> constraints. Also, the
<code>list_id</code> column on <code>list_items</code> is missing a foreign key constraint.</p>
<p>But I&rsquo;d like to focus on another missing constraint. Our domain model has
an implicit requirement that we haven&rsquo;t enforced with either validations
or database constraints: each list item&rsquo;s position should be <em>unique per
list</em>. No two list items in a list should have the same position. That
would make the ordering non-deterministic.</p>
<p>We could add a uniqueness validation for <code>position</code>, scoped to
the <code>list_id</code>. However, as <a href="https://robots.thoughtbot.com/post/55689359336/the-perils-of-uniqueness-validations" title="The Perils of Uniqueness Validations"  class="external-link" target="_blank" rel="noopener">thoughtbot recently warned</a>,
application-level uniqueness validations are insufficient <em>at best</em>, and
fail completely in concurrent environments.</p>
<p>The <code>position</code> column needs a database-level constraint.</p>
<h1 id="adding-constraints">
  Adding constraints
  <a class="heading-link" href="#adding-constraints">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h1>
<p>Adding the uniqueness constraint to <code>position</code> is fairly straightforward
in PostgreSQL. We&rsquo;ll just create a new migration with the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">AddUniquenessValidationOnListItems</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Migration</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">up</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">execute</span> <span style="color:#d88200">&lt;&lt;-SQL
</span></span></span><span style="display:flex;"><span><span style="color:#d88200"></span>      <span style="color:#111">alter</span> <span style="color:#111">table</span> <span style="color:#111">list_items</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">add</span> <span style="color:#111">constraint</span> <span style="color:#111">list_item_position</span> <span style="color:#111">unique</span> <span style="color:#111">(</span><span style="color:#111">list_id</span><span style="color:#111">,</span> <span style="color:#111">position</span><span style="color:#111">);</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">SQL</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">down</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">execute</span> <span style="color:#d88200">&lt;&lt;-SQL
</span></span></span><span style="display:flex;"><span><span style="color:#d88200"></span>      <span style="color:#111">alter</span> <span style="color:#111">table</span> <span style="color:#111">list_items</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">drop</span> <span style="color:#111">constraint</span> <span style="color:#00a8c8">if</span> <span style="color:#111">exists</span> <span style="color:#111">list_item_position</span><span style="color:#111">;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">SQL</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>Let&rsquo;s wrap our <code>UPDATE</code> statements in a transaction so that any failed
<code>UPDATE</code> of the position column will result in none of them being
updated:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">ListItem</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># ...</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">self</span><span style="color:#f92672">.</span><span style="color:#75af00">update_positions</span><span style="color:#111">(</span><span style="color:#111">ids</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">transaction</span> <span style="color:#00a8c8">do</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">ids</span><span style="color:#f92672">.</span><span style="color:#111">each_with_index</span> <span style="color:#00a8c8">do</span> <span style="color:#f92672">|</span><span style="color:#111">id</span><span style="color:#111">,</span> <span style="color:#111">index</span><span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">where</span><span style="color:#111">(</span><span style="color:#111">id</span><span style="color:#111">:</span> <span style="color:#111">id</span><span style="color:#111">)</span><span style="color:#f92672">.</span><span style="color:#111">update_all</span><span style="color:#111">(</span><span style="color:#d88200">position</span><span style="color:#111">:</span> <span style="color:#111">index</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>      <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>This ensures at the database level that positions of items are unique
per list; no two items in the same list can occupy the &ldquo;1&rdquo; position.
With regard to data integrity, this is a huge improvement over our
initial implementation. But it has one drawback: <strong>it doesn&rsquo;t work</strong>.</p>
<p>To illustrate why, imagine a list with the following items:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>id | position | name
</span></span><span style="display:flex;"><span>13 | 1        | Eggs
</span></span><span style="display:flex;"><span>18 | 2        | Milk
</span></span><span style="display:flex;"><span>35 | 3        | Bread
</span></span></code></pre></div><p>To move Bread to the top of the list, we would pass an array of ids,
<code>[35,13,18]</code> to the <code>update_positions</code> method. This method does a series
of <code>UPDATE</code> statements to the database. For the first id, the one for
Bread, we end up sending an update statement that would look like the
following:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#00a8c8">UPDATE</span> <span style="color:#111">list_items</span> <span style="color:#00a8c8">SET</span> <span style="color:#00a8c8">position</span><span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> <span style="color:#00a8c8">WHERE</span> <span style="color:#111">id</span><span style="color:#f92672">=</span><span style="color:#ae81ff">35</span><span style="color:#111">;</span>
</span></span></code></pre></div><p>After this statement is executed in the database, but before we move on
to the next id in the list, Postgres will fail its constraint checks. At
the moment that the <code>UPDATE</code> happens, the data would be:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>id | position | name
</span></span><span style="display:flex;"><span>13 | 1        | Eggs
</span></span><span style="display:flex;"><span>18 | 2        | Milk
</span></span><span style="display:flex;"><span>35 | 1        | Bread
</span></span></code></pre></div><p>With both Eggs and Bread occupying the same position, the <code>UPDATE</code> fails.
Of course, we know that we want to change the position of Eggs as well,
so that its position would be &ldquo;2&rdquo;, and that collision would not happen.
But at the time that the constraint-check happens, the database doesn&rsquo;t
know this.</p>
<p>Even within a transaction, database uniqueness constraints are enforced
immediately <strong>per row</strong>. It seems our dreams of data integrity are
smashed. If only there were a way to enforce uniqueness constraints at
the end of the transaction, rather than the end of each statement&hellip;</p>
<h1 id="deferrable-constraints">
  Deferrable Constraints
  <a class="heading-link" href="#deferrable-constraints">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h1>
<p>As mentioned before, constraints are immediately enforced. This
behavior can be changed within a transaction by changing a constraints
deferrable characteristics. In PostgreSQL, constraints are assumed to
be <code>NOT DEFERRABLE</code> by default.</p>
<p>However, constraints can also behave as deferrable in one of two ways:
<code>DEFERRABLE INITIALLY IMMEDIATE</code> or <code>DEFERRABLE INITIALLY DEFERRED</code>.
The first part, <code>DEFERRABLE</code>, is what allows the database constraint
behavior to change within transactions. The second part describes what
the default behavior will be within a transaction.</p>
<p>With a constraint that is deferrable, but initially immediate, the
constraint will by default behave just like a non-deferrable constraint,
checking every statement immediately. A constraint that is initially
deferred will, by default, defer its checks until the transaction is
committed. Both of these can change their behavior per-transaction with a
call to <code>SET CONSTRAINTS</code> (<a href="https://www.postgresql.org/docs/9.2/static/sql-set-constraints.html"  class="external-link" target="_blank" rel="noopener">documentation</a>).</p>
<p>With that information, let&rsquo;s change the definition of the constraint we
defined before:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">AddUniquenessValidationOnListItems</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Migration</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">up</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">execute</span> <span style="color:#d88200">&lt;&lt;-SQL
</span></span></span><span style="display:flex;"><span><span style="color:#d88200"></span>      <span style="color:#111">alter</span> <span style="color:#111">table</span> <span style="color:#111">list_items</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">add</span> <span style="color:#111">constraint</span> <span style="color:#111">list_item_position</span> <span style="color:#111">unique</span> <span style="color:#111">(</span><span style="color:#111">list_id</span><span style="color:#111">,</span> <span style="color:#111">position</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#00a8c8">DEFERRABLE</span> <span style="color:#00a8c8">INITIALLY</span> <span style="color:#00a8c8">IMMEDIATE</span><span style="color:#111">;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">SQL</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">down</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">execute</span> <span style="color:#d88200">&lt;&lt;-SQL
</span></span></span><span style="display:flex;"><span><span style="color:#d88200"></span>      <span style="color:#111">alter</span> <span style="color:#111">table</span> <span style="color:#111">list_items</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">drop</span> <span style="color:#111">constraint</span> <span style="color:#00a8c8">if</span> <span style="color:#111">exists</span> <span style="color:#111">list_item_position</span><span style="color:#111">;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">SQL</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>The only thing we&rsquo;ve changed from before is the <code>DEFERRABLE INITIALLY IMMEDIATE</code> bit. I think it is a good idea to use the <code>INITIALLY IMMEDIATE</code> option. This will ensure that other parts of our app, and
other consumers of the database will not be surprised by the behavior of
the constraint; it will continue to act a like a normal, non-deferrable
constraint, until we explicitly opt in to the deferral.</p>
<p>We now need to change our transaction block. In our case, the first
database statement within the transaction must be the <code>SET CONSTRAINTS</code>
statement:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">ListItem</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># ...</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">self</span><span style="color:#f92672">.</span><span style="color:#75af00">update_positions</span><span style="color:#111">(</span><span style="color:#111">ids</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">transaction</span> <span style="color:#00a8c8">do</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">connection</span><span style="color:#f92672">.</span><span style="color:#111">execute</span> <span style="color:#d88200">&#34;SET CONSTRAINTS list_item_position DEFERRED&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">ids</span><span style="color:#f92672">.</span><span style="color:#111">each_with_index</span> <span style="color:#00a8c8">do</span> <span style="color:#f92672">|</span><span style="color:#111">id</span><span style="color:#111">,</span> <span style="color:#111">index</span><span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">where</span><span style="color:#111">(</span><span style="color:#111">id</span><span style="color:#111">:</span> <span style="color:#111">id</span><span style="color:#111">)</span><span style="color:#f92672">.</span><span style="color:#111">update_all</span><span style="color:#111">(</span><span style="color:#d88200">position</span><span style="color:#111">:</span> <span style="color:#111">index</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>      <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>Having now opted in to deferring our uniqueness constraint, reordering
the items now works as expected. The constraint still ensures that we
don&rsquo;t have two items that occupy the same position, but waits until the
end of the transaction to do that check. We can have our data integrity
cake and eat it too.</p>
<h1 id="bonus-deferrable-introspection">
  Bonus: deferrable introspection
  <a class="heading-link" href="#bonus-deferrable-introspection">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h1>
<p>Having to name the constraint in two places is a bit of a bummer, and
introduces a coupling that could bite us if the constraint name ever
changed. Knowing that, we leverage PostgreSQL&rsquo;s introspective abilities
to query the constraint names instead.</p>
<p>For example, we can add the following module to our codebase:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#75715e"># lib/deferrable.rb</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">module</span> <span style="color:#111">Deferrable</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">deferrable_uniqueness_constraints_on</span><span style="color:#111">(</span><span style="color:#111">column_name</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">usage</span> <span style="color:#f92672">=</span> <span style="color:#00a8c8">Arel</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Table</span><span style="color:#f92672">.</span><span style="color:#111">new</span> <span style="color:#d88200">&#39;information_schema.constraint_column_usage&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">constraint</span> <span style="color:#f92672">=</span> <span style="color:#00a8c8">Arel</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Table</span><span style="color:#f92672">.</span><span style="color:#111">new</span> <span style="color:#d88200">&#39;pg_constraint&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">arel</span> <span style="color:#f92672">=</span> <span style="color:#111">usage</span><span style="color:#f92672">.</span><span style="color:#111">project</span><span style="color:#111">(</span><span style="color:#111">usage</span><span style="color:#f92672">[</span><span style="color:#d88200">:constraint_name</span><span style="color:#f92672">]</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">.</span><span style="color:#111">join</span><span style="color:#111">(</span><span style="color:#111">constraint</span><span style="color:#111">)</span><span style="color:#f92672">.</span><span style="color:#111">on</span><span style="color:#111">(</span><span style="color:#111">usage</span><span style="color:#f92672">[</span><span style="color:#d88200">:constraint_name</span><span style="color:#f92672">].</span><span style="color:#111">eq</span><span style="color:#111">(</span><span style="color:#111">constraint</span><span style="color:#f92672">[</span><span style="color:#d88200">:conname</span><span style="color:#f92672">]</span><span style="color:#111">))</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">.</span><span style="color:#111">where</span><span style="color:#111">(</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">(</span><span style="color:#111">constraint</span><span style="color:#f92672">[</span><span style="color:#d88200">:contype</span><span style="color:#f92672">].</span><span style="color:#111">eq</span><span style="color:#111">(</span><span style="color:#d88200">&#39;u&#39;</span><span style="color:#111">))</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">.</span><span style="color:#111">and</span><span style="color:#111">(</span><span style="color:#111">constraint</span><span style="color:#f92672">[</span><span style="color:#d88200">:condeferrable</span><span style="color:#f92672">]</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">.</span><span style="color:#111">and</span><span style="color:#111">(</span><span style="color:#111">usage</span><span style="color:#f92672">[</span><span style="color:#d88200">:table_name</span><span style="color:#f92672">].</span><span style="color:#111">eq</span><span style="color:#111">(</span><span style="color:#111">table_name</span><span style="color:#111">))</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">.</span><span style="color:#111">and</span><span style="color:#111">(</span><span style="color:#111">usage</span><span style="color:#f92672">[</span><span style="color:#d88200">:column_name</span><span style="color:#f92672">].</span><span style="color:#111">eq</span><span style="color:#111">(</span><span style="color:#111">column_name</span><span style="color:#111">))</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">connection</span><span style="color:#f92672">.</span><span style="color:#111">select_values</span> <span style="color:#111">arel</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">transaction_with_deferred_constraints_on</span><span style="color:#111">(</span><span style="color:#111">column_name</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">transaction</span> <span style="color:#00a8c8">do</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">constraints</span> <span style="color:#f92672">=</span> <span style="color:#111">deferrable_uniqueness_constraints_on</span><span style="color:#111">(</span><span style="color:#111">column_name</span><span style="color:#111">)</span><span style="color:#f92672">.</span><span style="color:#111">join</span> <span style="color:#d88200">&#34;,&#34;</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">connection</span><span style="color:#f92672">.</span><span style="color:#111">execute</span><span style="color:#111">(</span><span style="color:#d88200">&#34;SET CONSTRAINTS %s DEFERRED&#34;</span> <span style="color:#f92672">%</span> <span style="color:#111">constraints</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>      <span style="color:#00a8c8">yield</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>And now change our model to use it:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">ListItem</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">extend</span> <span style="color:#00a8c8">Deferrable</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># ...</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">self</span><span style="color:#f92672">.</span><span style="color:#75af00">update_positions</span><span style="color:#111">(</span><span style="color:#111">ids</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">transaction_with_deferred_constraints_on</span><span style="color:#111">(</span><span style="color:#d88200">:position</span><span style="color:#111">)</span> <span style="color:#00a8c8">do</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">ids</span><span style="color:#f92672">.</span><span style="color:#111">each_with_index</span> <span style="color:#00a8c8">do</span> <span style="color:#f92672">|</span><span style="color:#111">id</span><span style="color:#111">,</span> <span style="color:#111">index</span><span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">where</span><span style="color:#111">(</span><span style="color:#111">id</span><span style="color:#111">:</span> <span style="color:#111">id</span><span style="color:#111">)</span><span style="color:#f92672">.</span><span style="color:#111">update_all</span><span style="color:#111">(</span><span style="color:#d88200">position</span><span style="color:#111">:</span> <span style="color:#111">index</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>      <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>And, boom! Less coupling.</p>
<p><strong>NOTE</strong> That&rsquo;s a lot of Arel! Use at your own risk. ;-)</p>
<h1 id="example-application">
  Example application
  <a class="heading-link" href="#example-application">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h1>
<p>While writing this post, I created a <a href="https://github.com/jgdavey/uniqueness_constraints_example"  class="external-link" target="_blank" rel="noopener">sample Rails app</a> to iterate
quickly. I used TDD to write the initial approach, and reused the specs
while I &ldquo;refactored&rdquo; the implementation to the subsequent approaches.
Each <a href="https://github.com/jgdavey/uniqueness_constraints_example/commits/master"  class="external-link" target="_blank" rel="noopener">commit on the master branch</a> more or less follows the
sections above.</p>
]]></content>
  </entry>
  <entry>
    <title type="html">has_one view</title>
    <link href="https://joshuadavey.com/2013/02/12/sql-views-and-activerecord/" rel="alternate" type="text/html" title="has_one view"/>
    <published>2013-02-12T00:00:00+00:00</published>
    <updated>2013-02-12T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2013/02/12/sql-views-and-activerecord/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2013/02/12/sql-views-and-activerecord/"><![CDATA[<p><em>This is a repost. You can find the original <a href="https://hashrocket.com/blog/posts/sql-views-and-activerecord" title="has_one view"  class="external-link" target="_blank" rel="noopener">here</a></em></p>
<p>Sometimes, aggregating data can become overly complex in a normal
ActiveRecord model. Because Rails works well with SQL views, we can
create associations to SQL views that aggregate data for us, simplifying
our models and potentially speeding up queries.</p>
<h2 id="modeling-an-inbox">
  Modeling an inbox
  <a class="heading-link" href="#modeling-an-inbox">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>I&rsquo;ve got an inbox. A cat inbox. For real.</p>
<p><img src="https://s3.amazonaws.com/hashrocket-blog-production/inbox.png" alt=""></p>
<p>There are many possible implementations for modeling an inbox. I&rsquo;ve
gone with a relatively simple approach. Two users participate in a
conversation, sending messages back and forth to each other. The
Conversation model has a subject, but the body of the initial message is
part of the Message object.</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#75715e"># app/models/conversation.rb</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">Conversation</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># fields: to_id, from_id, subject</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">belongs_to</span> <span style="color:#d88200">:to</span><span style="color:#111">,</span> <span style="color:#d88200">class_name</span><span style="color:#111">:</span> <span style="color:#d88200">&#34;User&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">belongs_to</span> <span style="color:#d88200">:from</span><span style="color:#111">,</span> <span style="color:#d88200">class_name</span><span style="color:#111">:</span> <span style="color:#d88200">&#34;User&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">has_many</span> <span style="color:#d88200">:messages</span><span style="color:#111">,</span> <span style="color:#d88200">dependent</span><span style="color:#111">:</span> <span style="color:#d88200">:destroy</span><span style="color:#111">,</span> <span style="color:#d88200">inverse_of</span><span style="color:#111">:</span> <span style="color:#d88200">:conversation</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># app/models/message.rb</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">Message</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># fields: user_id, conversation_id, body</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">belongs_to</span> <span style="color:#d88200">:conversation</span><span style="color:#111">,</span> <span style="color:#d88200">inverse_of</span><span style="color:#111">:</span> <span style="color:#d88200">:messages</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">belongs_to</span> <span style="color:#d88200">:user</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>After the initial message, the two participants on the conversation send
messages back and forth. A user may have any number of conversations
with other users. As such, the main inbox view must list the
conversations a user is a participant on, as well as some summary
information about that conversation.</p>
<p>For our purposes, we&rsquo;ve decided on an HTML table view with the following
columns:</p>
<ul>
<li>From - Who the original message was sent from</li>
<li>To - The original recipient of the message</li>
<li>Message - the Subject of the conversation, as well as the first line
of the most recent message</li>
<li>Last post - The date/time of the most recent message</li>
<li>Replies - The number of replies on the conversation (excluding the
first message)</li>
</ul>
<p>Although the subject is part of the conversation itself, everything else
comes from its various associations. This is the view, which reveals the
expected interface each conversation object should have:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>%table#inbox
</span></span><span style="display:flex;"><span>  %thead
</span></span><span style="display:flex;"><span>    %tr
</span></span><span style="display:flex;"><span>      %th From
</span></span><span style="display:flex;"><span>      %th To
</span></span><span style="display:flex;"><span>      %th Message
</span></span><span style="display:flex;"><span>      %th Last post
</span></span><span style="display:flex;"><span>      %th Replies
</span></span><span style="display:flex;"><span>  %tbody
</span></span><span style="display:flex;"><span>    - conversations.each do |conversation|
</span></span><span style="display:flex;"><span>      %tr
</span></span><span style="display:flex;"><span>        %td= conversation.from_name
</span></span><span style="display:flex;"><span>        %td= conversation.to_name
</span></span><span style="display:flex;"><span>        %td
</span></span><span style="display:flex;"><span>          %p
</span></span><span style="display:flex;"><span>            %strong= conversation.subject
</span></span><span style="display:flex;"><span>            = conversation.most_recent_message_body
</span></span><span style="display:flex;"><span>        %td
</span></span><span style="display:flex;"><span>          = time_ago_in_words(conversation.most_recent_message_sent_at)
</span></span><span style="display:flex;"><span>          ago
</span></span><span style="display:flex;"><span>        %td= conversation.reply_count
</span></span></code></pre></div><p>Let&rsquo;s explore a typical way to model this in our model directly.</p>
<h2 id="a-typical-ruby-implementation">
  A typical Ruby implementation
  <a class="heading-link" href="#a-typical-ruby-implementation">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#75715e"># app/models/conversation.rb</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">Converation</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># associations, etc...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">most_recent_message_body</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">most_recent_message</span><span style="color:#f92672">.</span><span style="color:#111">body</span> <span style="color:#00a8c8">if</span> <span style="color:#111">most_recent_message</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">most_recent_message_sent_at</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">most_recent_message</span><span style="color:#f92672">.</span><span style="color:#111">created_at</span> <span style="color:#00a8c8">if</span> <span style="color:#111">most_recent_message</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">reply_count</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">messages</span><span style="color:#f92672">.</span><span style="color:#111">size</span> <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">to_name</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">to</span><span style="color:#f92672">.</span><span style="color:#111">name</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">from_name</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">from</span><span style="color:#f92672">.</span><span style="color:#111">name</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">private</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">most_recent_message</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">@most_recent_message</span> <span style="color:#f92672">||=</span> <span style="color:#111">messages</span><span style="color:#f92672">.</span><span style="color:#111">by_date</span><span style="color:#f92672">.</span><span style="color:#111">first</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># app/models/message.rb</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">Message</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># associations, etc...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">self</span><span style="color:#f92672">.</span><span style="color:#75af00">by_date</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">order</span><span style="color:#111">(</span><span style="color:#d88200">&#34;created_at DESC&#34;</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>This approach is fairly straightforward. We obtain the
<code>most_recent_message_body</code> and <code>most_recent_message_sent_at</code> from the
most recent message, which is trivial after we&rsquo;ve ordered the <code>messages</code>
association by date. The <code>to_name</code> and <code>from_name</code> methods are delegated
to their respective associations. And <code>reply_count</code> is simple the total
number of messages, minus one (the initial message doesn&rsquo;t count as a
&ldquo;reply&rdquo;).</p>
<p>This approach offers a number of advantages. For one, it is familiar.
I believe most Rails developers would be able to understand exactly
what&rsquo;s going on above. It also locates all of the domain logic within
the <code>Conversation</code> model, making it easy to find.</p>
<p>Having everything in the <code>Conversation</code> model is actually a blessing
and a curse. Although everything is easy to find, the model is also
quickly becoming bloated. It may not seem like much right now, but as
more information is added to the inbox, it will become unruly.</p>
<p>The other problem with the above is the multitude of N+1 queries that
it has introduced. With only 3 conversations in play, loading the inbox
outputs a log like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Started GET &#34;/&#34; for 127.0.0.1 at 2013-02-11 09:49:02 -0600
</span></span><span style="display:flex;"><span>Connecting to database specified by database.yml
</span></span><span style="display:flex;"><span>Processing by InboxesController#show as HTML
</span></span><span style="display:flex;"><span>  User Load (12.8ms)  SELECT &#34;users&#34;.* FROM &#34;users&#34; LIMIT 1
</span></span><span style="display:flex;"><span>  Conversation Load (0.6ms)  SELECT &#34;conversations&#34;.* FROM &#34;conversations&#34; WHERE (1 IN (from_id, to_id))
</span></span><span style="display:flex;"><span>  User Load (18.3ms)  SELECT &#34;users&#34;.* FROM &#34;users&#34; WHERE &#34;users&#34;.&#34;id&#34; = 2 LIMIT 1
</span></span><span style="display:flex;"><span>  User Load (0.5ms)  SELECT &#34;users&#34;.* FROM &#34;users&#34; WHERE &#34;users&#34;.&#34;id&#34; = 1 LIMIT 1
</span></span><span style="display:flex;"><span>  Message Load (12.4ms)  SELECT &#34;messages&#34;.* FROM &#34;messages&#34; WHERE &#34;messages&#34;.&#34;conversation_id&#34; = 7 ORDER BY created_at DESC LIMIT 1
</span></span><span style="display:flex;"><span>   (0.6ms)  SELECT COUNT(*) FROM &#34;messages&#34; WHERE &#34;messages&#34;.&#34;conversation_id&#34; = 7
</span></span><span style="display:flex;"><span>  User Load (0.4ms)  SELECT &#34;users&#34;.* FROM &#34;users&#34; WHERE &#34;users&#34;.&#34;id&#34; = 3 LIMIT 1
</span></span><span style="display:flex;"><span>  CACHE (0.0ms)  SELECT &#34;users&#34;.* FROM &#34;users&#34; WHERE &#34;users&#34;.&#34;id&#34; = 1 LIMIT 1
</span></span><span style="display:flex;"><span>  Message Load (0.4ms)  SELECT &#34;messages&#34;.* FROM &#34;messages&#34; WHERE &#34;messages&#34;.&#34;conversation_id&#34; = 8 ORDER BY created_at DESC LIMIT 1
</span></span><span style="display:flex;"><span>   (0.4ms)  SELECT COUNT(*) FROM &#34;messages&#34; WHERE &#34;messages&#34;.&#34;conversation_id&#34; = 8
</span></span><span style="display:flex;"><span>  CACHE (0.0ms)  SELECT &#34;users&#34;.* FROM &#34;users&#34; WHERE &#34;users&#34;.&#34;id&#34; = 1 LIMIT 1
</span></span><span style="display:flex;"><span>  CACHE (0.0ms)  SELECT &#34;users&#34;.* FROM &#34;users&#34; WHERE &#34;users&#34;.&#34;id&#34; = 2 LIMIT 1
</span></span><span style="display:flex;"><span>  Message Load (0.5ms)  SELECT &#34;messages&#34;.* FROM &#34;messages&#34; WHERE &#34;messages&#34;.&#34;conversation_id&#34; = 9 ORDER BY created_at DESC LIMIT 1
</span></span><span style="display:flex;"><span>   (0.4ms)  SELECT COUNT(*) FROM &#34;messages&#34; WHERE &#34;messages&#34;.&#34;conversation_id&#34; = 9
</span></span><span style="display:flex;"><span>  Rendered inboxes/show.html.haml within layouts/application (683.9ms)
</span></span><span style="display:flex;"><span>Completed 200 OK in 691ms (Views: 272.5ms | ActiveRecord: 418.1ms)
</span></span></code></pre></div><h2 id="eager-loading">
  Eager-loading
  <a class="heading-link" href="#eager-loading">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>We can definitely cut down on the N+1 query problem by introducing eager
loading. In our controller, the <code>conversations</code> exposure is currently
defined thusly:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#75715e"># app/controllers/inboxes_controller.rb</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">InboxesController</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ApplicationController</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">expose</span><span style="color:#111">(</span><span style="color:#d88200">:user</span><span style="color:#111">)</span> <span style="color:#111">{</span> <span style="color:#00a8c8">User</span><span style="color:#f92672">.</span><span style="color:#111">first</span> <span style="color:#111">}</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">expose</span><span style="color:#111">(</span><span style="color:#d88200">:conversations</span><span style="color:#111">)</span> <span style="color:#111">{</span> <span style="color:#111">user</span><span style="color:#f92672">.</span><span style="color:#111">conversations</span> <span style="color:#111">}</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>Let&rsquo;s change that to eagerly load its associations:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span>  <span style="color:#111">expose</span><span style="color:#111">(</span><span style="color:#d88200">:conversations</span><span style="color:#111">)</span> <span style="color:#111">{</span> <span style="color:#111">user</span><span style="color:#f92672">.</span><span style="color:#111">conversations</span><span style="color:#f92672">.</span><span style="color:#111">includes</span><span style="color:#111">(</span><span style="color:#d88200">:messages</span><span style="color:#111">,</span> <span style="color:#d88200">:to</span><span style="color:#111">,</span> <span style="color:#d88200">:from</span><span style="color:#111">)</span> <span style="color:#111">}</span>
</span></span></code></pre></div><p>With eager-loading in place, the log now looks slightly more reasonable:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Started GET &#34;/&#34; for 127.0.0.1 at 2013-02-11 09:55:24 -0600
</span></span><span style="display:flex;"><span>Processing by InboxesController#show as HTML
</span></span><span style="display:flex;"><span>  User Load (0.3ms)  SELECT &#34;users&#34;.* FROM &#34;users&#34; LIMIT 1
</span></span><span style="display:flex;"><span>  Conversation Load (0.3ms)  SELECT &#34;conversations&#34;.* FROM &#34;conversations&#34; WHERE (1 IN (from_id, to_id))
</span></span><span style="display:flex;"><span>  Message Load (0.3ms)  SELECT &#34;messages&#34;.* FROM &#34;messages&#34; WHERE &#34;messages&#34;.&#34;conversation_id&#34; IN (7, 8, 9)
</span></span><span style="display:flex;"><span>  User Load (0.2ms)  SELECT &#34;users&#34;.* FROM &#34;users&#34; WHERE &#34;users&#34;.&#34;id&#34; IN (1, 2)
</span></span><span style="display:flex;"><span>  User Load (0.2ms)  SELECT &#34;users&#34;.* FROM &#34;users&#34; WHERE &#34;users&#34;.&#34;id&#34; IN (2, 3, 1)
</span></span><span style="display:flex;"><span>  Message Load (0.4ms)  SELECT &#34;messages&#34;.* FROM &#34;messages&#34; WHERE &#34;messages&#34;.&#34;conversation_id&#34; = 7 ORDER BY created_at DESC LIMIT 1
</span></span><span style="display:flex;"><span>  Message Load (0.4ms)  SELECT &#34;messages&#34;.* FROM &#34;messages&#34; WHERE &#34;messages&#34;.&#34;conversation_id&#34; = 8 ORDER BY created_at DESC LIMIT 1
</span></span><span style="display:flex;"><span>  Message Load (0.3ms)  SELECT &#34;messages&#34;.* FROM &#34;messages&#34; WHERE &#34;messages&#34;.&#34;conversation_id&#34; = 9 ORDER BY created_at DESC LIMIT 1
</span></span><span style="display:flex;"><span>  Rendered inboxes/show.html.haml within layouts/application (9.5ms)
</span></span><span style="display:flex;"><span>Completed 200 OK in 13ms (Views: 10.0ms | ActiveRecord: 2.4ms)
</span></span></code></pre></div><p>There are more optimizations we could make here in Ruby land. But data
transformation and aggregation is something that databases are good at.
We can use a native feature of SQL to aggregate information for us:
views.</p>
<h2 id="sql-views">
  SQL views
  <a class="heading-link" href="#sql-views">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>A SQL view is essentially a virtual table. It can be queried just like a
normal table, but does not physically store anything itself. Instead, a
view has a query definition that it uses to represent its data.</p>
<p>In our case, SQL views can allow us to treat a complex SQL query as a
table, abstracting away the complexity into view itself. SQL views are
also read-only, and therefore are usually only used for querying, but
not updating data directly.</p>
<p>ActiveRecord plays nicely with SQL views out of the box. It considers a
SQL view a normal table, and all associations and querying methods work
like they would with a normal table, with one exception: the records are
read-only.</p>
<h2 id="add-a-migration-for-the-view">
  Add a migration for the view
  <a class="heading-link" href="#add-a-migration-for-the-view">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Let&rsquo;s create a view to handle the <code>to_name</code> and <code>from_name</code> methods on
conversation. We can do this in a normal migration, but it needs to be
created with raw SQL:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">CreateConversationSummaries</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Migration</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">up</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">execute</span> <span style="color:#d88200">&lt;&lt;-SQL
</span></span></span><span style="display:flex;"><span><span style="color:#d88200"></span>      <span style="color:#00a8c8">CREATE</span> <span style="color:#00a8c8">VIEW</span> <span style="color:#111">conversation_summaries</span> <span style="color:#00a8c8">AS</span>
</span></span><span style="display:flex;"><span>        <span style="color:#00a8c8">SELECT</span> <span style="color:#f92672">...</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">SQL</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">down</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">execute</span> <span style="color:#d88200">&#39;DROP VIEW conversation_summaries&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>This is the basic syntax for adding a view with ActiveRecord migrations.
Our view needs to incorporate <code>to_name</code> and <code>from_name</code>, so let&rsquo;s add
those fields:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#00a8c8">CREATE</span> <span style="color:#00a8c8">VIEW</span> <span style="color:#111">conversation_summaries</span> <span style="color:#00a8c8">AS</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">SELECT</span> <span style="color:#00a8c8">c</span><span style="color:#111">.</span><span style="color:#111">id</span><span style="color:#111">,</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">f</span><span style="color:#111">.</span><span style="color:#111">name</span> <span style="color:#00a8c8">as</span> <span style="color:#111">from_name</span><span style="color:#111">,</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">t</span><span style="color:#111">.</span><span style="color:#111">name</span> <span style="color:#00a8c8">as</span> <span style="color:#111">to_name</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">FROM</span> <span style="color:#111">conversations</span> <span style="color:#00a8c8">c</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">inner</span> <span style="color:#00a8c8">join</span> <span style="color:#111">users</span> <span style="color:#111">t</span> <span style="color:#00a8c8">on</span> <span style="color:#111">t</span><span style="color:#111">.</span><span style="color:#111">id</span> <span style="color:#f92672">=</span> <span style="color:#00a8c8">c</span><span style="color:#111">.</span><span style="color:#111">to_id</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">inner</span> <span style="color:#00a8c8">join</span> <span style="color:#111">users</span> <span style="color:#111">f</span> <span style="color:#00a8c8">on</span> <span style="color:#111">f</span><span style="color:#111">.</span><span style="color:#111">id</span> <span style="color:#f92672">=</span> <span style="color:#00a8c8">c</span><span style="color:#111">.</span><span style="color:#111">from_id</span>
</span></span></code></pre></div><p>After we migrate our database, we can use our database console to verify
that we see what we expect:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>mailbox_development=# select * from conversation_summaries;
</span></span><span style="display:flex;"><span> id |    from_name    |     to_name
</span></span><span style="display:flex;"><span>----+-----------------+-----------------
</span></span><span style="display:flex;"><span>  7 | Felionel Richie | Cat Stevens
</span></span><span style="display:flex;"><span>  8 | Nelly Purrtado  | Cat Stevens
</span></span><span style="display:flex;"><span>  9 | Cat Stevens     | Felionel Richie
</span></span><span style="display:flex;"><span>(3 rows)
</span></span></code></pre></div><p>Cool. The <code>id</code> corresponds to the conversation, and <code>to_name</code> and
<code>from_name</code> columns come from the users table, but it&rsquo;s all displayed to
us as one table.</p>
<h2 id="activerecord-associations-for-views">
  ActiveRecord associations for views
  <a class="heading-link" href="#activerecord-associations-for-views">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Now that our view exists, we can integrate it into our application:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">Conversation</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">class</span> <span style="color:#75af00">Summary</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">self</span><span style="color:#f92672">.</span><span style="color:#111">table_name</span> <span style="color:#f92672">=</span> <span style="color:#d88200">&#34;conversation_summaries&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">self</span><span style="color:#f92672">.</span><span style="color:#111">primary_key</span> <span style="color:#f92672">=</span> <span style="color:#d88200">&#34;id&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">belongs_to</span> <span style="color:#d88200">:conversation</span><span style="color:#111">,</span> <span style="color:#d88200">foreign_key</span><span style="color:#111">:</span> <span style="color:#d88200">&#34;id&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">has_one</span> <span style="color:#d88200">:summary</span><span style="color:#111">,</span> <span style="color:#d88200">foreign_key</span><span style="color:#111">:</span> <span style="color:#d88200">&#34;id&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>Let&rsquo;s break down what&rsquo;s going on here.</p>
<p>I&rsquo;ve chosen to nest the Summary model within the Conversation
namespace, mostly to call out the fact that we&rsquo;re doing something
non-standard. Also, the Summary class only makes sense in the context of
a Conversation. For that reason, we need to manually set the name of the
table.</p>
<p>We must also choose a primary key, because Rails cannot infer it for SQL
views. The association itself should be familiar. It works like a normal
<code>has_one</code>/<code>belongs_to</code> relationship, except that we override the foreign
key.</p>
<p>Now that the relationships are set up, let&rsquo;s actually take advantage
of our new view by changing the implementation of the <code>to_name</code> and
<code>from_name</code> methods.</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">Conversation</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">to_name</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># Used to be to.name</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">summary</span><span style="color:#f92672">.</span><span style="color:#111">to_name</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">from_name</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># Used to be from.name</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">summary</span><span style="color:#f92672">.</span><span style="color:#111">from_name</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>One the biggest benefits about this approach is that we can <em>eager-load
a view assocation</em>. We no longer need the <code>to</code> or <code>from</code> associations
eager-loaded, since we are no longer using any attributes from them in
the view. Let&rsquo;s update our controller&rsquo;s exposure to only eager-load the
necessary parts:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#111">expose</span><span style="color:#111">(</span><span style="color:#d88200">:conversations</span><span style="color:#111">)</span> <span style="color:#111">{</span> <span style="color:#111">user</span><span style="color:#f92672">.</span><span style="color:#111">conversations</span><span style="color:#f92672">.</span><span style="color:#111">includes</span><span style="color:#111">(</span><span style="color:#d88200">:summary</span><span style="color:#111">,</span> <span style="color:#d88200">:messages</span><span style="color:#111">)</span> <span style="color:#111">}</span>
</span></span></code></pre></div><p>And when we visit the inbox again, the log looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Started GET &#34;/&#34; for 127.0.0.1 at 2013-02-11 14:26:12 -0600
</span></span><span style="display:flex;"><span>Processing by InboxesController#show as HTML
</span></span><span style="display:flex;"><span>  User Load (0.5ms)  SELECT &#34;users&#34;.* FROM &#34;users&#34; LIMIT 1
</span></span><span style="display:flex;"><span>  Conversation Load (0.4ms)  SELECT &#34;conversations&#34;.* FROM &#34;conversations&#34; WHERE (1 IN (from_id, to_id))
</span></span><span style="display:flex;"><span>  Conversation::Summary Load (0.6ms)  SELECT &#34;conversation_summaries&#34;.* FROM &#34;conversation_summaries&#34; WHERE &#34;conversation_summaries&#34;.&#34;id&#34; IN (7, 8, 9)
</span></span><span style="display:flex;"><span>  Message Load (0.3ms)  SELECT &#34;messages&#34;.* FROM &#34;messages&#34; WHERE &#34;messages&#34;.&#34;conversation_id&#34; IN (7, 8, 9)
</span></span><span style="display:flex;"><span>  Message Load (0.4ms)  SELECT &#34;messages&#34;.* FROM &#34;messages&#34; WHERE &#34;messages&#34;.&#34;conversation_id&#34; = 7 ORDER BY created_at DESC LIMIT 1
</span></span><span style="display:flex;"><span>  Message Load (0.4ms)  SELECT &#34;messages&#34;.* FROM &#34;messages&#34; WHERE &#34;messages&#34;.&#34;conversation_id&#34; = 8 ORDER BY created_at DESC LIMIT 1
</span></span><span style="display:flex;"><span>  Message Load (0.4ms)  SELECT &#34;messages&#34;.* FROM &#34;messages&#34; WHERE &#34;messages&#34;.&#34;conversation_id&#34; = 9 ORDER BY created_at DESC LIMIT 1
</span></span><span style="display:flex;"><span>  Rendered inboxes/show.html.haml within layouts/application (10.4ms)
</span></span><span style="display:flex;"><span>Completed 200 OK in 13ms (Views: 9.5ms | ActiveRecord: 2.9ms)
</span></span></code></pre></div><p>That&rsquo;s definitely an improvement, albeit a small one. We&rsquo;ve pushed data
from the user model into our SQL view, but we don&rsquo;t need to stop there.</p>
<h2 id="push-it-down">
  Push it down!
  <a class="heading-link" href="#push-it-down">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Let&rsquo;s update our view migration to include more aggregated information
about each conversation.</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">CreateConversationSummaries</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Migration</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">up</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">execute</span> <span style="color:#d88200">&lt;&lt;-SQL
</span></span></span><span style="display:flex;"><span><span style="color:#d88200"></span>      <span style="color:#00a8c8">CREATE</span> <span style="color:#00a8c8">VIEW</span> <span style="color:#111">conversation_summaries</span> <span style="color:#00a8c8">AS</span>
</span></span><span style="display:flex;"><span>        <span style="color:#00a8c8">SELECT</span> <span style="color:#111">c</span><span style="color:#f92672">.</span><span style="color:#111">id</span><span style="color:#111">,</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">f</span><span style="color:#f92672">.</span><span style="color:#111">name</span> <span style="color:#111">as</span> <span style="color:#111">from_name</span><span style="color:#111">,</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">t</span><span style="color:#f92672">.</span><span style="color:#111">name</span> <span style="color:#111">as</span> <span style="color:#111">to_name</span><span style="color:#111">,</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">m</span><span style="color:#f92672">.</span><span style="color:#111">body</span> <span style="color:#111">as</span> <span style="color:#111">most_recent_message_body</span><span style="color:#111">,</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">m</span><span style="color:#f92672">.</span><span style="color:#111">created_at</span> <span style="color:#111">as</span> <span style="color:#111">most_recent_message_sent_at</span><span style="color:#111">,</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">(</span><span style="color:#111">select</span> <span style="color:#111">count</span><span style="color:#111">(</span><span style="color:#f92672">*</span><span style="color:#111">)</span> <span style="color:#111">from</span> <span style="color:#111">messages</span> <span style="color:#111">m2</span> <span style="color:#111">where</span> <span style="color:#111">m2</span><span style="color:#f92672">.</span><span style="color:#111">conversation_id</span> <span style="color:#f92672">=</span> <span style="color:#111">c</span><span style="color:#f92672">.</span><span style="color:#111">id</span><span style="color:#111">)</span> <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span> <span style="color:#111">as</span> <span style="color:#111">reply_count</span>
</span></span><span style="display:flex;"><span>        <span style="color:#00a8c8">FROM</span> <span style="color:#111">conversations</span> <span style="color:#111">c</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">inner</span> <span style="color:#111">join</span> <span style="color:#111">users</span> <span style="color:#111">t</span> <span style="color:#111">on</span> <span style="color:#111">t</span><span style="color:#f92672">.</span><span style="color:#111">id</span> <span style="color:#f92672">=</span> <span style="color:#111">c</span><span style="color:#f92672">.</span><span style="color:#111">to_id</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">inner</span> <span style="color:#111">join</span> <span style="color:#111">users</span> <span style="color:#111">f</span> <span style="color:#111">on</span> <span style="color:#111">f</span><span style="color:#f92672">.</span><span style="color:#111">id</span> <span style="color:#f92672">=</span> <span style="color:#111">c</span><span style="color:#f92672">.</span><span style="color:#111">from_id</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">left</span> <span style="color:#111">outer</span> <span style="color:#111">join</span> <span style="color:#111">(</span>
</span></span><span style="display:flex;"><span>          <span style="color:#111">select</span> <span style="color:#111">distinct</span> <span style="color:#111">on</span><span style="color:#111">(</span><span style="color:#111">conversation_id</span><span style="color:#111">)</span> <span style="color:#111">conversation_id</span><span style="color:#111">,</span> <span style="color:#111">body</span><span style="color:#111">,</span> <span style="color:#111">created_at</span>
</span></span><span style="display:flex;"><span>          <span style="color:#111">from</span> <span style="color:#111">messages</span> <span style="color:#111">m1</span>
</span></span><span style="display:flex;"><span>          <span style="color:#111">order</span> <span style="color:#111">by</span> <span style="color:#111">conversation_id</span><span style="color:#111">,</span> <span style="color:#111">created_at</span> <span style="color:#111">desc</span>
</span></span><span style="display:flex;"><span>        <span style="color:#111">)</span> <span style="color:#111">m</span> <span style="color:#00a8c8">ON</span> <span style="color:#111">m</span><span style="color:#f92672">.</span><span style="color:#111">conversation_id</span> <span style="color:#f92672">=</span> <span style="color:#111">c</span><span style="color:#f92672">.</span><span style="color:#111">id</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">SQL</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">down</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">execute</span> <span style="color:#d88200">&#39;DROP VIEW conversation_summaries&#39;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>After running <code>rake db:migrate:redo</code>, we can verify that everything is
still working as expect in the database console:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>mailbox_development=# select * from conversation_summaries;
</span></span><span style="display:flex;"><span> id |    from_name    |     to_name     |        most_recent_message_body        | most_recent_message_sent_at | reply_count 
</span></span><span style="display:flex;"><span>----+-----------------+-----------------+----------------------------------------+-----------------------------+-------------
</span></span><span style="display:flex;"><span>  7 | Felionel Richie | Cat Stevens     | Say you. Say meow.                     | 2013-02-08 02:45:27.07712   |           2
</span></span><span style="display:flex;"><span>  8 | Nelly Purrtado  | Cat Stevens     | Except that I&#39;m a cat                  | 2013-02-05 16:45:27.088292  |           0
</span></span><span style="display:flex;"><span>  9 | Cat Stevens     | Felionel Richie | I&#39;m sorry that you&#39;re feeling that way | 2013-01-30 16:45:27.092443  |           1
</span></span><span style="display:flex;"><span>(3 rows)
</span></span></code></pre></div><p>That&rsquo;s a lot of SQL! But actually, all I&rsquo;ve added are one join to a
subquery, and a subselect. Let&rsquo;s review both of these changes.</p>
<p>There are many ways to grab the most recent message for a conversation
in SQL, including using <a href="https://blog.hashrocket.com/posts/sql-window-functions"  class="external-link" target="_blank" rel="noopener">window functions</a>. The method I&rsquo;ve opted for
here is a <a href="https://www.postgresql.org/docs/9.2/static/queries-table-expressions.html#QUERIES-SUBQUERIES" title="Postgresql documentation"  class="external-link" target="_blank" rel="noopener">subquery in the table expression</a>. The subquery
alone would return rows for only the most recent messages for each
conversation:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>conversation_id |                  body                  |         created_at
</span></span><span style="display:flex;"><span>----------------+----------------------------------------+----------------------------
</span></span><span style="display:flex;"><span>              7 | Say you. Say meow.                     | 2013-02-08 02:45:27.07712
</span></span><span style="display:flex;"><span>              8 | Except that I&#39;m a cat                  | 2013-02-05 16:45:27.088292
</span></span><span style="display:flex;"><span>              9 | I&#39;m sorry that you&#39;re feeling that way | 2013-01-30 16:45:27.092443
</span></span></code></pre></div><p>By joining with only the most recent message per conversation, we avoid
duplicate rows and only get the <code>body</code> and <code>created_at</code> columns from
the most recent message. Then, joining against this subquery, we can
add the <code>body</code> and <code>created_at</code> to the list of projections, naming
them <code>most_recent_message_body</code> and <code>most_recent_message_sent_at</code>,
respectively.</p>
<p>The other thing we&rsquo;ve added to the view this iteration is the
<code>reply_count</code> column, which is a subselect to get the count. We also
subtract 1, just as before.</p>
<p>Let&rsquo;s take a look at our Conversation model now:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#75715e"># before</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">Conversation</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">belongs_to</span> <span style="color:#d88200">:to</span><span style="color:#111">,</span> <span style="color:#d88200">class_name</span><span style="color:#111">:</span> <span style="color:#d88200">&#34;User&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">belongs_to</span> <span style="color:#d88200">:from</span><span style="color:#111">,</span> <span style="color:#d88200">class_name</span><span style="color:#111">:</span> <span style="color:#d88200">&#34;User&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">has_many</span> <span style="color:#d88200">:messages</span><span style="color:#111">,</span> <span style="color:#d88200">dependent</span><span style="color:#111">:</span> <span style="color:#d88200">:destroy</span><span style="color:#111">,</span> <span style="color:#d88200">inverse_of</span><span style="color:#111">:</span> <span style="color:#d88200">:conversation</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">most_recent_message_body</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">most_recent_message</span><span style="color:#f92672">.</span><span style="color:#111">body</span> <span style="color:#00a8c8">if</span> <span style="color:#111">most_recent_message</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">most_recent_message_sent_at</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">most_recent_message</span><span style="color:#f92672">.</span><span style="color:#111">created_at</span> <span style="color:#00a8c8">if</span> <span style="color:#111">most_recent_message</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">reply_count</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">[</span><span style="color:#111">messages</span><span style="color:#f92672">.</span><span style="color:#111">size</span> <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span><span style="color:#111">,</span> <span style="color:#ae81ff">0</span><span style="color:#f92672">].</span><span style="color:#111">max</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">to_name</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">to</span><span style="color:#f92672">.</span><span style="color:#111">name</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">from_name</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">from</span><span style="color:#f92672">.</span><span style="color:#111">name</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">private</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">most_recent_message</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">@most_recent_message</span> <span style="color:#f92672">||=</span> <span style="color:#111">messages</span><span style="color:#f92672">.</span><span style="color:#111">by_date</span><span style="color:#f92672">.</span><span style="color:#111">first</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># after</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">Conversation</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">class</span> <span style="color:#75af00">Summary</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">self</span><span style="color:#f92672">.</span><span style="color:#111">table_name</span> <span style="color:#f92672">=</span> <span style="color:#d88200">&#34;conversation_summaries&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">self</span><span style="color:#f92672">.</span><span style="color:#111">primary_key</span> <span style="color:#f92672">=</span> <span style="color:#d88200">&#34;id&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">belongs_to</span> <span style="color:#d88200">:conversation</span><span style="color:#111">,</span> <span style="color:#d88200">foreign_key</span><span style="color:#111">:</span> <span style="color:#d88200">&#34;id&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">belongs_to</span> <span style="color:#d88200">:to</span><span style="color:#111">,</span> <span style="color:#d88200">class_name</span><span style="color:#111">:</span> <span style="color:#d88200">&#34;User&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">belongs_to</span> <span style="color:#d88200">:from</span><span style="color:#111">,</span> <span style="color:#d88200">class_name</span><span style="color:#111">:</span> <span style="color:#d88200">&#34;User&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">has_many</span> <span style="color:#d88200">:messages</span><span style="color:#111">,</span> <span style="color:#d88200">dependent</span><span style="color:#111">:</span> <span style="color:#d88200">:destroy</span><span style="color:#111">,</span> <span style="color:#d88200">inverse_of</span><span style="color:#111">:</span> <span style="color:#d88200">:conversation</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">has_one</span> <span style="color:#d88200">:summary</span><span style="color:#111">,</span> <span style="color:#d88200">foreign_key</span><span style="color:#111">:</span> <span style="color:#d88200">&#34;id&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">delegate</span> <span style="color:#d88200">:most_recent_message_sent_at</span><span style="color:#111">,</span> <span style="color:#d88200">:most_recent_message_body</span><span style="color:#111">,</span>
</span></span><span style="display:flex;"><span>    <span style="color:#d88200">:reply_count</span><span style="color:#111">,</span> <span style="color:#d88200">:to_name</span><span style="color:#111">,</span> <span style="color:#d88200">:from_name</span><span style="color:#111">,</span> <span style="color:#d88200">to</span><span style="color:#111">:</span> <span style="color:#d88200">:summary</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>With much of our data transformation and aggregation in our SQL view,
our model has become trivially simple. It literally only contains
assocations and delegation now. We update our exposure to only
eager-load the conversation summary:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#111">expose</span><span style="color:#111">(</span><span style="color:#d88200">:conversations</span><span style="color:#111">)</span> <span style="color:#111">{</span> <span style="color:#111">user</span><span style="color:#f92672">.</span><span style="color:#111">conversations</span><span style="color:#f92672">.</span><span style="color:#111">includes</span><span style="color:#111">(</span><span style="color:#d88200">:summary</span><span style="color:#111">)</span> <span style="color:#111">}</span>
</span></span></code></pre></div><p>Now, reloading the page yields the following log output:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Started GET &#34;/&#34; for 127.0.0.1 at 2013-02-11 15:37:49 -0600
</span></span><span style="display:flex;"><span>Processing by InboxesController#show as HTML
</span></span><span style="display:flex;"><span>  User Load (1.0ms)  SELECT &#34;users&#34;.* FROM &#34;users&#34; LIMIT 1
</span></span><span style="display:flex;"><span>  Conversation Load (0.2ms)  SELECT &#34;conversations&#34;.* FROM &#34;conversations&#34; WHERE (1 IN (from_id, to_id))
</span></span><span style="display:flex;"><span>  Conversation::Summary Load (0.8ms)  SELECT &#34;conversation_summaries&#34;.* FROM &#34;conversation_summaries&#34; WHERE &#34;conversation_summaries&#34;.&#34;id&#34; IN (7, 8, 9)
</span></span><span style="display:flex;"><span>  Rendered inboxes/show.html.haml within layouts/application (5.5ms)
</span></span><span style="display:flex;"><span>Completed 200 OK in 8ms (Views: 6.0ms | ActiveRecord: 2.0ms)
</span></span></code></pre></div><p>Now we see some real improvement. All N+1 queries are gone, replaced
instead with the eager-loading of the the Conversation::Summary model.</p>
<h2 id="real-world-benefits">
  Real World Benefits
  <a class="heading-link" href="#real-world-benefits">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>I used this technique in a real-world application. It helped abstract
some of the mundane details of the inbox and allowed us to think about
each conversation at a higher level with a summary.</p>
<p>In fact the app included even more business rules than I&rsquo;ve included
here. Each conversation had to include a read/unread status that updated
with each sent message. Although it was easily implemented in pure Ruby,
it cluttered the model and created yet more N+1 queries in the app view.</p>
<p>The inbox also had to be sorted by the most recent message date,
so that the conversation with the most recent activity would
appear first in the list. This kind of sorting without SQL is both
cumbersome and inefficient in Ruby; you have to load all messages
for each conversation. With the SQL view, it was as simple as
changing the scope from <code>user.conversations.include(:summary)</code> to
<code>user.conversations.include(:summary).order(&quot;conversation_summaries.most _recent_message_sent_at DESC&quot;)</code>.</p>
<h2 id="conclusion">
  Conclusion
  <a class="heading-link" href="#conclusion">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Any time that we push stuff into the database, we make a tradeoff.
In this case, when we move data transformation into the SQL view, we
sacrifice the co-location of the conversation model and the definition
of its summary. With the summary definition located in the database,
there&rsquo;s one extra layer of indirection.</p>
<p>The other tradeoff is that any time we&rsquo;d like to make a non-trivial
change the view, we actually have to create an entirely new view,
replacing the old one. If for example, we knew that our inbox was likely
to change or add fields, the SQL view approach might be too brittle.</p>
<p>On the other hand, we effectively removed N+1 queries from our
application and simplified our model considerably. By abstracting the
conversation&rsquo;s summary into a model backed by a SQL view, we&rsquo;re able
to think of the Summary as an object in its own right. This provides
a cognitive simplification, but also yields performance gains as the
dataset grows.</p>
<p>It may not be right for every situation, but knowing and understanding
how we can use SQL views in our Rails applications adds another tool to
our toolbelt.</p>
<h2 id="example-app">
  Example app
  <a class="heading-link" href="#example-app">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>As before, while writing this post, I created a <a href="https://github.com/jgdavey/sql_views"  class="external-link" target="_blank" rel="noopener">sample Rails app</a> to
iterate quickly. I used TDD to write the pure-ruby approach, and reused
the specs while I &ldquo;refactored&rdquo; the implementation to the subsequent
approaches. Of particular note is the <a href="https://github.com/jgdavey/sql_views/commits/master/app/models/conversation.rb"  class="external-link" target="_blank" rel="noopener">history of the Conversation
model</a>(<a href="https://github.com/jgdavey/tree-sql-example/commits/master/app/mo"  class="external-link" target="_blank" rel="noopener">https://github.com/jgdavey/tree-sql-example/commits/master/app/mo</a>
dels/category.rb), which mirrors the code above.</p>
]]></content>
  </entry>
  <entry>
    <title type="html">Using Recursive SQL with ActiveRecord trees</title>
    <link href="https://joshuadavey.com/2012/09/04/recursive-sql-in-activerecord/" rel="alternate" type="text/html" title="Using Recursive SQL with ActiveRecord trees"/>
    <published>2012-09-04T00:00:00+00:00</published>
    <updated>2012-09-04T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2012/09/04/recursive-sql-in-activerecord/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2012/09/04/recursive-sql-in-activerecord/"><![CDATA[<p><em>This is a repost. You can find the original <a href="https://hashrocket.com/blog/posts/recursive-sql-in-activerecord" title="Using Recursive SQL with ActiveRecord trees"  class="external-link" target="_blank" rel="noopener">here</a></em></p>
<p><strong>tl;dr</strong> When you have an ActiveRecord tree structure, using the WITH syntax for recursive SQL can provide large performance boons, especially when a tree get several levels deep.</p>
<p>In a <a href="https://blog.hashrocket.com/posts/sql-window-functions" title="SQL Window Functions and You"  class="external-link" target="_blank" rel="noopener">previous post</a>, I outlined a Cat Picture store application. As our store grows, more and more categories have to be created, and we end up with a tree of categories.
How can we create a homepage that includes all cat pictures for a given category <em>and</em> all of its subcategories?</p>
<p>Pictorally, the category tree might look like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>Cat Pictures
</span></span><span style="display:flex;"><span>|-- Funny
</span></span><span style="display:flex;"><span>|   |-- LOLCats
</span></span><span style="display:flex;"><span>|   `-- Animated
</span></span><span style="display:flex;"><span>`-- Classic
</span></span><span style="display:flex;"><span>    `-- Renaissance
</span></span></code></pre></div><p>On the landing page for the <em>Cat Pictures</em> category, we want to display
all cat pictures for any category below <em>Cat Pictures</em>. Navigating to
the <em>Funny</em> category would display all of its pictures, as well as the
pictures for <em>LOLCats</em> and <em>Animated</em>. This is the kind of interaction
seen on Amazon, for example. The store&rsquo;s categories become like an
ad-hoc filtering system.</p>
<p>Here&rsquo;s what the Category class looks like:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">Category</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">attr_accessible</span> <span style="color:#d88200">:name</span><span style="color:#111">,</span> <span style="color:#d88200">:parent</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">has_many</span> <span style="color:#d88200">:cat_pictures</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">belongs_to</span> <span style="color:#d88200">:parent</span><span style="color:#111">,</span> <span style="color:#d88200">:class_name</span> <span style="color:#f92672">=&gt;</span> <span style="color:#d88200">&#34;Category&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">has_many</span> <span style="color:#d88200">:children</span><span style="color:#111">,</span> <span style="color:#d88200">:class_name</span> <span style="color:#f92672">=&gt;</span> <span style="color:#d88200">&#34;Category&#34;</span><span style="color:#111">,</span> <span style="color:#d88200">:foreign_key</span> <span style="color:#f92672">=&gt;</span> <span style="color:#d88200">&#39;parent_id&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">scope</span> <span style="color:#d88200">:top_level</span><span style="color:#111">,</span> <span style="color:#111">where</span><span style="color:#111">(</span><span style="color:#d88200">:parent_id</span> <span style="color:#f92672">=&gt;</span> <span style="color:#00a8c8">nil</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">descendents</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e"># implement me!</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>Each category has a <code>parent_id</code> column that points at its parent
category. In database speak, modeling a tree like this is known as
an <em>Adjacency List</em>; each node of the tree can only see a children
immediately adjacent to it. For this reason, crawling an Adjacency List
requires recursion. This is actually the database setup common for use
with the <code>acts_as_tree</code> plugin. Let&rsquo;s see how we can implement the
<code>descendents</code> method to get all descendent categories.</p>
<h2 id="a-simple-ruby-approach">
  A Simple Ruby Approach
  <a class="heading-link" href="#a-simple-ruby-approach">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>As you&rsquo;ve probably already guessed, we need to recursively collect
children for each of our category&rsquo;s children.</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">Category</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">descendents</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">children</span><span style="color:#f92672">.</span><span style="color:#111">map</span> <span style="color:#00a8c8">do</span> <span style="color:#f92672">|</span><span style="color:#111">child</span><span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">[</span><span style="color:#111">child</span><span style="color:#f92672">]</span> <span style="color:#f92672">+</span> <span style="color:#111">child</span><span style="color:#f92672">.</span><span style="color:#111">descendents</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">end</span><span style="color:#f92672">.</span><span style="color:#111">flatten</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>This does the job quite nicely. However, our requirements above state
that we want all cat pictures for each descendent category, <em>and our
categories</em>. Right now, we&rsquo;ve omitted the root category, self. Let&rsquo;s add
a new method to include it into the equation:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">Category</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">descendents</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">children</span><span style="color:#f92672">.</span><span style="color:#111">map</span> <span style="color:#00a8c8">do</span> <span style="color:#f92672">|</span><span style="color:#111">child</span><span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">[</span><span style="color:#111">child</span><span style="color:#f92672">]</span> <span style="color:#f92672">+</span> <span style="color:#111">child</span><span style="color:#f92672">.</span><span style="color:#111">descendents</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">end</span><span style="color:#f92672">.</span><span style="color:#111">flatten</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">self_and_descendents</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">[</span><span style="color:#111">self</span><span style="color:#f92672">]</span> <span style="color:#f92672">+</span> <span style="color:#111">descendents</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>Good deal. Now gathering all cat pictures is just a matter of collecting
them for each category:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">Category</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># ...</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">descendent_pictures</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">self_and_descendents</span><span style="color:#f92672">.</span><span style="color:#111">map</span><span style="color:#111">(</span><span style="color:#f92672">&amp;</span><span style="color:#d88200">:cat_pictures</span><span style="color:#111">)</span><span style="color:#f92672">.</span><span style="color:#111">flatten</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>For a tree like we have above, this is probably good enough. Our tree is
only 3 levels deep. We&rsquo;ve introduced plenty of N+1 queries, but given
our small dataset, that shouldn&rsquo;t be a huge concern.</p>
<p>That said, as our store grows, and the tree gets deeper and more
detailed, this kind of implementation could become a bottleneck. Also,
because we&rsquo;re doing Array operations on the <code>children</code> collection,
we lose the ability to take advantage of ActiveRelation outside of the
<code>descendents</code> method itself. Among other things, this means that we
can&rsquo;t eager-load cat pictures unless we always eager-load them within
the <code>descendents</code> method.</p>
<p>Surely we can do better.</p>
<h2 id="sql-with-queries">
  SQL WITH queries
  <a class="heading-link" href="#sql-with-queries">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Since we&rsquo;re using PostgreSQL, we can take advantage of its special
features. In this case, we can use a WITH query. From the <a href="https://www.postgresql.org/docs/9.1/static/queries-with.html" title="WITH queries"  class="external-link" target="_blank" rel="noopener">PostgreSQL
documentation</a>:</p>
<blockquote>
<p>WITH provides a way to write auxiliary statements for use in a larger
query. These statements, which are often referred to as Common Table
Expressions or CTEs, can be thought of as defining temporary tables that
exist just for one query.</p>
</blockquote>
<p>On its own, this might not seem like a big deal, but when combined with
the optional RECURSIVE modifier, WITH queries can become quite powerful:</p>
<blockquote>
<p>The optional RECURSIVE modifier changes WITH from a mere syntactic
convenience into a feature that accomplishes things not otherwise
possible in standard SQL. Using RECURSIVE, a WITH query can refer to
its own output. A very simple example is this query to sum the integers
from 1 through 100:</p>
<pre><code>
 WITH RECURSIVE t(n) AS (
     VALUES (1)
   UNION ALL
     SELECT n+1 FROM t WHERE n < 100
 )
 SELECT sum(n) FROM t;
</code></pre>
<p>The general form of a recursive WITH query is always a non-recursive
term, then UNION (or UNION ALL), then a recursive term, where only the
recursive term can contain a reference to the query&rsquo;s own output.</p>
</blockquote>
<p>In other words, the expression contained in the AS statement has two
parts. The first part is executed just once. The second part, after the
UNION ALL, is executed until it returns an empty result set.</p>
<p>Taking advantage of WITH RECURSIVE, we can reduce our tree crawling
technique from <em>n</em> queries to just 1! Let&rsquo;s how we can use this to crawl
our category tree.</p>
<p>As a reminder, here&rsquo;s what our categories table looks like:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span># SELECT id, name, parent_id FROM categories;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> id |     name     | parent_id 
</span></span><span style="display:flex;"><span>----+--------------+-----------
</span></span><span style="display:flex;"><span>  1 | Cat Pictures |          
</span></span><span style="display:flex;"><span>  2 | Funny        |         1
</span></span><span style="display:flex;"><span>  3 | LOLCats      |         2
</span></span><span style="display:flex;"><span>  4 | Animated     |         2
</span></span><span style="display:flex;"><span>  5 | Classic      |         1
</span></span><span style="display:flex;"><span>  6 | Renaissance  |         5
</span></span></code></pre></div><p>And this is the query:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#00a8c8">WITH</span> <span style="color:#00a8c8">RECURSIVE</span> <span style="color:#111">category_tree</span><span style="color:#111">(</span><span style="color:#111">id</span><span style="color:#111">,</span> <span style="color:#111">name</span><span style="color:#111">,</span> <span style="color:#111">path</span><span style="color:#111">)</span> <span style="color:#00a8c8">AS</span> <span style="color:#111">(</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">SELECT</span> <span style="color:#111">id</span><span style="color:#111">,</span> <span style="color:#111">name</span><span style="color:#111">,</span> <span style="color:#111">ARRAY</span><span style="color:#111">[</span><span style="color:#111">id</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">FROM</span> <span style="color:#111">categories</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">WHERE</span> <span style="color:#111">parent_id</span> <span style="color:#00a8c8">IS</span> <span style="color:#00a8c8">NULL</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">UNION</span> <span style="color:#00a8c8">ALL</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">SELECT</span> <span style="color:#111">categories</span><span style="color:#111">.</span><span style="color:#111">id</span><span style="color:#111">,</span> <span style="color:#111">categories</span><span style="color:#111">.</span><span style="color:#111">name</span><span style="color:#111">,</span> <span style="color:#111">path</span> <span style="color:#f92672">||</span> <span style="color:#111">categories</span><span style="color:#111">.</span><span style="color:#111">id</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">FROM</span> <span style="color:#111">category_tree</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">JOIN</span> <span style="color:#111">categories</span> <span style="color:#00a8c8">ON</span> <span style="color:#111">categories</span><span style="color:#111">.</span><span style="color:#111">parent_id</span><span style="color:#f92672">=</span><span style="color:#111">category_tree</span><span style="color:#111">.</span><span style="color:#111">id</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">WHERE</span> <span style="color:#00a8c8">NOT</span> <span style="color:#111">categories</span><span style="color:#111">.</span><span style="color:#111">id</span> <span style="color:#f92672">=</span> <span style="color:#00a8c8">ANY</span><span style="color:#111">(</span><span style="color:#111">path</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">SELECT</span> <span style="color:#f92672">*</span> <span style="color:#00a8c8">FROM</span> <span style="color:#111">category_tree</span> <span style="color:#00a8c8">ORDER</span> <span style="color:#00a8c8">BY</span> <span style="color:#111">path</span><span style="color:#111">;</span>
</span></span></code></pre></div><p>Running the query above returns the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span> id |     name     |  path   
</span></span><span style="display:flex;"><span>----+--------------+---------
</span></span><span style="display:flex;"><span>  1 | Cat Pictures | {1}
</span></span><span style="display:flex;"><span>  2 | Funny        | {1,2}
</span></span><span style="display:flex;"><span>  3 | LOLCats      | {1,2,3}
</span></span><span style="display:flex;"><span>  4 | Animated     | {1,2,4}
</span></span><span style="display:flex;"><span>  5 | Classic      | {1,5}
</span></span><span style="display:flex;"><span>  6 | Renaissance  | {1,5,6}
</span></span></code></pre></div><p>Whoa! That&rsquo;s a lot of SQL. Let&rsquo;s break it down a bit.</p>
<h3 id="declare-the-table-expression">
  Declare the Table Expression
  <a class="heading-link" href="#declare-the-table-expression">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>First, we declare our &ldquo;temporary table&rdquo; using the WITH syntax. We&rsquo;re
going to call it <code>category_tree</code>. This &ldquo;table&rdquo; has 3 &ldquo;columns&rdquo;: <code>id</code>,
<code>name</code>, and <code>path</code>. The <code>id</code> and <code>name</code> columns are fairly obvious; they
refer to corresponding columns on the categories table. The <code>path</code> is an
array of ids that each row will have. More on this in a bit.</p>
<h3 id="define-the-non-recursive-term">
  Define the Non-recursive Term
  <a class="heading-link" href="#define-the-non-recursive-term">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>The non-recursive term is next:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#00a8c8">SELECT</span> <span style="color:#111">id</span><span style="color:#111">,</span> <span style="color:#111">name</span><span style="color:#111">,</span> <span style="color:#111">ARRAY</span><span style="color:#111">[</span><span style="color:#111">id</span><span style="color:#111">]</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">FROM</span> <span style="color:#111">categories</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">WHERE</span> <span style="color:#111">parent_id</span> <span style="color:#00a8c8">IS</span> <span style="color:#00a8c8">NULL</span>
</span></span></code></pre></div><p>It grabs the <code>id</code> and <code>name</code> for each top-level category, that is, each
category that has no parent. It also initializes an array containing just
its <code>id</code>. On its own, this isn&rsquo;t very interesting, but this array will
become helpful during the recursive step of the query.</p>
<h3 id="define-the-recursive-term">
  Define the Recursive Term
  <a class="heading-link" href="#define-the-recursive-term">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>The recursive term is the juiciest bit of the query:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#00a8c8">SELECT</span> <span style="color:#111">categories</span><span style="color:#111">.</span><span style="color:#111">id</span><span style="color:#111">,</span> <span style="color:#111">categories</span><span style="color:#111">.</span><span style="color:#111">name</span><span style="color:#111">,</span> <span style="color:#111">path</span> <span style="color:#f92672">||</span> <span style="color:#111">categories</span><span style="color:#111">.</span><span style="color:#111">id</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">FROM</span> <span style="color:#111">category_tree</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">JOIN</span> <span style="color:#111">categories</span> <span style="color:#00a8c8">ON</span> <span style="color:#111">categories</span><span style="color:#111">.</span><span style="color:#111">parent_id</span><span style="color:#f92672">=</span><span style="color:#111">category_tree</span><span style="color:#111">.</span><span style="color:#111">id</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">WHERE</span> <span style="color:#00a8c8">NOT</span> <span style="color:#111">categories</span><span style="color:#111">.</span><span style="color:#111">id</span> <span style="color:#f92672">=</span> <span style="color:#00a8c8">ANY</span><span style="color:#111">(</span><span style="color:#111">path</span><span style="color:#111">)</span>
</span></span></code></pre></div><p>Notice that we&rsquo;re selecting from <code>category_tree</code>. By doing this, we&rsquo;re
able to use each result set in the subsequent iteration. The first time
we recurse, the result set will be what we selected in the non-recursive
term above.</p>
<p>Given that we have a root result set, we join with <code>categories</code> to find
its children. From our new result set, we select <code>id</code> and <code>name</code>, as
before. But this time, we concatenate the child id onto the <code>path</code> array
using SQL&rsquo;s <code>||</code> operator. Having this materialized path allows us to
guard against infinite loops; the WHERE clause makes sure that the row
we&rsquo;re selecting has not appeared in the path before.</p>
<p>This infinite loop check is important. If two categories pointed at each
other as parents, the query would never return. Including this check
prevents such a mistake from killing our server.</p>
<h3 id="query-the-common-table-expression">
  Query the Common Table Expression
  <a class="heading-link" href="#query-the-common-table-expression">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<p>Finally, a WITH query is only useful if you select from it outside of
its declaration, so we&rsquo;ll do just that:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#00a8c8">SELECT</span> <span style="color:#f92672">*</span> <span style="color:#00a8c8">FROM</span> <span style="color:#111">category_tree</span> <span style="color:#00a8c8">ORDER</span> <span style="color:#00a8c8">BY</span> <span style="color:#111">path</span><span style="color:#111">;</span>
</span></span></code></pre></div><p>In addition to the infinite loop guard, the path column answers the
question &ldquo;How did I get here?&rdquo; Like a directory structure on a file
system, the path demonstrates the ids necessary to get from grandparent
to parent to child, etc.</p>
<p>You may have noticed that we&rsquo;re also ordering by the path column.
We do this because the default sort from a recursive query is
nondeterministic. Normal array sorting works well for us here, and
groups the categories just like we&rsquo;d expect, with parents listed before
their children.</p>
<h2 id="using-with-queries-in-activerecord">
  Using WITH queries in ActiveRecord
  <a class="heading-link" href="#using-with-queries-in-activerecord">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">Category</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># ...</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">descendents</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">self_and_descendents</span> <span style="color:#f92672">-</span> <span style="color:#f92672">[</span><span style="color:#111">self</span><span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">self_and_descendents</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">self</span><span style="color:#f92672">.</span><span style="color:#111">class</span><span style="color:#f92672">.</span><span style="color:#111">tree_for</span><span style="color:#111">(</span><span style="color:#111">self</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">descendent_pictures</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">subtree</span> <span style="color:#f92672">=</span> <span style="color:#111">self</span><span style="color:#f92672">.</span><span style="color:#111">class</span><span style="color:#f92672">.</span><span style="color:#111">tree_sql_for</span><span style="color:#111">(</span><span style="color:#111">self</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">CatPicture</span><span style="color:#f92672">.</span><span style="color:#111">where</span><span style="color:#111">(</span><span style="color:#d88200">&#34;category_id IN (</span><span style="color:#d88200">#{</span><span style="color:#111">subtree</span><span style="color:#d88200">}</span><span style="color:#d88200">)&#34;</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">self</span><span style="color:#f92672">.</span><span style="color:#75af00">tree_for</span><span style="color:#111">(</span><span style="color:#111">instance</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">where</span><span style="color:#111">(</span><span style="color:#d88200">&#34;</span><span style="color:#d88200">#{</span><span style="color:#111">table_name</span><span style="color:#d88200">}</span><span style="color:#d88200">.id IN (</span><span style="color:#d88200">#{</span><span style="color:#111">tree_sql_for</span><span style="color:#111">(</span><span style="color:#111">instance</span><span style="color:#111">)</span><span style="color:#d88200">}</span><span style="color:#d88200">)&#34;</span><span style="color:#111">)</span><span style="color:#f92672">.</span><span style="color:#111">order</span><span style="color:#111">(</span><span style="color:#d88200">&#34;</span><span style="color:#d88200">#{</span><span style="color:#111">table_name</span><span style="color:#d88200">}</span><span style="color:#d88200">.id&#34;</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">self</span><span style="color:#f92672">.</span><span style="color:#75af00">tree_sql_for</span><span style="color:#111">(</span><span style="color:#111">instance</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">tree_sql</span> <span style="color:#f92672">=</span>  <span style="color:#d88200">&lt;&lt;-SQL
</span></span></span><span style="display:flex;"><span><span style="color:#d88200"></span>      <span style="color:#00a8c8">WITH</span> <span style="color:#00a8c8">RECURSIVE</span> <span style="color:#111">search_tree</span><span style="color:#111">(</span><span style="color:#111">id</span><span style="color:#111">,</span> <span style="color:#111">path</span><span style="color:#111">)</span> <span style="color:#00a8c8">AS</span> <span style="color:#111">(</span>
</span></span><span style="display:flex;"><span>          <span style="color:#00a8c8">SELECT</span> <span style="color:#111">id</span><span style="color:#111">,</span> <span style="color:#00a8c8">ARRAY</span><span style="color:#f92672">[</span><span style="color:#111">id</span><span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span>          <span style="color:#00a8c8">FROM</span> <span style="color:#75715e">#{table_name}</span>
</span></span><span style="display:flex;"><span>          <span style="color:#00a8c8">WHERE</span> <span style="color:#111">id</span> <span style="color:#f92672">=</span> <span style="color:#75715e">#{instance.id}</span>
</span></span><span style="display:flex;"><span>        <span style="color:#00a8c8">UNION</span> <span style="color:#00a8c8">ALL</span>
</span></span><span style="display:flex;"><span>          <span style="color:#00a8c8">SELECT</span> <span style="color:#75715e">#{table_name}.id, path || #{table_name}.id</span>
</span></span><span style="display:flex;"><span>          <span style="color:#00a8c8">FROM</span> <span style="color:#111">search_tree</span>
</span></span><span style="display:flex;"><span>          <span style="color:#00a8c8">JOIN</span> <span style="color:#75715e">#{table_name} ON #{table_name}.parent_id = search_tree.id</span>
</span></span><span style="display:flex;"><span>          <span style="color:#00a8c8">WHERE</span> <span style="color:#00a8c8">NOT</span> <span style="color:#75715e">#{table_name}.id = ANY(path)</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>      <span style="color:#00a8c8">SELECT</span> <span style="color:#111">id</span> <span style="color:#00a8c8">FROM</span> <span style="color:#111">search_tree</span> <span style="color:#00a8c8">ORDER</span> <span style="color:#00a8c8">BY</span> <span style="color:#111">path</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">SQL</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>You should notice right away where our recursive query is. The
<code>tree_sql_for</code> class method returns a SQL string that can be used with
other queries. Compared to the WITH query we looked at before, there a
few differences worth mentioning.</p>
<p>First, and probably most importantly for our original problem, we&rsquo;ve
changed our starting place. The non-recursive term is our &ldquo;start here&rdquo;
result set. Rather than starting with all top-level categories, we&rsquo;re
using the id of whichever instance is passed in to scope our tree.</p>
<p>Another change we&rsquo;ve made is to remove the <code>name</code> column from the query.
It isn&rsquo;t necessary for what we&rsquo;re doing, but made the example easier to
demonstrate. We&rsquo;re also interpolating the table name. This makes the
method much more reusable. In fact, we could extract the method to a
<code>RecursiveTree</code> module to tidy up our class.</p>
<p>One big advantage of the SQL approach here is that we can create scopes
to further filter our results within just one database round-trip.
For example, the <code>tree_for</code> class method is really just a named scope
that takes a category instance as a parameter.</p>
<p>Likewise, the the <code>descendent_pictures</code> method returns a CatPicture
relation that includes all pictures from this category and all
subcategories. In other words, what used to take 2 database round trips
for each category in the tree (one to grab children, one to get its
pictures) will now only take 1 for the entire set.</p>
<h2 id="conclusion">
  Conclusion
  <a class="heading-link" href="#conclusion">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Taking advantage of PostgreSQL&rsquo;s advanced features can provide large
performance boons, especially when a tree get several levels deep.</p>
<p>Although using database recursion is an efficient way of improving
performance with our existing schema, other methods of handling tree
structures in SQL exist. The <a href="https://pragprog.com/book/bksqla/sql-antipatterns" title="SQL Antipatterns book"  class="external-link" target="_blank" rel="noopener">SQL Antipatterns</a> book has a great
breakdown of other tree solutions that would require schema changes.</p>
<h2 id="example-app">
  Example app
  <a class="heading-link" href="#example-app">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>As before, while writing this post, I created a <a href="https://github.com/jgdavey/tree-sql-example" title="Example application with recursive tree SQL"  class="external-link" target="_blank" rel="noopener">sample Rails app</a> to
iterate quickly. I used TDD to write the pure-ruby approach, and reused
the specs while I &ldquo;refactored&rdquo; the implementation to the subsequent
approaches. Of particular note is the [history of the Category
model](<a href="https://github.com/jgdavey/tree-sql-example/commits/master/app/mo"  class="external-link" target="_blank" rel="noopener">https://github.com/jgdavey/tree-sql-example/commits/master/app/mo</a>
dels/category.rb), which mirrors the code above.</p>
]]></content>
  </entry>
  <entry>
    <title type="html">SQL Window Functions and You</title>
    <link href="https://joshuadavey.com/2012/05/22/sql-window-functions/" rel="alternate" type="text/html" title="SQL Window Functions and You"/>
    <published>2012-05-22T00:00:00+00:00</published>
    <updated>2012-05-22T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2012/05/22/sql-window-functions/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2012/05/22/sql-window-functions/"><![CDATA[<p><em>This is a repost. You can find the original <a href="https://hashrocket.com/blog/posts/sql-window-functions" title="SQL Window Functions and You"  class="external-link" target="_blank" rel="noopener">here</a></em></p>
<p>Suppose you have a storefront application that sells pictures of cats.
These cat pictures are categorized in meaningful ways. For example,
there are LOLcats pictures and &ldquo;Classic&rdquo; cat pictures. Now, on the
landing page of the store, you&rsquo;d like to feature one picture from each
category. It can&rsquo;t be a random picture from each. You need to feature
the cheapest picture from each category, displaying its name and price.</p>
<p>Also, it turns out that some &ldquo;low&rdquo; prices are very common. For example,
$9.99 is a common sale price for LOLcats pictures. However, we should
only ever feature one picture per category. When there are multiple
pictures with the same low price, we fallback to the name, and show the
first one alphabetically. How can we solve this problem, while also
remaining performant?</p>
<p>As an aside, adding a cat to a Rennaisance painting amplifies its appeal
ninefold.</p>
<p><img src="https://s3.amazonaws.com/hashrocket-blog-production/catpictures.com.jpg" alt="catpictures.biz"></p>
<p>Let&rsquo;s look at some of the ways that we can approach this problem,
displaying a list of cat pictures that are the cheapest for their
respective category.</p>
<h2 id="approach-1-ruby">
  Approach 1: Ruby
  <a class="heading-link" href="#approach-1-ruby">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Implementing the solution in Ruby is fairly straightforward.
<del>ActiveSupport</del> Enumerable provides the <code>group_by</code> and <code>sort_by</code> methods on
collections, and we can use those to help us cut down on some typing.</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">CatPicture</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">attr_accessible</span> <span style="color:#d88200">:category_id</span><span style="color:#111">,</span> <span style="color:#d88200">:description</span><span style="color:#111">,</span> <span style="color:#d88200">:name</span><span style="color:#111">,</span> <span style="color:#d88200">:price</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">belongs_to</span> <span style="color:#d88200">:category</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">self</span><span style="color:#f92672">.</span><span style="color:#75af00">cheapest_per_category</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">all</span><span style="color:#f92672">.</span><span style="color:#111">group_by</span><span style="color:#111">(</span><span style="color:#f92672">&amp;</span><span style="color:#d88200">:category_id</span><span style="color:#111">)</span><span style="color:#f92672">.</span><span style="color:#111">map</span> <span style="color:#00a8c8">do</span> <span style="color:#f92672">|</span><span style="color:#111">category_id</span><span style="color:#111">,</span> <span style="color:#111">subset</span><span style="color:#f92672">|</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">subset</span><span style="color:#f92672">.</span><span style="color:#111">sort_by</span> <span style="color:#111">{</span> <span style="color:#f92672">|</span><span style="color:#111">pic</span><span style="color:#f92672">|</span> <span style="color:#f92672">[</span><span style="color:#111">pic</span><span style="color:#f92672">.</span><span style="color:#111">price</span><span style="color:#111">,</span> <span style="color:#111">pic</span><span style="color:#f92672">.</span><span style="color:#111">name</span><span style="color:#f92672">]</span> <span style="color:#111">}</span><span style="color:#f92672">.</span><span style="color:#111">first</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>First, we group all of the cat pictures by their category. Then, for
each set of pictures, we sort them by their price and name, and take
only the first one.</p>
<p>Perhaps you are wondering if inverting the responsibility would improve
the implementation, putting the mapping and reduction impetus in the
Category model instead. Although it would be possible to go through
the Category model to find its cheapest picture, that would lead to an
&ldquo;n+1&rdquo;, as each category would subsequently need fetch its cat pictures.
Alternatively, eager-loading all categories with their cat pictures
would be expensive, and would essentially duplicate what we&rsquo;ve done
above with the <code>group_by</code>.</p>
<p>Either way, as you can probably imagine, the above method would become
more expensive as the data set continued to grow. Additionally, we lose
the ability to continue to chain ActiveRecord scopes to filter the set
further: as soon as we fetch the collection from the database, all
filtering has to be done in Ruby.</p>
<p>Pros:</p>
<ul>
<li>Easy to grok</li>
<li>All domain logic stays in application</li>
</ul>
<p>Cons:</p>
<ul>
<li>Expensive (all objects loaded into memory)</li>
<li>No scope chaining</li>
<li>Once you go Ruby, you don&rsquo;t go back</li>
</ul>
<h2 id="approach-2-sql-subselects">
  Approach 2: SQL subselects
  <a class="heading-link" href="#approach-2-sql-subselects">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>We can improve performance by doing the filtering at the database level,
rather than loading all cat pictures into memory each time.</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">CatPicture</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">attr_accessible</span> <span style="color:#d88200">:category_id</span><span style="color:#111">,</span> <span style="color:#d88200">:description</span><span style="color:#111">,</span> <span style="color:#d88200">:name</span><span style="color:#111">,</span> <span style="color:#d88200">:price</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">belongs_to</span> <span style="color:#d88200">:category</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">self</span><span style="color:#f92672">.</span><span style="color:#75af00">cheapest_per_category</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">find_by_sql</span> <span style="color:#d88200">&lt;&lt;-SQL
</span></span></span><span style="display:flex;"><span><span style="color:#d88200"></span>      <span style="color:#00a8c8">SELECT</span> <span style="color:#00a8c8">DISTINCT</span> <span style="color:#00a8c8">ON</span><span style="color:#111">(</span><span style="color:#111">category_id</span><span style="color:#111">)</span> <span style="color:#111">cat_pictures</span><span style="color:#f92672">.</span><span style="color:#111">*</span>
</span></span><span style="display:flex;"><span>      <span style="color:#00a8c8">FROM</span> <span style="color:#111">cat_pictures</span>
</span></span><span style="display:flex;"><span>      <span style="color:#00a8c8">WHERE</span> <span style="color:#111">((</span><span style="color:#111">category_id</span><span style="color:#111">,</span> <span style="color:#111">price</span><span style="color:#111">)</span> <span style="color:#00a8c8">IN</span> <span style="color:#111">(</span>
</span></span><span style="display:flex;"><span>        <span style="color:#00a8c8">SELECT</span> <span style="color:#111">category_id</span><span style="color:#111">,</span> <span style="color:#111">min</span><span style="color:#111">(</span><span style="color:#111">price</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#00a8c8">FROM</span> <span style="color:#111">cat_pictures</span>
</span></span><span style="display:flex;"><span>        <span style="color:#00a8c8">GROUP</span> <span style="color:#00a8c8">BY</span> <span style="color:#111">category_id</span>
</span></span><span style="display:flex;"><span>      <span style="color:#111">))</span>
</span></span><span style="display:flex;"><span>      <span style="color:#00a8c8">ORDER</span> <span style="color:#00a8c8">BY</span> <span style="color:#111">category_id</span> <span style="color:#00a8c8">ASC</span><span style="color:#111">,</span> <span style="color:#111">cat_pictures</span><span style="color:#f92672">.</span><span style="color:#111">name</span> <span style="color:#00a8c8">ASC</span>
</span></span><span style="display:flex;"><span>    <span style="color:#00a8c8">SQL</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>Here, we use a subselect to filter the initial set down to only those
that have the cheapest price per category. In this inner query, each row
will contain a <code>category_id</code> and its lowest <code>price</code>. In the outer query,
we choose all cat pictures whose <code>price</code> and <code>category_id</code> match a row
from this inner query, using the <code>IN</code> syntax.</p>
<p>We would be done here, except that there still exists the possibility
that there could be more than one that have that low price for a given
category. So, depending on the database vendor, we can here find
&ldquo;distinct&rdquo; rows, according the columns of interest. In Postgresql,
the syntax for this is <code>DISTINCT ON([column,...])</code>, which will omit
duplicates of the listed columns. For our purposes, we don&rsquo;t want more
than one per category, so we distinct on <code>category_id</code>.</p>
<p>It is worth noting that without an <code>ORDER BY</code> clause, <code>DISTINCT ON</code> is
nondeterministic: we are not guaranteed to get the same result each
time. Thus, we order by <code>category_id</code> and <code>name</code>, so that only the first
cat picture alphabetically will show up.</p>
<p>We can improve the implementation above by making it a true chainable
scope. Whereas <code>find_by_sql</code> returns an array of objects, we can
refactor this to return an ActiveRelation instead.</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">CatPicture</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">attr_accessible</span> <span style="color:#d88200">:category_id</span><span style="color:#111">,</span> <span style="color:#d88200">:description</span><span style="color:#111">,</span> <span style="color:#d88200">:name</span><span style="color:#111">,</span> <span style="color:#d88200">:price</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">belongs_to</span> <span style="color:#d88200">:category</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">self</span><span style="color:#f92672">.</span><span style="color:#75af00">cheapest_per_category</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">where</span><span style="color:#111">(</span><span style="color:#d88200">&#34;(category_id, price) IN (</span><span style="color:#d88200">#{</span><span style="color:#111">category_id_and_lowest_price_sql</span><span style="color:#d88200">}</span><span style="color:#d88200">)&#34;</span><span style="color:#111">)</span><span style="color:#f92672">.</span><span style="color:#111">select</span><span style="color:#111">(</span><span style="color:#d88200">&#34;DISTINCT ON(category_id) </span><span style="color:#d88200">#{</span><span style="color:#111">table_name</span><span style="color:#d88200">}</span><span style="color:#d88200">.*&#34;</span><span style="color:#111">)</span><span style="color:#f92672">.</span><span style="color:#111">order</span><span style="color:#111">(</span><span style="color:#d88200">&#34;category_id ASC, </span><span style="color:#d88200">#{</span><span style="color:#111">table_name</span><span style="color:#d88200">}</span><span style="color:#d88200">.name ASC&#34;</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">private</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">self</span><span style="color:#f92672">.</span><span style="color:#75af00">category_id_and_lowest_price_sql</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">scoped</span><span style="color:#f92672">.</span><span style="color:#111">select</span><span style="color:#111">(</span><span style="color:#d88200">&#34;category_id, min(price)&#34;</span><span style="color:#111">)</span><span style="color:#f92672">.</span><span style="color:#111">group</span><span style="color:#111">(</span><span style="color:#d88200">:category_id</span><span style="color:#111">)</span><span style="color:#f92672">.</span><span style="color:#111">to_sql</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>Functionally, this generates the exact same query as before, but allows
further chaining. Using ActiveRelation&rsquo;s <code>to_sql</code> method, we&rsquo;re able
to build up our inner query without actually executing it. We then
interpolate that query into what was the outer query, which we&rsquo;ve
reduced to calls to <code>where</code>, <code>select</code> and <code>order</code>.</p>
<p>Pros:</p>
<ul>
<li>More performant than Ruby method</li>
<li>Scope chaining still possible</li>
</ul>
<p>Cons:</p>
<ul>
<li>Nested subselects</li>
<li>Very difficult to read in application code</li>
<li>The use of <code>DISTINCT ON</code> - only some RDBMS&rsquo; have such functionality</li>
</ul>
<h2 id="approach-3-window-functions">
  Approach 3: Window functions
  <a class="heading-link" href="#approach-3-window-functions">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>But there is still another option. The SQL standard defines a concept
called window functions, which act a lot like aggregates, but don&rsquo;t
change the result set. From the Postgresql documentation&rsquo;s <a href="https://www.postgresql.org/docs/9.1/static/tutorial-window.html" title="Introduction to using window functions"  class="external-link" target="_blank" rel="noopener">excellent
introduction to window functions</a>:</p>
<blockquote>
<p>A window function performs a calculation across a set of table rows that
are somehow related to the current row. This is comparable to the type
of calculation that can be done with an aggregate function. But unlike
regular aggregate functions, use of a window function does not cause
rows to become grouped into a single output row - the rows retain their
separate identities.</p>
</blockquote>
<p>Let&rsquo;s see how this would work with our dataset. First of all, let&rsquo;s assume the following cat pictures:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span># SELECT id, name, category_id, price FROM cat_pictures ORDER BY category_id, price;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> id |         name         | category_id | price 
</span></span><span style="display:flex;"><span>----+----------------------+-------------+-------
</span></span><span style="display:flex;"><span>  7 | Triple LOL           |           1 |  9.99
</span></span><span style="display:flex;"><span>  5 | Hugs not Drugs       |           1 |  9.99
</span></span><span style="display:flex;"><span>  2 | Puss in Boots        |           1 | 14.99
</span></span><span style="display:flex;"><span>  3 | Cats Gone By         |           1 | 19.99
</span></span><span style="display:flex;"><span>  6 | Cats in it for me    |           1 | 22.99
</span></span><span style="display:flex;"><span>  4 | Turkleton&#39;s Folly    |           2 | 11.99
</span></span><span style="display:flex;"><span>  1 | Meowna Lisa          |           2 | 19.99
</span></span><span style="display:flex;"><span>  8 | Lady Caterly&#39;s Lover |           2 | 22.99
</span></span></code></pre></div><p>Given this data, our goal is to select &ldquo;Hugs not Drugs&rdquo; and &ldquo;Turkleton&rsquo;s
Folly&rdquo;, which are the cheapest pictures from their categories.</p>
<p>Whereas a normal aggregate function with <code>GROUP BY</code> would collapse the
results, a window function retains the original row. Let&rsquo;s consider how
this would affect the inner query from the subselect approach above:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span># SELECT category_id, min(price) FROM cat_pictures GROUP BY category_id;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> category_id |  min  
</span></span><span style="display:flex;"><span>-------------+-------
</span></span><span style="display:flex;"><span>           1 |  9.99
</span></span><span style="display:flex;"><span>           2 | 11.99
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span># SELECT category_id, min(price) OVER (PARTITION BY category_id) FROM cat_pictures;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> category_id |  min  
</span></span><span style="display:flex;"><span>-------------+-------
</span></span><span style="display:flex;"><span>           1 |  9.99
</span></span><span style="display:flex;"><span>           1 |  9.99
</span></span><span style="display:flex;"><span>           1 |  9.99
</span></span><span style="display:flex;"><span>           1 |  9.99
</span></span><span style="display:flex;"><span>           1 |  9.99
</span></span><span style="display:flex;"><span>           2 | 11.99
</span></span><span style="display:flex;"><span>           2 | 11.99
</span></span><span style="display:flex;"><span>           2 | 11.99
</span></span></code></pre></div><p>Above, we&rsquo;ve replaced the <code>GROUP BY</code> clause with an <code>OVER</code> clause. We
have the original rows with an additional column for this aggregate
data. This is useful in its own right, but the real power of window
functions comes from this concept of window framing. The use of
<code>PARTITION BY</code> creates a frame for each group. In our case, we have
two frames, one for each <code>category_id</code>. Then, all aggregate and window
functions before the <code>OVER</code> clause operate against this frame. Each
window frame effectively has its own result set, according to the
defined partition.</p>
<p>When a window frame is ordered, using an <code>ORDER BY</code> clause, even more
options are possible. For example, consider the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span># SELECT id, name, category_id, price, rank() OVER (PARTITION BY category_id ORDER BY price) FROM cat_pictures;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> id |         name         | category_id | price | rank 
</span></span><span style="display:flex;"><span>----+----------------------+-------------+-------+------
</span></span><span style="display:flex;"><span>  7 | Triple LOL           |           1 |  9.99 |    1
</span></span><span style="display:flex;"><span>  5 | Hugs not Drugs       |           1 |  9.99 |    1
</span></span><span style="display:flex;"><span>  2 | Puss in Boots        |           1 | 14.99 |    3
</span></span><span style="display:flex;"><span>  3 | Cats Gone By         |           1 | 19.99 |    4
</span></span><span style="display:flex;"><span>  6 | Cats in it for me    |           1 | 22.99 |    5
</span></span><span style="display:flex;"><span>  4 | Turkleton&#39;s Folly    |           2 | 11.99 |    1
</span></span><span style="display:flex;"><span>  1 | Meowna Lisa          |           2 | 19.99 |    2
</span></span><span style="display:flex;"><span>  8 | Lady Caterly&#39;s Lover |           2 | 22.99 |    3
</span></span></code></pre></div><p>Look familiar? This is essentially the original , except we&rsquo;ve added a
new column: its price rank within a window partitioned by <code>category_id</code>.
It&rsquo;s a mouthful to describe, but we&rsquo;re very close to our original goal
of finding the cheapest cat picture per category. All we need to do now
is select rows that have a rank of 1.</p>
<p>Not so fast. Can you spot the issue with the above? The <code>rank()</code> window
function assigns the same rank to ties, but we need the first one
alphabetically in the case of &ldquo;ties&rdquo;. We can remedy that by using a
different window function, <code>row_number()</code>, which guarantees different
numbers.</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span># SELECT id, name, category_id, price, row_number() OVER (PARTITION BY category_id ORDER BY price, name) FROM cat_pictures;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> id |         name         | category_id | price | row_number 
</span></span><span style="display:flex;"><span>----+----------------------+-------------+-------+------------
</span></span><span style="display:flex;"><span>  5 | Hugs not Drugs       |           1 |  9.99 |          1
</span></span><span style="display:flex;"><span>  7 | Triple LOL           |           1 |  9.99 |          2
</span></span><span style="display:flex;"><span>  2 | Puss in Boots        |           1 | 14.99 |          3
</span></span><span style="display:flex;"><span>  3 | Cats Gone By         |           1 | 19.99 |          4
</span></span><span style="display:flex;"><span>  6 | Cats in it for me    |           1 | 22.99 |          5
</span></span><span style="display:flex;"><span>  4 | Turkleton&#39;s Folly    |           2 | 11.99 |          1
</span></span><span style="display:flex;"><span>  1 | Meowna Lisa          |           2 | 19.99 |          2
</span></span><span style="display:flex;"><span>  8 | Lady Caterly&#39;s Lover |           2 | 22.99 |          3
</span></span></code></pre></div><p>Perfect! Looking at the rows with &ldquo;1&rdquo; as their &ldquo;row_number&rdquo;, we see
what we expect, &ldquo;Hugs not Drugs&rdquo; and &ldquo;Turkleton&rsquo;s Folly&rdquo;, which are the
cheapest pictures from their categories. We can use an <code>IN</code> clause for
filtering, similar to the previous approach:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#00a8c8">SELECT</span> <span style="color:#111">id</span><span style="color:#111">,</span> <span style="color:#111">category_id</span><span style="color:#111">,</span> <span style="color:#111">name</span><span style="color:#111">,</span> <span style="color:#111">price</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">FROM</span> <span style="color:#111">cat_pictures</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">WHERE</span> <span style="color:#111">(</span><span style="color:#111">id</span><span style="color:#111">,</span> <span style="color:#ae81ff">1</span><span style="color:#111">)</span> <span style="color:#00a8c8">IN</span> <span style="color:#111">(</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">SELECT</span> <span style="color:#111">id</span><span style="color:#111">,</span> <span style="color:#111">row_number</span><span style="color:#111">()</span> <span style="color:#111">OVER</span> <span style="color:#111">(</span><span style="color:#111">PARTITION</span> <span style="color:#00a8c8">BY</span> <span style="color:#111">category_id</span> <span style="color:#00a8c8">ORDER</span> <span style="color:#00a8c8">BY</span> <span style="color:#111">price</span><span style="color:#111">,</span> <span style="color:#111">name</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">FROM</span> <span style="color:#111">cat_pictures</span>
</span></span><span style="display:flex;"><span><span style="color:#111">);</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span> id | category_id |         name         | price 
</span></span><span style="display:flex;"><span>----+-------------+----------------------+-------
</span></span><span style="display:flex;"><span>  5 |           1 | Hugs not Drugs       |  9.99
</span></span><span style="display:flex;"><span>  4 |           2 | Turkleton&#39;s Folly    | 11.99
</span></span></code></pre></div><p>The where clause above filters records that both have an id that appears
in the subquery next to a rank of 1. Now that we have the SQL down,
let&rsquo;s convert our Ruby model to take advantage of this window function
technique:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#00a8c8">class</span> <span style="color:#75af00">CatPicture</span> <span style="color:#f92672">&lt;</span> <span style="color:#00a8c8">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#00a8c8">Base</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">attr_accessible</span> <span style="color:#d88200">:category_id</span><span style="color:#111">,</span> <span style="color:#d88200">:description</span><span style="color:#111">,</span> <span style="color:#d88200">:name</span><span style="color:#111">,</span> <span style="color:#d88200">:price</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">belongs_to</span> <span style="color:#d88200">:category</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">self</span><span style="color:#f92672">.</span><span style="color:#75af00">cheapest_per_category</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">where</span><span style="color:#111">(</span><span style="color:#d88200">&#34;(</span><span style="color:#d88200">#{</span><span style="color:#111">table_name</span><span style="color:#d88200">}</span><span style="color:#d88200">.id, 1) IN (</span><span style="color:#d88200">#{</span><span style="color:#111">price_rank_sql</span><span style="color:#d88200">}</span><span style="color:#d88200">)&#34;</span><span style="color:#111">)</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">private</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">def</span> <span style="color:#75af00">self</span><span style="color:#f92672">.</span><span style="color:#75af00">price_rank_sql</span>
</span></span><span style="display:flex;"><span>    <span style="color:#111">scoped</span><span style="color:#f92672">.</span><span style="color:#111">select</span><span style="color:#111">(</span><span style="color:#d88200">&#34;id, row_number() OVER (PARTITION BY category_id ORDER BY price ASC, name ASC)&#34;</span><span style="color:#111">)</span><span style="color:#f92672">.</span><span style="color:#111">to_sql</span>
</span></span><span style="display:flex;"><span>  <span style="color:#00a8c8">end</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">end</span>
</span></span></code></pre></div><p>Groovy. Just like before, we can use to the power of ActiveRelation
to build up our subselect, which then gets interpolated into the
<code>where</code> clause. I&rsquo;ve also prepended <code>id</code> in the <code>where</code> clause with
<code>table_name</code>, to avoid potential ambiguous column problems.</p>
<p>There is one potential issue with using window functions: limited vendor
support. While most of the big boys implement window functions (Oracle,
Postgresql, and SQLServer, to name a few), MySQL and SQLite users are
out of luck.</p>
<p>Pros:</p>
<ul>
<li>Very performant (consistently twice as fast as Approach 2 on my laptop)</li>
<li>Much less noise than SQL subselect stuff</li>
<li>Easy to understand, assuming a basic knowledge of SQL window functions</li>
</ul>
<p>Cons:</p>
<ul>
<li>Not portable (window functions are not available in MySQL or SQLite)</li>
</ul>
<h2 id="conclusion">
  Conclusion
  <a class="heading-link" href="#conclusion">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>While they may not be appropriate for every situation, window functions
are a great tool for your toolbelt. They excel at filtering down rows
based on aggregate data, or adding aggregate data to the rows you&rsquo;d
already like to select.</p>
<p>For more information about window functions, the Postgres documentation
is an excellent resource, both for its <a href="https://www.postgresql.org/docs/9.1/static/tutorial-window.html" title="Introduction to using window functions"  class="external-link" target="_blank" rel="noopener">introduction</a>,
and its <a href="https://www.postgresql.org/docs/9.1/static/functions-window.html" title="List of PostgreSQL window functions"  class="external-link" target="_blank" rel="noopener">list of window functions</a>.</p>
<h2 id="example-app">
  Example app
  <a class="heading-link" href="#example-app">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>While writing this post, I created a <a href="https://github.com/jgdavey/windowing-example/" title="Window functions example application"  class="external-link" target="_blank" rel="noopener">sample Rails app</a> to iterate
quickly. I used TDD to write the pure-ruby approach, and reused the
specs while I &ldquo;refactored&rdquo; the implementation to the subsequent
approaches. Of particular note is <a href="https://github.com/jgdavey/windowing-example/commits/master/app/models/cat_picture.rb" title="Git history of the CatPicture model"  class="external-link" target="_blank" rel="noopener">the history of the CatPicture
model</a>, which mirrors the code above.</p>
]]></content>
  </entry>
  <entry>
    <title type="html">Easier Atomic Commits</title>
    <link href="https://joshuadavey.com/2012/05/16/easier-atomic-commits/" rel="alternate" type="text/html" title="Easier Atomic Commits"/>
    <published>2012-05-16T00:00:00+00:00</published>
    <updated>2012-05-16T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2012/05/16/easier-atomic-commits/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2012/05/16/easier-atomic-commits/"><![CDATA[<p><em>This is a repost. You can find the original <a href="https://hashrocket.com/blog/posts/easier-atomic-commits" title="Easier Atomic Commits"  class="external-link" target="_blank" rel="noopener">here</a></em></p>
<p>How do you keep your commits atomic easily? Let&rsquo;s explore one possible approach.</p>
<h2 id="problem">
  Problem
  <a class="heading-link" href="#problem">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>As a practitioner of good source control, you and your team have decided
to make all of your git commits <a href="http://goo.gl/P95KkZ"  class="external-link" target="_blank" rel="noopener">atomic</a> within your projects. That is,
every commit has a green test suite, and you prefer small, incremental
commits to large, monolithic ones. Keeping commits small and atomic has
tons of benefits, from more consistent continuous integration results,
to better team cohesion (have you ever gotten upset with another team
member for committing red?). But in practice, keeping all of your commits
atomic can present some challenges.</p>
<p>After doing a bunch of work, making incremental, atomic commits along
the way, it&rsquo;s time to push your work up. However, when you run <code>git pull --rebase</code>, you find that another team member has made changes since you
last pushed. Your commits are now sitting on top of a different git
history. Are all of your commits still atomic? Short of checking out
every single commit and running the suite, how can you be certain that
every commit is atomic? What a pain! I don&rsquo;t want to check out every
commit by hand.</p>
<h2 id="solution">
  Solution
  <a class="heading-link" href="#solution">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Enter <code>atomically</code>, a simple shell script designed to take the pain out
of checking every commit between your upstream and you. Before pushing,
you can ensure every commit is atomic by running the script.</p>
<p>To use, just pass atomically the command as arguments:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>$ atomically rake
</span></span></code></pre></div><p>The above command will start at the current branch&rsquo;s HEAD and run rake.
After that, it will check out the previous commit and run the command
again. It will do so for all commits between you and origin.</p>
<p>If you are confident that nothing in your spec suite changed, you can
run only your cucumber features the same way:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>$ atomically cucumber
</span></span></code></pre></div><p>Or just your spec suite:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>$ atomically rspec
</span></span></code></pre></div><p>Regardless, keeping atomic commits is a vital part of good source
control, and this tool makes it slightly easier to do so.</p>
<p>Here&rsquo;s the source of <code>atomically</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#272822;background-color:#fafafa;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e">#!/bin/bash
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">if</span> <span style="color:#f92672">[</span> -n <span style="color:#d88200">&#34;</span><span style="color:#00a8c8">$(</span>git status --porcelain<span style="color:#00a8c8">)</span><span style="color:#d88200">&#34;</span> <span style="color:#f92672">]</span><span style="color:#111">;</span> <span style="color:#00a8c8">then</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">echo</span> <span style="color:#d88200">&#34;ERROR: You have a dirty working copy. This command would remove any files not already checked in&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">exit</span> <span style="color:#ae81ff">1</span>
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#111">b</span><span style="color:#f92672">=</span><span style="color:#d88200">&#34;</span><span style="color:#00a8c8">$(</span>git symbolic-ref HEAD 2&gt;/dev/null<span style="color:#00a8c8">)</span><span style="color:#d88200">&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#111">branch</span><span style="color:#f92672">=</span><span style="color:#d88200">&#34;`basename </span><span style="color:#111">$b</span><span style="color:#d88200">`&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#111">program</span><span style="color:#f92672">=</span><span style="color:#111">$*</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>reset_branch<span style="color:#f92672">()</span> <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span>  git co <span style="color:#111">$branch</span> --quiet
</span></span><span style="display:flex;"><span><span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>git rev-list origin/<span style="color:#d88200">${</span><span style="color:#111">branch</span><span style="color:#111">-master</span><span style="color:#d88200">}</span>..<span style="color:#d88200">${</span><span style="color:#111">branch</span><span style="color:#111">-master</span><span style="color:#d88200">}</span> <span style="color:#111">|</span> <span style="color:#00a8c8">while</span> <span style="color:#111">read</span> rev<span style="color:#111">;</span> <span style="color:#00a8c8">do</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">trap</span> <span style="color:#d88200">&#34;exit 1&#34;</span> SIGINT SIGTERM
</span></span><span style="display:flex;"><span>  <span style="color:#111">echo</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">echo</span> <span style="color:#d88200">&#34;Running at revision </span><span style="color:#111">$rev</span><span style="color:#d88200">&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">echo</span>
</span></span><span style="display:flex;"><span>  git co <span style="color:#111">$rev</span> --quiet <span style="color:#f92672">&amp;&amp;</span> git clean -fd <span style="color:#f92672">&amp;&amp;</span> <span style="color:#111">$program</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">echo</span>
</span></span><span style="display:flex;"><span>  <span style="color:#111">trap</span> - SIGINT SIGTERM
</span></span><span style="display:flex;"><span><span style="color:#00a8c8">done</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>reset_branch
</span></span></code></pre></div><p>To use, just drop that in a file in your <code>$PATH</code>, and make sure it is executable.</p>
<p>Thanks to Gary Bernhardt for the scripts&rsquo; inspiration,
<code>run-command-on-git-revisions</code>, which you can see in <a href="https://github.com/garybernhardt/dotfiles/blob/master/bin/run-command-on-git-revisions"  class="external-link" target="_blank" rel="noopener">his
dotfiles</a>.</p>
]]></content>
  </entry>
  <entry>
    <title type="html">Simulate network latency for application testing</title>
    <link href="https://joshuadavey.com/2012/05/14/simulate-network-latency-for-application-testing/" rel="alternate" type="text/html" title="Simulate network latency for application testing"/>
    <published>2012-05-14T00:00:00+00:00</published>
    <updated>2012-05-14T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2012/05/14/simulate-network-latency-for-application-testing/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2012/05/14/simulate-network-latency-for-application-testing/"><![CDATA[<p>Sometimes it&rsquo;s useful to test your web application for situations where internet connections are less than stellar. It turns out that Mac OS X has a builtin utility called <code>ipfw</code> than, among other things, can do just this.</p>
<p>I first saw this technique from <a href="http://joemiller.me/2010/08/31/simulate-network-latency-packet-loss-and-bandwidth-on-mac-osx/"  class="external-link" target="_blank" rel="noopener">Joe Miller&rsquo;s post</a> on the subject. I packaged up the settings he mentioned into a little shell script:</p>
<script src="https://gist.github.com/jgdavey/2696974.js"></script>
<p>You can drop that somewhere in your <code>$PATH</code> and <code>chmod +x</code> to make it executable. You can call it whatever you want, but I called mine &ldquo;hinder&rdquo;. After that, it&rsquo;s simply a matter of using it:</p>
<pre><code>$ hinder www.google.com
</code></pre>
<p>Now when you visit google.com, you should see some marked slowness. To reset, just run:</p>
<pre><code>$ hinder reset
</code></pre>
<p>Google is now fast again.</p>
<p>The script works by adding 250ms delay to both directions of network traffic. It also adds a packet-loss percentage of 10%. You can play with these numbers to get even more latency simulation. Enjoy!</p>
]]></content>
  </entry>
  <entry>
    <title type="html">Add untracked files in git</title>
    <link href="https://joshuadavey.com/2012/03/30/add-untracked-files-in-git/" rel="alternate" type="text/html" title="Add untracked files in git"/>
    <published>2012-03-30T00:00:00+00:00</published>
    <updated>2012-03-30T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2012/03/30/add-untracked-files-in-git/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2012/03/30/add-untracked-files-in-git/"><![CDATA[<p>I&rsquo;ve often been annoyed that there&rsquo;s no way to non-interactively add untracked files in git. Well, I finally whipped an alias to speed that process up:</p>
<script src="https://gist.github.com/2256047.js?file=.gitconfig"></script>
<p>Just add it to your global git config file at <code>~/.gitconfig</code> and enjoy.</p>
]]></content>
  </entry>
  <entry>
    <title type="html">Faster TDD feedback with tmux, tslime.vim, and turbux.vim</title>
    <link href="https://joshuadavey.com/2012/01/10/faster-tdd-feedback-with-tmux-tslime-vim-and-turbux/" rel="alternate" type="text/html" title="Faster TDD feedback with tmux, tslime.vim, and turbux.vim"/>
    <published>2012-01-10T00:00:00+00:00</published>
    <updated>2012-01-10T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2012/01/10/faster-tdd-feedback-with-tmux-tslime-vim-and-turbux/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2012/01/10/faster-tdd-feedback-with-tmux-tslime-vim-and-turbux/"><![CDATA[<p><strong>tl;dr</strong> Developing in a tmux session has sped up my TDD cycle
considerably, especially with the help of tslime.vim and turbux.vim. I
get immediate feedback in one pane, but never have to leave vim or lose
context of what I&rsquo;m working on to see the test result.</p>
<p>Taking a cue from <a href="https://rhnh.net/2011/08/20/vim-and-tmux-on-osx" title="Robot Has No Heart - Vim and tmux on OSX"  class="external-link" target="_blank" rel="noopener">Xavier Shay&rsquo;s excellent intro to tmux</a>, I&rsquo;ve
been using tmux lately as my primary workspace. There are excellent
<a href="https://peterc.org/blog/2010/216-tmux.html" title="Peter Cooper&#39;s introduction to tmux"  class="external-link" target="_blank" rel="noopener">introductions to</a> <a href="https://blog.hawkhost.com/2010/06/28/tmux-the-terminal-multiplexer/" title="Hawkhost&#39;s introduction to tmux"  class="external-link" target="_blank" rel="noopener">tmux elsewhere</a>, but I&rsquo;ve really
enjoyed the switch from MacVim/Terminal to a single tmux session for
development. But rather than sing tmux&rsquo;s praises, I&rsquo;d like to talk about
how tmux and a vim plugin have changed my testing feedback loop for the
better.</p>
<h2 id="other-test-running-solutions">
  Other test-running solutions
  <a class="heading-link" href="#other-test-running-solutions">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Autotesting gives you immediate feedback, but runs everytime you save a
file. Even though this often is desired behavior, I can&rsquo;t tell you how
many times I&rsquo;ve saved a feature file, only to immediately notice a typo.
Especially with Rails project, this can be an expensive amount of time.
I end up feeling punished for saving my work.</p>
<p>I&rsquo;ve also tried more editor-embedding techniques of running tests. Both
<a href="https://github.com/tpope/vim-rails" title="Rails.vim: Ruby on Rails power tools"  class="external-link" target="_blank" rel="noopener">rails.vim</a> and rake.vim provide facilities for running <code>:Rake</code>. When
combined with a keyboard shortcut, this gets closer to the kind of
control I like to have, running my tests exactly when I want them. The
downside, though, is that I lose control of my editor and have to wait
for the command to finish before I can type, or even navigate again. And
I can&rsquo;t look at a failure message and my code at the same time.</p>
<h2 id="a-faster-feedback-loop">
  A faster feedback loop
  <a class="heading-link" href="#a-faster-feedback-loop">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>A practice that is quickly gaining popularity in the Ruby community is
isolating your business logic from your persistance logic and framework.
Rather than load Rails (or some other large library or framework),
you sequester all business logic in its own class or module, and then
test that class or module in isolation. This has a ton of benefits for
the longevity of your code, but one of the side benefits is the speed
increase for running individual specs or tests. This technique is being
championed by Gary Bernhardt and Corey Haines, among others.</p>
<p>Because tmux is so scriptable, it isn&rsquo;t hard to send commands to other
panes in a tmux session programmatically. Leveraging the power of
<a href="https://github.com/tpope/vim-rails" title="Rails.vim: Ruby on Rails power tools"  class="external-link" target="_blank" rel="noopener">rails.vim</a> and <a href="https://github.com/jgdavey/tslime.vim" title="Joshua Davey&#39;s tslime.vim fork"  class="external-link" target="_blank" rel="noopener">tslime.vim</a>, I&rsquo;ve created a vim plugin that shortens
the feedback loop when practicing TDD in a tmux session. It&rsquo;s called
<a href="https://github.com/jgdavey/vim-turbux" title="Turbux: turbo Ruby testing with tmux"  class="external-link" target="_blank" rel="noopener">turbux.vim</a>.</p>
<h2 id="using-tslimevim">
  Using tslime.vim
  <a class="heading-link" href="#using-tslimevim">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>My typical workflow now involves setting up a tmux session for my
project, splitting vertically (<code>&lt;C-b&gt; %</code>), and using layout 4 (<code>&lt;C-b&gt; &lt;Alt-4&gt;</code>). In fullscreen, the result is about 30% for my shell on the
left, and 70% for vim on the right.</p>
<p>The first time you use it, tslime.vim will prompt you to input your tmux
session name, window number, and pane number. There is completion for
each of these prompts, so you can happily mash <code>&lt;Tab&gt;</code>.</p>
<p>The plugin exposes a general-purpose function to send arbitrary text to
the configured tmux pane. For example, you can use it in the following way:</p>
<pre><code>:call Send_to_Tmux(&quot;rspec &quot;.expand(&quot;%&quot;).&quot;\n&quot;)
</code></pre>
<p>The above command would send <code>rspec path/to/spec.rb</code> to the configured
pane. For me, this pattern of running the test file that is currently
open is so common that I&rsquo;ve packaged up some useful defaults in
<a href="https://github.com/jgdavey/vim-turbux" title="Turbux: turbo Ruby testing with tmux"  class="external-link" target="_blank" rel="noopener">turbux.vim</a>.</p>
<h2 id="using-turbuxvim">
  Using turbux.vim
  <a class="heading-link" href="#using-turbuxvim">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Turbux.vim tries to intelligently choose the right spec, test or feature
to run when you invoke it. If you&rsquo;re in a spec, invoking the plugin
(by default with <code>&lt;leader&gt;t</code>) will run <code>rspec path/to/my_spec.rb</code> in
the corresponding pane. In a test-unit file, it will run <code>ruby -Itest path/to/test.rb</code>. In a cucumber feature file, it will run <code>cucumber path/to/my.feature</code>.</p>
<p>Thanks to <a href="https://github.com/tpope/vim-rails" title="Rails.vim: Ruby on Rails power tools"  class="external-link" target="_blank" rel="noopener">rails.vim</a>&rsquo;s awesomeness, I&rsquo;ve also provided some mappings for
when the current file has a corresponding test or spec. For example,
When I&rsquo;m in a file that has a corresponding spec, such as a model or
controller, the command will run the that spec.</p>
<p>Finally, if the plugin is invoked outside the context of any feature
or spec-related file, it will simply run the most recent test command
again.</p>
<p>Also, I&rsquo;ve added a mapping for <code>&lt;leader&gt;T</code> to run a more focused spec or
cucumber scenario. It works by adding the vim cursor&rsquo;s line number to
the rspec or cucumber command.</p>
<h2 id="conclusion">
  Conclusion
  <a class="heading-link" href="#conclusion">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>This setup has been really rewarding so far. There&rsquo;s far less context
switching, as I never have to leave my editor. There are also fewer
surprises. As far as I&rsquo;m concerned, the faster my feedback, the better.</p>
<p><em>Note:</em> You will probably want to use my fork of <a href="https://github.com/jgdavey/tslime.vim" title="Joshua Davey&#39;s tslime.vim fork"  class="external-link" target="_blank" rel="noopener">tslime.vim</a>, as the main
repository has some outstanding bugs, and the fixes have yet to been
merged in.</p>
<h2 id="bonus">
  Bonus
  <a class="heading-link" href="#bonus">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>See the plugin in action. If the video is hard to see, visit vimeo. There is a link to download at full size.</p>
<iframe src="https://player.vimeo.com/video/34879707?title=0&amp;byline=0&amp;portrait=0" width="600" height="400" frameborder="0"  allow="autoplay; fullscreen" allowfullscreen></iframe>
]]></content>
  </entry>
  <entry>
    <title type="html">zsh completion for hitch</title>
    <link href="https://joshuadavey.com/2011/10/11/zsh-completion-for-hitch/" rel="alternate" type="text/html" title="zsh completion for hitch"/>
    <published>2011-10-11T00:00:00+00:00</published>
    <updated>2011-10-11T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2011/10/11/zsh-completion-for-hitch/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2011/10/11/zsh-completion-for-hitch/"><![CDATA[<p>At <a href="https://www.hashrocket.com"  class="external-link" target="_blank" rel="noopener">hashrocket</a>, we use <a href="https://github.com/therubymug/hitch"  class="external-link" target="_blank" rel="noopener">hitch</a> to manage our paired git authors, written by our own Rogelio Samour.</p>
<p>Hitch works by setting the <code>GIT_AUTHOR_NAME</code> and <code>GIT_AUTHOR_EMAIL</code> environment variables. For the email itself, it joins the hitched author&rsquo;s github usernames and a prefix with a &ldquo;+&rdquo;, creating email addresses of the form &ldquo;<a href="mailto:dev&#43;jgdavey&#43;therubymug@hashrocket.com" >dev+jgdavey+therubymug@hashrocket.com</a>&rdquo;. Using it is as simple as <code>hitch &lt;github_username&gt; &lt;github_username&gt;</code>.</p>
<p>Recently, I was annoyed that I had to always remember the github username of the person I was pairing with. I was at the command line typing and found myself hitting <code>&lt;tab&gt;</code> repeatedly, hoping it would complete with the authors I pair with most often.</p>
<p>So I quickly whipped up a zsh completion script, and <em>boom</em>, pairing nirvana.</p>
<p>To use the completion script, save the following script as <code>_hitch</code> and add it to your <code>fpath</code>:</p>
<script src="https://gist.github.com/jgdavey/1257698.js"></script>
<p>If you see any way to improve the function, please fork the <a href="https://gist.github.com/1257698"  class="external-link" target="_blank" rel="noopener">gist</a> on github.</p>
]]></content>
  </entry>
  <entry>
    <title type="html">QR Codes in Ruby</title>
    <link href="https://joshuadavey.com/2011/06/13/qr-codes-in-ruby/" rel="alternate" type="text/html" title="QR Codes in Ruby"/>
    <published>2011-06-13T00:00:00+00:00</published>
    <updated>2011-06-13T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2011/06/13/qr-codes-in-ruby/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2011/06/13/qr-codes-in-ruby/"><![CDATA[<p>QR codes seem to be everywhere these days. I&rsquo;ve seen them in
advertisements on the train, on the back of magazines, and, most
recently, on someone&rsquo;s wedding invitation. A lot of folks are using
QR codes as a quick way to share URLs. But that&rsquo;s not all that they
are good for. We recently had a client that needed to authorize paper
tickets, and a QR-encoded token turned out to be a great solution.</p>
<h2 id="qr-code-generation-in-ruby">
  QR Code Generation in Ruby
  <a class="heading-link" href="#qr-code-generation-in-ruby">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>From my experience, there are two solid options for QR code generation in Ruby:</p>
<ul>
<li><a href="http://whomwah.github.com/rqrcode/"  class="external-link" target="_blank" rel="noopener">rQRCode</a> Pure ruby QR code generator</li>
<li><a href="https://github.com/harrisj/qrencoder"  class="external-link" target="_blank" rel="noopener">qrencoder</a> A Ruby wrapper around libqrencode</li>
</ul>
<h3 id="rqrcode">
  rQRCode
  <a class="heading-link" href="#rqrcode">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<ul>
<li>Pure ruby</li>
<li>Portable: will run on many Ruby platforms</li>
<li>Few dependencies</li>
<li>Relatively slow</li>
</ul>
<h3 id="qrencoder">
  QREncoder
  <a class="heading-link" href="#qrencoder">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<ul>
<li>Very, very fast</li>
<li>Uses a C library (libqrencode) that works wherever <code>make</code> does</li>
<li>Likely not to run on JRuby</li>
</ul>
<h2 id="qr-code-decoding-in-ruby">
  QR Code Decoding in Ruby
  <a class="heading-link" href="#qr-code-decoding-in-ruby">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>The QR Decoding landscape varies even more than its QR encoding
counterpart. There are several ooptions to choose from, and the best one
for you will likely depend your environment and stack. The top three
libraries that I&rsquo;ve found are:</p>
<ul>
<li><a href="https://github.com/rubysolo/qrio"  class="external-link" target="_blank" rel="noopener">qrio</a> Pure ruby QR Code decoder</li>
<li><a href="https://github.com/ecin/zxing.rb"  class="external-link" target="_blank" rel="noopener">zxing.rb</a> JRuby wrapper for ZXing 1D/2D barcode image processing library.</li>
<li><a href="https://github.com/jgdavey/qrdecoder"  class="external-link" target="_blank" rel="noopener">qrdecoder</a> A ruby wrapper for the C++ port of ZXing</li>
</ul>
<h3 id="qrio">
  Qrio
  <a class="heading-link" href="#qrio">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<ul>
<li>Pure ruby</li>
<li>Source is readable for ruby developers</li>
<li>Has intermediate hooks for displaying pattern detection</li>
<li>Only reads in PNG formatted images</li>
<li>Slower than the other options</li>
</ul>
<h3 id="zxingrb">
  Zxing.rb
  <a class="heading-link" href="#zxingrb">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<ul>
<li>Uses the well-maintained <a href="https://code.google.com/p/zxing/" title="Zebra Crossing"  class="external-link" target="_blank" rel="noopener">Zxing</a> (Zebra Crossing) Java barcode decoding library</li>
<li>Works with both JRuby and MRI (via <a href="https://rubygems.org/gems/jruby-jars"  class="external-link" target="_blank" rel="noopener">jruby-jars gem</a>)</li>
<li>MRI versions of ruby will notice a slow startup as a forked JRuby server is started</li>
</ul>
<h3 id="qrdecoder">
  QRDecoder
  <a class="heading-link" href="#qrdecoder">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h3>
<ul>
<li>Wrapper for libzxing, a C++ port of the ZXing Java library</li>
<li>By far the fastest option for MRI</li>
<li>The C++ port of ZXing is less maintained than its big brother</li>
<li>You must build <a href="https://github.com/jgdavey/zxing-cpp"  class="external-link" target="_blank" rel="noopener">libzxing</a> from source (a homebrew package is pending)</li>
</ul>
<h2 id="the-bottom-line">
  The Bottom Line
  <a class="heading-link" href="#the-bottom-line">
    <i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"></i>
    <span class="sr-only">Link to heading</span>
  </a>
</h2>
<p>Wherein I offer &ldquo;My Two Cents&rdquo;. If you&rsquo;re on a stack that can install
libraries and you&rsquo;re using an MRI ruby (1.8 or 1.9), use qrencoder and
qrdecoder. They are both very fast and awesome. Additionally, their
APIs complement each other. If you&rsquo;re on Heroku, use rQRCode and qrio.
They&rsquo;re both pure ruby and play nice with an environment where you can&rsquo;t
install you&rsquo;re own libraries. If you&rsquo;re on JRuby, use Zxing.rb for
decoding, and try both rQRCode and qrencoder. I haven&rsquo;t tried qrencoder
with JRuby&ndash;or any compiled gems for that matter&ndash;so your mileage may
vary.</p>
<p><strong>N.B.</strong> In the interest of full disclosure, I have contributed to both
qrencoder and zxing.rb, and maintain qrdecoder, and tend to favor them.
Nevertheless, the pure ruby options are solid and well worth a look.</p>
<p>Regardless of what your needs are, there are lots of options. Which one
fits your needs best will largely depend on your stack.</p>
]]></content>
  </entry>
  <entry>
    <title type="html">VIM script to toggle do/end and brackets</title>
    <link href="https://joshuadavey.com/2010/11/16/vim-script-to-toggle-do-end-and-brackets/" rel="alternate" type="text/html" title="VIM script to toggle do/end and brackets"/>
    <published>2010-11-16T00:00:00+00:00</published>
    <updated>2010-11-16T00:00:00+00:00</updated>
    <id>https://joshuadavey.com/2010/11/16/vim-script-to-toggle-do-end-and-brackets/</id>
    <author>
      <name>Joshua Davey</name>
    </author>
    <content type="html" xml:base="https://joshuadavey.com/2010/11/16/vim-script-to-toggle-do-end-and-brackets/"><![CDATA[<p><strike>This is still rough, but I&rsquo;m finding it quite useful:</strike></p>
<p>I&rsquo;ve converted this to a full-blown VIM plugin: <a href="https://github.com/jgdavey/vim-blockle"  class="external-link" target="_blank" rel="noopener">https://github.com/jgdavey/vim-blockle</a></p>
]]></content>
  </entry>
  
</feed>
