Skip to content

Commit

Permalink
Merge pull request sunspot#314 from throwern/master
Browse files Browse the repository at this point in the history
order_by_function field query
  • Loading branch information
njakobsen committed Nov 7, 2013
2 parents f4ac303 + 7d57ff2 commit d013ad3
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 0 deletions.
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,40 @@ Post.search do
end
```

**Solr 3.1 and above**

Solr supports sorting on multiple fields using custom functions. Supported
operators and more details are available on the [Solr Wiki](http://wiki.apache.org/solr/FunctionQuery)

To sort results by a custom function use the `order_by_function` method.
Functions are defined with prefix notation:

```ruby
# Order by sum of two example fields: rating1 + rating2
Post.search do
fulltext("pizza")
order_by_function(:sum, :rating1, :rating2, :desc)
end

# Order by nested functions: rating1 + (rating2*rating3)
Post.search do
fulltext("pizza")
order_by_function(:sum, :rating1, [:product, :rating2, :rating3], :desc)
end

# Order by fields and constants: rating1 + (rating2 * 5)
Post.search do
fulltext("pizza")
order_by_function(:sum, :rating1, [:product, :rating2, '5'], :desc)
end

# Order by average of three fields: (rating1 + rating2 + rating3) / 3
Post.search do
fulltext("pizza")
order_by_function(:div, [:sum, :rating1, :rating2, :rating3], '3', :desc)
end
```

### Grouping

**Solr 3.3 and above**
Expand Down
24 changes: 24 additions & 0 deletions sunspot/lib/sunspot/dsl/field_query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,30 @@ def dynamic(base_name, &block)
&block
)
end
#
# Specify that results should be ordered based on a
# FunctionQuery - http://wiki.apache.org/solr/FunctionQuery
# Solr 3.1 and up
#
# For example, to order by field1 + (field2*field3):
#
# order_by_function :sum, :field1, [:product, :field2, :field3], :desc
#
# ==== Parameters
# function_name<Symbol>::
# the function to run
# arguments::
# the arguments for this function.
# - Symbol for a field or function name
# - Array for a nested function
# - String for a literal constant
# direction<Symbol>::
# :asc or :desc
def order_by_function(*args)
@query.add_sort(
Sunspot::Query::Sort::FunctionSort.new(@setup,args)
)
end
end
end
end
35 changes: 35 additions & 0 deletions sunspot/lib/sunspot/query/sort.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,41 @@ def to_param
"geodist(#{@field.indexed_name.to_sym},#{@lat},#{@lon}) #{direction_for_solr}"
end
end

#
# A FunctionSort sorts by solr function.
# FunctionComp recursively parses arguments for nesting
#
class FunctionSort < Abstract
attr_reader :comp
def initialize(setup,args)
@direction = args.pop
@comp = FunctionComp.new(setup,args)
end
def to_param
"#{comp.to_s} #{direction_for_solr}"
end
end
class FunctionComp
attr_accessor :function,:fields
def initialize(setup,args)
@function=args.shift
@fields = []
args.each do |argument|
case argument.class.name
when "Array"
@fields<< FunctionComp.new(setup,argument)
when "Symbol"
@fields<< setup.field(argument).indexed_name
when "String"
@fields<< argument
end
end
end
def to_s
"#{function}(#{fields.map(&:to_s).join(",")})"
end
end
end
end
end
20 changes: 20 additions & 0 deletions sunspot/spec/integration/scoped_search_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -457,4 +457,24 @@ def self.test_field_type(name, attribute, field, *values)
end
end
end

describe 'ordering by function' do
before :all do
Sunspot.remove_all
@p1 = Post.new(:blog_id => 1, :category_ids => [3])
@p2 = Post.new(:blog_id => 2, :category_ids => [1])
Sunspot.index([@p1,@p2])
Sunspot.commit
end
it 'should order by sum' do
# 1+3 > 2+1
search = Sunspot.search(Post) {order_by_function :sum, :blog_id, :primary_category_id, :desc}
search.results.first.should == @p1
end
it 'should order by product and sum' do
# 1 * (1+3) < 2 * (2+1)
search = Sunspot.search(Post) { order_by_function :product, :blog_id, [:sum,:blog_id,:primary_category_id], :desc}
search.results.first.should == @p2
end
end
end

0 comments on commit d013ad3

Please sign in to comment.