Navigation Menu

Skip to content

Commit

Permalink
Refined the facet code, with thanks to Dan Pickett's fork, and added …
Browse files Browse the repository at this point in the history
…drilling down within a facet set
  • Loading branch information
pat committed Jan 17, 2009
1 parent c3cd9a3 commit 529d43e
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 26 deletions.
1 change: 1 addition & 0 deletions README
Expand Up @@ -103,3 +103,4 @@ Since I first released this library, there's been quite a few people who have su
- Lachie Cox - Lachie Cox
- Lourens Naude - Lourens Naude
- Tom Davies - Tom Davies
- Dan Pickett
1 change: 1 addition & 0 deletions README.textile
Expand Up @@ -103,3 +103,4 @@ Since I first released this library, there's been quite a few people who have su
* Lachie Cox * Lachie Cox
* Lourens Naude * Lourens Naude
* Tom Davies * Tom Davies
* Dan Pickett
23 changes: 21 additions & 2 deletions features/facets.feature
@@ -1,7 +1,26 @@
Feature: Search and browse models by their defined facets Feature: Search and browse models by their defined facets


Scenario: Test Scenario: Requesting facets
Given Sphinx is running Given Sphinx is running
And I am searching on developers And I am searching on developers
When I am requesting facet results When I am requesting facet results
Then I should see facet results Then I should have valid facet results
And I should have 4 facets
And I should have the facet State
And I should have the facet Country
And I should have the facet Age
And I should have the facet City

Scenario: Requesting facet results
Given Sphinx is running
And I am searching on developers
When I am requesting facet results
And I drill down where Country is Australia
Then I should get 11 results

Scenario: Requesting facet results by multiple facets
Given Sphinx is running
And I am searching on developers
When I am requesting facet results
And I drill down where Country is Australia and Age is 30
Then I should get 4 results
26 changes: 24 additions & 2 deletions features/step_definitions/facet_steps.rb
Expand Up @@ -2,7 +2,29 @@
@method = :facets @method = :facets
end end


Then "I should see facet results" do When /^I drill down where (\w+) is (\w+)$/ do |facet, value|
@results = results.for(facet.downcase.to_sym => value)
end

When /^I drill down where (\w+) is (\w+) and (\w+) is (\w+)$/ do |facet_one, value_one, facet_two, value_two|
value_one = value_one.to_i unless value_one[/^\d+$/].nil?
value_two = value_two.to_i unless value_two[/^\d+$/].nil?

@results = results.for(
facet_one.downcase.to_sym => value_one,
facet_two.downcase.to_sym => value_two
)
end

Then "I should have valid facet results" do
results.should be_kind_of(Hash) results.should be_kind_of(Hash)
results.values.each { |value| value.should be_kind_of(Hash) } results.values.each { |value| value.should be_kind_of(Hash) }
end end

Then /^I should have (\d+) facets?$/ do |count|
results.keys.length.should == count.to_i
end

Then /^I should have the facet (\w+)$/ do |name|
results[name.downcase.to_sym].should be_kind_of(Hash)
end
1 change: 1 addition & 0 deletions features/support/models/developer.rb
Expand Up @@ -3,5 +3,6 @@ class Developer < ActiveRecord::Base
indexes country, :facet => true indexes country, :facet => true
indexes state, :facet => true indexes state, :facet => true
has age, :facet => true has age, :facet => true
facet city
end end
end end
1 change: 1 addition & 0 deletions lib/thinking_sphinx.rb
Expand Up @@ -13,6 +13,7 @@
require 'thinking_sphinx/collection' require 'thinking_sphinx/collection'
require 'thinking_sphinx/configuration' require 'thinking_sphinx/configuration'
require 'thinking_sphinx/facet' require 'thinking_sphinx/facet'
require 'thinking_sphinx/facet_collection'
require 'thinking_sphinx/field' require 'thinking_sphinx/field'
require 'thinking_sphinx/index' require 'thinking_sphinx/index'
require 'thinking_sphinx/rails_additions' require 'thinking_sphinx/rails_additions'
Expand Down
2 changes: 1 addition & 1 deletion lib/thinking_sphinx/attribute.rb
Expand Up @@ -152,7 +152,7 @@ def type
def to_facet def to_facet
return nil unless @faceted return nil unless @faceted


ThinkingSphinx::Facet.new(unique_name, @columns, self) ThinkingSphinx::Facet.new(self)
end end


private private
Expand Down
10 changes: 9 additions & 1 deletion lib/thinking_sphinx/collection.rb
Expand Up @@ -130,5 +130,13 @@ def each_with_weighting(&block)
yield self[index], match[:weight] yield self[index], match[:weight]
end end
end end

