<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>contribute.rb</filename>
    </added>
    <added>
      <filename>features/facets.feature</filename>
    </added>
    <added>
      <filename>features/step_definitions/facet_steps.rb</filename>
    </added>
    <added>
      <filename>features/support/db/migrations/create_developers.rb</filename>
    </added>
    <added>
      <filename>features/support/db/migrations/create_tags.rb</filename>
    </added>
    <added>
      <filename>features/support/models/developer.rb</filename>
    </added>
    <added>
      <filename>features/support/models/tag.rb</filename>
    </added>
    <added>
      <filename>lib/thinking_sphinx/facet.rb</filename>
    </added>
    <added>
      <filename>lib/thinking_sphinx/facet_collection.rb</filename>
    </added>
    <added>
      <filename>lib/thinking_sphinx/tasks.rb</filename>
    </added>
    <added>
      <filename>tasks/distribution.rb</filename>
    </added>
    <added>
      <filename>tasks/rails.rake</filename>
    </added>
    <added>
      <filename>tasks/testing.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -10,6 +10,8 @@ h2. Contributing
 
 Fork on GitHub and after you've committed tested patches, send a pull request.
 
+To quickly see if your system is ready to run the thinking sphinx specs, run the contribute.rb script found in the project root directory. Use the following instructions to install any missing requirements.
+
 To get the spec suite running, you will need to install the not-a-mock gem if you don't already have it:
 
   git clone git://github.com/freelancing-god/not-a-mock.git
@@ -48,7 +50,7 @@ h2. Contributors
 Since I first released this library, there's been quite a few people who have submitted patches, to my immense gratitude. Others have suggested syntax changes and general improvements. So my thanks to the following people:
 
 * Joost Hietbrink
-* Jonathon Conway
+* Jonathan Conway
 * Gregory Mirzayantz
 * Tung Nguyen
 * Sean Cribbs
@@ -99,3 +101,13 @@ Since I first released this library, there's been quite a few people who have su
 * Thibaut Barrere
 * Kristopher Chambers
 * Dmitrij Smalko
+* Aleksey Yeschenko
+* Lachie Cox
+* Lourens Naude
+* Tom Davies
+* Dan Pickett
+* Alex Caudill
+* Jim Benton
+* John Aughey
+* Keith Pitty
+* Jeff Talbot</diff>
      <filename>README.textile</filename>
    </modified>
    <modified>
      <diff>@@ -1,128 +1,4 @@
-begin
-  require 'spec'
-rescue LoadError
-  require 'rubygems'
-  require 'spec'
-end
+require 'rubygems'
 
