API CHANGE Add :safe option for Collection#insert and Collection#save

The API change is minor: Collection#insert({...}, {...}, {...}) no
longer works for inserting multiple documents - instead you must pass
an explicit Array: Collection#insert([{...}, {...}, {...}])
This commit is contained in:
Mike Dirolf 2009-08-14 14:25:29 -04:00
parent 7e0a1b9721
commit 6cb8c9f49b
4 changed files with 60 additions and 15 deletions

View File

@ -97,29 +97,50 @@ module XGen
cursor.next_object # don't need to explicitly close b/c of limit cursor.next_object # don't need to explicitly close b/c of limit
end 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 # If +to_save+ already has an '_id' then an update (upsert) operation
# is performed and any existing document with that _id is overwritten. # 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 # :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'] 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 id
else else
insert(to_save) insert(to_save, :safe => options.delete(:safe))
end end
end end
# Insert +objects+, which are hashes. "<<" is aliased to this method. # Insert a document(s) into this collection.
# Returns either the single inserted object or a new array containing #
# +objects+. The object(s) may have been modified by the database's PK # "<<" is aliased to this method. Returns the _id of the inserted
# factory, if it has one. # document or a list of _ids of the inserted documents. The object(s)
def insert(*objects) # may have been modified by the database's PK factory, if it has one.
objects = objects.first if objects.size == 1 && objects.first.is_a?(Array) #
res = @db.insert_into_db(@name, objects) # :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 res.size > 1 ? res : res.first
end end
alias_method :<<, :insert alias_method :<<, :insert

View File

@ -491,8 +491,8 @@ module XGen
end end
# Insert +objects+ into +collection_name+. Normally called by # Insert +objects+ into +collection_name+. Normally called by
# Collection#insert. Returns a new array containing +objects+, # Collection#insert. Returns a new array containing the _ids
# possibly modified by @pk_factory. # of the inserted documents.
def insert_into_db(collection_name, objects) def insert_into_db(collection_name, objects)
_synchronize { _synchronize {
if @pk_factory if @pk_factory

View File

@ -31,6 +31,18 @@ class TestCollection < Test::Unit::TestCase
@@test.drop() @@test.drop()
end 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 def test_update
id1 = @@test.save("x" => 5) id1 = @@test.save("x" => 5)
@@test.update({}, {"$inc" => {"x" => 1}}) @@test.update({}, {"$inc" => {"x" => 1}})
@ -62,5 +74,17 @@ class TestCollection < Test::Unit::TestCase
@@test.update({}, {"$inc" => {"x" => 1}}, :safe => true) @@test.update({}, {"$inc" => {"x" => 1}}, :safe => true)
end end
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 end

View File

@ -60,7 +60,7 @@ class DBAPITest < Test::Unit::TestCase
end end
def test_insert_multiple def test_insert_multiple
ids = @@coll.insert({'a' => 2}, {'b' => 3}) ids = @@coll.insert([{'a' => 2}, {'b' => 3}])
ids.each do |i| ids.each do |i|
assert_kind_of ObjectID, i assert_kind_of ObjectID, i