Skip to content

Commit cf93893

Browse files
committed
RUBY-790 Allow count to work with query hints
1 parent f2ebbdf commit cf93893

File tree

4 files changed

+83
-6
lines changed

4 files changed

+83
-6
lines changed

lib/mongo/collection.rb

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,9 @@ def named_hint=(hint=nil)
202202
# @option opts [Array] :sort an array of [key, direction] pairs to sort by. Direction should
203203
# be specified as Mongo::ASCENDING (or :ascending / :asc) or Mongo::DESCENDING (or :descending / :desc)
204204
# @option opts [String, Array, OrderedHash] :hint hint for query optimizer, usually not necessary if
205-
# using MongoDB > 1.1
206-
# @option opts [String] :named_hint for specifying a named index as a hint, will be overriden by :hint
205+
# using MongoDB > 1.1. Note that if using hint with count, there is inconsistent behavior with hints
206+
# specified as documents. Use named_hint for reliable behavior with count. Please see SERVER-15107.
207+
# @option opts [String] :named_hint for specifying a named index as a hint, will be overridden by :hint
207208
# if :hint is also provided.
208209
# @option opts [Boolean] :snapshot (false) if true, snapshot mode will be used for this query.
209210
# Snapshot mode assures no duplicates are returned, or objects missed, which were preset at both the start and
@@ -1030,17 +1031,24 @@ def stats
10301031
# @option opts [Hash] :query ({}) A query selector for filtering the documents counted.
10311032
# @option opts [Integer] :skip (nil) The number of documents to skip.
10321033
# @option opts [Integer] :limit (nil) The number of documents to limit.
1034+
# @option opts [String, Array, OrderedHash] :hint hint for query optimizer, usually not necessary if
1035+
# using MongoDB > 1.1. Note that there is inconsistent behavior with hints
1036+
# specified as documents. Use named_hint for reliable behavior with count. Please see SERVER-15107.
1037+
# @option opts [String] :named_hint for specifying a named index as a hint, will be overridden by :hint
1038+
# if :hint is also provided.
10331039
# @option opts [:primary, :secondary] :read Read preference for this command. See Collection#find for
10341040
# more details.
10351041
# @option opts [String] :comment (nil) a comment to include in profiling logs
10361042
#
10371043
# @return [Integer]
10381044
def count(opts={})
10391045
find(opts[:query],
1040-
:skip => opts[:skip],
1041-
:limit => opts[:limit],
1042-
:read => opts[:read],
1043-
:comment => opts[:comment]).count(true)
1046+
:skip => opts[:skip],
1047+
:limit => opts[:limit],
1048+
:hint => opts[:hint],
1049+
:named_hint => opts[:named_hint],
1050+
:read => opts[:read],
1051+
:comment => opts[:comment]).count(true)
10441052
end
10451053

10461054
alias :size :count

lib/mongo/cursor.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ def count(skip_and_limit = false)
204204
end
205205

206206
command.merge!(BSON::OrderedHash["fields", @fields])
207+
command.merge!(BSON::OrderedHash["hint", @hint]) if @hint
207208

208209
response = @db.command(command, :read => @read, :comment => @comment)
209210
return response['n'].to_i if Mongo::Support.ok?(response)

test/functional/collection_test.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,40 @@ def test_count
10081008
assert_equal 0, @test.count(:skip => 2)
10091009
end
10101010

1011+
# @todo: change to test the :hint option when SERVER-15107 is fixed
1012+
# Right now, only hints specified as strings will work.
1013+
# The Ruby driver changes a :hint string to a document.
1014+
# We must therefore test using the :named_hint option.
1015+
def test_count_with_hint
1016+
@test.drop
1017+
@test.save(:i => 1)
1018+
@test.save(:i => 2)
1019+
1020+
assert_equal 2, @test.count
1021+
assert_equal 1, @test.count(:query => { :i => 1 }, :named_hint => '_id_')
1022+
assert_equal 2, @test.count(:query => { }, :named_hint => '_id_')
1023+
1024+
@test.ensure_index(BSON::OrderedHash[:i, Mongo::ASCENDING])
1025+
1026+
if @version > '2.6'
1027+
assert_raise Mongo::OperationFailure do
1028+
@test.count(:query => { :i => 1 }, :named_hint => 'bad_hint')
1029+
end
1030+
else
1031+
assert_equal 1, @test.count(:query => { :i => 1 }, :named_hint => 'bad_hint')
1032+
end
1033+
1034+
@test.ensure_index(BSON::OrderedHash[:x, Mongo::ASCENDING], :sparse => true)
1035+
1036+
if @version > '2.6'
1037+
assert_equal 0, @test.count(:query => { :i => 1 }, :named_hint => 'x_1')
1038+
else
1039+
assert_equal 1, @test.count(:query => { :i => 1 }, :named_hint => 'x_1')
1040+
end
1041+
1042+
assert_equal 2, @test.count(:query => { }, :named_hint => 'x_1')
1043+
end
1044+
10111045
# Note: #size is just an alias for #count.
10121046
def test_size
10131047
@test.drop

test/functional/cursor_test.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,40 @@ def test_count_with_fields
529529
end
530530
end
531531

532+
# @todo: change to test the :hint option when SERVER-15107 is fixed
533+
# Right now, only hints specified as strings will work.
534+
# The Ruby driver changes a :hint string to a document.
535+
# We must therefore test using the :named_hint option.
536+
def test_count_with_hint
537+
@coll.drop
538+
@coll.save(:i => 1)
539+
@coll.save(:i => 2)
540+
541+
assert_equal 2, @coll.find.count
542+
assert_equal 1, @coll.find({ :i => 1 }, :named_hint => '_id_').count
543+
assert_equal 2, @coll.find({ }, :named_hint => '_id_').count
544+
545+
@coll.ensure_index(BSON::OrderedHash[:i, Mongo::ASCENDING])
546+
547+
if @version > '2.6'
548+
assert_raise Mongo::OperationFailure do
549+
@coll.find({ :i => 1 }, :named_hint => 'bad_hint').count
550+
end
551+
else
552+
assert_equal 1, @coll.find({ :i => 1 }, :named_hint => 'bad_hint').count
553+
end
554+
555+
@coll.ensure_index(BSON::OrderedHash[:x, Mongo::ASCENDING], :sparse => true)
556+
557+
if @version > '2.6'
558+
assert_equal 0, @coll.find({ :i => 1 }, :named_hint => 'x_1').count
559+
else
560+
assert_equal 1, @coll.find({ :i => 1 }, :named_hint => 'x_1').count
561+
end
562+
563+
assert_equal 2, @coll.find({ }, :named_hint => 'x_1').count
564+
end
565+
532566
def test_has_next
533567
@coll.remove
534568
200.times do |n|

0 commit comments

Comments
 (0)