Skip to content

Commit 9cf6121

Browse files
committed
Adds ability to declare sort ordering via an ordered hash. If using Ruby < 1.9 BSON::OrderedHash is required.
1 parent 2c50d1d commit 9cf6121

File tree

4 files changed

+87
-11
lines changed

4 files changed

+87
-11
lines changed

lib/mongo/cursor.rb

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -199,21 +199,19 @@ def count(skip_and_limit = false)
199199
# This method overrides any sort order specified in the Collection#find
200200
# method, and only the last sort applied has an effect.
201201
#
202-
# @param [Symbol, Array] key_or_list either 1) a key to sort by or 2)
203-
# an array of [key, direction] pairs to sort by. Direction should
204-
# be specified as Mongo::ASCENDING (or :ascending / :asc) or Mongo::DESCENDING (or :descending / :desc)
202+
# @param [Symbol, Array] key_or_list either 1) a key to sort by 2)
203+
# an array of [key, direction] pairs to sort by or 3) a hash of
204+
# field => direction pairs to sort by. Direction should be specified as
205+
# Mongo::ASCENDING (or :ascending / :asc) or Mongo::DESCENDING
206+
# (or :descending / :desc)
205207
#
206208
# @raise [InvalidOperation] if this cursor has already been used.
207209
#
208210
# @raise [InvalidSortValueError] if the specified order is invalid.
209-
def sort(key_or_list, direction=nil)
211+
def sort(order, direction=nil)
210212
check_modifiable
211213

212-
if !direction.nil?
213-
order = [[key_or_list, direction]]
214-
else
215-
order = key_or_list
216-
end
214+
order = [[order, direction]] unless direction.nil?
217215

218216
@order = order
219217
self

lib/mongo/util/conversions.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,29 @@ module Conversions
2424
ASCENDING_CONVERSION = ["ascending", "asc", "1"]
2525
DESCENDING_CONVERSION = ["descending", "desc", "-1"]
2626

27+
# Allows sort parameters to be defined as a Hash.
28+
# Does not allow usage of un-ordered hashes, therefore
29+
# Ruby 1.8.x users must use BSON::OrderedHash.
30+
#
31+
# Example:
32+
#
33+
# <tt>hash_as_sort_parameters({:field1 => :asc, "field2" => :desc})</tt> =>
34+
# <tt>{ "field1" => 1, "field2" => -1}</tt>
35+
def hash_as_sort_parameters(value)
36+
if RUBY_VERSION < '1.9' && !value.is_a?(BSON::OrderedHash)
37+
raise InvalidSortValueError.new(
38+
"Hashes used to supply sort order must maintain ordering." +
39+
"Use BSON::OrderedHash."
40+
)
41+
else
42+
order_by = value.inject({}) do |memo, (key, direction)|
43+
memo[key.to_s] = sort_value(direction.to_s.downcase)
44+
memo
45+
end
46+
end
47+
order_by
48+
end
49+
2750
# Converts the supplied +Array+ to a +Hash+ to pass to mongo as
2851
# sorting parameters. The returned +Hash+ will vary depending
2952
# on whether the passed +Array+ is one or two dimensional.

lib/mongo/util/support.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ def validate_read_preference(value)
6969

7070
def format_order_clause(order)
7171
case order
72+
when Hash, BSON::OrderedHash then hash_as_sort_parameters(order)
7273
when String, Symbol then string_as_sort_parameters(order)
7374
when Array then array_as_sort_parameters(order)
7475
else

test/db_api_test.rb

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,13 +183,67 @@ def test_find_sorting
183183
# Sorting using empty array; no order guarantee should not blow up.
184184
docs = @@coll.find({'a' => { '$lt' => 10 }}, :sort => []).to_a
185185
assert_equal 4, docs.size
186+
end
186187

188+
def test_find_sorting_with_hash
187189
# Sorting using ordered hash. You can use an unordered one, but then the
188190
# order of the keys won't be guaranteed thus your sort won't make sense.
191+
192+
@@coll.remove
193+
@@coll.insert('a' => 1, 'b' => 2)
194+
@@coll.insert('a' => 2, 'b' => 1)
195+
@@coll.insert('a' => 3, 'b' => 2)
196+
@@coll.insert('a' => 4, 'b' => 1)
197+
189198
oh = BSON::OrderedHash.new
190199
oh['a'] = -1
191-
assert_raise InvalidSortValueError do
192-
docs = @@coll.find({'a' => { '$lt' => 10 }}, :sort => oh).to_a
200+
201+
# Sort as a method
202+
docs = @@coll.find.sort(oh).to_a
203+
assert_equal 4, docs.size
204+
assert_equal 4, docs[0]['a']
205+
assert_equal 3, docs[1]['a']
206+
assert_equal 2, docs[2]['a']
207+
assert_equal 1, docs[3]['a']
208+
209+
# Sort as an option
210+
docs = @@coll.find({}, :sort => oh).to_a
211+
assert_equal 4, docs.size
212+
assert_equal 4, docs[0]['a']
213+
assert_equal 3, docs[1]['a']
214+
assert_equal 2, docs[2]['a']
215+
assert_equal 1, docs[3]['a']
216+
217+
if RUBY_VERSION > '1.9'
218+
docs = @@coll.find({}, :sort => {:a => -1}).to_a
219+
assert_equal 4, docs.size
220+
assert_equal 4, docs[0]['a']
221+
assert_equal 3, docs[1]['a']
222+
assert_equal 2, docs[2]['a']
223+
assert_equal 1, docs[3]['a']
224+
225+
docs = @@coll.find.sort(:a => -1).to_a
226+
assert_equal 4, docs.size
227+
assert_equal 4, docs[0]['a']
228+
assert_equal 3, docs[1]['a']
229+
assert_equal 2, docs[2]['a']
230+
assert_equal 1, docs[3]['a']
231+
232+
docs = @@coll.find.sort(:b => -1, :a => 1).to_a
233+
assert_equal 4, docs.size
234+
assert_equal 1, docs[0]['a']
235+
assert_equal 3, docs[1]['a']
236+
assert_equal 2, docs[2]['a']
237+
assert_equal 4, docs[3]['a']
238+
else
239+
# Sort as an option
240+
assert_raise InvalidSortValueError do
241+
@@coll.find({}, :sort => {:a => -1}).to_a
242+
end
243+
# Sort as a method
244+
assert_raise InvalidSortValueError do
245+
@@coll.find.sort(:a => -1).to_a
246+
end
193247
end
194248
end
195249

0 commit comments

Comments
 (0)