diff --git a/lib/thinking_sphinx/class_facet.rb b/lib/thinking_sphinx/class_facet.rb index 1e5c27ef6..cb301b8e8 100644 --- a/lib/thinking_sphinx/class_facet.rb +++ b/lib/thinking_sphinx/class_facet.rb @@ -10,11 +10,6 @@ def attribute_name def value(object, attribute_value) object.class.name - # ThinkingSphinx.indexed_models.each do |i| - # return i if i.to_crc32 == attribute_value - # end - # - # raise "Unknown class crc" end end end diff --git a/lib/thinking_sphinx/facet.rb b/lib/thinking_sphinx/facet.rb index b4f4427e7..2bbe98692 100644 --- a/lib/thinking_sphinx/facet.rb +++ b/lib/thinking_sphinx/facet.rb @@ -42,7 +42,7 @@ def to_s name end - protected + private def translate(object, attribute_value) column.__stack.each { |method| diff --git a/lib/thinking_sphinx/facet_collection.rb b/lib/thinking_sphinx/facet_collection.rb index dbce81e6e..6a1d73e23 100644 --- a/lib/thinking_sphinx/facet_collection.rb +++ b/lib/thinking_sphinx/facet_collection.rb @@ -9,7 +9,9 @@ def initialize(arguments) end def add_from_results(facet, results) - self[facet.name] ||= {} + facet = facet_from_object(results.first, facet) if facet.is_a?(String) + + self[facet.name] ||= {} @attribute_values[facet.name] ||= {} @facets << facet @@ -41,5 +43,9 @@ def for(hash = {}) def facet_for_key(key) @facets.detect { |facet| facet.name == key } end + + def facet_from_object(object, name) + object.sphinx_facets.detect { |facet| facet.attribute_name == name } + end end end \ No newline at end of file diff --git a/lib/thinking_sphinx/index.rb b/lib/thinking_sphinx/index.rb index af32fd2ab..0cb57deec 100644 --- a/lib/thinking_sphinx/index.rb +++ b/lib/thinking_sphinx/index.rb @@ -48,7 +48,7 @@ def self.name(model) end def to_riddle_for_core(offset, index) - add_internal_attributes + add_internal_attributes_and_facets link! source = Riddle::Configuration::SQLSource.new( @@ -64,7 +64,7 @@ def to_riddle_for_core(offset, index) end def to_riddle_for_delta(offset, index) - add_internal_attributes + add_internal_attributes_and_facets link! source = Riddle::Configuration::SQLSource.new( @@ -246,50 +246,44 @@ def crc_column end end - def add_internal_attributes - @attributes << Attribute.new( - FauxColumn.new(@model.primary_key.to_sym), - :type => :integer, - :as => :sphinx_internal_id - ) unless @attributes.detect { |attr| attr.alias == :sphinx_internal_id } - - unless @attributes.detect { |attr| attr.alias == :class_crc } - @attributes << Attribute.new( - FauxColumn.new(crc_column), - :type => :integer, - :as => :class_crc, - :facet => true - ) - - @model.sphinx_facets << ThinkingSphinx::ClassFacet.new(@attributes.last) - end - - if @model.column_names.include?(@model.inheritance_column) - class_col = FauxColumn.new( - adapter.convert_nulls(adapter.quote_with_table(@model.inheritance_column), @model.to_s) - ) - else - class_col = FauxColumn.new("'#{@model.to_s}'") - end + def add_internal_attributes_and_facets + add_internal_attribute :sphinx_internal_id, :integer, @model.primary_key.to_sym + add_internal_attribute :class_crc, :integer, crc_column, true + add_internal_attribute :subclass_crcs, :multi, subclasses_to_s + add_internal_attribute :sphinx_deleted, :integer, "0" - @attributes << Attribute.new(class_col, - :type => :string, - :as => :class - ) + add_internal_facet :class_crc + end + + def add_internal_attribute(name, type, contents, facet = false) + return unless attribute_by_alias(name).nil? @attributes << Attribute.new( - FauxColumn.new("'" + (@model.send(:subclasses).collect { |klass| - klass.to_crc32.to_s - } << @model.to_crc32.to_s).join(",") + "'"), - :type => :multi, - :as => :subclass_crcs - ) unless @attributes.detect { |attr| attr.alias == :subclass_crcs } + FauxColumn.new(contents), + :type => type, + :as => name, + :facet => facet + ) + end + + def add_internal_facet(name) + return unless facet_by_alias(name).nil? - @attributes << Attribute.new( - FauxColumn.new("0"), - :type => :integer, - :as => :sphinx_deleted - ) unless @attributes.detect { |attr| attr.alias == :sphinx_deleted } + @model.sphinx_facets << ClassFacet.new(attribute_by_alias(name)) + end + + def attribute_by_alias(attr_alias) + @attributes.detect { |attrib| attrib.alias == attr_alias } + end + + def facet_by_alias(name) + @model.sphinx_facets.detect { |facet| facet.name == name } + end + + def subclasses_to_s + "'" + (@model.send(:subclasses).collect { |klass| + klass.to_crc32.to_s + } << @model.to_crc32.to_s).join(",") + "'" end def set_source_database_settings(source) diff --git a/lib/thinking_sphinx/search.rb b/lib/thinking_sphinx/search.rb index cdc121308..64c94b1f9 100644 --- a/lib/thinking_sphinx/search.rb +++ b/lib/thinking_sphinx/search.rb @@ -1,4 +1,4 @@ -module ThinkingSphinx +module ThinkingSphinx # Once you've got those indexes in and built, this is the stuff that # matters - how to search! This class provides a generic search # interface - which you can use to search all your indexed models at once. @@ -368,14 +368,18 @@ def search_for_id(*args) end end + # Model.facets *args + # ThinkingSphinx::Search.facets *args + # ThinkingSphinx::Search.facets *args, :all_attributes => true + # ThinkingSphinx::Search.facets *args, :class_facet => false + # def facets(*args) hash = ThinkingSphinx::FacetCollection.new args options = args.extract_options!.clone.merge! :group_function => :attr - klasses = options[:classes] || [options[:class]] - klasses = [] if options[:class].nil? + klasses = (options[:classes] || [options[:class]]).compact - #no classes specified so get classes from resultset + # No classes specified so get classes from resultset if klasses.empty? options[:group_by] = "class_crc" results = search(*(args + [options])) @@ -389,7 +393,7 @@ def facets(*args) options[:include_class_facets] = false end - #remove polymorphic classes and replace them with a parent class + # Remove polymorphic classes and replace them with a parent class klasses = klasses.inject([]) do |array, klass| if klass.superclass.name == "ActiveRecord::Base" array << klass @@ -415,8 +419,48 @@ def facets(*args) hash end + def facets_for_model(klass, args, options) + hash = ThinkingSphinx::FacetCollection.new args + [options] + options = options.clone.merge! :group_function => :attr + + klass.sphinx_facets.inject(hash) do |hash, facet| + options[:group_by] = facet.attribute_name + hash.add_from_results facet, search(*(args + [options])) + hash + end + end + + def facets_for_all_models(args, options) + hash = ThinkingSphinx::FacetCollection.new args + [options] + options = options.clone.merge! :group_function => :attr + + classes = ThinkingSphinx.indexed_models.collect { |model| + model.constantize + } + facet_names_common_to_all_classes(classes).inject(hash) do |hash, name| + options[:group_by] = attribute_name + hash.add_from_results facet, search(*(args + [options])) + hash + end + end + private + def facet_names_for_all_classes(classes) + classes.collect { |klass| + klass.sphinx_facets.collect { |facet| facet.attribute_name } + }.flatten.uniq + end + + def facet_names_common_to_all_classes(classes) + facet_names_for_all_classes.select { |name| + classes.all? { |klass| + klass.sphinx_facets.detect { |facet| + facet.attribute_name == attribute_name + } + } + } + end # This method handles the common search functionality, and returns both # the result hash and the client. Not super elegant, but it'll do for