diff --git a/lib/mongo/collection.rb b/lib/mongo/collection.rb index 7e63fa0..fce1be3 100644 --- a/lib/mongo/collection.rb +++ b/lib/mongo/collection.rb @@ -192,6 +192,35 @@ EOS }))["result"] end + # Rename this collection. + # + # If operating in auth mode, client must be authorized as an admin to + # perform this operation. Raises an error if +new_name+ is an invalid + # collection name. + # + # :new_name :: new name for this collection + def rename(new_name) + case new_name + when Symbol, String + else + raise RuntimeError, "new_name must be a string or symbol" + end + + new_name = new_name.to_s + + if new_name.empty? or new_name.include? ".." + raise RuntimeError, "collection names cannot be empty" + end + if new_name.include? "$" + raise RuntimeError, "collection names must not contain '$'" + end + if new_name.match(/^\./) or new_name.match(/\.$/) + raise RuntimeError, "collection names must not start or end with '.'" + end + + @db.rename_collection(@name, new_name) + end + # Get information on the indexes for the collection +collection_name+. # Returns a hash where the keys are index names (as returned by # Collection#create_index and the values are lists of [key, direction] diff --git a/lib/mongo/cursor.rb b/lib/mongo/cursor.rb index 427824b..e6dc1d3 100644 --- a/lib/mongo/cursor.rb +++ b/lib/mongo/cursor.rb @@ -31,8 +31,8 @@ module XGen attr_reader :db, :collection, :query - def initialize(db, collection, query) - @db, @collection, @query = db, collection, query + def initialize(db, collection, query, admin=false) + @db, @collection, @query, @admin = db, collection, query, admin @num_to_return = @query.number_to_return || 0 @cache = [] @closed = false @@ -194,7 +194,7 @@ module XGen def refill_via_get_more send_query_if_needed return if @cursor_id == 0 - @db.send_to_db(GetMoreMessage.new(@db.name, @collection.name, @cursor_id)) + @db.send_to_db(GetMoreMessage.new(@admin ? 'admin' : @db.name, @collection.name, @cursor_id)) read_all end @@ -212,7 +212,7 @@ module XGen def send_query_if_needed # Run query first time we request an object from the wire unless @query_run - @db.send_query_message(QueryMessage.new(@db.name, @collection.name, @query)) + @db.send_query_message(QueryMessage.new(@admin ? 'admin' : @db.name, @collection.name, @query)) @query_run = true read_all end diff --git a/lib/mongo/db.rb b/lib/mongo/db.rb index 160c71a..1fc4d4b 100644 --- a/lib/mongo/db.rb +++ b/lib/mongo/db.rb @@ -340,8 +340,8 @@ module XGen # Note that the query gets sent lazily; the cursor calls # #send_query_message when needed. If the caller never requests an # object from the cursor, the query never gets sent. - def query(collection, query) - Cursor.new(self, collection, query) + def query(collection, query, admin=false) + Cursor.new(self, collection, query, admin) end # Used by a Cursor to lazily send the query to the database. @@ -416,6 +416,16 @@ module XGen raise "Error with eval command: #{doc.inspect}" end + # Rename collection +from+ to +to+. Meant to be called by + # Collection#rename. + def rename_collection(from, to) + oh = OrderedHash.new + oh[:renameCollection] = "#{@name}.#{from}" + oh[:to] = "#{@name}.#{to}" + doc = db_command(oh, true) + raise "Error renaming collection: #{doc.inspect}" unless ok?(doc) + end + # Drop index +name+ from +collection_name+. Normally called from # Collection#drop_index or Collection#drop_indexes. def drop_index(collection_name, name) @@ -511,7 +521,7 @@ module XGen # that the "command" key be first. # # Do not call this. Intended for driver use only. - def db_command(selector) + def db_command(selector, use_admin_db=false) if !selector.kind_of?(OrderedHash) if !selector.kind_of?(Hash) || selector.keys.length > 1 raise "db_command must be given an OrderedHash when there is more than one key" @@ -520,7 +530,7 @@ module XGen q = Query.new(selector) q.number_to_return = 1 - query(Collection.new(self, SYSTEM_COMMAND_COLLECTION), q).next_object + query(Collection.new(self, SYSTEM_COMMAND_COLLECTION), q, use_admin_db).next_object end private diff --git a/tests/test_db_api.rb b/tests/test_db_api.rb index e1b7715..0089fea 100644 --- a/tests/test_db_api.rb +++ b/tests/test_db_api.rb @@ -713,6 +713,53 @@ class DBAPITest < Test::Unit::TestCase @@coll.modify({"hello" => "world"}, {"$inc" => "hello"}) end + def test_rename_collection + @@db.drop_collection("foo") + @@db.drop_collection("bar") + a = @@db.collection("foo") + b = @@db.collection("bar") + + assert_raise RuntimeError do + a.rename(5) + end + assert_raise RuntimeError do + a.rename("") + end + assert_raise RuntimeError do + a.rename("te$t") + end + assert_raise RuntimeError do + a.rename(".test") + end + assert_raise RuntimeError do + a.rename("test.") + end + assert_raise RuntimeError do + a.rename("tes..t") + end + + assert_equal 0, a.count() + assert_equal 0, b.count() + + a.insert("x" => 1) + a.insert("x" => 2) + + assert_equal 2, a.count() + + a.rename("bar") + + assert_equal 0, a.count() + assert_equal 2, b.count() + + assert_equal 1, b.find().to_a()[0]["x"] + assert_equal 2, b.find().to_a()[1]["x"] + + b.rename(:foo) + + assert_equal 2, a.count() + assert_equal 0, b.count() + end + # TODO this test fails with error message "Undefed Before end of object" # That is a database error. The undefined type may go away.