added find_and_modify helper

This commit is contained in:
Kyle Banker 2010-04-06 17:56:21 -04:00
parent 45d3b91882
commit 35dac1f31e
5 changed files with 63 additions and 14 deletions

View File

@ -27,9 +27,9 @@ end
require 'bson' require 'bson'
require 'mongo/util/conversions'
require 'mongo/util/support' require 'mongo/util/support'
require 'mongo/util/core_ext' require 'mongo/util/core_ext'
require 'mongo/util/conversions'
require 'mongo/util/server_version' require 'mongo/util/server_version'
require 'mongo/collection' require 'mongo/collection'

View File

@ -405,7 +405,6 @@ module Mongo
# Note: calling drop_indexes with no args will drop them all. # Note: calling drop_indexes with no args will drop them all.
@db.drop_index(@name, '*') @db.drop_index(@name, '*')
end end
# Drop the entire collection. USE WITH CAUTION. # Drop the entire collection. USE WITH CAUTION.
@ -413,6 +412,30 @@ module Mongo
@db.drop_collection(@name) @db.drop_collection(@name)
end end
# Atomically update and return a document using MongoDB's findAndModify command. (MongoDB > 1.3.0)
#
# @option opts [Hash] :update (nil) the update operation to perform on the matched document.
# @option opts [Hash] :query ({}) a query selector document for matching the desired document.
# @option opts [Array, String, OrderedHash] :sort ({}) specify a sort option for the query using any
# of the sort options available for Cursor#sort. Sort order is important if the query will be matching
# multiple documents since only the first matching document will be updated and returned.
# @option opts [Boolean] :remove (false) If true, removes the the returned document from the collection.
# @option opts [Boolean] :new (false) If true, returns the updated document; otherwise, returns the document
# prior to update.
#
# @return [Hash] the matched document.
#
# @core mapreduce map_reduce-instance_method
def find_and_modify(opts={})
cmd = OrderedHash.new
cmd[:findandmodify] = @name
cmd.merge!(opts)
cmd[:sort] = Mongo::Support.format_order_clause(opts[:sort]) if opts[:sort]
@db.command(cmd, false, true)['value']
end
# Perform a map/reduce operation on the current collection. # Perform a map/reduce operation on the current collection.
# #
# @param [String, BSON::Code] map a map function, written in JavaScript. # @param [String, BSON::Code] map a map function, written in JavaScript.

View File

@ -365,7 +365,7 @@ module Mongo
return @selector if @selector.has_key?('$query') return @selector if @selector.has_key?('$query')
spec = OrderedHash.new spec = OrderedHash.new
spec['$query'] = @selector spec['$query'] = @selector
spec['$orderby'] = formatted_order_clause if @order spec['$orderby'] = Mongo::Support.format_order_clause(@order) if @order
spec['$hint'] = @hint if @hint && @hint.length > 0 spec['$hint'] = @hint if @hint && @hint.length > 0
spec['$explain'] = true if @explain spec['$explain'] = true if @explain
spec['$snapshot'] = true if @snapshot spec['$snapshot'] = true if @snapshot
@ -377,16 +377,6 @@ module Mongo
@order || @explain || @hint || @snapshot @order || @explain || @hint || @snapshot
end end
def formatted_order_clause
case @order
when String, Symbol then string_as_sort_parameters(@order)
when Array then array_as_sort_parameters(@order)
else
raise InvalidSortValueError, "Illegal sort clause, '#{@order.class.name}'; must be of the form " +
"[['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]"
end
end
def to_s def to_s
"DBResponse(flags=#@result_flags, cursor_id=#@cursor_id, start=#@starting_from)" "DBResponse(flags=#@result_flags, cursor_id=#@cursor_id, start=#@starting_from)"
end end

View File

@ -18,6 +18,7 @@ require 'digest/md5'
module Mongo module Mongo
module Support module Support
include Mongo::Conversions
extend self extend self
# Generate an MD5 for authentication. # Generate an MD5 for authentication.
@ -55,5 +56,15 @@ module Mongo
raise Mongo::InvalidNSName, "database name cannot be the empty string" if db_name.empty? raise Mongo::InvalidNSName, "database name cannot be the empty string" if db_name.empty?
db_name db_name
end end
def format_order_clause(order)
case order
when String, Symbol then string_as_sort_parameters(order)
when Array then array_as_sort_parameters(order)
else
raise InvalidSortValueError, "Illegal sort clause, '#{order.class.name}'; must be of the form " +
"[['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]"
end
end
end end
end end

View File

@ -7,7 +7,7 @@ class TestCollection < Test::Unit::TestCase
@@version = @@connection.server_version @@version = @@connection.server_version
def setup def setup
@@test.drop() @@test.remove
end end
def test_optional_pk_factory def test_optional_pk_factory
@ -161,6 +161,7 @@ class TestCollection < Test::Unit::TestCase
assert_raise OperationFailure do assert_raise OperationFailure do
@@test.update({}, {"$inc" => {"x" => 1}}, :safe => true) @@test.update({}, {"$inc" => {"x" => 1}}, :safe => true)
end end
@@test.drop
end end
else else
def test_safe_update def test_safe_update
@ -176,6 +177,7 @@ class TestCollection < Test::Unit::TestCase
assert_raise OperationFailure do assert_raise OperationFailure do
@@test.update({}, {"x" => 10}, :safe => true) @@test.update({}, {"x" => 10}, :safe => true)
end end
@@test.drop
end end
end end
@ -188,6 +190,7 @@ class TestCollection < Test::Unit::TestCase
assert_raise OperationFailure do assert_raise OperationFailure do
@@test.save({"hello" => "world"}, :safe => true) @@test.save({"hello" => "world"}, :safe => true)
end end
@@test.drop
end end
def test_mocked_safe_remove def test_mocked_safe_remove
@ -374,6 +377,28 @@ class TestCollection < Test::Unit::TestCase
end end
end end
if @@version > "1.3.0"
def test_find_and_modify
@@test << { :a => 1, :processed => false }
@@test << { :a => 2, :processed => false }
@@test << { :a => 3, :processed => false }
@@test.find_and_modify(:query => {}, :sort => [['a', -1]], :update => {"$set" => {:processed => true}})
assert @@test.find_one({:a => 3})['processed']
end
def test_find_and_modify_with_invalid_options
@@test << { :a => 1, :processed => false }
@@test << { :a => 2, :processed => false }
@@test << { :a => 3, :processed => false }
assert_raise Mongo::OperationFailure do
@@test.find_and_modify(:blimey => {})
end
end
end
def test_saving_dates_pre_epoch def test_saving_dates_pre_epoch
begin begin
@@test.save({'date' => Time.utc(1600)}) @@test.save({'date' => Time.utc(1600)})