angelf-thinking-sphinx 1.3.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. data/LICENCE +20 -0
  2. data/README.textile +170 -0
  3. data/VERSION +1 -0
  4. data/features/abstract_inheritance.feature +10 -0
  5. data/features/alternate_primary_key.feature +27 -0
  6. data/features/attribute_transformation.feature +22 -0
  7. data/features/attribute_updates.feature +77 -0
  8. data/features/deleting_instances.feature +67 -0
  9. data/features/direct_attributes.feature +11 -0
  10. data/features/excerpts.feature +13 -0
  11. data/features/extensible_delta_indexing.feature +9 -0
  12. data/features/facets.feature +90 -0
  13. data/features/facets_across_model.feature +29 -0
  14. data/features/handling_edits.feature +92 -0
  15. data/features/retry_stale_indexes.feature +24 -0
  16. data/features/searching_across_models.feature +20 -0
  17. data/features/searching_by_index.feature +40 -0
  18. data/features/searching_by_model.feature +175 -0
  19. data/features/searching_with_find_arguments.feature +56 -0
  20. data/features/sphinx_detection.feature +25 -0
  21. data/features/sphinx_scopes.feature +42 -0
  22. data/features/step_definitions/alpha_steps.rb +16 -0
  23. data/features/step_definitions/beta_steps.rb +7 -0
  24. data/features/step_definitions/common_steps.rb +193 -0
  25. data/features/step_definitions/extensible_delta_indexing_steps.rb +7 -0
  26. data/features/step_definitions/facet_steps.rb +96 -0
  27. data/features/step_definitions/find_arguments_steps.rb +36 -0
  28. data/features/step_definitions/gamma_steps.rb +15 -0
  29. data/features/step_definitions/scope_steps.rb +15 -0
  30. data/features/step_definitions/search_steps.rb +89 -0
  31. data/features/step_definitions/sphinx_steps.rb +35 -0
  32. data/features/sti_searching.feature +19 -0
  33. data/features/support/env.rb +21 -0
  34. data/features/support/lib/generic_delta_handler.rb +8 -0
  35. data/features/thinking_sphinx/database.example.yml +3 -0
  36. data/features/thinking_sphinx/db/fixtures/alphas.rb +10 -0
  37. data/features/thinking_sphinx/db/fixtures/authors.rb +1 -0
  38. data/features/thinking_sphinx/db/fixtures/betas.rb +11 -0
  39. data/features/thinking_sphinx/db/fixtures/boxes.rb +9 -0
  40. data/features/thinking_sphinx/db/fixtures/categories.rb +1 -0
  41. data/features/thinking_sphinx/db/fixtures/cats.rb +3 -0
  42. data/features/thinking_sphinx/db/fixtures/comments.rb +24 -0
  43. data/features/thinking_sphinx/db/fixtures/developers.rb +31 -0
  44. data/features/thinking_sphinx/db/fixtures/dogs.rb +3 -0
  45. data/features/thinking_sphinx/db/fixtures/extensible_betas.rb +10 -0
  46. data/features/thinking_sphinx/db/fixtures/foxes.rb +3 -0
  47. data/features/thinking_sphinx/db/fixtures/gammas.rb +10 -0
  48. data/features/thinking_sphinx/db/fixtures/music.rb +4 -0
  49. data/features/thinking_sphinx/db/fixtures/people.rb +1001 -0
  50. data/features/thinking_sphinx/db/fixtures/posts.rb +6 -0
  51. data/features/thinking_sphinx/db/fixtures/robots.rb +14 -0
  52. data/features/thinking_sphinx/db/fixtures/tags.rb +27 -0
  53. data/features/thinking_sphinx/db/migrations/create_alphas.rb +8 -0
  54. data/features/thinking_sphinx/db/migrations/create_animals.rb +5 -0
  55. data/features/thinking_sphinx/db/migrations/create_authors.rb +3 -0
  56. data/features/thinking_sphinx/db/migrations/create_authors_posts.rb +6 -0
  57. data/features/thinking_sphinx/db/migrations/create_betas.rb +5 -0
  58. data/features/thinking_sphinx/db/migrations/create_boxes.rb +5 -0
  59. data/features/thinking_sphinx/db/migrations/create_categories.rb +3 -0
  60. data/features/thinking_sphinx/db/migrations/create_comments.rb +10 -0
  61. data/features/thinking_sphinx/db/migrations/create_developers.rb +7 -0
  62. data/features/thinking_sphinx/db/migrations/create_extensible_betas.rb +5 -0
  63. data/features/thinking_sphinx/db/migrations/create_gammas.rb +3 -0
  64. data/features/thinking_sphinx/db/migrations/create_genres.rb +3 -0
  65. data/features/thinking_sphinx/db/migrations/create_music.rb +6 -0
  66. data/features/thinking_sphinx/db/migrations/create_people.rb +13 -0
  67. data/features/thinking_sphinx/db/migrations/create_posts.rb +5 -0
  68. data/features/thinking_sphinx/db/migrations/create_robots.rb +4 -0
  69. data/features/thinking_sphinx/db/migrations/create_taggings.rb +5 -0
  70. data/features/thinking_sphinx/db/migrations/create_tags.rb +4 -0
  71. data/features/thinking_sphinx/models/alpha.rb +22 -0
  72. data/features/thinking_sphinx/models/animal.rb +5 -0
  73. data/features/thinking_sphinx/models/author.rb +3 -0
  74. data/features/thinking_sphinx/models/beta.rb +8 -0
  75. data/features/thinking_sphinx/models/box.rb +8 -0
  76. data/features/thinking_sphinx/models/cat.rb +3 -0
  77. data/features/thinking_sphinx/models/category.rb +4 -0
  78. data/features/thinking_sphinx/models/comment.rb +10 -0
  79. data/features/thinking_sphinx/models/developer.rb +16 -0
  80. data/features/thinking_sphinx/models/dog.rb +3 -0
  81. data/features/thinking_sphinx/models/extensible_beta.rb +9 -0
  82. data/features/thinking_sphinx/models/fox.rb +5 -0
  83. data/features/thinking_sphinx/models/gamma.rb +5 -0
  84. data/features/thinking_sphinx/models/genre.rb +3 -0
  85. data/features/thinking_sphinx/models/medium.rb +5 -0
  86. data/features/thinking_sphinx/models/music.rb +8 -0
  87. data/features/thinking_sphinx/models/person.rb +23 -0
  88. data/features/thinking_sphinx/models/post.rb +21 -0
  89. data/features/thinking_sphinx/models/robot.rb +12 -0
  90. data/features/thinking_sphinx/models/tag.rb +3 -0
  91. data/features/thinking_sphinx/models/tagging.rb +4 -0
  92. data/lib/cucumber/thinking_sphinx/external_world.rb +8 -0
  93. data/lib/cucumber/thinking_sphinx/internal_world.rb +127 -0
  94. data/lib/cucumber/thinking_sphinx/sql_logger.rb +20 -0
  95. data/lib/thinking_sphinx.rb +242 -0
  96. data/lib/thinking_sphinx/active_record.rb +380 -0
  97. data/lib/thinking_sphinx/active_record/attribute_updates.rb +50 -0
  98. data/lib/thinking_sphinx/active_record/delta.rb +61 -0
  99. data/lib/thinking_sphinx/active_record/has_many_association.rb +51 -0
  100. data/lib/thinking_sphinx/active_record/scopes.rb +75 -0
  101. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +47 -0
  102. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +58 -0
  103. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +147 -0
  104. data/lib/thinking_sphinx/association.rb +164 -0
  105. data/lib/thinking_sphinx/attribute.rb +380 -0
  106. data/lib/thinking_sphinx/auto_version.rb +22 -0
  107. data/lib/thinking_sphinx/class_facet.rb +15 -0
  108. data/lib/thinking_sphinx/configuration.rb +292 -0
  109. data/lib/thinking_sphinx/context.rb +74 -0
  110. data/lib/thinking_sphinx/core/array.rb +7 -0
  111. data/lib/thinking_sphinx/core/string.rb +15 -0
  112. data/lib/thinking_sphinx/deltas.rb +28 -0
  113. data/lib/thinking_sphinx/deltas/default_delta.rb +62 -0
  114. data/lib/thinking_sphinx/deploy/capistrano.rb +100 -0
  115. data/lib/thinking_sphinx/excerpter.rb +22 -0
  116. data/lib/thinking_sphinx/facet.rb +125 -0
  117. data/lib/thinking_sphinx/facet_search.rb +146 -0
  118. data/lib/thinking_sphinx/field.rb +80 -0
  119. data/lib/thinking_sphinx/index.rb +157 -0
  120. data/lib/thinking_sphinx/index/builder.rb +302 -0
  121. data/lib/thinking_sphinx/index/faux_column.rb +118 -0
  122. data/lib/thinking_sphinx/join.rb +37 -0
  123. data/lib/thinking_sphinx/property.rb +168 -0
  124. data/lib/thinking_sphinx/rails_additions.rb +150 -0
  125. data/lib/thinking_sphinx/search.rb +785 -0
  126. data/lib/thinking_sphinx/search_methods.rb +439 -0
  127. data/lib/thinking_sphinx/source.rb +164 -0
  128. data/lib/thinking_sphinx/source/internal_properties.rb +46 -0
  129. data/lib/thinking_sphinx/source/sql.rb +130 -0
  130. data/lib/thinking_sphinx/tasks.rb +121 -0
  131. data/lib/thinking_sphinx/test.rb +55 -0
  132. data/rails/init.rb +16 -0
  133. data/spec/thinking_sphinx/active_record/delta_spec.rb +128 -0
  134. data/spec/thinking_sphinx/active_record/has_many_association_spec.rb +71 -0
  135. data/spec/thinking_sphinx/active_record/scopes_spec.rb +177 -0
  136. data/spec/thinking_sphinx/active_record_spec.rb +618 -0
  137. data/spec/thinking_sphinx/association_spec.rb +239 -0
  138. data/spec/thinking_sphinx/attribute_spec.rb +548 -0
  139. data/spec/thinking_sphinx/auto_version_spec.rb +39 -0
  140. data/spec/thinking_sphinx/configuration_spec.rb +271 -0
  141. data/spec/thinking_sphinx/context_spec.rb +126 -0
  142. data/spec/thinking_sphinx/core/array_spec.rb +9 -0
  143. data/spec/thinking_sphinx/core/string_spec.rb +9 -0
  144. data/spec/thinking_sphinx/excerpter_spec.rb +49 -0
  145. data/spec/thinking_sphinx/facet_search_spec.rb +176 -0
  146. data/spec/thinking_sphinx/facet_spec.rb +333 -0
  147. data/spec/thinking_sphinx/field_spec.rb +113 -0
  148. data/spec/thinking_sphinx/index/builder_spec.rb +495 -0
  149. data/spec/thinking_sphinx/index/faux_column_spec.rb +36 -0
  150. data/spec/thinking_sphinx/index_spec.rb +183 -0
  151. data/spec/thinking_sphinx/rails_additions_spec.rb +203 -0
  152. data/spec/thinking_sphinx/search_methods_spec.rb +152 -0
  153. data/spec/thinking_sphinx/search_spec.rb +1206 -0
  154. data/spec/thinking_sphinx/source_spec.rb +243 -0
  155. data/spec/thinking_sphinx_spec.rb +204 -0
  156. data/tasks/distribution.rb +46 -0
  157. data/tasks/rails.rake +1 -0
  158. data/tasks/testing.rb +76 -0
  159. metadata +342 -0
