<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Binary Logic &#187; caching</title>
	<atom:link href="http://www.binarylogic.com/tag/caching/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.binarylogic.com</link>
	<description>Ben Johnson's thoughts and programming techniques</description>
	<lastBuildDate>Sat, 23 Jan 2010 21:19:58 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Tutorial: Caching virtual attributes in the database</title>
		<link>http://www.binarylogic.com/2008/10/05/tutorial-caching-virtual-attributes-in-the-database/</link>
		<comments>http://www.binarylogic.com/2008/10/05/tutorial-caching-virtual-attributes-in-the-database/#comments</comments>
		<pubDate>Sun, 05 Oct 2008 16:32:00 +0000</pubDate>
		<dc:creator>benjohnson</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Searchlogic]]></category>
		<category><![CDATA[caching]]></category>
		<category><![CDATA[searchgasm]]></category>
		<category><![CDATA[virtual_column]]></category>

		<guid isPermaLink="false">0/2009/03/23/tutorial-caching-virtual-attributes-in-the-database</guid>
		<description><![CDATA[Ever since I released Searchlogic I&#8217;ve been getting a lot of really cool feedback. I love when people contact me with a unique way they used Searchlogic or a challenging / cool new feature they would like to see added. Challenges are what make programming fun, to see what you are capable of. So here [...]]]></description>
			<content:encoded><![CDATA[<p>Ever since I released <a href="http://github.com/binarylogic/searchlogic">Searchlogic</a> I&#8217;ve been getting a lot of really cool feedback. I love when people contact me with a unique way they used <a href="http://github.com/binarylogic/searchlogic">Searchlogic</a> or a challenging / cool new feature they would like to see added. Challenges are what make programming fun, to see what you are capable of. So here is my challenge for the day:</p>
<p>I&#8217;ve been having a lot of conversations via email with people using <a href="http://github.com/binarylogic/searchlogic">Searchlogic</a>, that go like the following:</p>
<blockquote>
<p><strong>Some person:</strong> Hi Ben, How do I order by an order&#8217;s total with Searchlogic?</p>
<p><strong>Me:</strong> Cache the total in your database. Your idol, Ben</p>
<p><strong>Some person:</strong> Before Searchlogic, I was just doing: Order.all(:order =&gt; &#8220;(quantity * price) ASC&#8221;). Is there a way to do this in Searchlogic?</p>
<p><strong>Me:</strong> No, you shouldn&#8217;t order data like that. &#8230;long explanation of what to do&#8230;. Your idol, Ben</p>
<p><strong>Some person:</strong> Thank you so much Ben, I can&#8217;t thank you enough for everything you have done for me. You truly are my idol. There should be a statue in your honor.</p>
</blockquote>
<p>Maybe it didn&#8217;t go exactly like that, but you get the point. So instead of having to retype the &#8220;long explanation of what to do&#8221;, I can just link them to this post:</p>
<p>This can be simple or complicated depending on how you calculate your total. Let&#8217;s start with the simple:</p>
<p>            <span id="more-33"></span></p>
<h2>Simple solution</h2>
<p>Your total method looks something like to following. Dealing with only local attributes.</p>
<pre class="cobalt">
<span class="Keyword">def</span> <span class="Entity">total</span>
    quantity <span class="Keyword">*</span> price
<span class="Keyword">end</span>
</pre>
<p>Your first thought might be to sort via the ruby sort method. This is wrong on so many levels, not to mention you already have a tool that is extremely good at sorting data: your database.</p>
<p>Awesome! We&#8217;ll just do something like this:</p>
<pre class="cobalt">
<span class="Support">Order</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">all</span></span><span class="Punctuation">(</span><span class="Constant"><span class="Punctuation">:</span>order</span> <span class="Punctuation">=&gt;</span> <span class="String"><span class="Punctuation">&quot;</span>(quantity * price) ASC<span class="Punctuation">&quot;</span></span><span class="Punctuation">)</span>
</pre>
<p>Wrong again! This is bad for a number of reasons:</p>
<ol>
<li>Your code is no longer DRY. What if you want to add in tax later on down the road? You&#8217;ll have to do it in multiple places.</li>
<li>(quantity * price) can not be indexed. The whole point of indexing is to sort.</li>
</ol>
<p>Now you&#8217;re saying &#8220;WTF Ben?!?!?1!?11&#8243;. Relax, why not just cache the total in the database?</p>
<pre class="cobalt">
<span class="Keyword">class</span> <span class="Entity">Order<span class="EntityInheritedClass"> <span class="Punctuation">&lt;</span> ActiveRecord::Base</span></span>
    before_validation <span class="Constant"><span class="Punctuation">:</span>cache_virtual_columns</span>

    <span class="Keyword">def</span> <span class="Entity">total</span>
        quantity <span class="Keyword">*</span> price
    <span class="Keyword">end</span>

    <span class="Keyword">private</span>
        <span class="Keyword">def</span> <span class="Entity">cache_virtual_columns</span>
            <span class="LangVariable">self</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">total</span></span> <span class="Keyword">=</span> total
        <span class="Keyword">end</span>
<span class="Keyword">end</span>
</pre>
<p>Now you can index the total column and order just like you would with any other column. Yay!</p>
<p>Don&#8217;t celebrate yet. Chances are your total probably isn&#8217;t that simple.</p>
<h2>Advanced solution</h2>
<p>Maybe your total method looks like:</p>
<pre class="cobalt">
<span class="Keyword">def</span> <span class="Entity">total</span>
    line_items<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">sum</span></span><span class="Punctuation">(</span><span class="Constant"><span class="Punctuation">:</span>total</span><span class="Punctuation">)</span> <span class="Keyword">+</span> tax <span class="Keyword">+</span> shipping_cost
<span class="Keyword">end</span>
</pre>
<p>Oh no! What happens when the total of a line item changes? The total in the orders table is no longer accurate. Your total now depends on other models. This is where observers shine:</p>
<pre class="cobalt">
<span class="Keyword">class</span> <span class="Entity">OrderObserver<span class="EntityInheritedClass"> <span class="Punctuation">&lt;</span> ActiveRecord::Observer</span></span>
  observe <span class="Constant"><span class="Punctuation">:</span>order</span><span class="Punctuation">,</span> <span class="Constant"><span class="Punctuation">:</span>line_item</span>

  <span class="Keyword">def</span> <span class="Entity">before_validation</span><span class="Punctuation">(</span><span class="Variable">obj</span><span class="Punctuation">)</span>
    <span class="Keyword">case</span> obj
    <span class="Keyword">when</span> <span class="Variable">Order</span>
      obj<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">total</span></span> <span class="Keyword">=</span> obj<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">total</span></span>
    <span class="Keyword">end</span>
  <span class="Keyword">end</span>

  <span class="Keyword">def</span> <span class="Entity">after_save</span><span class="Punctuation">(</span><span class="Variable">obj</span><span class="Punctuation">)</span>
    <span class="Keyword">case</span> obj
    <span class="Keyword">when</span> <span class="Variable">LineItem</span>
      obj<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">order</span></span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">update_attribute</span></span><span class="Punctuation">(</span><span class="Constant"><span class="Punctuation">:</span>total</span><span class="Punctuation">,</span> obj<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">order</span></span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">total</span></span><span class="Punctuation">)</span>
    <span class="Keyword">end</span>
  <span class="Keyword">end</span>
<span class="Keyword">end</span>
</pre>
<p>You could really set this up a number of ways. You could create a CacheObserver that handles all caching for all models. This could get messy and confusing really fast, which is why I recommend the above example. My philosophy on OO programming is that classes should be responsible for their self. If the Order class is calculating the total it should be responsible for keeping it up to date in the database too.</p>
<p>&#8220;But Ben, this looks messy and complicated&#8221;. I agree with you, to an extent, but there is no simple solution here, because there is no pattern. Your total method could be anything you want. For all I know, your total method could be outsourced to a team in India that calculates the total by hand.</p>
<p>Sometimes in programming you have to give up something to gain something. I feel like writing some simple code in an observer is worth having a cached value of the total in the database. It makes everything so much cleaner, easier, and simpler in your controllers and views. Models should do all of the heavy lifting anyways.</p>
<p>Obviously, this is not just limited to total. You could do this for pretty much any virtual attribute in your models.</p>
<p>What do you think? I am always interested in feedback.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.binarylogic.com/2008/10/05/tutorial-caching-virtual-attributes-in-the-database/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
	</channel>
</rss>