def inject_with_groupby_and_count(initial = nil, &block)
index = -1
results[:matches].inject(initial) do |memo, match|
index += 1
yield memo, self[index], match[:attributes]["@groupby"], match[:attributes]["@count"]
end
end
end end
end end
27 changes: 18 additions & 9 deletions lib/thinking_sphinx/facet.rb
@@ -1,11 +1,17 @@
module ThinkingSphinx module ThinkingSphinx
class Facet class Facet
attr_reader :name, :column, :reference attr_reader :reference


def initialize(name, columns, reference) def initialize(reference)
@name = name
@columns = columns
@reference = reference @reference = reference

if reference.columns.length != 1
raise "Can't translate Facets on multiple-column field or attribute"
end
end

def name
reference.unique_name
end end


def attribute_name def attribute_name
Expand All @@ -32,18 +38,21 @@ def value(object, attribute_value)
end end
end end


def to_s
name
end

private private


def translate(object, attribute_value) def translate(object, attribute_value)
if @columns.length > 1
raise "Can't translate Facets on multiple-column field or attribute"
end

column = @columns.first
column.__stack.each { |method| column.__stack.each { |method|
object = object.send(method) object = object.send(method)
} }
object.send(column.__name) object.send(column.__name)
end end

def column
@reference.columns.first
end
end end
end end
44 changes: 44 additions & 0 deletions lib/thinking_sphinx/facet_collection.rb
@@ -0,0 +1,44 @@
module ThinkingSphinx
class FacetCollection < Hash
attr_accessor :arguments

def initialize(arguments)
@arguments = arguments.clone
@attribute_values = {}
@facets = []
end

def add_from_results(facet, results)
self[facet.name] = {}
@attribute_values[facet.name] = {}
@facets << facet

results.each_with_groupby_and_count { |result, group, count|
facet_value = facet.value(result, group)

self[facet.name][facet_value] = count
@attribute_values[facet.name][facet_value] = group
}
end

def for(hash = {})
arguments = @arguments.clone
options = arguments.extract_options!
options[:with] ||= {}

hash.each do |key, value|
attrib = facet_for_key(key).attribute_name
options[:with][attrib] = @attribute_values[key][value]
end

arguments << options
ThinkingSphinx::Search.search *arguments
end

private

def facet_for_key(key)
@facets.detect { |facet| facet.name == key }
end
end
end
2 changes: 1 addition & 1 deletion lib/thinking_sphinx/field.rb
Expand Up @@ -115,7 +115,7 @@ def unique_name
def to_facet def to_facet
return nil unless @faceted return nil unless @faceted


ThinkingSphinx::Facet.new(unique_name, @columns, self) ThinkingSphinx::Facet.new(self)
end end


private private
Expand Down
9 changes: 9 additions & 0 deletions lib/thinking_sphinx/index/builder.rb
Expand Up @@ -147,6 +147,15 @@ def has(*args)
end end
alias_method :attribute, :has alias_method :attribute, :has


def facet(*args)
options = args.extract_options!
options[:facet] = true

args.each do |columns|
attributes << Attribute.new(FauxColumn.coerce(columns), options)
end
end

# Use this method to add some manual SQL conditions for your index # 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 # request. You can pass in as many strings as you like, they'll get
# joined together with ANDs later on. # joined together with ANDs later on.
Expand Down
15 changes: 5 additions & 10 deletions lib/thinking_sphinx/search.rb
Expand Up @@ -21,7 +21,7 @@ def search_for_ids(*args)


options = args.extract_options! options = args.extract_options!
page = options[:page] ? options[:page].to_i : 1 page = options[:page] ? options[:page].to_i : 1

ThinkingSphinx::Collection.ids_from_results(results, page, client.limit, options) ThinkingSphinx::Collection.ids_from_results(results, page, client.limit, options)
end end


Expand Down Expand Up @@ -353,18 +353,13 @@ def search_for_id(*args)
end end


def facets(*args) def facets(*args)
options = args.extract_options!.merge! :group_function => :attr hash = ThinkingSphinx::FacetCollection.new args
options = args.extract_options!.clone.merge! :group_function => :attr


options[:class].sphinx_facets.inject({}) do |hash, facet| options[:class].sphinx_facets.inject(hash) do |hash, facet|
facet_result = {}
options[:group_by] = facet.attribute_name options[:group_by] = facet.attribute_name


results = search *(args + [options]) hash.add_from_results facet, search(*(args + [options]))
results.each_with_groupby_and_count do |result, group, count|
facet_result[facet.value(result, group)] = count
end
hash[facet.name] = facet_result

hash hash
end end
end end
Expand Down

0 comments on commit 529d43e

Please sign in to comment.