<?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; Searchlogic</title>
	<atom:link href="http://www.binarylogic.com/category/my-projects/searchlogic/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>Polymorphic association support added to searchlogic</title>
		<link>http://www.binarylogic.com/2010/01/23/polymorphic-association-support-added-to-searchlogic/</link>
		<comments>http://www.binarylogic.com/2010/01/23/polymorphic-association-support-added-to-searchlogic/#comments</comments>
		<pubDate>Sat, 23 Jan 2010 21:19:58 +0000</pubDate>
		<dc:creator>benjohnson</dc:creator>
				<category><![CDATA[Searchlogic]]></category>
		<category><![CDATA[polymorphic]]></category>

		<guid isPermaLink="false">http://www.binarylogic.com/?p=839</guid>
		<description><![CDATA[If you have ever worked with polymorphic associations you know that using them in a search can be a pain in the ass. Rightfully so, because a polymorphic association is ambiguous, in order for ActiveRecord to do its magic with creating joins it needs more information that it can&#8217;t get. Such as, are you wanting [...]]]></description>
			<content:encoded><![CDATA[<p>If you have ever worked with polymorphic associations you know that using them in a search can be a pain in the ass. Rightfully so, because a polymorphic association is ambiguous, in order for ActiveRecord to do its magic with creating joins it needs more information that it can&#8217;t get. Such as, are you wanting to search type A or type B? In my projects I&#8217;ve been dealing with this by writing my own named scopes with my own joins. Not a big deal. But here&#8217;s the problem&#8230;.</p>
<p>My favorite feature in Searchlogic is named scope delegation. I&#8217;ve noticed in a number of projects I&#8217;ve seen where programmers copy over named scope logic into related models. Which makes sense, because AR gives you no way of easily accessing related scopes. For example:</p>
<pre>
Order.named_scope :pending, :conditions => {:state => "pending"}
User.named_scope :pending_orders, :joins => :orders, :conditions => "orders.state = 'pending'"
</pre>
<p>Most people would look at that and never see anything wrong with it. What is the conditions for a pending order changes?</p>
<pre>
Order.named_scope :pending, :conditions => "orders.state = 'pending' AND approved = true
</pre>
<p>Now you have to remember to change this logic in associated models. This is a programming no-no. With searchlogic you don&#8217;t even need to touch the User model. You can access the order&#8217;s named scope, and it delegates that call:</p>
<pre>
User.orders_pending
</pre>
<p>Now when you change what defines an order as pending it will change everywhere in your application. This is great, but where this feature comes to a screeching halt is when you encounter a polymorphic association. This is because AR has no internal support for this either. For example, you can&#8217;t do:</p>
<pre>
Audit.belongs_to :auditable, :polymorphic => true
User.has_many :audits, :as => :auditable

Audit.all(:joins => :user)
</pre>
<p>Obviously the above won&#8217;t work. AR has no idea that there is a relationship with users through the polymorphic auditable relationship. To overcome this you have to write your own join. Again, there is nothing wrong with writing SQL, I don&#8217;t want to send the wrong message. The problem is that you are back to square one with my named scope delegation problem above. To solve this I added a way to overcome this. With searchlogic you could do this:</p>
<pre>
Audit.auditable_user_type_name_like("ben")
</pre>
<p>Searchlogic will take care of creating the join on the polymorphic relationship and then delegate the call to the User.name_like named_scope. I think it&#8217;s pretty nifty.</p>
<p>I hope I explained this well. I figured I would mention the new feature because I&#8217;m sure I&#8217;m not the only person that has encountered this problem.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.binarylogic.com/2010/01/23/polymorphic-association-support-added-to-searchlogic/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Using Geokit with Searchlogic</title>
		<link>http://www.binarylogic.com/2010/01/09/using-geokit-with-searchlogic/</link>
		<comments>http://www.binarylogic.com/2010/01/09/using-geokit-with-searchlogic/#comments</comments>
		<pubDate>Sun, 10 Jan 2010 03:06:21 +0000</pubDate>
		<dc:creator>benjohnson</dc:creator>
				<category><![CDATA[Searchlogic]]></category>
		<category><![CDATA[geokit]]></category>

		<guid isPermaLink="false">http://www.binarylogic.com/?p=832</guid>
		<description><![CDATA[For those of you that don&#8217;t know, geokit is a great little gem that allows you to geocode addresses. It adds in very easy to use options in the ActiveRecord find method. Ex:

# will return all venues within 10 miles of the 10018 zip code
Venue.find(:all, :origin => "10018", :within => 10)

The problem is that you [...]]]></description>
			<content:encoded><![CDATA[<p>For those of you that don&#8217;t know, <a href="http://github.com/andre/geokit-gem">geokit</a> is a great little gem that allows you to geocode addresses. It adds in very easy to use options in the ActiveRecord find method. Ex:</p>
<pre>
# will return all venues within 10 miles of the 10018 zip code
Venue.find(:all, :origin => "10018", :within => 10)
</pre>
<p>The problem is that you can&#8217;t use geokit options in a named scope. Since searchlogic is named scope driven this presented a problem. So I wrote a little module that transfers geokit options from named scopes to the actual arguments of the method:</p>
<pre class="ruby">
module GeokitForNamedScopes
  OPTIONS = [:origin, :within, :beyond, :range, :formula, :bounds]

  def find(*args)
    super(*transfer_from_scope_to_args(args))
  end

  def count(*args)
    super(*transfer_from_scope_to_args(args))
  end

  private
    def transfer_from_scope_to_args(args)
      find_options = scope(:find)
      if find_options.is_a?(Hash)
        options = args.extract_options!
        OPTIONS.each do |key|
          options[key] = find_options.delete(key) if find_options.key?(key)
        end
        args << options
      else
        args
      end
    end
end

class ActiveRecord::Base
  class << self
    def acts_as_mappable(*args)
      result = super(*args)
      extend GeokitForNamedScopes

      GeokitForNamedScopes::OPTIONS.each do |key|
        named_scope key, lambda { |value| {key => value} }
      end

      result
    end

    VALID_FIND_OPTIONS += GeokitForNamedScopes::OPTIONS
  end
end
</pre>
<p>It&#8217;s far from perfect. The glaring problem is obviously redefining the VALID_FIND_OPTIONS constant, which throws a warning. But it works and it&#8217;s good enough for now. If anyone has any suggestions on how to improve this I&#8217;d to hear them. But you should be able to use geokit options in a named_scope with the above code. Ex:</p>
<pre>
Venue.origin("10017").within(10).all # => will return all venues within 10 miles of 10017
</pre>
<h2>Using this through associations</h2>
<p>Geokit doesn&#8217;t really support making these calls through associations. But if you read the <a href="http://github.com/andre/geokit-rails">documentation</a>. You will notice they do support this:</p>
<pre>
acts_as_mappable :through => :whatever
</pre>
<p>You can use that feature to bridge the gap</p>
<p>Hopefully this helps some of you.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.binarylogic.com/2010/01/09/using-geokit-with-searchlogic/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Using Searchlogic to combine named scopes with &#8216;OR&#8217;</title>
		<link>http://www.binarylogic.com/2009/08/26/using-searchlogic-to-combine-named-scopes-with-or/</link>
		<comments>http://www.binarylogic.com/2009/08/26/using-searchlogic-to-combine-named-scopes-with-or/#comments</comments>
		<pubDate>Wed, 26 Aug 2009 07:47:14 +0000</pubDate>
		<dc:creator>benjohnson</dc:creator>
				<category><![CDATA[Searchlogic]]></category>
		<category><![CDATA[named scope procedure]]></category>
		<category><![CDATA[or]]></category>
		<category><![CDATA[scope procedure]]></category>

		<guid isPermaLink="false">http://www.binarylogic.com/?p=796</guid>
		<description><![CDATA[A few days ago I released a feature that Ryan Bates requested: combining named scopes with &#8216;OR&#8217;. As you know, when you combine named scopes they join the conditions with &#8216;AND&#8217;. This obviously makes sense, but what if you want to combine conditions with &#8216;OR&#8217;? With this new feature you can do the following:

User.first_name_or_last_name_like("ben")
User.id_lt_or_age_gt(10)

I think [...]]]></description>
			<content:encoded><![CDATA[<p>A few days ago I released <a href="http://github.com/binarylogic/searchlogic/issues/closed#issue/20">a feature that Ryan Bates requested</a>: combining named scopes with &#8216;OR&#8217;. As you know, when you combine named scopes they join the conditions with &#8216;AND&#8217;. This obviously makes sense, but what if you want to combine conditions with &#8216;OR&#8217;? With this new feature you can do the following:</p>
<pre class="ruby">
User.first_name_or_last_name_like("ben")
User.id_lt_or_age_gt(10)
</pre>
<p>I think this is nice feature on a variety of levels, but what I particularly like is the implementation.<span id="more-796"></span> It uses method_missing to accomplish this. You can call any scope you want, call scopes provided by <a href="http://github.com/binarylogic/searchlogic/tree/master">searchlogic</a>, call your own scopes, call scopes on associations, etc. As long as it maps to a named scope it will join the conditions with &#8216;OR&#8217;. So you have a little more flexibility when building a query with named scopes:</p>
<pre class="ruby">
User.id_gt(10).first_name_or_last_name_like("ben")
# => (id > 10) AND (first_name LIKE '%ben% OR last_name LIKE '%ben%')
</pre>
<h2>More than just named scopes</h2>
<p>A lot of people think <a href="http://github.com/binarylogic/searchlogic/tree/master">searchlogic</a> is just a bunch of named scopes, what people don&#8217;t realize is that it provides tools around how you use named scopes in general.</p>
<p>The other day I was reviewing an application for a friend who was using <a href="http://github.com/binarylogic/searchlogic/tree/master">searchlogic</a>. I noticed this:</p>
<pre class="ruby">
User.named_scope :with_recent_orders, :conditions => ["orders.created_at >= ?", 1.week.ago], :joins => :orders
Order.named_scope :recent, :conditions => ["orders.created_at >= ?", 1.week.ago]
</pre>
<p>See the duplication there? With more complex named scopes this becomes more obvious, but the scope was so simple I think he missed it. I even catch myself doing this sometimes, then I realize I don&#8217;t need to. The named_scope in the User class is pointless. With searchlogic, you can accomplish the same thing by doing:</p>
<pre class="ruby">
User.orders_recent
</pre>
<p><a href="http://github.com/binarylogic/searchlogic/tree/master">searchlogic</a> dynamically creates the scope for you, pulls over the scope details, and create the necessary join.</p>
<h2>The goal of named scopes</h2>
<p>In my opinion the simpler the named scope, the better. If you find yourself writing a complicated named scope, take a step back and look at it. I&#8217;d be willing to bet you could split that named scope into multiple smaller scopes. For example, instead of calling a named scope &#8216;Product.popular_expensive&#8217;, create 2 named scopes called &#8216;popular&#8217; and &#8216;expensive&#8217;. Break your scope into small parts and then piece them together when you need them.</p>
<p>Another good idea is to create scope procedures. This allows you to create small named scopes and still get the advantage of calling a single scope. Creating a procedure is as easy as creating a class level method:</p>
<pre class="ruby">
class User
  def self.awesome
    name_begins_with("Ben").name_ends_with("Johnson")
  end
end
</pre>
<p>Even better, used searchlogic&#8217;s scope procedure:</p>
<pre class="ruby">
User.scope_procedure :awesome, lambda { name_begins_with("Ben").name_ends_with("Johnson") }
</pre>
<p>I&#8217;ll leave it at that. If you are bored one day checkout the <a href="http://rdoc.info/projects/binarylogic/searchlogic">searchlogic docs</a>. I think you&#8217;ll find some small things like this that could help save you time and DRY up your code.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.binarylogic.com/2009/08/26/using-searchlogic-to-combine-named-scopes-with-or/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Searchlogic v2 officially released</title>
		<link>http://www.binarylogic.com/2009/06/15/searchlogic-v2-officially-released/</link>
		<comments>http://www.binarylogic.com/2009/06/15/searchlogic-v2-officially-released/#comments</comments>
		<pubDate>Mon, 15 Jun 2009 06:14:47 +0000</pubDate>
		<dc:creator>benjohnson</dc:creator>
				<category><![CDATA[Searchlogic]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[search]]></category>
		<category><![CDATA[v2]]></category>

		<guid isPermaLink="false">http://www.binarylogic.com/?p=768</guid>
		<description><![CDATA[Tonight I went ahead and officially released searchlogic v2. If you didn&#8217;t read my last post, v2 is a complete rewrite of the library. It takes a new approach. It went from ~2300 lines of code to ~400 lines of code. It&#8217;s simpler, easier to use, much more light weight, faster, and more importantly, easier [...]]]></description>
			<content:encoded><![CDATA[<p>Tonight I went ahead and officially released <a href="http://github.com/binarylogic/searchlogic/tree/master">searchlogic v2</a>. If you didn&#8217;t read my last post, v2 is a <em>complete</em> rewrite of the library. It takes a new approach. It went from ~2300 lines of code to ~400 lines of code. It&#8217;s simpler, easier to use, much more light weight, faster, and more importantly, easier to maintain, understand, and improve upon.</p>
<p>I&#8217;m not going to ramble about the changes, because you can read my <a hef="http://www.binarylogic.com/2009/06/09/searchlogic-v2-beta-released/">previous post</a> or you can check out the <a href="http://github.com/binarylogic/searchlogic/tree/master">read me</a> on github.</p>
<h3>Using github not rubyforge</h3>
<p>I have decided to start using github to host my gems, if you want to start using v2 as a gem you need to install the binarylogic-searchlogic gem from the github source. (checkout the <a href="http://github.com/binarylogic/searchlogic/tree/master">read me</a> for easy installation instructions).</p>
<h3>Thank you</h3>
<p>Sorry for such a quick post. I figured this was an important announcement.</p>
<p>Thank you for being patient with v1, I know there were some people having issues. This should address all of them.</p>
<p>As always, I did my best with the <a href="http://github.com/binarylogic/searchlogic/tree/master">readme</a> and the <a href="http://rdoc.info/projects/binarylogic/searchlogic">rdocumentation</a>. If you really want to see what v2 is all about I suggest you take a glance at those, they should be comprehensive.</p>
<p>I hope you enjoy v2, I&#8217;m really excited to release it. I have already integrated it into the app that I am working on, and I love it. It works great and all of my tests pass.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.binarylogic.com/2009/06/15/searchlogic-v2-officially-released/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Searchlogic v2 beta released</title>
		<link>http://www.binarylogic.com/2009/06/09/searchlogic-v2-beta-released/</link>
		<comments>http://www.binarylogic.com/2009/06/09/searchlogic-v2-beta-released/#comments</comments>
		<pubDate>Tue, 09 Jun 2009 04:07:14 +0000</pubDate>
		<dc:creator>benjohnson</dc:creator>
				<category><![CDATA[Searchlogic]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[search]]></category>
		<category><![CDATA[v2]]></category>

		<guid isPermaLink="false">http://www.binarylogic.com/?p=756</guid>
		<description><![CDATA[Searchlogic v2 beta was released today, it is a complete rewrite of the library, no code was reused. Right now its in a separate branch under the Searchlogic project, and will be merged when it is taken out of beta status. Let me explain how it&#8217;s different.
So, the project I&#8217;m working on now required something [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://github.com/binarylogic/searchlogic/tree/v2">Searchlogic v2 beta</a> was released today, it is a <strong>complete</strong> rewrite of the library, no code was reused. Right now its in a separate branch under the <a href="http://github.com/binarylogic/searchlogic/tree/master">Searchlogic project</a>, and will be merged when it is taken out of beta status. Let me explain how it&#8217;s different.</p>
<p>So, the project I&#8217;m working on now required something from Searchlogic v1 that it couldn&#8217;t provide. That is, searching with existing named scopes. I could do this, but it wasn&#8217;t as elegant as it was with the built in conditions (Ex: username_like, etc.).</p>
<h2>The idea behind Searchlogic v2</h2>
<p>So this got me thinking, how can I get Searchlogic v1 to use existing named scopes? That&#8217;s when it hit me, why not use named scopes for everything?<span id="more-756"></span>I think we can all agree named scopes are the heart of searching and breaking up common search logic in your models. Instead of having all of this crazy logic write SQL and chain it together, as in v1, why not have Searchlogic dynamically create these common named scopes for you and then just leverage those? That&#8217;s exactly what v2 does. Instead of going into detail about v2, check out the <a href="http://github.com/binarylogic/searchlogic/tree/v2">current README</a>. It explains everything v2 has to offer up to this point.</p>
<p>The best part about this approach is that it fits nicely into your application. All that it is doing is creating named scopes. More importantly, if you need some condition that Searchlogic doesn&#8217;t offer, no problem, just create your own named scope. This is what you should be doing anyways.</p>
<p>To bring it all home, let me show a simple search form that uses v2. Assume we have</p>
<pre>
User(name:string, age:integer)
Order(user_id:integer, total:float)

User.has_many :orders
User.named_scope :four_year_olds, :conditions => {:age => 4}
User.named_scope :name_sounds_like, lamda { |value| {:conditions => ["name SOUNDS LIKE ?", value]} }
</pre>
<p>Your search form could look like this:</p>
<pre class="ruby">
- form_for @search do |f|
  = f.text_field :name_like
  = f.select :age_greater_than, (1..100)
  = f.text_field :orders_total_less_than
  = f.text_field :name_sounds_like
  = f.check_box :four_year_olds
  = f.submit
</pre>
<p>Notice the use of custom named scopes. Also the other conditions are also named scopes that searchlogic dynamically defines as you need them.</p>
<h2>The best part of v2</h2>
<ul>
<li><strong>V1:</strong> ~2300 lines of code</li>
<li><strong>V2:</strong> ~350 lines of code</li>
</ul>
<p>As you might have saw <a href="http://www.binarylogic.com/2009/04/17/need-some-help-searchlogic-20/">in this post</a> Searchlogic v1 was too big for me to handle. The issues people were having would take way too long to solve and I just didn&#8217;t like the code. The library really wasn&#8217;t advancing and it was just an annoyance. It was regretably the first library I ever wrote, and as such there are a lot of things I would do differently, which should be evident in v2.</p>
<p>That being said, v2 is lean, makes sense, and just feels right. I&#8217;m really looking forward to maintaing the library and seeing where it goes. I hope everyone enjoys it. Let me know what you think. There are just a couple of things left to do before I officially release it.</p>
<h2>Applications using v1</h2>
<p>Lastly, because this is such a big change, backwards compatibility <em>will</em> be broken. Before I officially release the library I will write a document explaining the differences and how to transition. In the mean time, your gem declarations in your application should look like this, so that your app doesn&#8217;t break when the new version is released:</p>
<pre>
 config.gem "searchlogic", :version => "~> 1.6.6"
# the ~ will let you advance minor and tiny versions, but not major versions.
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.binarylogic.com/2009/06/09/searchlogic-v2-beta-released/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>Searchlogic 1.5.7 &#8211; Complex searching no longer a problem</title>
		<link>http://www.binarylogic.com/2008/11/30/searchlogic-1-5-7-complex-searching-no-longer-a-problem/</link>
		<comments>http://www.binarylogic.com/2008/11/30/searchlogic-1-5-7-complex-searching-no-longer-a-problem/#comments</comments>
		<pubDate>Sun, 30 Nov 2008 07:59:00 +0000</pubDate>
		<dc:creator>benjohnson</dc:creator>
				<category><![CDATA[Searchlogic]]></category>
		<category><![CDATA[advanced searching]]></category>
		<category><![CDATA[complex searching]]></category>

		<guid isPermaLink="false">0/2009/03/23/searchlogic-1-5-7-complex-searching-no-longer-a-problem</guid>
		<description><![CDATA[Searchlogic 1.5.7 is by far my favorite release because it takes Searchlogic to a whole new level. It solves a problem I thought it would never solve. Before I explain the new features, let me give you a quick run down on my perspective of Searchlogic:

Fillin&#8217; a brotha in
A lot of people think Searchlogic is [...]]]></description>
			<content:encoded><![CDATA[<p>Searchlogic 1.5.7 is by far my favorite release because it takes Searchlogic to a whole new level. It solves a problem I thought it would never solve. Before I explain the new features, let me give you a quick run down on my perspective of Searchlogic:</p>
<p><span id="more-280"></span></p>
<h2>Fillin&#8217; a brotha in</h2>
<p>A lot of people think Searchlogic is a &#8220;console&#8221; searching utility. Meaning you can pop into your console and execute some simple searches quickly and easily, and it is, but by accident. My goal with Searchlogic has always been freeing your application of searching clutter. If you&#8217;ve ever done an app with searching you know there is a lot of &#8220;cruft&#8221; that goes along with it: nasty controller actions, excessive named_scopes, etc. Searchlogic rids you of this by representing an entire search&#8217;s criteria with a hash: conditions, ordering, pagination, the whole package. Why is this nice? Because GET and POST parameters are a hash. What&#8217;s nice about that? Because an HTML form&#8217;s sole purpose is to send GET and POST parameters to a URI. This means you can build a form that represents your entire search. Adding a condition to your search is as easy as adding a field to your form. This ultimately makes your controller dead simple, frees it of any search clutter, and rids your models of excessive named_scopes. Here is what your controller action should look like with Searchlogic:</p>
<pre class="ruby">@search = User.new_search(params[:search])
@users = @search.all</pre>
<p><!--more--></p>
<h2>Complex searching in tha house</h2>
<p>This is great for simple searching, but how do you represent complex searches this way? Let&#8217;s take this query:</p>
<pre class="ruby">select * from users where id &gt; 5 AND
  (email LIKE 'ben%' or email LIKE '%binarylogic.com') AND
  first_name LIKE '%ben%'</pre>
<p>Before, Searchlogic couldn&#8217;t handle this. It did not support grouping (parenthesis) and it did not allow you to mix and match &#8220;AND&#8221; and &#8220;OR&#8221;, you had to pick one or the other. Why? Because I never intended for Searchlogic to replace complex searching. If you need to perform a complex search there is nothing wrong with using Searchlogic in conjunction with some named_scopes. SQL is not bad, but what is bad is that all of that searching cruft starts seeping back into your project. All of a sudden your controller is not clean, your form has to stray away from using the form builder, and your model gets cluttered with named scopes that you are only using for one section in your application. Searchlogic fails to meet its goal of keeping your application free of &#8220;search clutter&#8221; and a tear runs down your cheek.</p>
<h2>Slap a prefix on it!</h2>
<p>Solving the problem of mixing &#8220;AND&#8221; and &#8220;OR&#8221; was easier than I thought. The only major change I had to make was making the conditions order relevant. The order you set your conditions is the same order they will appear in your SQL statement. Before, the conditions were stored in a hash, where order was not preserved. Now you can slap an &#8220;and_&#8221; or &#8220;or_&#8221; prefix in front of your conditions:</p>
<pre class="ruby">search = User.new_search
search.conditions.name_like = "Ben"
search.conditions.or_email_like = "a@a.com"
search.conditions.and_id_gt = 5
# =&gt; name LIKE '%Ben%' OR email like '%a@a.com%' AND id &gt; 5</pre>
<p>The last condition &#8220;and_id_gt&#8221; could also be written as &#8220;id_gt&#8221;, because, by default, we are joining with &#8220;AND&#8221;. Not specifying a prefix uses whatever you are joining your conditions with by default, specifying a prefix will <em>always</em> use that join. If you want your conditions to be joined by &#8220;OR&#8221; by default just do:</p>
<pre class="ruby">search.any = true</pre>
<h2>You best wrap it with some parenthesis</h2>
<p>What about grouping conditions with parenthesis? No problem. Take the above query:</p>
<pre class="ruby">search = User.new_search
search.conditions.id_gt = 5
search.conditions.group do |group|
  group.email_begins_with = "ben"
  group.or_email_ends_with = "binarylogic.com"
end
search.conditions.first_name_contains = "ben"
search.all</pre>
<p>What about a hash?</p>
<pre class="ruby">search = User.new_search(:conditions =&gt; [
  {:id_gt =&gt; 5},
  {:group =&gt; [
    {:email_begins_with =&gt; "ben"},
    {:or_email_ends_with =&gt; "binarylogic.com"}
  ]},
  {:first_name_contains =&gt; "ben"}
])</pre>
<p>What about a form?</p>
<pre class="ruby">- form_for @search do |search|
  - search.fields_for "conditions" do |conditions_array|
    - conditions_array.fields_for "[]", search.object.conditions do |conditions|
      = conditions.text_field :id_gt
      - conditions.fields_for "group" do |group_array|
        - group_array.fields_for "[]" do |group|
          = group.text_field :email_begins_with
          = group.text_field :or_email_ends_with
      = conditions.text_field :first_name_contains</pre>
<p>You will notice the hash and the form are implementing arrays, thats because hashes do not preserve the order. In our case above, order is important to us. What does preserve the order? An array. So if order is relevant to you, then you need to structure your conditions into an array. There is no other choice with this, because forms can either submit a hash or an array of parameters.</p>
<p>I know what you are thinking: &#8220;the above looks a little confusing and messy&#8221;. I agree with you, to a point. The word &#8220;messy&#8221; is a relative term. In our case, we have no other choice, this is the 100% correct way to do this. That being said, this is clean, because there is no other cleaner option. Sorry to be a debbie downer here, but this is how forms work, there is nothing I can do about that. Regardless, the above is still cleaner than having a nasty controller action or adding a bunch of named_scopes into you model. Your search logic is nice and DRY, in one spot: the view.</p>
<h2>99 problems, but complex searching aint one</h2>
<p>I think this is pretty nifty, now you have the tools to construct any type of search in your form. I&#8217;ve been getting a lot of emails asking me about this, and I had to be a debbie downer and tell them to use named scopes. Now you can ditch the named scopes and keep your search logic nice and DRY in your form, where it probably belongs.</p>
<p>The last thing I want to say is that there is a time and a place for a named scope, by no means am I discouraging them, because they are by far one of the nicest features in ActiveRecord. But something about creating a complex named scope or a bunch of small named scopes for a single action in one of my controllers felt dirty. It felt like clutter. I knew I would never use those named scopes anywhere else, and they were solely for that search form. After a while all of these named scopes start overlapping and start sharing search logic. If you&#8217;re extremely picky like me, your don&#8217;t feel like your code is DRY. So how far do you go to break down a named scope? Breaking them down too much defeats their purpose, but not breaking them down enough seems to create redundant search logic. Searchlogic solved my problem with this, now all of my view specific search logic is where it should be, in my view.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.binarylogic.com/2008/11/30/searchlogic-1-5-7-complex-searching-no-longer-a-problem/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<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>
		<item>
		<title>Searchlogic 1.3.0 &#8211; Adding modifiers into the mix</title>
		<link>http://www.binarylogic.com/2008/10/02/searchlogic-1-3-0-adding-modifiers-into-the-mix/</link>
		<comments>http://www.binarylogic.com/2008/10/02/searchlogic-1-3-0-adding-modifiers-into-the-mix/#comments</comments>
		<pubDate>Thu, 02 Oct 2008 12:40:00 +0000</pubDate>
		<dc:creator>benjohnson</dc:creator>
				<category><![CDATA[Searchlogic]]></category>
		<category><![CDATA[modifiers]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[searchgasm]]></category>

		<guid isPermaLink="false">0/2009/03/23/searchlogic-1-3-0-adding-modifiers-into-the-mix</guid>
		<description><![CDATA[Searchlogic 1.3.0 was released today and has some pretty cool features, mainly modifiers. I&#8217;m going to assume you know what Searchlogic is. If you don&#8217;t, you&#8217;re missing out. Take a quick glance at the readme on github, otherwise this article probably won&#8217;t make a whole lot of sense.
        [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://github.com/binarylogic/searchlogic">Searchlogic 1.3.0</a> was released today and has some pretty cool features, mainly modifiers. I&#8217;m going to assume you know what <a href="http://github.com/binarylogic/searchlogic">Searchlogic</a> is. If you don&#8217;t, you&#8217;re missing out. Take <a href="http://github.com/binarylogic/searchlogic">a quick glance at the readme</a> on github, otherwise this article probably won&#8217;t make a whole lot of sense.</p>
<p>            <span id="more-44"></span></p>
<h2>Why modifiers are necessary?</h2>
<p>ActiveRecord does a great job when it comes to keeping your code database agnostic. But I feel like it neglected searching when it came to that goal. What if you want to find all records that were created after 7am? Depending on your database you would have to do something like the following:</p>
<pre><code>MySQL:        HOUR(created_at)
PostgreSQL:   date_part('hour', created_at)
SQLite:       strftime('%H', created_at)
</code></pre>
<p>All of a sudden your app is not database agnostic. Oh no!</p>
<p>All of that bragging to your girlfriend about how you can switch databases in a matter of minutes was all a lie. How&#8217;s she going to feel when she finds out HOUR(created_at) is a MySQL specific function? Searchlogic to the rescue!</p>
<h2>Searchlogic modifiers</h2>
<p>Remember how you can do this in searchlogic?</p>
<pre class="cobalt">
<span class="Support">User</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>condition</span> <span class="Punctuation">=&gt;</span> <span class="Punctuation">{</span><span class="Constant"><span class="Punctuation">:</span>created_at_gt</span> <span class="Punctuation">=&gt;</span> <span class="Support">Time</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">parse</span></span><span class="Punctuation">(</span><span class="String"><span class="Punctuation">&quot;</span>Jan 1, 2008<span class="Punctuation">&quot;</span></span><span class="Punctuation">)</span><span class="Punctuation">}</span><span class="Punctuation">)</span>
<span class="Comment"><span class="Punctuation">#</span> =&gt; &quot;created_at &gt; 2008-01-01 00:00:00</span>
</pre>
<p>Now you can do this too:</p>
<pre class="cobalt">
<span class="Support">User</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>conditions</span> <span class="Punctuation">=&gt;</span> <span class="Punctuation">{</span><span class="Constant"><span class="Punctuation">:</span>hour_of_created_at_gt</span> <span class="Punctuation">=&gt;</span> <span class="Constant">10</span><span class="Punctuation">}</span><span class="Punctuation">)</span>
<span class="Comment"><span class="Punctuation">#</span> =&gt; HOUR(created_at) &gt; 10</span>
</pre>
<p>or this:</p>
<pre class="cobalt">
<span class="Support">User</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>conditions</span> <span class="Punctuation">=&gt;</span> <span class="Punctuation">{</span><span class="Constant"><span class="Punctuation">:</span>tan_of_sin_of_cos_of_minute_of_created_at_gt</span> <span class="Punctuation">=&gt;</span> <span class="Constant">20</span><span class="Punctuation">}</span><span class="Punctuation">)</span>
<span class="Comment"><span class="Punctuation">#</span> =&gt; TAN(SIN(COS(MINUTE(created_at)))) &gt; 20</span>
</pre>
<p>What the hell was that? That was me making the point that you can chain modifiers. Obviously a very unrealistic example, but cool none-the-less.</p>
<p><a href="http://github.com/binarylogic/searchlogic">Check out the readme for a list of modifiers</a> and <a href="http://github.com/binarylogic/searchlogic/tree/master/CHANGELOG.rdoc">check out the changelog for a complete list of changes</a></p>
<p>What do you think?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.binarylogic.com/2008/10/02/searchlogic-1-3-0-adding-modifiers-into-the-mix/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Tutorial: Pagination, ordering, and searching with Searchlogic</title>
		<link>http://www.binarylogic.com/2008/09/07/tutorial-pagination-ordering-and-searching-with-searchlogic/</link>
		<comments>http://www.binarylogic.com/2008/09/07/tutorial-pagination-ordering-and-searching-with-searchlogic/#comments</comments>
		<pubDate>Sun, 07 Sep 2008 01:05:00 +0000</pubDate>
		<dc:creator>benjohnson</dc:creator>
				<category><![CDATA[Searchlogic]]></category>
		<category><![CDATA[benchmark]]></category>
		<category><![CDATA[ordering]]></category>
		<category><![CDATA[pagination]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[searchgasm]]></category>
		<category><![CDATA[searching]]></category>

		<guid isPermaLink="false">0/2009/03/23/tutorial-pagination-ordering-and-searching-with-searchlogic</guid>
		<description><![CDATA[Paginating, ordering, searching &#8211; no longer a pain in the ass
Maybe it&#8217;s not a pain in the ass for you, maybe it is, or maybe it is and you don&#8217;t know it. We&#8217;ll never know. Either way, this tutorial is pain-in-the-ass free. Guaranteed. That&#8217;s what this is all about, making your life easier when it [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Paginating, ordering, searching &#8211; no longer a pain in the ass</strong></p>
<p>Maybe it&#8217;s not a pain in the ass for you, maybe it is, or maybe it is and you don&#8217;t know it. We&#8217;ll never know. Either way, this tutorial is pain-in-the-ass free. Guaranteed. That&#8217;s what this is all about, making your life easier when it comes to paginating, ordering, and searching. Things you do multiple times in almost every application.</p>
<p>My solution: <a href="http://github.com/binarylogic/searchlogic">Searchlogic</a>. It has saved me a lot time, shortened my code, and ultimately given me the proper tools to paginate, order, and search my data. Hopefully it will do the same for you.</p>
<p>            <span id="more-13"></span></p>
<p>Enough talk, let&#8217;s dive in.</p>
<p>&#8220;Does this really work? Am I about to waste my time?&#8221;. I hope not. But just for you, I made this live example:</p>
<h3><a href="http://searchlogic_example.binarylogic.com">Live example based on this tutorial</a></h3>
<p>Paginating, ordering, and searching doesn&#8217;t get any easier than this. Before we start I want to make the following assumptions about you:</p>
<ol>
<li>You have a working rails application on rails edge (rake rails:freeze:edge)</li>
<li>You have the following model structure: UserGroup =&gt; User =&gt; Order (=&gt; = has_many). Make sure you have the has_many and belongs_to relationships set up. The fields are irrelevant, just change the field names in the view to whatever you have.</li>
</ol>
<div class="spacer5"></div>
<h2>1. Install <a href="http://github.com/binarylogic/searchlogic">Searchlogic</a></h2>
<pre class="cobalt">
$ sudo gem install searchlogic
</pre>
<p>Now add the gem dependency to your rails config:</p>
<pre class="cobalt">
<span class="Comment"><span class="Punctuation">#</span> config/environment.rb</span>
config<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">gem</span></span> <span class="String"><span class="Punctuation">&quot;</span>searchlogic<span class="Punctuation">&quot;</span></span>
</pre>
<p>As always you can install searchlogic as a plugin, but plugins are becoming a thing of the past. I recommend the above approach, but here is the plugin installation if you prefer to do it this way:</p>
<pre class="cobalt">
$ script/plugin install git://github.com/binarylogic/searchlogic.git
</pre>
<div class="spacer5"></div>
<h2>2. Create your controller</h2>
<p>Let&#8217;s create a controller that searches, orders, and paginates users. Like an admin area. Create app/controllers/users_controller.rb with the following content:</p>
<pre class="cobalt">
<span class="Comment"><span class="Punctuation">#</span> app/controllers/users_controller.rb</span>
<span class="Keyword">class</span> <span class="Entity">UsersController<span class="EntityInheritedClass"> <span class="Punctuation">&lt;</span> ApplicationController</span></span>
    <span class="Keyword">def</span> <span class="Entity">index</span>
        <span class="Variable"><span class="Punctuation">@</span>search</span> <span class="Keyword">=</span> <span class="Support">User</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">new_search</span></span><span class="Punctuation">(</span>params<span class="Punctuation">[</span><span class="Constant"><span class="Punctuation">:</span>search</span><span class="Punctuation">]</span><span class="Punctuation">)</span>
        <span class="Variable"><span class="Punctuation">@</span>users</span><span class="Punctuation">,</span> <span class="Variable"><span class="Punctuation">@</span>users_count</span> <span class="Keyword">=</span> <span class="Variable"><span class="Punctuation">@</span>search</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">all</span></span><span class="Punctuation">,</span> <span class="Variable"><span class="Punctuation">@</span>search</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">count</span></span>
    <span class="Keyword">end</span>
<span class="Keyword">end</span>
</pre>
<h3>What did we do here?</h3>
<p>It&#8217;s simple actually, if you haven&#8217;t looked at <a href="http://github.com/binarylogic/searchlogic">Searchlogic</a> I suggest you take a quick glance. I&#8217;m not going to get into crazy detail about what it does, because the README in the library covers that, but to make this short it &#8220;enhances&#8221; searching with ActiveRecord.</p>
<p>Notice that we started a new search with the @search object. This lets you search via an object, which is really handy for your view, which you will see below. It also added in some nifty methods such as page, per_page, order_by, order_as, and a plethora of conditions for each of your columns. The second line does a simple search and counts the results. The thing to remember here is that the .count method ignores pagination. It ignores the limit and offset values. So if you want to know how many users matched the search, use count, not @users.size. If the first page is limited to 10 reconds @user.size will return 10.</p>
<div class="spacer5"></div>
<h2>3. Create the view and your&#8217;re done</h2>
<pre class="cobalt">
# app/views/users/index.html.erb
<span class="EmbeddedSource"><span class="Punctuation">&lt;%</span> <span class="Keyword">if</span> <span class="Variable"><span class="Punctuation">@</span>users_count</span> <span class="Keyword">&gt;</span> <span class="Constant">0</span> <span class="Punctuation">%&gt;</span></span>
    <span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> <span class="Variable"><span class="Punctuation">@</span>users_count</span> <span class="Punctuation">%&gt;</span></span> users found

    <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">table</span> <span class="MetaTagA">border</span>=<span class="String"><span class="Punctuation">&quot;</span>1<span class="Punctuation">&quot;</span></span> <span class="MetaTagA">cellpadding</span>=<span class="String"><span class="Punctuation">&quot;</span>5<span class="Punctuation">&quot;</span></span><span class="Punctuation">&gt;</span></span>
        <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">tr</span><span class="Punctuation">&gt;</span></span>
            <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">th</span><span class="Punctuation">&gt;</span></span><span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> order_by_link <span class="Constant"><span class="Punctuation">:</span>id</span> <span class="Punctuation">%&gt;</span></span><span class="MetaTagA"><span class="Punctuation">&lt;/</span><span class="MetaTagA">th</span><span class="Punctuation">&gt;</span></span>
            <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">th</span><span class="Punctuation">&gt;</span></span><span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> order_by_link <span class="Constant"><span class="Punctuation">:</span>user_group</span> <span class="Punctuation">=&gt;</span> <span class="Constant"><span class="Punctuation">:</span>name</span> <span class="Punctuation">%&gt;</span></span><span class="MetaTagA"><span class="Punctuation">&lt;/</span><span class="MetaTagA">th</span><span class="Punctuation">&gt;</span></span>
            <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">th</span><span class="Punctuation">&gt;</span></span><span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> order_by_link <span class="Constant"><span class="Punctuation">:</span>first_name</span> <span class="Punctuation">%&gt;</span></span><span class="MetaTagA"><span class="Punctuation">&lt;/</span><span class="MetaTagA">th</span><span class="Punctuation">&gt;</span></span>
            <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">th</span><span class="Punctuation">&gt;</span></span><span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> order_by_link <span class="Constant"><span class="Punctuation">:</span>last_name</span> <span class="Punctuation">%&gt;</span></span><span class="MetaTagA"><span class="Punctuation">&lt;/</span><span class="MetaTagA">th</span><span class="Punctuation">&gt;</span></span>
            <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">th</span><span class="Punctuation">&gt;</span></span><span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> order_by_link <span class="Punctuation">[</span><span class="Constant"><span class="Punctuation">:</span>email</span><span class="Punctuation">,</span> <span class="Constant"><span class="Punctuation">:</span>first_name</span><span class="Punctuation">]</span> <span class="Punctuation">%&gt;</span></span><span class="MetaTagA"><span class="Punctuation">&lt;/</span><span class="MetaTagA">th</span><span class="Punctuation">&gt;</span></span>
        <span class="MetaTagA"><span class="Punctuation">&lt;/</span><span class="MetaTagA">tr</span><span class="Punctuation">&gt;</span></span>
        <span class="EmbeddedSource"><span class="Punctuation">&lt;%</span> <span class="Variable"><span class="Punctuation">@</span>users</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">each</span></span> <span class="Keyword">do </span><span class="Punctuation">|</span><span class="Variable">user</span><span class="Punctuation">|</span> <span class="Punctuation">%&gt;</span></span>
            <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">tr</span><span class="Punctuation">&gt;</span></span>
                <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">td</span><span class="Punctuation">&gt;</span></span><span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> user<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">id</span></span> <span class="Punctuation">%&gt;</span></span><span class="MetaTagA"><span class="Punctuation">&lt;/</span><span class="MetaTagA">td</span><span class="Punctuation">&gt;</span></span>
                <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">td</span><span class="Punctuation">&gt;</span></span><span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> user<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">user_group</span></span> <span class="Keyword">?</span> user<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">user_group</span></span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">name</span></span> <span class="Punctuation">:</span> <span class="String"><span class="Punctuation">&quot;</span>-<span class="Punctuation">&quot;</span></span> <span class="Punctuation">%&gt;</span></span><span class="MetaTagA"><span class="Punctuation">&lt;/</span><span class="MetaTagA">td</span><span class="Punctuation">&gt;</span></span>
                <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">td</span><span class="Punctuation">&gt;</span></span><span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> user<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">first_name</span></span> <span class="Punctuation">%&gt;</span></span><span class="MetaTagA"><span class="Punctuation">&lt;/</span><span class="MetaTagA">td</span><span class="Punctuation">&gt;</span></span>
                <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">td</span><span class="Punctuation">&gt;</span></span><span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> user<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">last_name</span></span> <span class="Punctuation">%&gt;</span></span><span class="MetaTagA"><span class="Punctuation">&lt;/</span><span class="MetaTagA">td</span><span class="Punctuation">&gt;</span></span>
                <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">td</span><span class="Punctuation">&gt;</span></span><span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> user<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">email</span></span> <span class="Punctuation">%&gt;</span></span><span class="MetaTagA"><span class="Punctuation">&lt;/</span><span class="MetaTagA">td</span><span class="Punctuation">&gt;</span></span>
            <span class="MetaTagA"><span class="Punctuation">&lt;/</span><span class="MetaTagA">tr</span><span class="Punctuation">&gt;</span></span>
        <span class="EmbeddedSource"><span class="Punctuation">&lt;%</span> <span class="Keyword">end</span> <span class="Punctuation">%&gt;</span></span>
    <span class="MetaTagA"><span class="Punctuation">&lt;/</span><span class="MetaTagA">table</span><span class="Punctuation">&gt;</span></span>

    <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">br</span> /<span class="Punctuation">&gt;</span></span>
    <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">br</span> /<span class="Punctuation">&gt;</span></span>

    Per page: <span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> per_page_select <span class="Punctuation">%&gt;</span></span>

    <span class="EmbeddedSource"><span class="Punctuation">&lt;%</span> <span class="Keyword">if</span> <span class="Variable"><span class="Punctuation">@</span>search</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">page_count</span></span> <span class="Keyword">&gt;</span> <span class="Constant">1</span> <span class="Punctuation">%&gt;</span></span>
        <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">br</span> /<span class="Punctuation">&gt;</span></span>Page: <span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> page_select <span class="Punctuation">%&gt;</span></span>
    <span class="EmbeddedSource"><span class="Punctuation">&lt;%</span> <span class="Keyword">end</span> <span class="Punctuation">%&gt;</span></span>
<span class="EmbeddedSource"><span class="Punctuation">&lt;%</span> <span class="Keyword">else</span> <span class="Punctuation">%&gt;</span></span>
    No users were returned
<span class="EmbeddedSource"><span class="Punctuation">&lt;%</span> <span class="Keyword">end</span> <span class="Punctuation">%&gt;</span></span>
</pre>
<h3>What did we do here?</h3>
<p>All that we did was iterate through our users, list them in a table format, and then add page and per_page controls. Notice any unusual helpers? (order_by_link, per_page_select, and page_select). I won&#8217;t go into detail about these helpers because its all <a href="http://searchlogic.rubyforge.org">in the documentation</a> under Searchlogic::Helpers::ControlTypes.</p>
<p>These are only a few ways to use these helpers, this is really just the tip of the iceberg. The sky is the limit: create a select that lets users navigate through the pages, create a list of links (like flickr) that lets you navigate through pages, create a link that lets you order by any number of columns you want, etc. I know ruby has a bad rep when it comes to documentation, but I actually put some decent time into the <a href="http://searchlogic.rubyforge.org">Searchlogic documentation</a> and I think you will find it helpful. That&#8217;s your best resource for finding out everything <a href="http://github.com/binarylogic/searchlogic">Searchlogic</a> has to offer.</p>
<div class="spacer5"></div>
<h2>Adding a search form</h2>
<p>&#8220;This is all great, but you said I could search my data&#8221;. Searching your data is just as easy. Just add this to the top of your index.html.erb</p>
<pre class="cobalt">
# app/views/users/index.html.erb
<span class="EmbeddedSource"><span class="Punctuation">&lt;%</span> form_for <span class="Variable"><span class="Punctuation">@</span>search</span> <span class="Keyword">do </span><span class="Punctuation">|</span><span class="Variable">f</span><span class="Punctuation">|</span> <span class="Punctuation">%&gt;</span></span>
    <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">fieldset</span><span class="Punctuation">&gt;</span></span>
        <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">legend</span><span class="Punctuation">&gt;</span></span>Search Users<span class="MetaTagA"><span class="Punctuation">&lt;/</span><span class="MetaTagA">legend</span><span class="Punctuation">&gt;</span></span>

        <span class="EmbeddedSource"><span class="Punctuation">&lt;%</span> f<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">fields_for</span></span> <span class="Variable"><span class="Punctuation">@</span>search</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">conditions</span></span> <span class="Keyword">do </span><span class="Punctuation">|</span><span class="Variable">users</span><span class="Punctuation">|</span> <span class="Punctuation">%&gt;</span></span>
            <span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> users<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">label</span></span> <span class="Constant"><span class="Punctuation">:</span>first_name_keywords</span> <span class="Punctuation">%&gt;</span></span><span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">br</span> /<span class="Punctuation">&gt;</span></span>
            <span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> users<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">text_field</span></span> <span class="Constant"><span class="Punctuation">:</span>first_name_keywords</span> <span class="Punctuation">%&gt;</span></span><span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">br</span> /<span class="Punctuation">&gt;</span></span>
            <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">br</span> /<span class="Punctuation">&gt;</span></span>

            <span class="EmbeddedSource"><span class="Punctuation">&lt;%</span> users<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">fields_for</span></span> users<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">object</span></span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">orders</span></span> <span class="Keyword">do </span><span class="Punctuation">|</span><span class="Variable">orders</span><span class="Punctuation">|</span> <span class="Punctuation">%&gt;</span></span>
                <span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> orders<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">label</span></span> <span class="Constant"><span class="Punctuation">:</span>total_gt</span><span class="Punctuation">,</span> <span class="String"><span class="Punctuation">&quot;</span>Has orders with a total greater than<span class="Punctuation">&quot;</span></span> <span class="Punctuation">%&gt;</span></span><span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">br</span> /<span class="Punctuation">&gt;</span></span>
                $<span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> orders<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">text_field</span></span> <span class="Constant"><span class="Punctuation">:</span>total_gt</span> <span class="Punctuation">%&gt;</span></span><span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">br</span> /<span class="Punctuation">&gt;</span></span>
                <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">br</span> /<span class="Punctuation">&gt;</span></span>
            <span class="EmbeddedSource"><span class="Punctuation">&lt;%</span> <span class="Keyword">end</span> <span class="Punctuation">%&gt;</span></span>

            <span class="EmbeddedSource"><span class="Punctuation">&lt;%</span> users<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">fields_for</span></span> users<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">object</span></span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">user_group</span></span> <span class="Keyword">do </span><span class="Punctuation">|</span><span class="Variable">user_group</span><span class="Punctuation">|</span> <span class="Punctuation">%&gt;</span></span>
                <span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> user_group<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">label</span></span> <span class="Constant"><span class="Punctuation">:</span>name_starts_with</span><span class="Punctuation">,</span> <span class="String"><span class="Punctuation">&quot;</span>Belongs to user group with name that starts with<span class="Punctuation">&quot;</span></span> <span class="Punctuation">%&gt;</span></span><span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">br</span> /<span class="Punctuation">&gt;</span></span>
                <span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> user_group<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">text_field</span></span> <span class="Constant"><span class="Punctuation">:</span>name_starts_with</span> <span class="Punctuation">%&gt;</span></span><span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">br</span> /<span class="Punctuation">&gt;</span></span>
                <span class="MetaTagA"><span class="Punctuation">&lt;</span><span class="MetaTagA">br</span> /<span class="Punctuation">&gt;</span></span>
            <span class="EmbeddedSource"><span class="Punctuation">&lt;%</span> <span class="Keyword">end</span> <span class="Punctuation">%&gt;</span></span>
        <span class="EmbeddedSource"><span class="Punctuation">&lt;%</span> <span class="Keyword">end</span> <span class="Punctuation">%&gt;</span></span>
    <span class="MetaTagA"><span class="Punctuation">&lt;/</span><span class="MetaTagA">fieldset</span><span class="Punctuation">&gt;</span></span>
    <span class="EmbeddedSource"><span class="Punctuation">&lt;%=</span> f<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">submit</span></span> <span class="String"><span class="Punctuation">&quot;</span>Search<span class="Punctuation">&quot;</span></span> <span class="Punctuation">%&gt;</span></span>
<span class="EmbeddedSource"><span class="Punctuation">&lt;%</span> <span class="Keyword">end</span> <span class="Punctuation">%&gt;</span></span>
</pre>
<p>As I mentioned above <a href="http://github.com/binarylogic/searchlogic">Searchlogic</a> creates default conditions on your columns based on the type. Letting you use a form builder to call those conditions. When it receives these conditions on the back-end it will do its &#8220;magic&#8221; and creates the proper SQL. Don&#8217;t worry about SQL injections, <a href="http://github.com/binarylogic/searchlogic">Searchlogic</a> has you covered on that (see <a href="http://searchlogic.rubyforge.org">the documentation</a> for more info).</p>
<p>What&#8217;s great about this method of searching?</p>
<ol>
<li>Your search logic is in one place: the view.</li>
<li>You can add conditions by doing f.text_field &#8220;[column name]_[condition]&#8220;. So when your picky client calls up and says &#8220;Hey, we need a search field for finding emails that end with&#8230;&#8221; No prob: f.text_field :email_ends_with. Done!</li>
<li>You can traverse your relationships with fields_for and set conditions on related objects. Saweet!</li>
</ol>
<div class="spacer5"></div>
<h2>Ajaxified</h2>
<p>So you&#8217;re saying &#8220;this is great, but super old school, where&#8217;s the AJAX?!?!?&#8221; So you&#8217;re all about AJAX? No problem. In the example I have 3 examples: a <a href="http://searchlogic_example.binarylogic.com/non_ajax/users">non AJAX example</a>, an <a href="http://searchlogic_example.binarylogic.com/rails_ajax/users">AJAX example using the built in rails helpers</a>, and <a href="http://searchlogic_example.binarylogic.com/jquery/users">a jQuery AJAX example</a>. Check them out. You can <a href="https://github.com/binarylogic/searchlogic_example">view the source of the examples on github</a> (each example if named spaced into its own controller). On a side note, I highly recommend <a href="http://jquery.com/">jQuery</a>. I recently started using it and love it. I actually get excited to write javascript because I always find something new when digging through the documentation or plugins and unobtrusive javascript never felt so good.</p>
<div class="spacer5"></div>
<h2>Some helpful links</h2>
<p>This tutorial is really just the tip of the ice berg with <a href="http://github.com/binarylogic/searchlogic">Searchlogic</a>. Checkout these links to see all it has to offer:</p>
<ul>
<li><a href="http://searchlogic_example.binarylogic.com">Live example based on this tutorial</a></li>
<li><a href="http://github.com/binarylogic/searchlogic_example">Source code for the live tutorial</a></li>
<li><a href="http://github.com/binarylogic/searchlogic">Searchlogic, the library that makes all of this magic happen</a></li>
<li><a href="http://searchlogic.rubyforge.org">Searchlogic documentation</a></li>
</ul>
<p>I am always interested in hearing feedback. Let me know what you think, what you like, what you don&#8217;t, that you hate the name, etc. I love criticism. <a href="http://github.com/binarylogic/searchlogic">Searchlogic</a> is under active development and I am always trying to improve it. I hope this tutorial was helpful to you and ultimately makes your life easier.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.binarylogic.com/2008/09/07/tutorial-pagination-ordering-and-searching-with-searchlogic/feed/</wfw:commentRss>
		<slash:comments>28</slash:comments>
		</item>
		<item>
		<title>Searchlogic released!</title>
		<link>http://www.binarylogic.com/2008/09/01/searchlogic-released/</link>
		<comments>http://www.binarylogic.com/2008/09/01/searchlogic-released/#comments</comments>
		<pubDate>Mon, 01 Sep 2008 21:56:00 +0000</pubDate>
		<dc:creator>benjohnson</dc:creator>
				<category><![CDATA[Searchlogic]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[searchgasm]]></category>
		<category><![CDATA[searching]]></category>

		<guid isPermaLink="false">0/2009/03/23/searchlogic-released</guid>
		<description><![CDATA[Searchlogic&#8217;s inspiration comes right from ActiveRecord. ActiveRecord lets you create objects that represent a record in the database, so why can&#8217;t you create objects that represent searching the database? Now you can! It&#8217;s searching, ordering, and pagination all in one.
            
Enough said, let me [...]]]></description>
			<content:encoded><![CDATA[<p><strong><a href="http://github.com/binarylogic/searchlogic/">Searchlogic&#8217;s</a> inspiration comes right from ActiveRecord. ActiveRecord lets you create objects that represent a record in the database, so why can&#8217;t you create objects that represent searching the database? Now you can! It&#8217;s searching, ordering, and pagination all in one.</strong></p>
<p>            <span id="more-12"></span></p>
<p>Enough said, let me show you some of my favorite features. If you are still interested you can just jump over to the <a href="http://github.com/binarylogic/searchlogic/">searchlogic github page</a> for a more detailed explanation.</p>
<p><strong>For the following examples lets assume these relationships: User =&gt; Orders =&gt; LineItems</strong></p>
<h2>Simple Searching Example</h2>
<pre class="cobalt">
<span class="Support">User</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>conditions</span> <span class="Punctuation">=&gt;</span> <span class="Punctuation">{</span>
    <span class="Constant"><span class="Punctuation">:</span>first_name_contains</span> <span class="Punctuation">=&gt;</span> <span class="String"><span class="Punctuation">&quot;</span>Ben<span class="Punctuation">&quot;</span></span><span class="Punctuation">,</span>          <span class="Comment"><span class="Punctuation">#</span> first_name like '%Ben%'</span>
    <span class="Constant"><span class="Punctuation">:</span>email_ends_with</span> <span class="Punctuation">=&gt;</span> <span class="String"><span class="Punctuation">&quot;</span>binarylogic.com<span class="Punctuation">&quot;</span></span>   <span class="Comment"><span class="Punctuation">#</span> email like '%binarylogic.com'</span>
  <span class="Punctuation">}</span><span class="Punctuation">,</span>
  <span class="Constant"><span class="Punctuation">:</span>per_page</span> <span class="Punctuation">=&gt;</span> <span class="Constant">20</span><span class="Punctuation">,</span>                      <span class="Comment"><span class="Punctuation">#</span> limit 20</span>
  <span class="Constant"><span class="Punctuation">:</span>page</span> <span class="Punctuation">=&gt;</span> <span class="Constant">3</span><span class="Punctuation">,</span>                           <span class="Comment"><span class="Punctuation">#</span> offset 40, which starts us on page 3</span>
  <span class="Constant"><span class="Punctuation">:</span>order_as</span> <span class="Punctuation">=&gt;</span> <span class="String"><span class="Punctuation">&quot;</span>ASC<span class="Punctuation">&quot;</span></span><span class="Punctuation">,</span>
  <span class="Constant"><span class="Punctuation">:</span>order_by</span> <span class="Punctuation">=&gt;</span> <span class="Punctuation">{</span><span class="Constant"><span class="Punctuation">:</span>user_group</span> <span class="Punctuation">=&gt;</span> <span class="Constant"><span class="Punctuation">:</span>name</span><span class="Punctuation">}</span>   <span class="Comment"><span class="Punctuation">#</span> order user_groups.name ASC</span>
<span class="Punctuation">)</span>
</pre>
<p>same as above, but object based</p>
<pre class="cobalt">
search <span class="Keyword">=</span> <span class="Support">User</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">new_search</span></span>
search<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">conditions</span></span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">first_name_contains</span></span> <span class="Keyword">=</span> <span class="String"><span class="Punctuation">&quot;</span>Ben<span class="Punctuation">&quot;</span></span>
search<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">conditions</span></span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">email_ends_with</span></span> <span class="Keyword">=</span> <span class="String"><span class="Punctuation">&quot;</span>binarylogic.com<span class="Punctuation">&quot;</span></span>
search<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">per_page</span></span> <span class="Keyword">=</span> <span class="Constant">20</span>
search<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">page</span></span> <span class="Keyword">=</span> <span class="Constant">3</span>
search<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">order_as</span></span> <span class="Keyword">=</span> <span class="String"><span class="Punctuation">&quot;</span>ASC<span class="Punctuation">&quot;</span></span>
search<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">order_by</span></span> <span class="Keyword">=</span> <span class="Punctuation">{</span><span class="Constant"><span class="Punctuation">:</span>user_group</span> <span class="Punctuation">=&gt;</span> <span class="Constant"><span class="Punctuation">:</span>name</span><span class="Punctuation">}</span>
search<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">all</span></span>
</pre>
<p>In both examples, instead of using the &#8220;all&#8221; method you could use any search method: first, find(:all), find(:first), count, sum, average, etc, just like ActiveRecord.</p>
<h2>The beauty of searchlogic, integration with rails</h2>
<p>Using Searchlogic in rails is the best part, because rails has all kinds of nifty methods to make dealing with ActiveRecord objects quick and easy, especially with forms. So let’s take advantage of them! That’s the idea behind this plugin. Searchlogic is searching, ordering, and pagination all rolled into one simple plugin. Take all of that pagination and searching cruft out of your models and let Searchlogic handle it. Check it out: </p>
<pre class="cobalt">
<span class="Comment"><span class="Punctuation">#</span> app/controllers/users_controller.rb</span>
<span class="Keyword">def</span> <span class="Entity">index</span>
    <span class="Variable"><span class="Punctuation">@</span>search</span> <span class="Keyword">=</span> <span class="Support">User</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">new_search</span></span><span class="Punctuation">(</span>params<span class="Punctuation">[</span><span class="Constant"><span class="Punctuation">:</span>search</span><span class="Punctuation">]</span><span class="Punctuation">)</span>
    <span class="Variable"><span class="Punctuation">@</span>users</span><span class="Punctuation">,</span> <span class="Variable"><span class="Punctuation">@</span>users_count</span> <span class="Keyword">=</span> <span class="Variable"><span class="Punctuation">@</span>search</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">all</span></span><span class="Punctuation">,</span> <span class="Variable"><span class="Punctuation">@</span>search</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">count</span></span>
<span class="Keyword">end</span>
</pre>
<p>Now your view.</p>
<pre class="cobalt">
# app/views/users/index.html.haml
- form_for @search do |f|
 - f.fields_for @search.conditions do |users|
  = users.text_field :first_name_contains
  = users.calendar_date_select :created_after # nice rails plugin for replacing date_select
  - users.fields_for users.object.orders do |orders|
    = orders.select :total_gt, (1..100)
  = f.submit &quot;Search&quot;

- if @users_count &gt; 0
  %table
    %tr
      %th= order_by_link :account =&gt; :name
      %th= order_by_link :first_name
      %th= order_by_link :last_name
      %th= order_by_link :email
    - @users.each do |user|
      %tr
        %td= user.first_name
        %td= user.last_name
        %td= user.email

  Per page:
  = per_page_select
  Page:
  = page_select
- else
  No users returned
</pre>
<h3>Things to notice</h3>
<ol>
<li>Passing a search object right into form_for and fields_for</li>
<li>The built in conditions for each column and how you can traverse the relationships and set conditions on them</li>
<li>The order_by_link helper</li>
<li>The page_select and per_page_select helpers</li>
<li>All of your search logic is in 1 spot: your view. Nice and DRY.</li>
</ol>
<p><strong><a href="http://www.binarylogic.com/2008/9/7/tutorial-pagination-ordering-and-searching-with-searchlogic">See my tutorial on this example</a></strong></p>
<h2>Nifty methods for searching trees</h2>
<p>Just like the conditions you automatically get with each column. You automatically get conditions for searching tree data structures, if you&#8217;re model is a tree data structure.</p>
<pre class="cobalt">
<span class="Comment"><span class="Punctuation">#</span> User is a tree data structure.</span>
conditions <span class="Keyword">=</span> <span class="Support">User</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">new_conditions</span></span>
conditions<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">child_of</span></span> <span class="Keyword">=</span> <span class="Support">User</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">roots</span></span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">first</span></span>                  <span class="Comment"><span class="Punctuation">#</span> Finds all children</span>
conditions<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">sibling_of</span></span> <span class="Keyword">=</span> <span class="Support">User</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">first</span></span>                      <span class="Comment"><span class="Punctuation">#</span> Finds siblings</span>
conditions<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">descendant_of</span></span> <span class="Keyword">=</span> <span class="Support">User</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">roots</span></span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">first</span></span>             <span class="Comment"><span class="Punctuation">#</span> Finds all children, grandchildren, etc</span>
conditions<span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">inclusive_descendant_of</span></span> <span class="Keyword">=</span> <span class="Support">User</span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">roots</span></span><span class="FunctionCall"><span class="Punctuation">.</span><span class="Entity">first</span></span>   <span class="Comment"><span class="Punctuation">#</span> Includes root + children, grandchildren, etc.</span>
</pre>
<p>Any of these conditions can accept a User object or a User id.</p>
<h2>Get Started</h2>
<p>Check out the GitHub repository to get started:</p>
<p><strong><a href="http://github.com/binarylogic/searchlogic/">http://github.com/binarylogic/searchlogic/</a></strong></p>
<p>Or a tutorial I posted on using Searchlogic in rails:</p>
<p><strong><a href="http://www.binarylogic.com/2008/9/7/tutorial-pagination-ordering-and-searching-with-searchlogic">http://www.binarylogic.com/2008/9/7/tutorial-pagination-ordering-and-searching-with-searchlogic</a></strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.binarylogic.com/2008/09/01/searchlogic-released/feed/</wfw:commentRss>
		<slash:comments>20</slash:comments>
		</item>
	</channel>
</rss>
