diff --git a/lib/mongo/collection.rb b/lib/mongo/collection.rb index b89977f..194b293 100644 --- a/lib/mongo/collection.rb +++ b/lib/mongo/collection.rb @@ -23,27 +23,18 @@ module XGen # A named collection of records in a database. class Collection - attr_reader :db, :name, :hint_fields + attr_reader :db, :name, :hint def initialize(db, name) @db = db @name = name end - # Set hint fields to use and return +self+. hint_fields may be a + # Set hint fields to use and return +self+. hint may be a # single field name, array of field names, or a hash whose keys will # become the hint field names. May be +nil+. - def hint(hint_fields) - @hint_fields = case hint_fields - when String - [hint_fields] - when Hash - hint_fields.keys - when nil - nil - else - hint_fields.to_a - end + def hint=(hint) + @hint = normalize_hint_fields(hint) self end @@ -57,14 +48,21 @@ module XGen # :sort :: Either hash of field names as keys and 1/-1 as values; 1 == # ascending, -1 == descending, or array of field names (all # assumed to be sorted in ascending order). + # :hint :: See #hint. This option overrides the collection-wide value. def find(selector={}, options={}) fields = options.delete(:fields) fields = nil if fields && fields.empty? offset = options.delete(:offset) || 0 limit = options.delete(:limit) || 0 sort = options.delete(:sort) + hint = options.delete(:hint) + if hint + hint = normalize_hint_fields(hint) + else + hint = @hint + end raise RuntimeError, "Unknown options [#{options.inspect}]" unless options.empty? - @db.query(self, Query.new(selector, fields, offset, limit, sort)) + @db.query(self, Query.new(selector, fields, offset, limit, sort, hint)) end # Insert +objects+, which are hashes. "<<" is aliased to this method. @@ -154,6 +152,20 @@ module XGen @db.count(@name, selector || {}) end + protected + + def normalize_hint_fields(hint) + case hint + when String + [hint] + when Hash + hint.keys + when nil + nil + else + hint.to_a + end + end end end end diff --git a/lib/mongo/cursor.rb b/lib/mongo/cursor.rb index dc6bb3e..56c87df 100644 --- a/lib/mongo/cursor.rb +++ b/lib/mongo/cursor.rb @@ -42,24 +42,6 @@ module XGen def closed?; @closed; end - # Set hint fields to use and return +self+. hint_fields may be a - # single field name, array of field names, or a hash whose keys will - # become the hint field names. May be +nil+. If no hint fields are - # specified, the ones in the collection are used if they exist. - def hint(hint_fields) - @hint_fields = case hint_fields - when String - [hint_fields] - when Hash - hint_fields.keys - when nil - nil - else - hint_fields.to_a - end - self - end - # Return +true+ if there are more records to retrieve. We do not check # @num_to_return; #each is responsible for doing that. def more? @@ -223,12 +205,8 @@ module XGen def send_query_if_needed # Run query first time we request an object from the wire unless @query_run - hints = @hint_fields || @collection.hint_fields - old_hints = @query.hint_fields - @query.hint_fields = hints @db.send_query_message(QueryMessage.new(@db.name, @collection.name, @query)) @query_run = true - @query.hint_fields = old_hints read_all end end diff --git a/lib/mongo/message/query_message.rb b/lib/mongo/message/query_message.rb index 8c51f35..d070327 100644 --- a/lib/mongo/message/query_message.rb +++ b/lib/mongo/message/query_message.rb @@ -44,9 +44,9 @@ module XGen raise "illegal order_by: is a #{query.order_by.class.name}, must be String, Array, Hash, or OrderedHash" end end - if query.hint_fields && query.hint_fields.length > 0 + if query.hint && query.hint.length > 0 hints = OrderedHash.new - query.hint_fields.each { |hf| hints[hf] = 1 } + query.hint.each { |hf| hints[hf] = 1 } sel['$hint'] = hints end if query.explain diff --git a/lib/mongo/query.rb b/lib/mongo/query.rb index 2e5140d..ed8ceb7 100644 --- a/lib/mongo/query.rb +++ b/lib/mongo/query.rb @@ -29,7 +29,7 @@ module XGen # If true, $explain will be set in QueryMessage that uses this query. attr_accessor :explain # Either +nil+ or an array of hint field names. - attr_accessor :hint_fields + attr_accessor :hint attr_reader :selector # writer defined below # sel :: A hash describing the query. See the Mongo docs for details. @@ -58,8 +58,12 @@ module XGen # probably will not be what you intend because key order # is not preserved. (order_by is called :sort in calls to # Collection#find.) - def initialize(sel={}, return_fields=nil, number_to_skip=0, number_to_return=0, order_by=nil) - @number_to_skip, @number_to_return, @order_by = number_to_skip, number_to_return, order_by + # + # hint :: If not +nil+, specifies query hint fields. See + # Collection#hint. + def initialize(sel={}, return_fields=nil, number_to_skip=0, number_to_return=0, order_by=nil, hint=nil) + @number_to_skip, @number_to_return, @order_by, @hint = + number_to_skip, number_to_return, order_by, hint self.selector = sel self.fields = return_fields end @@ -102,7 +106,7 @@ module XGen end def contains_special_fields - (@order_by != nil && @order_by.length > 0) || @explain || @hint_fields + (@order_by != nil && @order_by.length > 0) || @explain || @hint end end end diff --git a/tests/test_cursor.rb b/tests/test_cursor.rb index 0a156f5..2caa2ac 100644 --- a/tests/test_cursor.rb +++ b/tests/test_cursor.rb @@ -54,13 +54,4 @@ class CursorTest < Test::Unit::TestCase end end - def test_hint - begin - cursor = @coll.find('a' => 1).hint('a') - assert_equal 1, cursor.to_a.size - rescue => ex - fail ex.to_s - end - end - end diff --git a/tests/test_db_api.rb b/tests/test_db_api.rb index 46d829f..307270f 100644 --- a/tests/test_db_api.rb +++ b/tests/test_db_api.rb @@ -372,4 +372,31 @@ class DBAPITest < Test::Unit::TestCase assert_equal "#{@db.host}:#{@db.port}", @db.master end + def test_hint + begin + assert_nil @coll.hint + assert_equal 1, @coll.find({'a' => 1}, :hint => 'a').to_a.size + assert_equal 1, @coll.find({'a' => 1}, :hint => ['a']).to_a.size + assert_equal 1, @coll.find({'a' => 1}, :hint => {'a' => 1}).to_a.size + + @coll.hint = 'a' + assert_equal ['a'], @coll.hint + assert_equal 1, @coll.find('a' => 1).to_a.size + + @coll.hint = ['a'] + assert_equal ['a'], @coll.hint + assert_equal 1, @coll.find('a' => 1).to_a.size + + @coll.hint = {'a' => 1} + assert_equal ['a'], @coll.hint + assert_equal 1, @coll.find('a' => 1).to_a.size + + @coll.hint = nil + assert_nil @coll.hint + assert_equal 1, @coll.find('a' => 1).to_a.size + rescue => ex + fail ex.to_s + end + end + end