@@ -0,0 +1,62 @@
1
+ module ThinkingSphinx
2
+ module Deltas
3
+ class DefaultDelta
4
+ attr_accessor :column
5
+
6
+ def initialize(index, options)
7
+ @index = index
8
+ @column = options.delete(:delta_column) || :delta
9
+ end
10
+
11
+ def index(model, instance = nil)
12
+ return true unless ThinkingSphinx.updates_enabled? &&
13
+ ThinkingSphinx.deltas_enabled?
14
+ return true if instance && !toggled(instance)
15
+
16
+ update_delta_indexes model
17
+ delete_from_core model, instance if instance
18
+
19
+ true
20
+ end
21
+
22
+ def toggle(instance)
23
+ instance.delta = true
24
+ end
25
+
26
+ def toggled(instance)
27
+ instance.delta
28
+ end
29
+
30
+ def reset_query(model)
31
+ "UPDATE #{model.quoted_table_name} SET " +
32
+ "#{model.connection.quote_column_name(@column.to_s)} = #{adapter.boolean(false)} " +
33
+ "WHERE #{model.connection.quote_column_name(@column.to_s)} = #{adapter.boolean(true)}"
34
+ end
35
+
36
+ def clause(model, toggled)
37
+ "#{model.quoted_table_name}.#{model.connection.quote_column_name(@column.to_s)}" +
38
+ " = #{adapter.boolean(toggled)}"
39
+ end
40
+
41
+ private
42
+
43
+ def update_delta_indexes(model)
44
+ config = ThinkingSphinx::Configuration.instance
45
+ rotate = ThinkingSphinx.sphinx_running? ? "--rotate" : ""
46
+
47
+ output = `#{config.bin_path}#{config.indexer_binary_name} --config "#{config.config_file}" #{rotate} #{model.delta_index_names.join(' ')}`
48
+ puts(output) unless ThinkingSphinx.suppress_delta_output?
49
+ end
50
+
51
+ def delete_from_core(model, instance)
52
+ model.core_index_names.each do |index_name|
53
+ model.delete_in_index index_name, instance.sphinx_document_id
54
+ end
55
+ end
56
+
57
+ def adapter
58
+ @adapter = @index.model.sphinx_database_adapter
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,100 @@
1
+ Capistrano::Configuration.instance(:must_exist).load do
2
+ namespace :thinking_sphinx do
3
+ namespace :install do
4
+ desc <<-DESC
5
+ Install Sphinx by source
6
+
7
+ If Postgres is available, Sphinx will use it.
8
+
9
+ If the variable :thinking_sphinx_configure_args is set, it will
10
+ be passed to the Sphinx configure script. You can use this to
11
+ install Sphinx in a non-standard location:
12
+
13
+ set :thinking_sphinx_configure_args, "--prefix=$HOME/software"
14
+ DESC
15
+
16
+ task :sphinx do
17
+ with_postgres = false
18
+ begin
19
+ run "which pg_config" do |channel, stream, data|
20
+ with_postgres = !(data.nil? || data == "")
21
+ end
22
+ rescue Capistrano::CommandError => e
23
+ puts "Continuing despite error: #{e.message}"
24
+ end
25
+
26
+ args = []
27
+ if with_postgres
28
+ run "pg_config --pkgincludedir" do |channel, stream, data|
29
+ args << "--with-pgsql=#{data}"
30
+ end
31
+ end
32
+ args << fetch(:thinking_sphinx_configure_args, '')
33
+
34
+ commands = <<-CMD
35
+ wget -q http://www.sphinxsearch.com/downloads/sphinx-0.9.8.1.tar.gz >> sphinx.log
36
+ tar xzvf sphinx-0.9.8.1.tar.gz
37
+ cd sphinx-0.9.8.1
38
+ ./configure #{args.join(" ")}
39
+ make
40
+ #{try_sudo} make install
41
+ rm -rf sphinx-0.9.8.1 sphinx-0.9.8.1.tar.gz
42
+ CMD
43
+ run commands.split(/\n\s+/).join(" && ")
44
+ end
45
+
46
+ desc "Install Thinking Sphinx as a gem from GitHub"
47
+ task :ts do
48
+ run "#{try_sudo} gem install thinking-sphinx --source http://gemcutter.org"
49
+ end
50
+ end
51
+
52
+ desc "Generate the Sphinx configuration file"
53
+ task :configure do
54
+ rake "thinking_sphinx:configure"
55
+ end
56
+
57
+ desc "Index data"
58
+ task :index do
59
+ rake "thinking_sphinx:index"
60
+ end
61
+
62
+ desc "Start the Sphinx daemon"
63
+ task :start do
64
+ configure
65
+ rake "thinking_sphinx:start"
66
+ end
67
+
68
+ desc "Stop the Sphinx daemon"
69
+ task :stop do
70
+ configure
71
+ rake "thinking_sphinx:stop"
72
+ end
73
+
74
+ desc "Stop and then start the Sphinx daemon"
75
+ task :restart do
76
+ stop
77
+ start
78
+ end
79
+
80
+ desc "Stop, re-index and then start the Sphinx daemon"
81
+ task :rebuild do
82
+ stop
83
+ index
84
+ start
85
+ end
86
+
87
+ desc "Add the shared folder for sphinx files for the production environment"
88
+ task :shared_sphinx_folder, :roles => :web do
89
+ run "mkdir -p #{shared_path}/db/sphinx/production"
90
+ end
91
+
92
+ def rake(*tasks)
93
+ rails_env = fetch(:rails_env, "production")
94
+ rake = fetch(:rake, "rake")
95
+ tasks.each do |t|
96
+ run "if [ -d #{release_path} ]; then cd #{release_path}; else cd #{current_path}; fi; #{rake} RAILS_ENV=#{rails_env} #{t}"
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,22 @@
1
+ module ThinkingSphinx
2
+ class Excerpter
3
+ CoreMethods = %w( kind_of? object_id respond_to? should should_not stub! )
4
+ # Hide most methods, to allow them to be passed through to the instance.
5
+ instance_methods.select { |method|
6
+ method.to_s[/^__/].nil? && !CoreMethods.include?(method.to_s)
7
+ }.each { |method|
8
+ undef_method method
9
+ }
10
+
11
+ def initialize(search, instance)
12
+ @search = search
13
+ @instance = instance
14
+ end
15
+
16
+ def method_missing(method, *args, &block)
17
+ string = @instance.send(method, *args, &block).to_s
18
+
19
+ @search.excerpt_for(string, @instance.class)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,125 @@
1
+ module ThinkingSphinx
2
+ class Facet
3
+ attr_reader :property
4
+
5
+ def initialize(property)
6
+ @property = property
7
+
8
+ if property.columns.length != 1
9
+ raise "Can't translate Facets on multiple-column field or attribute"
10
+ end
11
+ end
12
+
13
+ def self.name_for(facet)
14
+ case facet
15
+ when Facet
16
+ facet.name
17
+ when String, Symbol
18
+ facet.to_s.gsub(/(_facet|_crc)$/,'').to_sym
19
+ end
20
+ end
21
+
22
+ def self.attribute_name_for(name)
23
+ name.to_s == 'class' ? 'class_crc' : "#{name}_facet"
24
+ end
25
+
26
+ def self.attribute_name_from_value(name, value)
27
+ case value
28
+ when String
29
+ attribute_name_for(name)
30
+ when Array
31
+ if value.all? { |val| val.is_a?(Integer) }
32
+ name
33
+ else
34
+ attribute_name_for(name)
35
+ end
36
+ else
37
+ name
38
+ end
39
+ end
40
+
41
+ def self.translate?(property)
42
+ return true if property.is_a?(Field)
43
+
44
+ case property.type
45
+ when :string
46
+ true
47
+ when :integer, :boolean, :datetime, :float
48
+ false
49
+ when :multi
50
+ !property.all_ints?
51
+ end
52
+ end
53
+
54
+ def name
55
+ property.unique_name
56
+ end
57
+
58
+ def attribute_name
59
+ if translate?
60
+ Facet.attribute_name_for(@property.unique_name)
61
+ else
62
+ @property.unique_name.to_s
63
+ end
64
+ end
65
+
66
+ def translate?
67
+ Facet.translate?(@property)
68
+ end
69
+
70
+ def type
71
+ @property.is_a?(Field) ? :string : @property.type
72
+ end
73
+
74
+ def value(object, attribute_value)
75
+ return translate(object, attribute_value) if translate? || float?
76
+
77
+ case @property.type
78
+ when :datetime
79
+ Time.at(attribute_value)
80
+ when :boolean
81
+ attribute_value > 0
82
+ else
83
+ attribute_value
84
+ end
85
+ end
86
+
87
+ def to_s
88
+ name
89
+ end
90
+
91
+ private
92
+
93
+ def translate(object, attribute_value)
94
+ objects = source_objects(object)
95
+ return nil if objects.nil? || objects.empty?
96
+
97
+ if objects.length > 1
98
+ objects.collect { |item| item.send(column.__name) }.detect { |item|
99
+ item.to_crc32 == attribute_value
100
+ }
101
+ else
102
+ objects.first.send(column.__name)
103
+ end
104
+ end
105
+
106
+ def source_objects(object)
107
+ column.__stack.each { |method|
108
+ object = Array(object).collect { |item|
109
+ item.send(method)
110
+ }.flatten.compact
111
+
112
+ return nil if object.empty?
113
+ }
114
+ Array(object)
115
+ end
116
+
117
+ def column
118
+ @property.columns.first
119
+ end
120
+
121
+ def float?
122
+ @property.type == :float
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,146 @@
1
+ module ThinkingSphinx
2
+ class FacetSearch < Hash
3
+ attr_accessor :args, :options
4
+
5
+ def initialize(*args)
6
+ ThinkingSphinx.context.define_indexes
7
+
8
+ @options = args.extract_options!
9
+ @args = args
10
+
11
+ set_default_options
12
+
13
+ populate
14
+ end
15
+
16
+ def for(hash = {})
17
+ for_options = {:with => {}}.merge(options)
18
+
19
+ hash.each do |key, value|
20
+ attrib = ThinkingSphinx::Facet.attribute_name_from_value(key, value)
21
+ for_options[:with][attrib] = underlying_value key, value
22
+ end
23
+
24
+ ThinkingSphinx.search *(args + [for_options])
25
+ end
26
+
27
+ def facet_names
28
+ @facet_names ||= begin
29
+ names = options[:all_facets] ?
30
+ facet_names_for_all_classes : facet_names_common_to_all_classes
31
+
32
+ names.delete "class_crc" unless options[:class_facet]
33
+ names
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def set_default_options
40
+ options[:all_facets] ||= false
41
+ if options[:class_facet].nil?
42
+ options[:class_facet] = ((options[:classes] || []).length != 1)
43
+ end
44
+ end
45
+
46
+ def populate
47
+ facet_names.each do |name|
48
+ search_options = facet_search_options.merge(:group_by => name)
49
+ add_from_results name, ThinkingSphinx.search(
50
+ *(args + [search_options])
51
+ )
52
+ end
53
+ end
54
+
55
+ def facet_search_options
56
+ config = ThinkingSphinx::Configuration.instance
57
+ max = config.configuration.searchd.max_matches || 1000
58
+
59
+ options.merge(
60
+ :group_function => :attr,
61
+ :limit => max,
62
+ :max_matches => max,
63
+ :page => 1
64
+ )
65
+ end
66
+
67
+ def facet_classes
68
+ (
69
+ options[:classes] || ThinkingSphinx.context.indexed_models.collect { |model|
70
+ model.constantize
71
+ }
72
+ ).select { |klass| klass.sphinx_facets.any? }
73
+ end
74
+
75
+ def all_facets
76
+ facet_classes.collect { |klass|
77
+ klass.sphinx_facets
78
+ }.flatten.select { |facet|
79
+ options[:facets].blank? || Array(options[:facets]).include?(facet.name)
80
+ }
81
+ end
82
+
83
+ def facet_names_for_all_classes
84
+ all_facets.group_by { |facet|
85
+ facet.name
86
+ }.collect { |name, facets|
87
+ if facets.collect { |facet| facet.type }.uniq.length > 1
88
+ raise "Facet #{name} exists in more than one model with different types"
89
+ end
90
+ facets.first.attribute_name
91
+ }
92
+ end
93
+
94
+ def facet_names_common_to_all_classes
95
+ facet_names_for_all_classes.select { |name|
96
+ facet_classes.all? { |klass|
97
+ klass.sphinx_facets.detect { |facet|
98
+ facet.attribute_name == name
99
+ }
100
+ }
101
+ }
102
+ end
103
+
104
+ def add_from_results(facet, results)
105
+ name = ThinkingSphinx::Facet.name_for(facet)
106
+
107
+ self[name] ||= {}
108
+
109
+ return if results.empty?
110
+
111
+ facet = facet_from_object(results.first, facet) if facet.is_a?(String)
112
+
113
+ results.each_with_groupby_and_count { |result, group, count|
114
+ facet_value = facet.value(result, group)
115
+
116
+ self[name][facet_value] ||= 0
117
+ self[name][facet_value] += count
118
+ }
119
+ end
120
+
121
+ def underlying_value(key, value)
122
+ case value
123
+ when Array
124
+ value.collect { |item| underlying_value(key, item) }
125
+ when String
126
+ value.to_crc32
127
+ else
128
+ value
129
+ end
130
+ end
131
+
132
+ def facet_from_object(object, name)
133
+ facet = nil
134
+ klass = object.class
135
+
136
+ while klass != ::ActiveRecord::Base && facet.nil?
137
+ facet = klass.sphinx_facets.detect { |facet|
138
+ facet.attribute_name == name
139
+ }
140
+ klass = klass.superclass
141
+ end
142
+
143
+ facet
144
+ end
145
+ end
146
+ end