Permalink
Browse files

Support has_many associations in Sphincter::Search#add_index :include…

… list.

[git-p4: depot-paths = "//src/Sphincter/dev/": change = 3358]
  • Loading branch information...
1 parent cb29f4a commit b1280a559146056ae6d9d48f54c6b6504b207c34 @drbrain drbrain committed Aug 4, 2007
Showing with 114 additions and 39 deletions.
  1. +1 −1 History.txt
  2. +55 −20 lib/sphincter/configure.rb
  3. +6 −4 lib/sphincter/search.rb
  4. +7 −1 test/sphincter_test_case.rb
  5. +45 −13 test/test_sphincter_configure.rb
View
@@ -1,7 +1,7 @@
== 1.1.0 / ??
* N major enhancements
- * Fields across belongs_to relationships may be included via add_index.
+ * Fields across relationships may be included via add_index.
== 1.0.0 / 2007-07-26
View
@@ -183,6 +183,7 @@ def self.get_sources
source_conf = {}
fields = []
where = []
+ group = false
options = index_defaults.merge options
conn = klass.connection
@@ -203,25 +204,12 @@ def self.get_sources
end
options[:include].each do |as_include|
- as_name, as_field = as_include.split '.', 2
+ t, f, w, g = get_sources_include as_include, source_conf, klass
- as_assoc = klass.reflect_on_all_associations.find do |assoc|
- assoc.name == as_name.intern
- end
-
- if as_assoc.nil? then
- raise Sphincter::Error,
- "could not find association \"#{as_name}\" in model \"#{klass.name}\""
- end
-
- as_klass = as_assoc.class_name.constantize
- as_table = as_klass.table_name
- as_pkey = conn.quote_column_name as_assoc.primary_key_name.to_s
- as_fkey = conn.quote_column_name as_klass.primary_key.to_s
-
- tables << as_table
- fields << get_sources_field(source_conf, as_klass, as_field, as_table)
- where << "#{table}.#{as_pkey} = #{as_table}.#{as_fkey}"
+ tables << t
+ fields << f
+ where << w
+ group ||= g
end
fields = fields.join ', '
@@ -231,8 +219,10 @@ def self.get_sources
where.push(*options[:conditions])
where = where.compact.join ' AND '
- source_conf['sql_query'] =
- "SELECT #{fields} FROM #{tables.join ', '} WHERE #{where}"
+ query = "SELECT #{fields} FROM #{tables.join ', '} WHERE #{where}"
+ query << " GROUP BY #{table}.#{pk}" if group
+
+ source_conf['sql_query'] = query
source_conf['sql_query_info'] =
"SELECT * FROM #{table} " \
"WHERE #{table}.#{pk} = (($id - #{index_id}) / #{index_count})"
@@ -277,6 +267,51 @@ def self.get_sources_field(source_conf, klass, field, as_table = nil)
"#{type} AS #{as_name}"
end
+ def self.get_sources_include(assoc_include, source_conf, klass)
+ conn = klass.connection
+
+ as_name, as_field = assoc_include.split '.', 2
+
+ as_assoc = klass.reflect_on_all_associations.find do |assoc|
+ assoc.name == as_name.intern
+ end
+
+ if as_assoc.nil? then
+ raise Sphincter::Error,
+ "could not find association \"#{as_name}\" in #{klass.name}"
+ end
+
+ as_klass = as_assoc.class_name.constantize
+ as_table = as_klass.table_name
+ as_klass_key = conn.quote_column_name as_klass.primary_key.to_s
+ as_assoc_key = conn.quote_column_name as_assoc.primary_key_name.to_s
+
+ case as_assoc.macro
+ when :belongs_to then
+ [as_table,
+ get_sources_field(source_conf, as_klass, as_field, as_table),
+ "#{klass.table_name}.#{as_assoc_key} = #{as_table}.#{as_klass_key}",
+ false]
+ when :has_many then
+ as_pkey = conn.quote_column_name as_klass.primary_key.to_s
+ as_fkey = conn.quote_column_name as_assoc.primary_key_name.to_s
+
+ as_name = [as_table, as_field].compact.join '_'
+ as_name = conn.quote_column_name as_name
+
+ field = conn.quote_column_name as_field
+
+ [as_table,
+ "GROUP_CONCAT(#{as_table}.#{field} SEPARATOR ' ') AS #{as_name}",
+ "#{klass.table_name}.#{as_klass_key} = #{as_table}.#{as_assoc_key}",
+ true]
+ else
+ raise Sphincter::Error,
+ "unsupported macro #{as_assoc.macro} for \"#{as_name}\" " \
+ "in #{klass.name}.add_index"
+ end
+ end
+
##
# Retrieves the database configuration for ActiveRecord::Base and adapts it
# for a sphinx.conf file.
View
@@ -46,8 +46,8 @@ def self.indexes
# :name:: Name of index. Defaults to ActiveRecord::Base::table_name.
# :fields:: Array of fields to index. Foreign key columns for belongs_to
# associations are automatically added.
- # :include:: Array of columns from belongs_to associations to include in the
- # index.
+ # :include:: Array of "association.column" for fields from associations to
+ # include in the index.
# :conditions:: Array of SQL conditions that will be ANDed together to
# predicate inclusion in the search index.
#
@@ -56,8 +56,10 @@ def self.indexes
# class Post < ActiveRecord::Base
# belongs_to :user
# belongs_to :blog
- #
- # add_index :fields => %w[title body], :include => %w[user.name],
+ # has_many :comments
+ #
+ # add_index :fields => %w[title body],
+ # :include => %w[user.name comments.body],
# :conditions => ['published = 1']
# end
@@ -5,7 +5,13 @@
$TESTING = true
class String
- def constantize() SphincterTestCase::BelongsTo end
+ def constantize()
+ case self
+ when /belongs_to/i then SphincterTestCase::BelongsTo
+ when /many/i then SphincterTestCase::HasMany
+ else raise "missing klass for #{self}"
+ end
+ end
end
require 'sphincter'
@@ -151,24 +151,40 @@ def test_self_get_sources
assert_equal expected, Sphincter::Configure.get_sources
end
- def test_self_get_sources_include_nonexistent_association
+ def test_self_get_sources_include_belongs_to
Sphincter::Search.indexes[Model] << {
:fields => %w[text],
- :include => %w[nonexistent.string]
+ :include => %w[belongs_to.string]
}
- e = assert_raise Sphincter::Error do
- Sphincter::Configure.get_sources
- end
+ expected = {
+ "models" => {
+ "strip_html" => 0,
+ "sql_group_column" => ["sphincter_index_id"],
+ "sql_query_range" => "SELECT MIN(`id`), MAX(`id`) FROM models",
+ "sql_query_info" =>
+ "SELECT * FROM models WHERE models.`id` = (($id - 0) / 1)",
+ "sql_date_column" => [],
+ "sql_query" =>
+ "SELECT (models.`id` * 1 + 0) AS `id`, " \
+ "0 AS sphincter_index_id, " \
+ "'Model' AS sphincter_klass, " \
+ "models.`text` AS `text`, " \
+ "belongs_tos.`string` AS `belongs_tos_string` " \
+ "FROM models, belongs_tos " \
+ "WHERE models.`belongs_to_id` = belongs_tos.`id` AND "\
+ "models.`id` >= $start AND " \
+ "models.`id` <= $end"
+ }
+ }
- assert_equal "could not find association \"nonexistent\" in model \"Model\"",
- e.message
+ assert_equal expected, Sphincter::Configure.get_sources
end
- def test_self_get_sources_include_belongs_to
+ def test_self_get_sources_include_has_many
Sphincter::Search.indexes[Model] << {
:fields => %w[text],
- :include => %w[belongs_to.string]
+ :include => %w[manys.string]
}
expected = {
@@ -184,17 +200,33 @@ def test_self_get_sources_include_belongs_to
"0 AS sphincter_index_id, " \
"'Model' AS sphincter_klass, " \
"models.`text` AS `text`, " \
- "belongs_tos.`string` AS `belongs_tos_string` " \
- "FROM models, belongs_tos " \
- "WHERE models.`belongs_to_id` = belongs_tos.`id` AND "\
+ "GROUP_CONCAT(has_manys.`string` SEPARATOR ' ') AS " \
+ "`has_manys_string` " \
+ "FROM models, has_manys " \
+ "WHERE models.`id` = has_manys.`manys_id` AND "\
"models.`id` >= $start AND " \
- "models.`id` <= $end"
+ "models.`id` <= $end " \
+ "GROUP BY models.`id`"
}
}
assert_equal expected, Sphincter::Configure.get_sources
end
+ def test_self_get_sources_include_nonexistent_association
+ Sphincter::Search.indexes[Model] << {
+ :fields => %w[text],
+ :include => %w[nonexistent.string]
+ }
+
+ e = assert_raise Sphincter::Error do
+ Sphincter::Configure.get_sources
+ end
+
+ assert_equal "could not find association \"nonexistent\" in Model",
+ e.message
+ end
+
def test_self_get_sources_field
source_conf = { 'sql_group_column' => [], 'sql_date_column' => [] }
klass = Model

0 comments on commit b1280a5

Please sign in to comment.