Spree Commerce

Try It Now

Searchable Models - Now With Searchlogic

Posted on March 05, 2009 by Sean Schofield

The upcoming release of Spree is now using searchlogic to improve the way we do searching and paging. Checkout the very excellent README for more details on how searchlogic works. Unfortunately there is currently a small error in the date searching. I’ve fixed the error but we need to release before this patch can be accepted. Normally I would just vendor a hacked gem but lately we’ve been having problems with vendored gems. So we’ve created our own gem. This means there are a few additional steps you will need to do to get Spree running.

Special Steps For Obtaining the Modified Searchlogic Gem

1. Add the GitHub Gem Repository to your Gem Resources


<p>gem sources -a http://gems.github.com</p>

2. Install this gem (and any other dependencies) the usual way


<p>rake gems:install</p>

Possible Changes Needed for your Extension Controllers and Views

There are some important implications for the controllers and views in your custom extensions. If you have any views where you are using paging you are going to need to change your paging controls slightly.

The Old Way

Inside of your index.html.erb you would have something like this in your `index.html.erb`


<p>&lt;%= render :partial =&gt; &#8216;shared/paginate&#8217;, :locals =&gt; {:collection =&gt; <code>orders, :options =&gt; generate_search_options(</code>filter)} unless @orders.empty? %&gt;</p>

You then had a big giant pile of code in the `shared/paginate` partial


<p><!-- Code by Ilya Grigorik - http://www.igvita.com/blog/2006/09/10/faster-pagination-in-rails/ --><br />
&lt;% if collection.page_count != collection.first_page -%&gt;<br />
<div class="pagination"><br />
  <ul><br />
    &lt;% if collection.previous_page? -%&gt;<br />
    <li class="nextpage"></p>
&lt;%= link_to &#8220;&#171; #{t(&#8216;previous&#8217;)}&#8221;, { :p =&gt; collection.previous_page }.merge(options) %&gt;
</li>
&lt;% else -%&gt;
    <li class="disablepage">&#171; &lt;%= t(&#8216;previous&#8217;) %&gt;</li>
&lt;% end -%&gt;
&lt;% last_page = 0 -%&gt;
&lt;% windowed_pagination_links(collection, :window_size =&gt; 2, :link_to_current_page =&gt; true, :always_show_anchors =&gt; true) do |n| -%&gt;
&lt;% if collection.page == n -%&gt;
    <li class="currentpage">&lt;%= n %&gt;</li>
&lt;% else -%&gt;
    <li>&lt;%= &#8220;&#8230;&#8221; if last_page+1 &lt; n %&gt;<br />
<br />
        &lt;%= link_to n, {:id =&gt; params[:id], :p =&gt; n}.merge(options) %&gt;    </li>
&lt;% end -%&gt;
&lt;% last_page = n -%&gt;
&lt;% end -%&gt;
&lt;% if collection.next_page? -%&gt;
<li class="nextpage">
&lt;%=  link_to &#8220;#{t(&#8216;next&#8217;)} &#187;&#8221;, { :p =&gt; collection.next_page }.merge(options) %&gt;
</li>
&lt;% else -%&gt;
    <li class="disablepage">&#171; &lt;%= t(&#8216;next&#8217;) %&gt;</li>
&lt;% end -%&gt;
</ul>
</div>
<p>&lt;% end -%&gt;</p>
The New Way

Inside of your `index.html.erb` you have this


<p>&lt;%= page_links(:prev =&gt; &#8220;&#171; #{t(&#8216;previous&#8217;)}&#8221;, :next =&gt; &#8220;#{t(&#8216;next&#8217;)} &#187;&#8221;) if @search.page_count &gt; 1 %&gt;</p>

That’s it. No more shared partials. Now you’re starting to see how this simplifies things.

Now lets move on to the controllers. Each controller that needs to have any kind of search or paging needs to have a special `collection` method which is used by `resource_controller`.

The Old Way

<p>&lt;%= render :partial =&gt; &#8216;shared/paginate&#8217;, :locals =&gt; {:collection =&gt; <code>orders, :options =&gt; generate_search_options(</code>filter)} unless @orders.empty? %&gt;<br />
<ruby></p>
<h5>The New Way</h5>
<ruby>
<p>def collection<br />
  @search = Order.new_search(params[:search])</p>
if params[:search].nil? || params[:search][:conditions].nil?
@search.conditions.checkout_complete = true
end
@search.order_by ||= :created_at
@search.order_as ||= &#8220;<span class="caps">DESC</span>&#8221;
@search.per_page = Spree::Config[:orders_per_page]
@collection = @search.find(:all, :include =&gt; [:user, :shipments, {:creditcards =&gt; :address}] )
<p>end</p>

Obviously the new way is simpler with less lines of code to maintain. The searchlogic gem also takes care of a lot of what we used to do ourselves in terms of performing searches. All existing Spree searches have been overhauled to work with searchlogic. So if you’re doing any custom searching in your applications you will want to look at admin/orders_controller.rb or other relevant controllers for some ideas on how to pull it off. Just to repeat, the searchlogic README is very detailed. It will explain everything you need to know about how searchlogic works.

Most applications will only have to make minor changes. Again, you don’t need to do anything unless you are overriding a Spree view that uses paging or a Spree controller that provides a searchable list and/or paging. This should also allow us to build a very powerful advanced search form for the customer facing portion. We’re steadily working towards that and other great features so stay tuned.