diff --git a/lib/mongo/collection.rb b/lib/mongo/collection.rb index 6cda9ca..aa63698 100644 --- a/lib/mongo/collection.rb +++ b/lib/mongo/collection.rb @@ -97,29 +97,50 @@ module XGen cursor.next_object # don't need to explicitly close b/c of limit end - # Save a document in this collection + # Save a document in this collection. # # If +to_save+ already has an '_id' then an update (upsert) operation # is performed and any existing document with that _id is overwritten. - # Otherwise an insert operation is performed. + # Otherwise an insert operation is performed. Returns the _id of the + # saved document. # # :to_save :: the document (a hash) to be saved - def save(to_save) + # + # Options: + # :safe :: if true, check that the save succeeded. OperationFailure + # will be raised on an error. Checking for safety requires an extra + # round-trip to the database + def save(to_save, options={}) if id = to_save[:_id] || to_save['_id'] - update({:_id => id}, to_save, :upsert => true) + update({:_id => id}, to_save, :upsert => true, :safe => options.delete(:safe)) id else - insert(to_save) + insert(to_save, :safe => options.delete(:safe)) end end - # Insert +objects+, which are hashes. "<<" is aliased to this method. - # Returns either the single inserted object or a new array containing - # +objects+. The object(s) may have been modified by the database's PK - # factory, if it has one. - def insert(*objects) - objects = objects.first if objects.size == 1 && objects.first.is_a?(Array) - res = @db.insert_into_db(@name, objects) + # Insert a document(s) into this collection. + # + # "<<" is aliased to this method. Returns the _id of the inserted + # document or a list of _ids of the inserted documents. The object(s) + # may have been modified by the database's PK factory, if it has one. + # + # :doc_or_docs :: a document (as a hash) or Array of documents to be + # inserted + # + # Options: + # :safe :: if true, check that the insert succeeded. OperationFailure + # will be raised on an error. Checking for safety requires an extra + # round-trip to the database + def insert(doc_or_docs, options={}) + doc_or_docs = [doc_or_docs] if !doc_or_docs.is_a?(Array) + res = @db.insert_into_db(@name, doc_or_docs) + if options.delete(:safe) + error = @db.error + if error + raise OperationFailure, error + end + end res.size > 1 ? res : res.first end alias_method :<<, :insert diff --git a/lib/mongo/db.rb b/lib/mongo/db.rb index 29b5b26..a0bf087 100644 --- a/lib/mongo/db.rb +++ b/lib/mongo/db.rb @@ -491,8 +491,8 @@ module XGen end # Insert +objects+ into +collection_name+. Normally called by - # Collection#insert. Returns a new array containing +objects+, - # possibly modified by @pk_factory. + # Collection#insert. Returns a new array containing the _ids + # of the inserted documents. def insert_into_db(collection_name, objects) _synchronize { if @pk_factory diff --git a/tests/test_collection.rb b/tests/test_collection.rb index 5edc74b..bfe9adc 100644 --- a/tests/test_collection.rb +++ b/tests/test_collection.rb @@ -31,6 +31,18 @@ class TestCollection < Test::Unit::TestCase @@test.drop() end + def test_safe_insert + a = {"hello" => "world"} + @@test.insert(a) + a = @@test.find_first() # TODO we need this because insert doesn't add _id + @@test.insert(a) + assert @@db.error.include? "E11000" + + assert_raise OperationFailure do + @@test.insert(a, :safe => true) + end + end + def test_update id1 = @@test.save("x" => 5) @@test.update({}, {"$inc" => {"x" => 1}}) @@ -62,5 +74,17 @@ class TestCollection < Test::Unit::TestCase @@test.update({}, {"$inc" => {"x" => 1}}, :safe => true) end end + + def test_safe_save + @@test.create_index("hello", true) + + @@test.save("hello" => "world") + @@test.save("hello" => "world") + assert @@db.error.include? "E11000" + + assert_raise OperationFailure do + @@test.save({"hello" => "world"}, :safe => true) + end + end end diff --git a/tests/test_db_api.rb b/tests/test_db_api.rb index 0a4d521..06a4ebb 100644 --- a/tests/test_db_api.rb +++ b/tests/test_db_api.rb @@ -60,7 +60,7 @@ class DBAPITest < Test::Unit::TestCase end def test_insert_multiple - ids = @@coll.insert({'a' => 2}, {'b' => 3}) + ids = @@coll.insert([{'a' => 2}, {'b' => 3}]) ids.each do |i| assert_kind_of ObjectID, i