-require 'rake/rdoctask'
-require 'spec/rake/spectask'
-require 'rake/gempackagetask'
-require 'cucumber/rake/task'
-
-# allow require of spec/spec_helper
-$LOAD_PATH.unshift File.dirname(__FILE__) + '/../'
-$LOAD_PATH.unshift File.dirname(__FILE__) + '/lib'
-
-require 'thinking_sphinx'
-
-desc 'Generate documentation'
-Rake::RDocTask.new(:rdoc) do |rdoc|
-  rdoc.rdoc_dir = 'rdoc'
-  rdoc.title    = 'Thinking Sphinx - ActiveRecord Sphinx Plugin'
-  rdoc.options &lt;&lt; '--line-numbers' &lt;&lt; '--inline-source'
-  rdoc.rdoc_files.include('README')
-  rdoc.rdoc_files.include('lib/**/*.rb')
-end
-
-desc &quot;Run the specs under spec&quot;
-Spec::Rake::SpecTask.new do |t|
-  t.spec_files = FileList['spec/**/*_spec.rb']
-  t.spec_opts &lt;&lt; &quot;-c&quot;
-end
-
-desc &quot;Run all feature-set configurations&quot;
-task :features do
-  # 
-end
-
-task :features do |t|
-  puts   &quot;rake features:mysql&quot;
-  system &quot;rake features:mysql&quot;
-  puts   &quot;rake features:postgresql&quot;
-  system &quot;rake features:postgresql&quot;
-end
-
-namespace :features do
-  Cucumber::Rake::Task.new(:mysql, &quot;Run feature-set against MySQL&quot;) do |t|
-    t.cucumber_opts = &quot;--format pretty&quot;
-    t.step_pattern  = [
-      &quot;features/support/env&quot;,
-      &quot;features/support/db/mysql&quot;,
-      &quot;features/support/db/active_record&quot;,
-      &quot;features/support/post_database&quot;,
-      &quot;features/step_definitions/**.rb&quot;
-    ]
-  end
-  
-  Cucumber::Rake::Task.new(:postgresql, &quot;Run feature-set against PostgreSQL&quot;) do |t|
-    t.cucumber_opts = &quot;--format pretty&quot;
-    t.step_pattern  = [
-      &quot;features/support/env&quot;,
-      &quot;features/support/db/postgresql&quot;,
-      &quot;features/support/db/active_record&quot;,
-      &quot;features/support/post_database&quot;,
-      &quot;features/step_definitions/**.rb&quot;
-    ]
-  end
-end
-
-desc &quot;Generate RCov reports&quot;
-Spec::Rake::SpecTask.new(:rcov) do |t|
-  t.libs &lt;&lt; 'lib'
-  t.spec_files = FileList['spec/**/*_spec.rb']
-  t.rcov = true
-  t.rcov_opts = ['--exclude', 'spec', '--exclude', 'gems', '--exclude', 'riddle']
-end
-
-spec = Gem::Specification.new do |s|
-  s.name              = &quot;thinking-sphinx&quot;
-  s.version           = ThinkingSphinx::Version::String
-  s.summary           = &quot;A concise and easy-to-use Ruby library that connects ActiveRecord to the Sphinx search daemon, managing configuration, indexing and searching.&quot;
-  s.description       = &quot;A concise and easy-to-use Ruby library that connects ActiveRecord to the Sphinx search daemon, managing configuration, indexing and searching.&quot;
-  s.author            = &quot;Pat Allan&quot;
-  s.email             = &quot;pat@freelancing-gods.com&quot;
-  s.homepage          = &quot;http://ts.freelancing-gods.com&quot;
-  s.has_rdoc          = true
-  s.rdoc_options     &lt;&lt; &quot;--title&quot; &lt;&lt; &quot;Thinking Sphinx -- Rails/Merb Sphinx Plugin&quot; &lt;&lt;
-                        &quot;--line-numbers&quot;
-  s.rubyforge_project = &quot;thinking-sphinx&quot;
-  s.test_files        = FileList[&quot;spec/**/*_spec.rb&quot;]
-  s.files             = FileList[
-    &quot;lib/**/*.rb&quot;,
-    &quot;LICENCE&quot;,
-    &quot;README&quot;,
-    &quot;tasks/**/*.rb&quot;,
-    &quot;tasks/**/*.rake&quot;,
-    &quot;vendor/**/*&quot;
-  ]
-end
-
-Rake::GemPackageTask.new(spec) do |p|
-  p.gem_spec = spec
-  p.need_tar = true
-  p.need_zip = true
-end
-
-desc &quot;Build gemspec file&quot;
-task :build do
-  File.open('thinking-sphinx.gemspec', 'w') { |f| f.write spec.to_ruby }
-end
-
-desc &quot;Build cucumber.yml file&quot;
-task :cucumber_defaults do
-  default_requires = %w(
-    --require features/support/env.rb
-    --require features/support/db/mysql.rb
-    --require features/support/db/active_record.rb
-    --require features/support/post_database.rb
-  ).join(&quot; &quot;)
-  
-  step_definitions = FileList[&quot;features/step_definitions/**.rb&quot;].collect { |path|
-    &quot;--require #{path}&quot;
-  }.join(&quot; &quot;)
-  
-  File.open('cucumber.yml', 'w') { |f|
-    f.write &quot;default: \&quot;#{default_requires} #{step_definitions}\&quot;&quot;
-  }
-end
+require 'tasks/distribution'
+require 'tasks/testing'
\ No newline at end of file</diff>
      <filename>Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -1 +1 @@
-default: &quot;--require features/support/env.rb --require features/support/db/mysql.rb --require features/support/db/active_record.rb --require features/support/post_database.rb --require features/step_definitions/alpha_steps.rb --require features/step_definitions/beta_steps.rb --require features/step_definitions/cat_steps.rb --require features/step_definitions/common_steps.rb --require features/step_definitions/datetime_delta_steps.rb --require features/step_definitions/delayed_delta_indexing_steps.rb --require features/step_definitions/find_arguments_steps.rb --require features/step_definitions/gamma_steps.rb --require features/step_definitions/search_steps.rb --require features/step_definitions/sphinx_steps.rb&quot;
\ No newline at end of file
+default: &quot;--require features/support/env.rb --require features/support/db/mysql.rb --require features/support/db/active_record.rb --require features/support/post_database.rb --require features/step_definitions/alpha_steps.rb --require features/step_definitions/beta_steps.rb --require features/step_definitions/cat_steps.rb --require features/step_definitions/common_steps.rb --require features/step_definitions/datetime_delta_steps.rb --require features/step_definitions/delayed_delta_indexing_steps.rb --require features/step_definitions/facet_steps.rb --require features/step_definitions/find_arguments_steps.rb --require features/step_definitions/gamma_steps.rb --require features/step_definitions/search_steps.rb --require features/step_definitions/sphinx_steps.rb&quot;
\ No newline at end of file</diff>
      <filename>cucumber.yml</filename>
    </modified>
    <modified>
      <diff>@@ -51,5 +51,5 @@ Feature: Datetime Delta Indexing
     And I search for thirteen
     Then I should get 1 result
     
-    When I search for the specific id of 107 in the theta_core index
+    When I search for the document id of theta thirteen in the theta_core index
     Then it should exist
\ No newline at end of file</diff>
      <filename>features/datetime_deltas.feature</filename>
    </modified>
    <modified>
      <diff>@@ -11,10 +11,10 @@ Feature: Searching across multiple model
   
   Scenario: Confirming existance of a document id in a given index
     Given Sphinx is running
-    When I search for the specific id of 51 in the person_core index
+    When I search for the document id of alpha one in the alpha_core index
     Then it should exist
   
-  Scenario: Unsuccessfully confirming existance of a document id in a given index
+  Scenario: Retrieving results from multiple models
     Given Sphinx is running
-    When I search for the specific id of 52 in the person_core index
-    Then it should not exist
\ No newline at end of file
+    When I search for ten
+    Then I should get 5 results</diff>
      <filename>features/searching_across_models.feature</filename>
    </modified>
    <modified>
      <diff>@@ -21,6 +21,16 @@ Feature: Searching on a single model
     When I search for James on first_name
     And I search for Chamberlain on last_name
     Then I should get 1 result
+
+  Scenario: Searching on association content
+	  Given Sphinx is running
+	  And I am searching on posts
+	
+	  When I search for &quot;Waffles&quot;
+	  Then I should get 1 result
+
+	  When I search for &quot;Turtle&quot;
+	  Then I should get 1 result
   
   Scenario: Searching with a filter
     Given Sphinx is running
@@ -34,6 +44,15 @@ Feature: Searching on a single model
     When I filter by 2 on width
     And I filter by 2 on length
     Then I should get 1 result
+    
+  Scenario: Searching to filter multiple values on an MVA
+    Given Sphinx is running
+    And I am searching on boxes
+    When I filter by 11 and 12 on dimensions
+    Then I should get 2 results
+    When I clear existing filters
+    And I filter by both 11 and 12 on dimensions
+    Then I should get 1 result
   
   Scenario: Searching with ordering by attribute
     Given Sphinx is running</diff>
      <filename>features/searching_by_model.feature</filename>
    </modified>
    <modified>
      <diff>@@ -3,7 +3,7 @@ Feature: Checking whether Sphinx is running or not
   Thinking Sphinx
   Should be able to determine whether Sphinx is running or not
   
-  Scenario: Deleting instances from the core index
+  Scenario: Checking Sphinx's status
     Given Sphinx is running
     Then Sphinx should be running
     
@@ -13,4 +13,9 @@ Feature: Checking whether Sphinx is running or not
     
     When I start Sphinx
     And I wait for Sphinx to catch up
-    Then Sphinx should be running
\ No newline at end of file
+    Then Sphinx should be running
+    
+    Given Sphinx is running
+    When I kill the Sphinx process
+    And I wait for Sphinx to catch up
+    Then Sphinx should not be running
\ No newline at end of file</diff>
      <filename>features/sphinx_detection.feature</filename>
    </modified>
    <modified>
      <diff>@@ -8,6 +8,7 @@ Before do
   @conditions = {}
   @with       = {}
   @without    = {}
+  @with_all   = {}
   @options    = {}
 end
 
@@ -40,11 +41,27 @@ When /^I search for (\w+) on (\w+)$/ do |query, field|
   @conditions[field.to_sym] = query
 end
 
+When /^I clear existing filters$/ do
+  @with     = {}
+  @without  = {}
+  @with_all = {}
+end
+
 When /^I filter by (\w+) on (\w+)$/ do |filter, attribute|
   @results = nil
   @with[attribute.to_sym] = filter.to_i
 end
 
+When /^I filter by (\d+) and (\d+) on (\w+)$/ do |value_one, value_two, attribute|
+  @results = nil
+  @with[attribute.to_sym] = [value_one.to_i, value_two.to_i]
+end
+
+When /^I filter by both (\d+) and (\d+) on (\w+)$/ do |value_one, value_two, attribute|
+  @results = nil
+  @with_all[attribute.to_sym] = [value_one.to_i, value_two.to_i]
+end
+
 When /^I filter between ([\d\.]+) and ([\d\.]+) on (\w+)$/ do |first, last, attribute|
   @results = nil
   if first[/\./].nil? &amp;&amp; last[/\./].nil?
@@ -130,7 +147,8 @@ def results
     @options.merge(
       :conditions =&gt; @conditions,
       :with       =&gt; @with,
-      :without    =&gt; @without
+      :without    =&gt; @without,
+      :with_all   =&gt; @with_all
     )
   )
 end
\ No newline at end of file</diff>
      <filename>features/step_definitions/common_steps.rb</filename>
    </modified>
    <modified>
      <diff>@@ -32,10 +32,10 @@ Then &quot;it should not exist if using Rails 2.1 or newer&quot; do
 end
 
 Then /^I can iterate by result and group and count$/ do
-  results.each_with_group_and_count do |result, group, count|
+  results.each_with_groupby_and_count do |result, group, count|
     result.should be_kind_of(@model)
     count.should  be_kind_of(Integer)
-    group.should  be_kind_of(Integer) unless group.nil?
+    group.should  be_kind_of(Integer)
   end
 end
 </diff>
      <filename>features/step_definitions/search_steps.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,6 +2,10 @@ Given &quot;Sphinx is running&quot; do
   ThinkingSphinx::Configuration.instance.controller.should be_running
 end
 
+When &quot;I kill the Sphinx process&quot; do
+  Process.kill(9, ThinkingSphinx.sphinx_pid.to_i)
+end
+
 When &quot;I wait for Sphinx to catch up&quot; do
   sleep(0.25)
 end
@@ -20,4 +24,4 @@ end
 
 Then &quot;Sphinx should not be running&quot; do
   ThinkingSphinx.sphinx_running?.should be_false
-end
\ No newline at end of file
+end</diff>
      <filename>features/step_definitions/sphinx_steps.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,3 +7,7 @@ end
 (1..10).each do |i|
   Box.create :width =&gt; i, :length =&gt; i, :depth =&gt; i
 end
+
+(11..20).each do |i|
+  Box.create :width =&gt; i, :length =&gt; i+1, :depth =&gt; i+2
+end</diff>
      <filename>features/support/db/migrations/create_boxes.rb</filename>
    </modified>
    <modified>
      <diff>@@ -11,3 +11,13 @@ Comment.create(
   :content  =&gt; &quot;+1&quot;,
   :post_id  =&gt; 1
 )
+
+Comment.create :name =&gt; 'A', :post_id =&gt; 1, :content =&gt; 'Es un hecho establecido hace demasiado tiempo que un lector se distraer&#225; con el contenido del texto'
+Comment.create :name =&gt; 'B', :post_id =&gt; 1, :content =&gt; 'de un sitio mientras que mira su dise&#241;o. El punto de usar Lorem Ipsum es que tiene una distribuci&#243;n'
+Comment.create :name =&gt; 'C', :post_id =&gt; 1, :content =&gt; 'm&#225;s o menos normal de las letras, al contrario de usar textos como por ejemplo &quot;Contenido aqu&#237;'
+Comment.create :name =&gt; 'D', :post_id =&gt; 1, :content =&gt; 'contenido aqu&#237;&quot;. Estos textos hacen parecerlo un espa&#241;ol que se puede leer. Muchos paquetes de'
+Comment.create :name =&gt; 'E', :post_id =&gt; 1, :content =&gt; 'autoedici&#243;n y editores de p&#225;ginas web usan el Lorem Ipsum como su texto por defecto, y al hacer una'
+Comment.create :name =&gt; 'F', :post_id =&gt; 1, :content =&gt; 'b&#250;squeda de &quot;Lorem Ipsum&quot; va a dar por resultado muchos sitios web que usan este texto si se'
+
+# The one we'll really want to find via Sphinx search
+Comment.create :name =&gt; 'G', :post_id =&gt; 1, :content =&gt; 'Turtle'</diff>
      <filename>features/support/db/migrations/create_comments.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,4 +3,4 @@ ActiveRecord::Base.connection.create_table :posts, :force =&gt; true do |t|
   t.column :content, :text
 end
 
-Post.create :subject =&gt; &quot;Hello World&quot;, :content =&gt; &quot;Um Text&quot;
+Post.create :subject =&gt; &quot;Hello World&quot;, :content =&gt; &quot;Um Text&quot;, :id =&gt; 1</diff>
      <filename>features/support/db/migrations/create_posts.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,5 +3,6 @@ class Box &lt; ActiveRecord::Base
     indexes width, :as =&gt; :width_field
     
     has width, length, depth
+    has [width, length, depth], :as =&gt; :dimensions
   end
-end
\ No newline at end of file
+end</diff>
      <filename>features/support/models/box.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,8 +1,11 @@
 class Post &lt; ActiveRecord::Base
-  has_many :comments
+  has_many :comments, :dependent =&gt; :destroy
+  has_many :tags, :dependent =&gt; :destroy
   
   define_index do
     indexes subject
     indexes content
+    indexes tags.text, :as =&gt; :tags
+    indexes comments.content, :as =&gt; :comments
   end
 end
\ No newline at end of file</diff>
      <filename>features/support/models/post.rb</filename>
    </modified>
    <modified>
      <diff>@@ -20,7 +20,7 @@ ActiveRecord::Base.logger = Logger.new open(&quot;tmp/active_record.log&quot;, &quot;a&quot;)
 ThinkingSphinx.deltas_enabled = false
 
 # Load Models
-Dir[&quot;features/support/models/*.rb&quot;].each do |file|
+Dir[&quot;features/support/models/*.rb&quot;].sort.each do |file|
   require file.gsub(/\.rb$/, '')
 end
 </diff>
      <filename>features/support/post_database.rb</filename>
    </modified>
    <modified>
      <diff>@@ -20,5 +20,9 @@ Ginger.configure do |config|
   ar_2_2_0[/^active_?support$/] = &quot;2.2.0&quot;
   ar_2_2_0[/^active_?record$/] = &quot;2.2.0&quot;
   
-  config.scenarios &lt;&lt; ar_1_2_6 &lt;&lt; ar_2_0_4 &lt;&lt; ar_2_1_2 &lt;&lt; ar_2_2_0
+  ar_2_3_0 = Ginger::Scenario.new
+  ar_2_3_0[/^active_?support$/] = &quot;2.3.0&quot;
+  ar_2_3_0[/^active_?record$/] = &quot;2.3.0&quot;
+  
+  config.scenarios &lt;&lt; ar_1_2_6 &lt;&lt; ar_2_0_4 &lt;&lt; ar_2_1_2 &lt;&lt; ar_2_2_0 &lt;&lt; ar_2_3_0
 end
\ No newline at end of file</diff>
      <filename>ginger_scenarios.rb</filename>
    </modified>
    <modified>
      <diff>@@ -12,6 +12,8 @@ require 'thinking_sphinx/association'
 require 'thinking_sphinx/attribute'
 require 'thinking_sphinx/collection'
 require 'thinking_sphinx/configuration'
+require 'thinking_sphinx/facet'
+require 'thinking_sphinx/facet_collection'
 require 'thinking_sphinx/field'
 require 'thinking_sphinx/index'
 require 'thinking_sphinx/rails_additions'
@@ -24,11 +26,15 @@ require 'thinking_sphinx/adapters/postgresql_adapter'
 
 ActiveRecord::Base.send(:include, ThinkingSphinx::ActiveRecord)
 
+Merb::Plugins.add_rakefiles(
+  File.join(File.dirname(__FILE__), &quot;thinking_sphinx&quot;, &quot;tasks&quot;)
+) if defined?(Merb)
+
 module ThinkingSphinx
   module Version #:nodoc:
     Major = 1
     Minor = 1
-    Tiny  = 2
+    Tiny  = 5
     
     String = [Major, Minor, Tiny].join('.')
   end
@@ -152,15 +158,20 @@ module ThinkingSphinx
   end
   
   def self.sphinx_running?
-    !!sphinx_pid
+    !!sphinx_pid &amp;&amp; pid_active?(sphinx_pid)
   end
   
   def self.sphinx_pid
     pid_file = ThinkingSphinx::Configuration.instance.pid_file    
     `cat #{pid_file}`[/\d+/] if File.exists?(pid_file)
   end
-end
-
-Merb::Plugins.add_rakefiles(
-  File.join(File.dirname(__FILE__), &quot;..&quot;, &quot;tasks&quot;, &quot;thinking_sphinx_tasks&quot;)
-) if ThinkingSphinx.merb?
\ No newline at end of file
+  
+  def self.pid_active?(pid)
+    begin
+      Process.getpgid(pid.to_i)
+      true
+    rescue Exception =&gt; e
+      false
+    end
+  end
+end
\ No newline at end of file</diff>
      <filename>lib/thinking_sphinx.rb</filename>
    </modified>
    <modified>
      <diff>@@ -10,7 +10,7 @@ module ThinkingSphinx
   module ActiveRecord
     def self.included(base)
       base.class_eval do
-        class_inheritable_array :sphinx_indexes
+        class_inheritable_array :sphinx_indexes, :sphinx_facets
         class &lt;&lt; self
           # Allows creation of indexes for Sphinx. If you don't do this, there
           # isn't much point trying to search (or using this plugin at all,
@@ -114,13 +114,18 @@ module ThinkingSphinx
           end
           
           def to_riddle(offset)
-            ThinkingSphinx::AbstractAdapter.detect(self).setup
+            sphinx_database_adapter.setup
             
             indexes = [to_riddle_for_core(offset)]
             indexes &lt;&lt; to_riddle_for_delta(offset) if sphinx_delta?
             indexes &lt;&lt; to_riddle_for_distributed
           end
           
+          def sphinx_database_adapter
+            @sphinx_database_adapter ||=
+              ThinkingSphinx::AbstractAdapter.detect(self)
+          end
+          
           private
           
           def sphinx_name</diff>
      <filename>lib/thinking_sphinx/active_record.rb</filename>
    </modified>
    <modified>
      <diff>@@ -42,6 +42,13 @@ module ThinkingSphinx
               args &lt;&lt; options
               ThinkingSphinx::Search.search_for_id(*args)
             end
+            
+            def facets(*args)
+              options = args.extract_options!
+              options[:class] = self
+              args &lt;&lt; options
+              ThinkingSphinx::Search.facets(*args)
+            end
           end
         end
       end</diff>
      <filename>lib/thinking_sphinx/active_record/search.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,27 +1,34 @@
 module ThinkingSphinx
   class AbstractAdapter
-    class &lt;&lt; self
-      def setup
-        # Deliberately blank - subclasses should do something though. Well, if
-        # they need to.
-      end
-      
-      def detect(model)
-        case model.connection.class.name
-        when &quot;ActiveRecord::ConnectionAdapters::MysqlAdapter&quot;, &quot;ActiveRecord::ConnectionAdapters::MysqlplusAdapter&quot;
-          ThinkingSphinx::MysqlAdapter
-        when &quot;ActiveRecord::ConnectionAdapters::PostgreSQLAdapter&quot;
-          ThinkingSphinx::PostgreSQLAdapter
-        else
-          raise &quot;Invalid Database Adapter: Sphinx only supports MySQL and PostgreSQL&quot;
-        end
-      end
-      
-      protected
+    def initialize(model)
+      @model = model
+    end
+    
+    def setup
+      # Deliberately blank - subclasses should do something though. Well, if
+      # they need to.
+    end
       
-      def connection
-        @connection ||= ::ActiveRecord::Base.connection
+    def self.detect(model)
+      case model.connection.class.name
+      when &quot;ActiveRecord::ConnectionAdapters::MysqlAdapter&quot;,
+           &quot;ActiveRecord::ConnectionAdapters::MysqlplusAdapter&quot;
+        ThinkingSphinx::MysqlAdapter.new model
+      when &quot;ActiveRecord::ConnectionAdapters::PostgreSQLAdapter&quot;
+        ThinkingSphinx::PostgreSQLAdapter.new model
+      else
+        raise &quot;Invalid Database Adapter: Sphinx only supports MySQL and PostgreSQL, not #{model.connection.class.name}&quot;
       end
     end
+    
+    def quote_with_table(column)
+      &quot;#{@model.quoted_table_name}.#{@model.connection.quote_column_name(column)}&quot;
+    end
+    
+    protected
+    
+    def connection
+      @connection ||= @model.connection
+    end
   end
-end
\ No newline at end of file
+end</diff>
      <filename>lib/thinking_sphinx/adapters/abstract_adapter.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,9 +1,53 @@
 module ThinkingSphinx
   class MysqlAdapter &lt; AbstractAdapter
-    class &lt;&lt; self
-      def setup
-        # Does MySQL actually need to do anything?
-      end
+    def setup
+      # Does MySQL actually need to do anything?
+    end
+    
+    def sphinx_identifier
+      &quot;mysql&quot;
+    end
+    
+    def concatenate(clause, separator = ' ')
+      &quot;CONCAT_WS('#{separator}', #{clause})&quot;
+    end
+    
+    def group_concatenate(clause, separator = ' ')
+      &quot;GROUP_CONCAT(DISTINCT #{clause} SEPARATOR '#{separator}')&quot;
+    end
+    
+    def cast_to_string(clause)
+      &quot;CAST(#{clause} AS CHAR)&quot;
+    end
+    
+    def cast_to_datetime(clause)
+      &quot;UNIX_TIMESTAMP(#{clause})&quot;
+    end
+    
+    def cast_to_unsigned(clause)
+      &quot;CAST(#{clause} AS UNSIGNED)&quot;
+    end
+    
+    def convert_nulls(clause, default = '')
+      default = &quot;'#{default}'&quot; if default.is_a?(String)
+      
+      &quot;IFNULL(#{clause}, #{default})&quot;
+    end
+    
+    def boolean(value)
+      value ? 1 : 0
+    end
+    
+    def crc(clause)
+      &quot;CRC32(#{clause})&quot;
+    end
+    
+    def utf8_query_pre
+      &quot;SET NAMES utf8&quot;
+    end
+    
+    def time_difference(diff)
+      &quot;DATE_SUB(NOW(), INTERVAL #{diff} SECOND)&quot;
     end
   end
 end
\ No newline at end of file</diff>
      <filename>lib/thinking_sphinx/adapters/mysql_adapter.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,83 +1,129 @@
 module ThinkingSphinx
   class PostgreSQLAdapter &lt; AbstractAdapter
-    class &lt;&lt; self
-      def setup
-        create_array_accum_function
-        create_crc32_function
-      end
-      
-      private
+    def setup
+      create_array_accum_function
+      create_crc32_function
+    end
+    
+    def sphinx_identifier
+      &quot;pgsql&quot;
+    end
+    
+    def concatenate(clause, separator = ' ')
+      clause.split(', ').collect { |field|
+        &quot;COALESCE(CAST(#{field} as varchar), '')&quot;
+      }.join(&quot; || '#{separator}' || &quot;)
+    end
+    
+    def group_concatenate(clause, separator = ' ')
+      &quot;array_to_string(array_accum(#{clause}), '#{separator}')&quot;
+    end
+    
+    def cast_to_string(clause)
+      clause
+    end
+    
+    def cast_to_datetime(clause)
+      &quot;cast(extract(epoch from #{clause}) as int)&quot;
+    end
+    
+    def cast_to_unsigned(clause)
+      clause
+    end
+    
+    def convert_nulls(clause, default = '')
+      default = &quot;'#{default}'&quot; if default.is_a?(String)
       
-      def execute(command, output_error = false)
-        connection.execute &quot;begin&quot;
-        connection.execute &quot;savepoint ts&quot;
-        begin
-          connection.execute command
-        rescue StandardError =&gt; err
-          puts err if output_error
-          connection.execute &quot;rollback to savepoint ts&quot;
-        end
-        connection.execute &quot;release savepoint ts&quot;
-        connection.execute &quot;commit&quot;
+      &quot;COALESCE(#{clause}, #{default})&quot;
+    end
+    
+    def boolean(value)
+      value ? 'TRUE' : 'FALSE'
+    end
+    
+    def crc(clause)
+      &quot;crc32(#{clause})&quot;
+    end
+    
+    def utf8_query_pre
+      nil
+    end
+    
+    def time_difference(diff)
+      &quot;current_timestamp - interval '#{diff} seconds'&quot;
+    end
+    
+    private
+    
+    def execute(command, output_error = false)
+      connection.execute &quot;begin&quot;
+      connection.execute &quot;savepoint ts&quot;
+      begin
+        connection.execute command
+      rescue StandardError =&gt; err
+        puts err if output_error
+        connection.execute &quot;rollback to savepoint ts&quot;
       end
-      
-      def create_array_accum_function
-        if connection.raw_connection.server_version &gt; 80200
-          execute &lt;&lt;-SQL
-            CREATE AGGREGATE array_accum (anyelement)
-            (
-                sfunc = array_append,
-                stype = anyarray,
-                initcond = '{}'
-            );
-          SQL
-        else
-          execute &lt;&lt;-SQL
-            CREATE AGGREGATE array_accum
-            (
-                basetype = anyelement,
-                sfunc = array_append,
-                stype = anyarray,
-                initcond = '{}'
-            );
-          SQL
-        end
+      connection.execute &quot;release savepoint ts&quot;
+      connection.execute &quot;commit&quot;
+    end
+    
+    def create_array_accum_function
+      if connection.raw_connection.respond_to?(:server_version) &amp;&amp; connection.raw_connection.server_version &gt; 80200
+        execute &lt;&lt;-SQL
+          CREATE AGGREGATE array_accum (anyelement)
+          (
+              sfunc = array_append,
+              stype = anyarray,
+              initcond = '{}'
+          );
+        SQL
+      else
+        execute &lt;&lt;-SQL
+          CREATE AGGREGATE array_accum
+          (
+              basetype = anyelement,
+              sfunc = array_append,
+              stype = anyarray,
+              initcond = '{}'
+          );
+        SQL
       end
-      
-      def create_crc32_function
-        execute &quot;CREATE LANGUAGE 'plpgsql';&quot;
-        function = &lt;&lt;-SQL
-          CREATE OR REPLACE FUNCTION crc32(word text)
-          RETURNS bigint AS $$
-            DECLARE tmp bigint;
-            DECLARE i int;
-            DECLARE j int;
-            DECLARE word_array bytea;
-            BEGIN
-              i = 0;
-              tmp = 4294967295;
-              word_array = decode(replace(word, E'\\\\', E'\\\\\\\\'), 'escape');
+    end
+    
+    def create_crc32_function
+      execute &quot;CREATE LANGUAGE 'plpgsql';&quot;
+      function = &lt;&lt;-SQL
+        CREATE OR REPLACE FUNCTION crc32(word text)
+        RETURNS bigint AS $$
+          DECLARE tmp bigint;
+          DECLARE i int;
+          DECLARE j int;
+          DECLARE word_array bytea;
+          BEGIN
+            i = 0;
+            tmp = 4294967295;
+            word_array = decode(replace(word, E'\\\\', E'\\\\\\\\'), 'escape');
+            LOOP
+              tmp = (tmp # get_byte(word_array, i))::bigint;
+              i = i + 1;
+              j = 0;
               LOOP
-                tmp = (tmp # get_byte(word_array, i))::bigint;
-                i = i + 1;
-                j = 0;
-                LOOP
-                  tmp = ((tmp &gt;&gt; 1) # (3988292384 * (tmp &amp; 1)))::bigint;
-                  j = j + 1;
-                  IF j &gt;= 8 THEN
-                    EXIT;
-                  END IF;
-                END LOOP;
-                IF i &gt;= char_length(word) THEN
+                tmp = ((tmp &gt;&gt; 1) # (3988292384 * (tmp &amp; 1)))::bigint;
+                j = j + 1;
+                IF j &gt;= 8 THEN
                   EXIT;
                 END IF;
               END LOOP;
-              return (tmp # 4294967295);
-            END
-          $$ IMMUTABLE STRICT LANGUAGE plpgsql;
-        SQL
-        execute function, true
-      end
+              IF i &gt;= char_length(word) THEN
+                EXIT;
+              END IF;
+            END LOOP;
+            return (tmp # 4294967295);
+          END
+        $$ IMMUTABLE STRICT LANGUAGE plpgsql;
+      SQL
+      execute function, true
     end
   end
 end
\ No newline at end of file</diff>
      <filename>lib/thinking_sphinx/adapters/postgresql_adapter.rb</filename>
    </modified>
    <modified>
      <diff>@@ -9,7 +9,7 @@ module ThinkingSphinx
   # associations. Which can get messy. Use Index.link!, it really helps.
   # 
   class Attribute
-    attr_accessor :alias, :columns, :associations, :model
+    attr_accessor :alias, :columns, :associations, :model, :faceted
     
     # To create a new attribute, you'll need to pass in either a single Column
     # or an array of them, and some (optional) options.
@@ -78,10 +78,10 @@ module ThinkingSphinx
       
       separator = all_ints? ? ',' : ' '
       
-      clause = concatenate(clause, separator)       if concat_ws?
-      clause = group_concatenate(clause, separator) if is_many?
-      clause = cast_to_datetime(clause)             if type == :datetime
-      clause = convert_nulls(clause)                if type == :string
+      clause = adapter.concatenate(clause, separator)       if concat_ws?
+      clause = adapter.group_concatenate(clause, separator) if is_many?
+      clause = adapter.cast_to_datetime(clause)             if type == :datetime
+      clause = adapter.convert_nulls(clause)                if type == :string
       
       &quot;#{clause} AS #{quote_column(unique_name)}&quot;
     end
@@ -135,63 +135,31 @@ module ThinkingSphinx
       end
     end
     
-    private
-    
-    def concatenate(clause, separator = ' ')
-      case @model.connection.class.name
-      when &quot;ActiveRecord::ConnectionAdapters::MysqlAdapter&quot;, &quot;ActiveRecord::ConnectionAdapters::MysqlplusAdapter&quot;
-        &quot;CONCAT_WS('#{separator}', #{clause})&quot;
-      when &quot;ActiveRecord::ConnectionAdapters::PostgreSQLAdapter&quot;
-        clause.split(', ').collect { |attribute|
-          &quot;COALESCE(#{attribute}, '')&quot;
-        }.join(&quot; || ' ' || &quot;)
-      else
-        clause
-      end
-    end
-    
-    def group_concatenate(clause, separator = ' ')
-      case @model.connection.class.name
-      when &quot;ActiveRecord::ConnectionAdapters::MysqlAdapter&quot;, &quot;ActiveRecord::ConnectionAdapters::MysqlplusAdapter&quot;
-        &quot;GROUP_CONCAT(#{clause} SEPARATOR '#{separator}')&quot;
-      when &quot;ActiveRecord::ConnectionAdapters::PostgreSQLAdapter&quot;
-        &quot;array_to_string(array_accum(#{clause}), '#{separator}')&quot;
+    # Returns the type of the column. If that's not already set, it returns
+    # :multi if there's the possibility of more than one value, :string if
+    # there's more than one association, otherwise it figures out what the
+    # actual column's datatype is and returns that.
+    def type
+      @type ||= case
+      when is_many?, is_many_ints?
+        :multi
+      when @associations.values.flatten.length &gt; 1
+        :string
       else
-        clause
+        translated_type_from_database
       end
     end
     
-    def cast_to_string(clause)
-      case @model.connection.class.name
-      when &quot;ActiveRecord::ConnectionAdapters::MysqlAdapter&quot;, &quot;ActiveRecord::ConnectionAdapters::MysqlplusAdapter&quot;
-        &quot;CAST(#{clause} AS CHAR)&quot;
-      when &quot;ActiveRecord::ConnectionAdapters::PostgreSQLAdapter&quot;
-        clause
-      else
-        clause
-      end
+    def to_facet
+      return nil unless @faceted
+      
+      ThinkingSphinx::Facet.new(self)
     end
     
-    def cast_to_datetime(clause)
-      case @model.connection.class.name
-      when &quot;ActiveRecord::ConnectionAdapters::MysqlAdapter&quot;, &quot;ActiveRecord::ConnectionAdapters::MysqlplusAdapter&quot;
-        &quot;UNIX_TIMESTAMP(#{clause})&quot;
-      when &quot;ActiveRecord::ConnectionAdapters::PostgreSQLAdapter&quot;
-        &quot;cast(extract(epoch from #{clause}) as int)&quot;
-      else
-        clause
-      end
-    end
+    private
     
-    def convert_nulls(clause)
-      case @model.connection.class.name
-      when &quot;ActiveRecord::ConnectionAdapters::MysqlAdapter&quot;, &quot;ActiveRecord::ConnectionAdapters::MysqlplusAdapter&quot;
-        &quot;IFNULL(#{clause}, '')&quot;
-      when &quot;ActiveRecord::ConnectionAdapters::PostgreSQLAdapter&quot;
-        &quot;COALESCE(#{clause}, '')&quot;
-      else
-        clause
-      end
+    def adapter
+      @adapter ||= @model.sphinx_database_adapter
     end
     
     def quote_column(column)
@@ -205,16 +173,7 @@ module ThinkingSphinx
     def concat_ws?
       multiple_associations? || @columns.length &gt; 1
     end
-    
-    # Checks the association tree for each column - if they're all the same,
-    # returns false.
-    # 
-    def multiple_sources?
-      first = associations[@columns.first]
-      
-      !@columns.all? { |col| associations[col] == first }
-    end
-    
+        
     # Checks whether any column requires multiple associations (which only
     # happens for polymorphic situations).
     # 
@@ -248,6 +207,10 @@ module ThinkingSphinx
       associations.values.flatten.any? { |assoc| assoc.is_many? }
     end
     
+    def is_many_ints?
+      concat_ws? &amp;&amp; all_ints?
+    end
+    
     # Returns true if any of the columns are string values, instead of database
     # column references.
     def is_string?
@@ -342,4 +305,4 @@ explicitly convert the column's value in your define_index block:
       end
     end
   end
-end
\ No newline at end of file
+end</diff>
      <filename>lib/thinking_sphinx/attribute.rb</filename>
    </modified>
    <modified>
      <diff>@@ -33,40 +33,60 @@ module ThinkingSphinx
     end
     
     def self.instances_from_matches(matches, options = {})
-      return matches.collect { |match|
-        instance_from_match match, options
-      } unless klass = options[:class]
-      
+      if klass = options[:class]
+        instances_from_class klass, matches, options
+      else
+        instances_from_classes matches, options
+      end
+    end
+    
+    def self.instances_from_class(klass, matches, options = {})
       index_options = klass.sphinx_index_options
-      
+
       ids = matches.collect { |match| match[:attributes][&quot;sphinx_internal_id&quot;] }
       instances = ids.length &gt; 0 ? klass.find(
         :all,
         :conditions =&gt; {klass.primary_key.to_sym =&gt; ids},
         :include    =&gt; (options[:include] || index_options[:include]),
-        :select     =&gt; (options[:select] || index_options[:select])
+        :select     =&gt; (options[:select]  || index_options[:select]),
+        :order      =&gt; (options[:sql_order] || index_options[:sql_order])
       ) : []
-      
-      # Raise an exception if we find records in Sphinx but not in the DB, so the search method
-      # can retry without them. See ThinkingSphinx::Search.retry_search_on_stale_index.
+
+      # Raise an exception if we find records in Sphinx but not in the DB, so
+      # the search method can retry without them. See 
+      # ThinkingSphinx::Search.retry_search_on_stale_index.
       if options[:raise_on_stale] &amp;&amp; instances.length &lt; ids.length
         stale_ids = ids - instances.map {|i| i.id }
         raise StaleIdsException, stale_ids
       end
 
+      # if the user has specified an SQL order, return the collection
+      # without rearranging it into the Sphinx order
+      return instances if options[:sql_order]
+
       ids.collect { |obj_id|
         instances.detect { |obj| obj.id == obj_id }
       }
     end
     
-    def self.instance_from_match(match, options)
-      class_from_crc(match[:attributes][&quot;class_crc&quot;]).find(
-        match[:attributes][&quot;sphinx_internal_id&quot;],
-        :include =&gt; options[:include],
-        :select  =&gt; options[:select]
-      )
-    rescue ::ActiveRecord::RecordNotFound
-      nil
+    # Group results by class and call #find(:all) once for each group to reduce
+    # the number of #find's in multi-model searches.
+    # 
+    def self.instances_from_classes(matches, options = {})
+      groups = matches.group_by { |match| match[:attributes][&quot;class_crc&quot;] }
+      groups.each do |crc, group|
+        group.replace(
+          instances_from_class(class_from_crc(crc), group, options)
+        )
+      end
+      
+      matches.collect do |match|
+        groups.detect { |crc, group|
+          crc == match[:attributes][&quot;class_crc&quot;]
+        }[1].detect { |obj|
+          obj.id == match[:attributes][&quot;sphinx_internal_id&quot;]
+        }
+      end
     end
     
     def self.class_from_crc(crc)
@@ -98,9 +118,9 @@ module ThinkingSphinx
       each_with_attribute method.to_s.gsub(/^each_with_/, ''), &amp;block
     end
     
-    def each_with_group_and_count(&amp;block)
+    def each_with_groupby_and_count(&amp;block)
       results[:matches].each_with_index do |match, index|
-        yield self[index], match[:attributes][&quot;@group&quot;], match[:attributes][&quot;@count&quot;]
+        yield self[index], match[:attributes][&quot;@groupby&quot;], match[:attributes][&quot;@count&quot;]
       end
     end
     
@@ -115,5 +135,13 @@ module ThinkingSphinx
         yield self[index], match[:weight]
       end
     end
+    
+    def inject_with_groupby_and_count(initial = nil, &amp;block)
+      index = -1
+      results[:matches].inject(initial) do |memo, match|
+        index += 1
+        yield memo, self[index], match[:attributes][&quot;@groupby&quot;], match[:attributes][&quot;@count&quot;]
+      end
+    end
   end
-end
\ No newline at end of file
+end</diff>
      <filename>lib/thinking_sphinx/collection.rb</filename>
    </modified>
    <modified>
      <diff>@@ -164,31 +164,6 @@ module ThinkingSphinx
       end
     end
     
-    def hash_to_config(hash)
-      hash.collect { |key, value|
-        translated_value = case value
-        when TrueClass
-          &quot;1&quot;
-        when FalseClass
-          &quot;0&quot;
-        when NilClass, &quot;&quot;
-          next
-        else
-          value
-        end
-        &quot;  #{key} = #{translated_value}&quot;
-      }.join(&quot;\n&quot;)
-    end
-    
-    def self.options_merge(base, extra)
-      base = base.clone
-      extra.each do |key, value|
-        next if value.nil? || value == &quot;&quot;
-        base[key] = value
-      end
-      base
-    end
-    
     def address
       @configuration.searchd.address
     end</diff>
      <filename>lib/thinking_sphinx/configuration.rb</filename>
    </modified>
    <modified>
      <diff>@@ -40,22 +40,11 @@ module ThinkingSphinx
       def clause(model, toggled)
         if toggled
           &quot;#{model.quoted_table_name}.#{@index.quote_column(@column.to_s)}&quot; +
-          &quot; &gt; #{time_difference}&quot;
+          &quot; &gt; #{adapter.time_difference(@threshold)}&quot;
         else
           nil
         end
       end
-      
-      def time_difference
-        case @index.adapter
-        when :mysql
-          &quot;DATE_SUB(NOW(), INTERVAL #{@threshold} SECOND)&quot;
-        when :postgres
-          &quot;current_timestamp - interval '#{@threshold} seconds'&quot;
-        else
-          raise &quot;Unknown Database Adapter&quot;
-        end
-      end
     end
   end
 end</diff>
      <filename>lib/thinking_sphinx/deltas/datetime_delta.rb</filename>
    </modified>
    <modified>
      <diff>@@ -37,12 +37,12 @@ module ThinkingSphinx
       
       def reset_query(model)
         &quot;UPDATE #{model.quoted_table_name} SET &quot; +
-        &quot;#{@index.quote_column(@column.to_s)} = #{@index.db_boolean(false)}&quot;
+        &quot;#{@index.quote_column(@column.to_s)} = #{adapter.boolean(false)}&quot;
       end
       
       def clause(model, toggled)
         &quot;#{model.quoted_table_name}.#{@index.quote_column(@column.to_s)}&quot; +
-        &quot; = #{@index.db_boolean(toggled)}&quot;
+        &quot; = #{adapter.boolean(toggled)}&quot;
       end
       
       protected
@@ -53,7 +53,13 @@ module ThinkingSphinx
       
       def delta_index_name(model)
         &quot;#{model.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_delta&quot;
-      end      
+      end  
+      
+      private
+      
+      def adapter
+        @adapter = @index.model.sphinx_database_adapter
+      end
     end
   end
 end</diff>
      <filename>lib/thinking_sphinx/deltas/default_delta.rb</filename>
    </modified>
    <modified>
      <diff>@@ -8,7 +8,8 @@ module ThinkingSphinx
   # associations. Which can get messy. Use Index.link!, it really helps.
   # 
   class Field
-    attr_accessor :alias, :columns, :sortable, :associations, :model, :infixes, :prefixes
+    attr_accessor :alias, :columns, :sortable, :associations, :model, :infixes,
+      :prefixes, :faceted
     
     # To create a new field, you'll need to pass in either a single Column
     # or an array of them, and some (optional) options. The columns are
@@ -58,10 +59,11 @@ module ThinkingSphinx
 
       raise &quot;Cannot define a field with no columns. Maybe you are trying to index a field with a reserved name (id, name). You can fix this error by using a symbol rather than a bare name (:id instead of id).&quot; if @columns.empty? || @columns.any? { |column| !column.respond_to?(:__stack) }
       
-      @alias        = options[:as]
-      @sortable     = options[:sortable] || false
-      @infixes      = options[:infixes]  || false
-      @prefixes     = options[:prefixes] || false
+      @alias    = options[:as]
+      @sortable = options[:sortable] || false
+      @infixes  = options[:infixes]  || false
+      @prefixes = options[:prefixes] || false
+      @faceted  = options[:facet]    || false
     end
     
     # Get the part of the SELECT clause related to this field. Don't forget
@@ -75,10 +77,10 @@ module ThinkingSphinx
         column_with_prefix(column)
       }.join(', ')
       
-      clause = concatenate(clause) if concat_ws?
-      clause = group_concatenate(clause) if is_many?
+      clause = adapter.concatenate(clause) if concat_ws?
+      clause = adapter.group_concatenate(clause) if is_many?
       
-      &quot;#{cast_to_string clause } AS #{quote_column(unique_name)}&quot;
+      &quot;#{adapter.cast_to_string clause } AS #{quote_column(unique_name)}&quot;
     end
     
     # Get the part of the GROUP BY clause related to this field - if one is
@@ -110,31 +112,17 @@ module ThinkingSphinx
       end
     end
     
-    private
-    
-    def concatenate(clause)
-      case @model.connection.class.name
-      when &quot;ActiveRecord::ConnectionAdapters::MysqlAdapter&quot;, &quot;ActiveRecord::ConnectionAdapters::MysqlplusAdapter&quot;
-        &quot;CONCAT_WS(' ', #{clause})&quot;
-      when &quot;ActiveRecord::ConnectionAdapters::PostgreSQLAdapter&quot;
-        clause.split(', ').collect { |field|
-          &quot;COALESCE(#{field}, '')&quot;
-        }.join(&quot; || ' ' || &quot;)
-      else
-        clause
-      end
+    def to_facet
+      return nil unless @faceted
+      
+      ThinkingSphinx::Facet.new(self)
     end
     
-    def group_concatenate(clause)
-      case @model.connection.class.name
-      when &quot;ActiveRecord::ConnectionAdapters::MysqlAdapter&quot;, &quot;ActiveRecord::ConnectionAdapters::MysqlplusAdapter&quot;
-        &quot;GROUP_CONCAT(#{clause} SEPARATOR ' ')&quot;
-      when &quot;ActiveRecord::ConnectionAdapters::PostgreSQLAdapter&quot;
-        &quot;array_to_string(array_accum(#{clause}), ' ')&quot;
-      else
-        clause
-      end
-    end
+    private
+    
+    def adapter
+      @adapter ||= @model.sphinx_database_adapter
+    end    
     
     def cast_to_string(clause)
       case @model.connection.class.name
@@ -158,16 +146,7 @@ module ThinkingSphinx
     def concat_ws?
       @columns.length &gt; 1 || multiple_associations?
     end
-    
-    # Checks the association tree for each column - if they're all the same,
-    # returns false.
-    # 
-    def multiple_sources?
-      first = associations[@columns.first]
-      
-      !@columns.all? { |col| associations[col] == first }
-    end
-    
+        
     # Checks whether any column requires multiple associations (which only
     # happens for polymorphic situations).
     # </diff>
      <filename>lib/thinking_sphinx/field.rb</filename>
    </modified>
    <modified>
      <diff>@@ -46,18 +46,13 @@ module ThinkingSphinx
     def self.name(model)
       model.name.underscore.tr(':/\\', '_')
     end
-        
-    def to_riddle(model, index, offset)
-      add_internal_attributes
-      link!
-    end
     
     def to_riddle_for_core(offset, index)
       add_internal_attributes
       link!
       
       source = Riddle::Configuration::SQLSource.new(
-        &quot;#{name}_core_#{index}&quot;, riddle_adapter
+        &quot;#{name}_core_#{index}&quot;, adapter.sphinx_identifier
       )
       
       set_source_database_settings  source
@@ -73,7 +68,7 @@ module ThinkingSphinx
       link!
       
       source = Riddle::Configuration::SQLSource.new(
-        &quot;#{name}_delta_#{index}&quot;, riddle_adapter
+        &quot;#{name}_delta_#{index}&quot;, adapter.sphinx_identifier
       )
       source.parent = &quot;#{name}_core_#{index}&quot;
       
@@ -111,92 +106,6 @@ module ThinkingSphinx
       }
     end
     
-    # Generates the big SQL statement to get the data back for all the fields
-    # and attributes, using all the relevant association joins. If you want
-    # the version filtered for delta values, send through :delta =&gt; true in the
-    # options. Won't do much though if the index isn't set up to support a
-    # delta sibling.
-    # 
-    # Examples:
-    # 
-    #   index.to_sql
-    #   index.to_sql(:delta =&gt; true)
-    #
-    def to_sql(options={})
-      assocs = all_associations
-      
-      where_clause = &quot;&quot;
-      if self.delta? &amp;&amp; !@delta_object.clause(@model, options[:delta]).blank?
-        where_clause &lt;&lt; &quot; AND #{@delta_object.clause(@model, options[:delta])}&quot;
-      end
-      unless @conditions.empty?
-        where_clause &lt;&lt; &quot; AND &quot; &lt;&lt; @conditions.join(&quot; AND &quot;)
-      end
-      
-      internal_groupings = []
-      if @model.column_names.include?(@model.inheritance_column)
-         internal_groupings &lt;&lt; &quot;#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)}&quot;
-      end
-      
-      unique_id_expr = &quot;* #{ThinkingSphinx.indexed_models.size} + #{options[:offset] || 0}&quot;
-      
-      sql = &lt;&lt;-SQL
-SELECT #{ (
-  [&quot;#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} #{unique_id_expr} AS #{quote_column(@model.primary_key)} &quot;] + 
-  @fields.collect { |field| field.to_select_sql } +
-  @attributes.collect { |attribute| attribute.to_select_sql }
-).join(&quot;, &quot;) }
-FROM #{ @model.table_name }
-  #{ assocs.collect { |assoc| assoc.to_sql }.join(' ') }
-WHERE #{@model.quoted_table_name}.#{quote_column(@model.primary_key)} &gt;= $start
-  AND #{@model.quoted_table_name}.#{quote_column(@model.primary_key)} &lt;= $end
-  #{ where_clause }
-GROUP BY #{ (
-  [&quot;#{@model.quoted_table_name}.#{quote_column(@model.primary_key)}&quot;] + 
-  @fields.collect { |field| field.to_group_sql }.compact +
-  @attributes.collect { |attribute| attribute.to_group_sql }.compact +
-  @groupings + internal_groupings
-).join(&quot;, &quot;) }
-      SQL
-      
-      if @model.connection.class.name == &quot;ActiveRecord::ConnectionAdapters::MysqlAdapter&quot; || @model.connection.class.name == &quot;ActiveRecord::ConnectionAdapters::MysqlplusAdapter&quot;
-        sql += &quot; ORDER BY NULL&quot;
-      end
-      
-      sql
-    end
-    
-    # Simple helper method for the query info SQL - which is a statement that
-    # returns the single row for a corresponding id.
-    # 
-    def to_sql_query_info(offset)
-      &quot;SELECT * FROM #{@model.quoted_table_name} WHERE &quot; +
-      &quot; #{quote_column(@model.primary_key)} = (($id - #{offset}) / #{ThinkingSphinx.indexed_models.size})&quot;
-    end
-    
-    # Simple helper method for the query range SQL - which is a statement that
-    # returns minimum and maximum id values. These can be filtered by delta -
-    # so pass in :delta =&gt; true to get the delta version of the SQL.
-    # 
-    def to_sql_query_range(options={})
-      min_statement = &quot;MIN(#{quote_column(@model.primary_key)})&quot;
-      max_statement = &quot;MAX(#{quote_column(@model.primary_key)})&quot;
-      
-      # Fix to handle Sphinx PostgreSQL bug (it doesn't like NULLs or 0's)
-      if adapter == :postgres
-        min_statement = &quot;COALESCE(#{min_statement}, 1)&quot;
-        max_statement = &quot;COALESCE(#{max_statement}, 1)&quot;
-      end
-      
-      sql = &quot;SELECT #{min_statement}, #{max_statement} &quot; +
-            &quot;FROM #{@model.quoted_table_name} &quot;
-      if self.delta? &amp;&amp; !@delta_object.clause(@model, options[:delta]).blank?
-        sql &lt;&lt; &quot;WHERE #{@delta_object.clause(@model, options[:delta])}&quot;
-      end
-      
-      sql
-    end
-    
     # Flag to indicate whether this index has a corresponding delta index.
     #
     def delta?
@@ -204,14 +113,7 @@ GROUP BY #{ (
     end
     
     def adapter
-      @adapter ||= case @model.connection.class.name
-      when &quot;ActiveRecord::ConnectionAdapters::MysqlAdapter&quot;, &quot;ActiveRecord::ConnectionAdapters::MysqlplusAdapter&quot;
-        :mysql
-      when &quot;ActiveRecord::ConnectionAdapters::PostgreSQLAdapter&quot;
-        :postgres
-      else
-        raise &quot;Invalid Database Adapter: Sphinx only supports MySQL and PostgreSQL&quot;
-      end
+      @adapter ||= @model.sphinx_database_adapter
     end
         
     def prefix_fields
@@ -229,30 +131,11 @@ GROUP BY #{ (
       }.each { |key| all_index_options[key.to_sym] = @options[key] }
       all_index_options
     end
-    
-    def source_options
-      all_source_options = ThinkingSphinx::Configuration.instance.source_options.clone
-      @options.keys.select { |key|
-        ThinkingSphinx::Configuration::SourceOptions.include?(key.to_s)
-      }.each { |key| all_source_options[key.to_sym] = @options[key] }
-      all_source_options
-    end
-    
+        
     def quote_column(column)
       @model.connection.quote_column_name(column)
     end
     
-    # Returns the proper boolean value string literal for the
-    # current database adapter.
-    #
-    def db_boolean(val)
-      if adapter == :postgres
-        val ? 'TRUE' : 'FALSE'
-      else
-        val ? '1' : '0'
-      end
-    end
-    
     private
     
     def utf8?
@@ -276,14 +159,23 @@ GROUP BY #{ (
         stored_class = @model.store_full_sti_class ? @model.name : @model.name.demodulize
         builder.where(&quot;#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)} = '#{stored_class}'&quot;)
       end
-
-      @fields       = builder.fields
-      @attributes   = builder.attributes
+      
+      set_model = Proc.new { |item| item.model = @model }
+      
+      @fields       = builder.fields &amp;set_model
+      @attributes   = builder.attributes.each &amp;set_model
       @conditions   = builder.conditions
       @groupings    = builder.groupings
       @delta_object = ThinkingSphinx::Deltas.parse self, builder.properties
       @options      = builder.properties
       
+      is_faceted = Proc.new { |item| item.faceted }
+      add_facet  = Proc.new { |item| @model.sphinx_facets &lt;&lt; item.to_facet }
+      
+      @model.sphinx_facets ||= []
+      @fields.select(    &amp;is_faceted).each &amp;add_facet
+      @attributes.select(&amp;is_faceted).each &amp;add_facet
+      
       # We want to make sure that if the database doesn't exist, then Thinking
       # Sphinx doesn't mind when running non-TS tasks (like db:create, db:drop
       # and db:migrate). It's a bit hacky, but I can't think of a better way.
@@ -345,12 +237,10 @@ GROUP BY #{ (
 
     def crc_column
       if @model.column_names.include?(@model.inheritance_column)
-        case adapter
-        when :postgres
-          &quot;COALESCE(crc32(#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)}), #{@model.to_crc32.to_s})&quot;
-        when :mysql
-          &quot;CAST(IFNULL(CRC32(#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)}), #{@model.to_crc32.to_s}) AS UNSIGNED)&quot;
-        end
+        adapter.cast_to_unsigned(adapter.convert_nulls(
+          adapter.crc(adapter.quote_with_table(@model.inheritance_column)),
+          @model.to_crc32
+        ))
       else
         @model.to_crc32.to_s
       end
@@ -383,24 +273,13 @@ GROUP BY #{ (
         :as   =&gt; :sphinx_deleted
       ) unless @attributes.detect { |attr| attr.alias == :sphinx_deleted }
     end
-    
-    def riddle_adapter
-      case adapter
-      when :postgres
-        &quot;pgsql&quot;
-      when :mysql
-        &quot;mysql&quot;
-      else
-        raise &quot;Unsupported Database Adapter: Sphinx only supports MySQL and PosgreSQL&quot;
-      end
-    end
-    
+        
     def set_source_database_settings(source)
       config = @model.connection.instance_variable_get(:@config)
       
-      source.sql_host = config[:host]      || &quot;localhost&quot;
-      source.sql_user = config[:username]  || config[:user]
-      source.sql_pass = (config[:password] || &quot;&quot;).gsub('#', '\#')
+      source.sql_host = config[:host]           || &quot;localhost&quot;
+      source.sql_user = config[:username]       || config[:user] || &quot;&quot;
+      source.sql_pass = (config[:password].to_s || &quot;&quot;).gsub('#', '\#')
       source.sql_db   = config[:database]
       source.sql_port = config[:port]
       source.sql_sock = config[:socket]
@@ -423,9 +302,7 @@ GROUP BY #{ (
         source.sql_query_pre &lt;&lt; &quot;SET SESSION group_concat_max_len = #{@options[:group_concat_max_len]}&quot;
       end
       
-      if utf8? &amp;&amp; adapter == :mysql
-        source.sql_query_pre &lt;&lt; &quot;SET NAMES utf8&quot;
-      end
+      source.sql_query_pre += [adapter.utf8_query_pre].compact if utf8?
     end
     
     def set_source_settings(source)
@@ -449,5 +326,89 @@ GROUP BY #{ (
     def sql_query_pre_for_delta
       [&quot;&quot;]
     end
+    
+    # Generates the big SQL statement to get the data back for all the fields
+    # and attributes, using all the relevant association joins. If you want
+    # the version filtered for delta values, send through :delta =&gt; true in the
+    # options. Won't do much though if the index isn't set up to support a
+    # delta sibling.
+    # 
+    # Examples:
+    # 
+    #   index.to_sql
+    #   index.to_sql(:delta =&gt; true)
+    #
+    def to_sql(options={})
+      assocs = all_associations
+      
+      where_clause = &quot;&quot;
+      if self.delta? &amp;&amp; !@delta_object.clause(@model, options[:delta]).blank?
+        where_clause &lt;&lt; &quot; AND #{@delta_object.clause(@model, options[:delta])}&quot;
+      end
+      unless @conditions.empty?
+        where_clause &lt;&lt; &quot; AND &quot; &lt;&lt; @conditions.join(&quot; AND &quot;)
+      end
+      
+      internal_groupings = []
+      if @model.column_names.include?(@model.inheritance_column)
+         internal_groupings &lt;&lt; &quot;#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)}&quot;
+      end
+      
+      unique_id_expr = &quot;* #{ThinkingSphinx.indexed_models.size} + #{options[:offset] || 0}&quot;
+      
+      sql = &lt;&lt;-SQL
+SELECT #{ (
+  [&quot;#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} #{unique_id_expr} AS #{quote_column(@model.primary_key)} &quot;] + 
+  @fields.collect { |field| field.to_select_sql } +
+  @attributes.collect { |attribute| attribute.to_select_sql }
+).join(&quot;, &quot;) }
+FROM #{ @model.table_name }
+  #{ assocs.collect { |assoc| assoc.to_sql }.join(' ') }
+WHERE #{@model.quoted_table_name}.#{quote_column(@model.primary_key)} &gt;= $start
+  AND #{@model.quoted_table_name}.#{quote_column(@model.primary_key)} &lt;= $end
+  #{ where_clause }
+GROUP BY #{ (
+  [&quot;#{@model.quoted_table_name}.#{quote_column(@model.primary_key)}&quot;] + 
+  @fields.collect { |field| field.to_group_sql }.compact +
+  @attributes.collect { |attribute| attribute.to_group_sql }.compact +
+  @groupings + internal_groupings
+).join(&quot;, &quot;) }
+      SQL
+      
+      if @model.connection.class.name == &quot;ActiveRecord::ConnectionAdapters::MysqlAdapter&quot;
+        sql += &quot; ORDER BY NULL&quot;
+      end
+      
+      sql
+    end
+    
+    # Simple helper method for the query info SQL - which is a statement that
+    # returns the single row for a corresponding id.
+    # 
+    def to_sql_query_info(offset)
+      &quot;SELECT * FROM #{@model.quoted_table_name} WHERE &quot; +
+      &quot; #{quote_column(@model.primary_key)} = (($id - #{offset}) / #{ThinkingSphinx.indexed_models.size})&quot;
+    end
+    
+    # Simple helper method for the query range SQL - which is a statement that
+    # returns minimum and maximum id values. These can be filtered by delta -
+    # so pass in :delta =&gt; true to get the delta version of the SQL.
+    # 
+    def to_sql_query_range(options={})
+      min_statement = adapter.convert_nulls(
+        &quot;MIN(#{quote_column(@model.primary_key)})&quot;, 1
+      )
+      max_statement = adapter.convert_nulls(
+        &quot;MAX(#{quote_column(@model.primary_key)})&quot;, 1
+      )
+      
+      sql = &quot;SELECT #{min_statement}, #{max_statement} &quot; +
+            &quot;FROM #{@model.quoted_table_name} &quot;
+      if self.delta? &amp;&amp; !@delta_object.clause(@model, options[:delta]).blank?
+        sql &lt;&lt; &quot;WHERE #{@delta_object.clause(@model, options[:delta])}&quot;
+      end
+      
+      sql
+    end
   end
 end</diff>
      <filename>lib/thinking_sphinx/index.rb</filename>
    </modified>
    <modified>
      <diff>@@ -90,13 +90,13 @@ module ThinkingSphinx
           args.each do |columns|
             fields &lt;&lt; Field.new(FauxColumn.coerce(columns), options)
             
-            if fields.last.sortable
+            if fields.last.sortable || fields.last.faceted
               attributes &lt;&lt; Attribute.new(
                 fields.last.columns.collect { |col| col.clone },
                 options.merge(
                   :type =&gt; :string,
                   :as =&gt; fields.last.unique_name.to_s.concat(&quot;_sort&quot;).to_sym
-                )
+                ).except(:facet)
               )
             end
           end
@@ -149,6 +149,15 @@ module ThinkingSphinx
         end
         alias_method :attribute, :has
         
+        def facet(*args)
+          options = args.extract_options!
+          options[:facet] = true
+          
+          args.each do |columns|
+            attributes &lt;&lt; Attribute.new(FauxColumn.coerce(columns), options)
+          end
+        end
+        
         # Use this method to add some manual SQL conditions for your index
         # request. You can pass in as many strings as you like, they'll get
         # joined together with ANDs later on.
@@ -217,8 +226,8 @@ module ThinkingSphinx
         # 
         # Example: indexes assoc(:properties).column
         # 
-        def assoc(assoc)
-          FauxColumn.new(method)
+        def assoc(assoc, *args)
+          FauxColumn.new(assoc, *args)
         end
       end
     end</diff>
      <filename>lib/thinking_sphinx/index/builder.rb</filename>
    </modified>
    <modified>
      <diff>@@ -21,7 +21,7 @@ module ThinkingSphinx
         
         options = args.extract_options!
         page    = options[:page] ? options[:page].to_i : 1
-
+        
         ThinkingSphinx::Collection.ids_from_results(results, page, client.limit, options)
       end
 
@@ -186,6 +186,12 @@ module ThinkingSphinx
       # documentation[http://sphinxsearch.com/doc.html] for that level of
       # detail though.
       #
+      # If desired, you can sort by a column in your model instead of a sphinx
+      # field or attribute. This sort only applies to the current page, so is
+      # most useful when performing a search with a single page of results.
+      #
+      #   User.search(&quot;pat&quot;, :sql_order =&gt; &quot;name&quot;)
+      #
       # == Grouping
       # 
       # For this you can use the group_by, group_clause and group_function
@@ -352,6 +358,18 @@ module ThinkingSphinx
         end
       end
       
+      def facets(*args)
+        hash    = ThinkingSphinx::FacetCollection.new args
+        options = args.extract_options!.clone.merge! :group_function =&gt; :attr
+        
+        options[:class].sphinx_facets.inject(hash) do |hash, facet|
+          options[:group_by] = facet.attribute_name
+          
+          hash.add_from_results facet, search(*(args + [options]))
+          hash
+        end
+      end
+      
       private
       
       # This method handles the common search functionality, and returns both
@@ -377,6 +395,7 @@ module ThinkingSphinx
         
         client.limit  = options[:per_page].to_i if options[:per_page]
         page          = options[:page] ? options[:page].to_i : 1
+        page          = 1 if page &lt;= 0
         client.offset = (page - 1) * client.limit
 
         begin
@@ -457,6 +476,13 @@ module ThinkingSphinx
           Riddle::Client::Filter.new attr.to_s, filter_value(klass, attr, val), true
         } if options[:without]
         
+        # every-match attribute filters
+        client.filters += options[:with_all].collect { |attr,vals|
+          Array(vals).collect { |val|
+            Riddle::Client::Filter.new attr.to_s, filter_value(val)
+          }
+        }.flatten if options[:with_all]
+        
         # exclusive attribute filter on primary key
         client.filters += Array(options[:without_ids]).collect { |id|
           Riddle::Client::Filter.new 'sphinx_internal_id', filter_value(klass, 'sphinx_internal_id', id), true</diff>
      <filename>lib/thinking_sphinx/search.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,10 +3,12 @@ $:.unshift File.dirname(__FILE__) + '/../lib'
 require 'rubygems'
 require 'fileutils'
 require 'ginger'
+
+require 'lib/thinking_sphinx'
+
 require 'not_a_mock'
 require 'will_paginate'
 
-require 'lib/thinking_sphinx'
 require 'spec/sphinx_helper'
 
 ActiveRecord::Base.logger = Logger.new(StringIO.new)</diff>
      <filename>spec/spec_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -230,7 +230,8 @@ describe &quot;ThinkingSphinx::ActiveRecord&quot; do
 
     it &quot;should allow associations to other STI models&quot; do
       Child.sphinx_indexes.last.link!
-      sql = Child.sphinx_indexes.last.to_sql.gsub('$start', '0').gsub('$end', '100')
+      sql = Child.sphinx_indexes.last.to_riddle_for_core(0, 0).sql_query
+      sql.gsub!('$start', '0').gsub!('$end', '100')
       lambda { Child.connection.execute(sql) }.should_not raise_error(ActiveRecord::StatementInvalid)
     end
   end</diff>
      <filename>spec/unit/thinking_sphinx/active_record_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -161,7 +161,7 @@ describe ThinkingSphinx::Attribute do
     end
     
     it &quot;should return :string if there's more than one association&quot; do
-      @attribute.associations = {:a =&gt; :assoc, :b =&gt; :assoc}
+      @attribute.associations = {:a =&gt; [:assoc], :b =&gt; [:assoc]}
       @attribute.send(:type).should == :string
     end
     </diff>
      <filename>spec/unit/thinking_sphinx/attribute_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,12 +1,12 @@
 require 'spec/spec_helper'
 
 describe ThinkingSphinx::Index do
-  describe &quot;to_sql method&quot; do
+  describe &quot;generated sql_query&quot; do
     it &quot;should include explicit groupings if requested&quot; do
       @index = ThinkingSphinx::Index.new(Person)
       
       @index.groupings &lt;&lt; &quot;custom_sql&quot;
-      @index.to_sql.should match(/GROUP BY.+custom_sql/)
+      @index.to_riddle_for_core(0, 0).sql_query.should match(/GROUP BY.+custom_sql/)
     end
   end
   </diff>
      <filename>spec/unit/thinking_sphinx/index_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,22 +2,22 @@
 
 Gem::Specification.new do |s|
   s.name = %q{thinking-sphinx}
-  s.version = &quot;1.1.2&quot;
+  s.version = &quot;1.1.5&quot;
 
   s.required_rubygems_version = Gem::Requirement.new(&quot;&gt;= 0&quot;) if s.respond_to? :required_rubygems_version=
   s.authors = [&quot;Pat Allan&quot;]
-  s.date = %q{2009-01-08}
+  s.date = %q{2009-02-09}
   s.description = %q{A concise and easy-to-use Ruby library that connects ActiveRecord to the Sphinx search daemon, managing configuration, indexing and searching.}
   s.email = %q{pat@freelancing-gods.com}
-  s.files = [&quot;lib/thinking_sphinx/active_record/delta.rb&quot;, &quot;lib/thinking_sphinx/active_record/has_many_association.rb&quot;, &quot;lib/thinking_sphinx/active_record/search.rb&quot;, &quot;lib/thinking_sphinx/active_record.rb&quot;, &quot;lib/thinking_sphinx/adapters/abstract_adapter.rb&quot;, &quot;lib/thinking_sphinx/adapters/mysql_adapter.rb&quot;, &quot;lib/thinking_sphinx/adapters/postgresql_adapter.rb&quot;, &quot;lib/thinking_sphinx/association.rb&quot;, &quot;lib/thinking_sphinx/attribute.rb&quot;, &quot;lib/thinking_sphinx/collection.rb&quot;, &quot;lib/thinking_sphinx/configuration.rb&quot;, &quot;lib/thinking_sphinx/deltas/datetime_delta.rb&quot;, &quot;lib/thinking_sphinx/deltas/default_delta.rb&quot;, &quot;lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb&quot;, &quot;lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb&quot;, &quot;lib/thinking_sphinx/deltas/delayed_delta/job.rb&quot;, &quot;lib/thinking_sphinx/deltas/delayed_delta.rb&quot;, &quot;lib/thinking_sphinx/deltas.rb&quot;, &quot;lib/thinking_sphinx/field.rb&quot;, &quot;lib/thinking_sphinx/index/builder.rb&quot;, &quot;lib/thinking_sphinx/index/faux_column.rb&quot;, &quot;lib/thinking_sphinx/index.rb&quot;, &quot;lib/thinking_sphinx/rails_additions.rb&quot;, &quot;lib/thinking_sphinx/search.rb&quot;, &quot;lib/thinking_sphinx.rb&quot;, &quot;LICENCE&quot;, &quot;README&quot;, &quot;tasks/thinking_sphinx_tasks.rb&quot;, &quot;tasks/thinking_sphinx_tasks.rake&quot;, &quot;vendor/after_commit&quot;, &quot;vendor/after_commit/init.rb&quot;, &quot;vendor/after_commit/lib&quot;, &quot;vendor/after_commit/lib/after_commit&quot;, &quot;vendor/after_commit/lib/after_commit/active_record.rb&quot;, &quot;vendor/after_commit/lib/after_commit/connection_adapters.rb&quot;, &quot;vendor/after_commit/lib/after_commit.rb&quot;, &quot;vendor/after_commit/LICENSE&quot;, &quot;vendor/after_commit/Rakefile&quot;, &quot;vendor/after_commit/README&quot;, &quot;vendor/after_commit/test&quot;, &quot;vendor/after_commit/test/after_commit_test.rb&quot;, &quot;vendor/delayed_job&quot;, &quot;vendor/delayed_job/lib&quot;, &quot;vendor/delayed_job/lib/delayed&quot;, &quot;vendor/delayed_job/lib/delayed/job.rb&quot;, &quot;vendor/delayed_job/lib/delayed/message_sending.rb&quot;, &quot;vendor/delayed_job/lib/delayed/performable_method.rb&quot;, &quot;vendor/delayed_job/lib/delayed/worker.rb&quot;, &quot;vendor/riddle&quot;, &quot;vendor/riddle/lib&quot;, &quot;vendor/riddle/lib/riddle&quot;, &quot;vendor/riddle/lib/riddle/client&quot;, &quot;vendor/riddle/lib/riddle/client/filter.rb&quot;, &quot;vendor/riddle/lib/riddle/client/message.rb&quot;, &quot;vendor/riddle/lib/riddle/client/response.rb&quot;, &quot;vendor/riddle/lib/riddle/client.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration&quot;, &quot;vendor/riddle/lib/riddle/configuration/distributed_index.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration/index.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration/indexer.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration/remote_index.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration/searchd.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration/section.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration/source.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration/sql_source.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration/xml_source.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration.rb&quot;, &quot;vendor/riddle/lib/riddle/controller.rb&quot;, &quot;vendor/riddle/lib/riddle.rb&quot;, &quot;spec/unit/thinking_sphinx/active_record/delta_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/active_record/has_many_association_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/active_record/search_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/active_record_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/association_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/attribute_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/collection_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/configuration_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/field_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/index/builder_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/index/faux_column_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/index_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/search_spec.rb&quot;, &quot;spec/unit/thinking_sphinx_spec.rb&quot;]
+  s.files = [&quot;lib/thinking_sphinx/active_record/delta.rb&quot;, &quot;lib/thinking_sphinx/active_record/has_many_association.rb&quot;, &quot;lib/thinking_sphinx/active_record/search.rb&quot;, &quot;lib/thinking_sphinx/active_record.rb&quot;, &quot;lib/thinking_sphinx/adapters/abstract_adapter.rb&quot;, &quot;lib/thinking_sphinx/adapters/mysql_adapter.rb&quot;, &quot;lib/thinking_sphinx/adapters/postgresql_adapter.rb&quot;, &quot;lib/thinking_sphinx/association.rb&quot;, &quot;lib/thinking_sphinx/attribute.rb&quot;, &quot;lib/thinking_sphinx/collection.rb&quot;, &quot;lib/thinking_sphinx/configuration.rb&quot;, &quot;lib/thinking_sphinx/core/string.rb&quot;, &quot;lib/thinking_sphinx/deltas/datetime_delta.rb&quot;, &quot;lib/thinking_sphinx/deltas/default_delta.rb&quot;, &quot;lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb&quot;, &quot;lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb&quot;, &quot;lib/thinking_sphinx/deltas/delayed_delta/job.rb&quot;, &quot;lib/thinking_sphinx/deltas/delayed_delta.rb&quot;, &quot;lib/thinking_sphinx/deltas.rb&quot;, &quot;lib/thinking_sphinx/facet.rb&quot;, &quot;lib/thinking_sphinx/facet_collection.rb&quot;, &quot;lib/thinking_sphinx/field.rb&quot;, &quot;lib/thinking_sphinx/index/builder.rb&quot;, &quot;lib/thinking_sphinx/index/faux_column.rb&quot;, &quot;lib/thinking_sphinx/index.rb&quot;, &quot;lib/thinking_sphinx/rails_additions.rb&quot;, &quot;lib/thinking_sphinx/search.rb&quot;, &quot;lib/thinking_sphinx/tasks.rb&quot;, &quot;lib/thinking_sphinx.rb&quot;, &quot;LICENCE&quot;, &quot;README&quot;, &quot;tasks/distribution.rb&quot;, &quot;tasks/testing.rb&quot;, &quot;tasks/rails.rake&quot;, &quot;vendor/after_commit&quot;, &quot;vendor/after_commit/init.rb&quot;, &quot;vendor/after_commit/lib&quot;, &quot;vendor/after_commit/lib/after_commit&quot;, &quot;vendor/after_commit/lib/after_commit/active_record.rb&quot;, &quot;vendor/after_commit/lib/after_commit/connection_adapters.rb&quot;, &quot;vendor/after_commit/lib/after_commit.rb&quot;, &quot;vendor/after_commit/LICENSE&quot;, &quot;vendor/after_commit/Rakefile&quot;, &quot;vendor/after_commit/README&quot;, &quot;vendor/after_commit/test&quot;, &quot;vendor/after_commit/test/after_commit_test.rb&quot;, &quot;vendor/delayed_job&quot;, &quot;vendor/delayed_job/lib&quot;, &quot;vendor/delayed_job/lib/delayed&quot;, &quot;vendor/delayed_job/lib/delayed/job.rb&quot;, &quot;vendor/delayed_job/lib/delayed/message_sending.rb&quot;, &quot;vendor/delayed_job/lib/delayed/performable_method.rb&quot;, &quot;vendor/delayed_job/lib/delayed/worker.rb&quot;, &quot;vendor/riddle&quot;, &quot;vendor/riddle/lib&quot;, &quot;vendor/riddle/lib/riddle&quot;, &quot;vendor/riddle/lib/riddle/client&quot;, &quot;vendor/riddle/lib/riddle/client/filter.rb&quot;, &quot;vendor/riddle/lib/riddle/client/message.rb&quot;, &quot;vendor/riddle/lib/riddle/client/response.rb&quot;, &quot;vendor/riddle/lib/riddle/client.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration&quot;, &quot;vendor/riddle/lib/riddle/configuration/distributed_index.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration/index.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration/indexer.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration/remote_index.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration/searchd.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration/section.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration/source.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration/sql_source.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration/xml_source.rb&quot;, &quot;vendor/riddle/lib/riddle/configuration.rb&quot;, &quot;vendor/riddle/lib/riddle/controller.rb&quot;, &quot;vendor/riddle/lib/riddle.rb&quot;, &quot;spec/unit/thinking_sphinx/active_record/delta_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/active_record/has_many_association_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/active_record/search_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/active_record_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/association_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/attribute_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/collection_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/configuration_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/core/string_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/field_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/index/builder_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/index/faux_column_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/index_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/search_spec.rb&quot;, &quot;spec/unit/thinking_sphinx_spec.rb&quot;]
   s.has_rdoc = true
   s.homepage = %q{http://ts.freelancing-gods.com}
   s.rdoc_options = [&quot;--title&quot;, &quot;Thinking Sphinx -- Rails/Merb Sphinx Plugin&quot;, &quot;--line-numbers&quot;]
   s.require_paths = [&quot;lib&quot;]
   s.rubyforge_project = %q{thinking-sphinx}
-  s.rubygems_version = %q{1.3.0}
+  s.rubygems_version = %q{1.3.1}
   s.summary = %q{A concise and easy-to-use Ruby library that connects ActiveRecord to the Sphinx search daemon, managing configuration, indexing and searching.}
-  s.test_files = [&quot;spec/unit/thinking_sphinx/active_record/delta_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/active_record/has_many_association_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/active_record/search_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/active_record_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/association_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/attribute_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/collection_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/configuration_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/field_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/index/builder_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/index/faux_column_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/index_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/search_spec.rb&quot;, &quot;spec/unit/thinking_sphinx_spec.rb&quot;]
+  s.test_files = [&quot;spec/unit/thinking_sphinx/active_record/delta_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/active_record/has_many_association_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/active_record/search_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/active_record_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/association_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/attribute_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/collection_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/configuration_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/core/string_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/field_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/index/builder_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/index/faux_column_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/index_spec.rb&quot;, &quot;spec/unit/thinking_sphinx/search_spec.rb&quot;, &quot;spec/unit/thinking_sphinx_spec.rb&quot;]
 
   if s.respond_to? :specification_version then
     current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION</diff>
      <filename>thinking-sphinx.gemspec</filename>
    </modified>
    <modified>
      <diff>@@ -39,4 +39,4 @@ ActiveRecord::Base.send(:include, AfterCommit::ActiveRecord)
 
 Object.subclasses_of(ActiveRecord::ConnectionAdapters::AbstractAdapter).each do |klass|
   klass.send(:include, AfterCommit::ConnectionAdapters)
-end
\ No newline at end of file
+end</diff>
      <filename>vendor/after_commit/lib/after_commit.rb</filename>
    </modified>
    <modified>
      <diff>@@ -71,19 +71,42 @@ module AfterCommit
         # after_commit callback can be made from the ConnectionAdapters when
         # the commit for the transaction has finally succeeded. 
         def after_commit_callback
-          callback(:after_commit)
+          call_after_commit_callback :after_commit
         end
         
         def after_commit_on_create_callback
-          callback(:after_commit_on_create)
+          call_after_commit_callback :after_commit_on_create
         end
         
         def after_commit_on_update_callback
-          callback(:after_commit_on_update)
+          call_after_commit_callback :after_commit_on_update
         end
         
         def after_commit_on_destroy_callback
-          callback(:after_commit_on_destroy)
+          call_after_commit_callback :after_commit_on_destroy
+        end
+        
+        private
+        
+        def call_after_commit_callback(call)
+          if can_call_after_commit call
+            callback call
+            clear_after_commit_call call
+          end
+        end
+        
+        def can_call_after_commit(call)
+          @calls ||= {}
+          @calls[call] ||= false
+          if @calls[call]
+            return false
+          else
+            @calls[call] = true
+          end
+        end
+        
+        def clear_after_commit_call(call)
+          @calls[call] = false
         end
       end
     end</diff>
      <filename>vendor/after_commit/lib/after_commit/active_record.rb</filename>
    </modified>
    <modified>
      <diff>@@ -7,7 +7,7 @@ module AfterCommit
         # override it so that after this happens, any records that were saved
         # or destroyed within this transaction now get their after_commit
         # callback fired.
-        def commit_db_transaction_with_callback          
+        def commit_db_transaction_with_callback
           commit_db_transaction_without_callback
           trigger_after_commit_callbacks
           trigger_after_commit_on_create_callbacks</diff>
      <filename>vendor/after_commit/lib/after_commit/connection_adapters.rb</filename>
    </modified>
    <modified>
      <diff>@@ -18,7 +18,7 @@ module Riddle #:nodoc:
     Rev     = 1533
     # Release number to mark my own fixes, beyond feature parity with
     # Sphinx itself.
-    Release = 3
+    Release = 4
     
     String      = [Major, Minor, Tiny].join('.')
     GemVersion  = [Major, Minor, Tiny, Rev, Release].join('.')</diff>
      <filename>vendor/riddle/lib/riddle.rb</filename>
    </modified>
    <modified>
      <diff>@@ -456,7 +456,7 @@ module Riddle
         header = socket.recv(8)
         status, version, length = header.unpack('n2N')
         
-        while response.length &lt; length
+        while response.length &lt; (length || 0)
           part = socket.recv(length - response.length)
           response &lt;&lt; part if part
         end</diff>
      <filename>vendor/riddle/lib/riddle/client.rb</filename>
    </modified>
    <modified>
      <diff>@@ -20,13 +20,18 @@ module Riddle
           if send(setting) == &quot;&quot;
             conf = &quot;  #{setting} = &quot;
           else
-            conf = Array(send(setting)).collect { |set|
+            conf = setting_to_array(setting).collect { |set|
               &quot;  #{setting} = #{set}&quot;  
             }
           end
           conf.length == 0 ? nil : conf
         }.flatten.compact
       end
+      
+      def setting_to_array(setting)
+        value = send(setting)
+        value.is_a?(Array) ? value : [value]
+      end
     end
   end
-end
\ No newline at end of file
+end</diff>
      <filename>vendor/riddle/lib/riddle/configuration/section.rb</filename>
    </modified>
    <modified>
      <diff>@@ -14,7 +14,7 @@ module Riddle
     def start
       return if running?
 
-      cmd = &quot;searchd --config #{@path}&quot;
+      cmd = &quot;searchd --pidfile --config #{@path}&quot;
       `#{cmd}`    
 
       sleep(1)</diff>
      <filename>vendor/riddle/lib/riddle/controller.rb</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>tasks/thinking_sphinx_tasks.rake</filename>
    </removed>
    <removed>
      <filename>tasks/thinking_sphinx_tasks.rb</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>67386f3b8e1de3abfeebc00646bb189bf5c5c5bb</id>
    </parent>
    <parent>
      <id>b9e4294028a541fc58f6a5829db70be30b85fa19</id>
    </parent>
  </parents>
  <author>
    <name>Lourens Naude</name>
    <email>lourens@methodmissing.com</email>
  </author>
  <url>http://github.com/methodmissing/thinking-sphinx/commit/02796217621a85253b53b784114aedd7adf730a9</url>
  <id>02796217621a85253b53b784114aedd7adf730a9</id>
  <committed-date>2009-03-06T01:35:24-08:00</committed-date>
  <authored-date>2009-03-06T01:35:24-08:00</authored-date>
  <message>sync</message>
  <tree>da4e30d73218111da26b7199c062fb492c205b51</tree>
  <committer>
    <name>Lourens Naude</name>
    <email>lourens@methodmissing.com</email>
  </committer>
</commit>
