Merge branch 'master' of github.com:mongodb/mongo-ruby-driver

This commit is contained in:
Mike Dirolf 2009-12-17 09:47:29 -05:00
commit f9fb823c53
29 changed files with 193 additions and 200 deletions

View File

@ -47,11 +47,13 @@ Les Hill, leshill on github
* OrderedHash#each returns self * OrderedHash#each returns self
Sean Cribbs, seancribbs on github Sean Cribbs, seancribbs on github
* Modify standard_benchmark to allow profiling * Modified standard_benchmark to allow profiling
* c ext for faster ObjectID creation
Sunny Hirai Sunny Hirai
* Suggested hashcode fix for Mongo::ObjectID * Suggested hashcode fix for Mongo::ObjectID
* Noted index ordering bug. * Noted index ordering bug.
* GridFS performance boost
Christos Trochalakis Christos Trochalakis
* Added map/reduce helper * Added map/reduce helper

View File

@ -1,26 +0,0 @@
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
require 'mongo'
db = Mongo::Connection.new({:left => ["localhost", 27017], :right => ["localhost", 27018]}, nil, :auto_reconnect => true).db("ruby_test")
db['test'].clear
10.times do |i|
db['test'].save("x" => i)
end
while true do
begin
exit() if not db['test'].count() == 10
x = 0
db['test'].find().each do |doc|
x += doc['x']
end
exit() if not x == 45
print "."
STDOUT.flush
sleep 1
rescue
sleep 1
end
end

View File

@ -2,7 +2,7 @@
require 'rubygems' require 'rubygems'
require 'mongo' require 'mongo'
require 'mongo/gridfs' require 'mongo/gridfs'
require 'benchmark' #require 'ruby-prof'
include Mongo include Mongo
include GridFS include GridFS
@ -15,10 +15,15 @@ length = sample_data.length
mb = length / 1048576.0 mb = length / 1048576.0
t1 = Time.now t1 = Time.now
#RubyProf.start
GridStore.open(db, 'mongodb.pdf', 'w') do |f| GridStore.open(db, 'mongodb.pdf', 'w') do |f|
f.write(sample_data) f.write(sample_data)
end end
#result = RubyProf.stop
puts "Write: #{mb / (Time.now - t1)} mb/s" puts "Write: #{mb / (Time.now - t1)} mb/s"
#printer = RubyProf::FlatPrinter.new(result)
#printer.print(STDOUT, 0)
t1 = Time.now t1 = Time.now
GridStore.open(db, 'mongodb.pdf', 'r') do |f| GridStore.open(db, 'mongodb.pdf', 'r') do |f|

View File

@ -33,14 +33,14 @@ array = cursor.to_a
cursor.each { |row| pp row } cursor.each { |row| pp row }
# You can get the next object # You can get the next object
first_object = coll.find().next_object first_object = coll.find().next_document
# next_object returns nil if there are no more objects that match # next_document returns nil if there are no more objects that match
cursor = coll.find() cursor = coll.find()
obj = cursor.next_object obj = cursor.next_document
while obj while obj
pp obj pp obj
obj = cursor.next_object obj = cursor.next_document
end end
# Destroy the collection # Destroy the collection

View File

@ -30,6 +30,6 @@ coll.insert('array' => [1, 2, 3],
'null' => nil, 'null' => nil,
'symbol' => :zildjian) 'symbol' => :zildjian)
pp coll.find().next_object pp coll.find().next_document
coll.clear coll.clear

View File

@ -459,6 +459,7 @@ static void write_doc(buffer_t buffer, VALUE hash, VALUE check_keys) {
// make sure that length doesn't exceed 4MB // make sure that length doesn't exceed 4MB
if (length > 4 * 1024 * 1024) { if (length > 4 * 1024 * 1024) {
buffer_free(buffer);
rb_raise(InvalidDocument, "Document too large: BSON documents are limited to 4MB."); rb_raise(InvalidDocument, "Document too large: BSON documents are limited to 4MB.");
return; return;
} }

View File

@ -59,8 +59,7 @@ module Mongo
raise "Error with profile command: #{doc.inspect}" unless @db.ok?(doc) raise "Error with profile command: #{doc.inspect}" unless @db.ok?(doc)
end end
# Return an array contining current profiling information from the # Returns an array containing current profiling information.
# database.
def profiling_info def profiling_info
Cursor.new(Collection.new(@db, DB::SYSTEM_PROFILE_COLLECTION), :selector => {}).to_a Cursor.new(Collection.new(@db, DB::SYSTEM_PROFILE_COLLECTION), :selector => {}).to_a
end end

View File

@ -158,7 +158,7 @@ module Mongo
else else
raise TypeError, "spec_or_object_id must be an instance of ObjectID or Hash, or nil" raise TypeError, "spec_or_object_id must be an instance of ObjectID or Hash, or nil"
end end
find(spec, options.merge(:limit => -1)).next_object find(spec, options.merge(:limit => -1)).next_document
end end
# Save a document in this collection. # Save a document in this collection.
@ -497,7 +497,7 @@ EOS
# 'create' will be the collection name. For the other possible keys # 'create' will be the collection name. For the other possible keys
# and values, see DB#create_collection. # and values, see DB#create_collection.
def options def options
@db.collections_info(@name).next_object()['options'] @db.collections_info(@name).next_document['options']
end end
# Get the number of documents in this collection. # Get the number of documents in this collection.
@ -526,7 +526,7 @@ EOS
private private
# Sends an Mongo::Constants::OP_INSERT message to the database. # Sends a Mongo::Constants::OP_INSERT message to the database.
# Takes an array of +documents+, an optional +collection_name+, and a # Takes an array of +documents+, an optional +collection_name+, and a
# +check_keys+ setting. # +check_keys+ setting.
def insert_documents(documents, collection_name=@name, check_keys=true, safe=false) def insert_documents(documents, collection_name=@name, check_keys=true, safe=false)

View File

@ -49,19 +49,17 @@ module Mongo
@query_run = false @query_run = false
end end
# Return the next object or nil if there are no more. Raises an error # Return the next document or nil if there are no more.
# if necessary. def next_document
def next_object
refill_via_get_more if num_remaining == 0 refill_via_get_more if num_remaining == 0
o = @cache.shift doc = @cache.shift
if o && o['$err'] if doc && doc['$err']
err = o['$err'] err = doc['$err']
# If the server has stopped being the master (e.g., it's one of a # If the server has stopped being the master (e.g., it's one of a
# pair but it has died or something like that) then we close that # pair but it has died or something like that) then we close that
# connection. If the db has auto connect option and a pair of # connection. The next request will re-open on master server.
# servers, next request will re-open on master server.
if err == "not master" if err == "not master"
raise ConnectionFailure, err raise ConnectionFailure, err
@connection.close @connection.close
@ -70,12 +68,17 @@ module Mongo
raise OperationFailure, err raise OperationFailure, err
end end
o doc
end end
# Get the size of the results set for this query. def next_object
warn "Cursor#next_object is deprecated; please use Cursor#next_document instead."
next_document
end
# Get the size of the result set for this query.
# #
# Returns the number of objects in the results set for this query. Does # Returns the number of objects in the result set for this query. Does
# not take limit and skip into account. Raises OperationFailure on a # not take limit and skip into account. Raises OperationFailure on a
# database error. # database error.
def count def count
@ -88,17 +91,17 @@ module Mongo
raise OperationFailure, "Count failed: #{response['errmsg']}" raise OperationFailure, "Count failed: #{response['errmsg']}"
end end
# Sort this cursor's result # Sort this cursor's results.
# #
# Takes either a single key and a direction, or an array of [key, # Takes either a single key and a direction, or an array of [key,
# direction] pairs. Directions should be specified as Mongo::ASCENDING # direction] pairs. Directions should be specified as Mongo::ASCENDING / Mongo::DESCENDING
# or Mongo::DESCENDING (or :ascending or :descending) (or :asc or :desc). # (or :ascending / :descending, :asc / :desc).
# #
# Raises InvalidOperation if this cursor has already been used. Raises # Raises InvalidOperation if this cursor has already been used. Raises
# InvalidSortValueError if specified order is invalid. # InvalidSortValueError if the specified order is invalid.
# #
# This method overrides any sort order specified in the Collection#find # This method overrides any sort order specified in the Collection#find
# method, and only the last sort applied has an effect # method, and only the last sort applied has an effect.
def sort(key_or_list, direction=nil) def sort(key_or_list, direction=nil)
check_modifiable check_modifiable
@ -151,15 +154,15 @@ module Mongo
def each def each
num_returned = 0 num_returned = 0
while more? && (@limit <= 0 || num_returned < @limit) while more? && (@limit <= 0 || num_returned < @limit)
yield next_object() yield next_document
num_returned += 1 num_returned += 1
end end
end end
# Return all of the documents in this cursor as an array of hashes. # Return all of the documents in this cursor as an array of hashes.
# #
# Raises InvalidOperation if this cursor has already been used (including # Raises InvalidOperation if this cursor has already been used or if
# any previous calls to this method). # this methods has already been called on the cursor.
# #
# Use of this method is discouraged - iterating over a cursor is much # Use of this method is discouraged - iterating over a cursor is much
# more efficient in most cases. # more efficient in most cases.
@ -168,22 +171,22 @@ module Mongo
rows = [] rows = []
num_returned = 0 num_returned = 0
while more? && (@limit <= 0 || num_returned < @limit) while more? && (@limit <= 0 || num_returned < @limit)
rows << next_object() rows << next_document
num_returned += 1 num_returned += 1
end end
rows rows
end end
# Returns an explain plan record for this cursor. # Returns an explain plan document for this cursor.
def explain def explain
c = Cursor.new(@collection, query_options_hash.merge(:limit => -@limit.abs, :explain => true)) c = Cursor.new(@collection, query_options_hash.merge(:limit => -@limit.abs, :explain => true))
explanation = c.next_object explanation = c.next_document
c.close c.close
explanation explanation
end end
# Close the cursor. # Closes the cursor.
# #
# Note: if a cursor is read until exhausted (read until Mongo::Constants::OP_QUERY or # Note: if a cursor is read until exhausted (read until Mongo::Constants::OP_QUERY or
# Mongo::Constants::OP_GETMORE returns zero for the cursor id), there is no need to # Mongo::Constants::OP_GETMORE returns zero for the cursor id), there is no need to
@ -215,7 +218,7 @@ module Mongo
slave_ok + timeout slave_ok + timeout
end end
# Returns the query options set on this Cursor. # Returns the query options for this Cursor.
def query_options_hash def query_options_hash
{ :selector => @selector, { :selector => @selector,
:fields => @fields, :fields => @fields,
@ -245,7 +248,7 @@ module Mongo
end end
end end
# Set query selector hash. If the selector is a Code or String object, # Set the query selector hash. If the selector is a Code or String object,
# the selector will be used in a $where clause. # the selector will be used in a $where clause.
# See http://www.mongodb.org/display/DOCS/Server-side+Code+Execution # See http://www.mongodb.org/display/DOCS/Server-side+Code+Execution
def convert_selector_for_query(selector) def convert_selector_for_query(selector)
@ -266,20 +269,21 @@ module Mongo
@order || @explain || @hint || @snapshot @order || @explain || @hint || @snapshot
end end
# Return a number of documents remaining for this cursor.
def num_remaining def num_remaining
refill_via_get_more if @cache.length == 0 refill_via_get_more if @cache.length == 0
@cache.length @cache.length
end end
# Internal method, not for general use. Return +true+ if there are # Internal method, not for general use. Return +true+ if there are
# more records to retrieve. This methods does not check @limit; # more records to retrieve. This method does not check @limit;
# #each is responsible for doing that. # Cursor#each is responsible for doing that.
def more? def more?
num_remaining > 0 num_remaining > 0
end end
def refill_via_get_more def refill_via_get_more
return if send_query_if_needed || @cursor_id.zero? return if send_initial_query || @cursor_id.zero?
message = ByteBuffer.new message = ByteBuffer.new
# Reserved. # Reserved.
message.put_int(0) message.put_int(0)
@ -299,7 +303,7 @@ module Mongo
end end
# Run query the first time we request an object from the wire # Run query the first time we request an object from the wire
def send_query_if_needed def send_initial_query
if @query_run if @query_run
false false
else else

View File

@ -334,7 +334,7 @@ module Mongo
end end
cursor = Cursor.new(Collection.new(self, SYSTEM_COMMAND_COLLECTION), :admin => use_admin_db, :limit => -1, :selector => selector, :socket => sock) cursor = Cursor.new(Collection.new(self, SYSTEM_COMMAND_COLLECTION), :admin => use_admin_db, :limit => -1, :selector => selector, :socket => sock)
cursor.next_object cursor.next_document
end end
# Sends a command to the database. # Sends a command to the database.
@ -357,7 +357,7 @@ module Mongo
end end
result = Cursor.new(system_command_collection, :admin => admin, result = Cursor.new(system_command_collection, :admin => admin,
:limit => -1, :selector => selector, :socket => sock).next_object :limit => -1, :selector => selector, :socket => sock).next_document
if check_response && !ok?(result) if check_response && !ok?(result)
raise OperationFailure, "Database command '#{selector.keys.first}' failed." raise OperationFailure, "Database command '#{selector.keys.first}' failed."

View File

@ -73,7 +73,7 @@ module GridFS
class << self class << self
def exist?(db, name, root_collection=DEFAULT_ROOT_COLLECTION) def exist?(db, name, root_collection=DEFAULT_ROOT_COLLECTION)
db.collection("#{root_collection}.files").find({'filename' => name}).next_object != nil db.collection("#{root_collection}.files").find({'filename' => name}).next_document != nil
end end
def open(db, name, mode, options={}) def open(db, name, mode, options={})
@ -94,12 +94,12 @@ module GridFS
} }
end end
# List the contains of all GridFS files stored in the given db and # List the contents of all GridFS files stored in the given db and
# root collection. # root collection.
# #
# :db :: the database to use # :db :: the database to use
# #
# :root_collection :: the root collection to use # :root_collection :: the root collection to use. If not specified, will use default root collection.
def list(db, root_collection=DEFAULT_ROOT_COLLECTION) def list(db, root_collection=DEFAULT_ROOT_COLLECTION)
db.collection("#{root_collection}.files").find().map { |f| db.collection("#{root_collection}.files").find().map { |f|
f['filename'] f['filename']
@ -148,7 +148,7 @@ module GridFS
@db, @filename, @mode = db, name, mode @db, @filename, @mode = db, name, mode
@root = options[:root] || DEFAULT_ROOT_COLLECTION @root = options[:root] || DEFAULT_ROOT_COLLECTION
doc = collection.find({'filename' => @filename}).next_object doc = collection.find({'filename' => @filename}).next_document
if doc if doc
@files_id = doc['_id'] @files_id = doc['_id']
@content_type = doc['contentType'] @content_type = doc['contentType']
@ -495,7 +495,7 @@ module GridFS
end end
def nth_chunk(n) def nth_chunk(n)
mongo_chunk = chunk_collection.find({'files_id' => @files_id, 'n' => n}).next_object mongo_chunk = chunk_collection.find({'files_id' => @files_id, 'n' => n}).next_document
Chunk.new(self, mongo_chunk || {}) Chunk.new(self, mongo_chunk || {})
end end

View File

@ -18,6 +18,7 @@ module Mongo
# JavaScript code to be evaluated by MongoDB # JavaScript code to be evaluated by MongoDB
class Code < String class Code < String
# Hash mapping identifiers to their values # Hash mapping identifiers to their values
attr_accessor :scope attr_accessor :scope

View File

@ -359,12 +359,12 @@ class TestCollection < Test::Unit::TestCase
@@test.save(:foo => i) @@test.save(:foo => i)
end end
assert_equal 5, @@test.find({}, :skip => 5).next_object()["foo"] assert_equal 5, @@test.find({}, :skip => 5).next_document()["foo"]
assert_equal nil, @@test.find({}, :skip => 10).next_object() assert_equal nil, @@test.find({}, :skip => 10).next_document()
assert_equal 5, @@test.find({}, :limit => 5).to_a.length assert_equal 5, @@test.find({}, :limit => 5).to_a.length
assert_equal 3, @@test.find({}, :skip => 3, :limit => 5).next_object()["foo"] assert_equal 3, @@test.find({}, :skip => 3, :limit => 5).next_document()["foo"]
assert_equal 5, @@test.find({}, :skip => 3, :limit => 5).to_a.length assert_equal 5, @@test.find({}, :skip => 3, :limit => 5).to_a.length
end end

View File

@ -54,8 +54,8 @@ class TestConnection < Test::Unit::TestCase
def test_copy_database def test_copy_database
@mongo.db('old').collection('copy-test').insert('a' => 1) @mongo.db('old').collection('copy-test').insert('a' => 1)
@mongo.copy_database('old', 'new') @mongo.copy_database('old', 'new')
old_object = @mongo.db('old').collection('copy-test').find.next_object old_object = @mongo.db('old').collection('copy-test').find.next_document
new_object = @mongo.db('new').collection('copy-test').find.next_object new_object = @mongo.db('new').collection('copy-test').find.next_document
assert_equal old_object, new_object assert_equal old_object, new_object
end end

View File

@ -58,33 +58,40 @@ class CursorTest < Test::Unit::TestCase
assert_equal 0, @@db['acollectionthatdoesn'].count() assert_equal 0, @@db['acollectionthatdoesn'].count()
end end
def test_next_object_deprecation
@@coll.remove
@@coll.insert({"a" => 1})
assert_equal 1, @@coll.find().next_object["a"]
end
def test_sort def test_sort
@@coll.remove @@coll.remove
5.times{|x| @@coll.insert({"a" => x}) } 5.times{|x| @@coll.insert({"a" => x}) }
assert_kind_of Cursor, @@coll.find().sort(:a, 1) assert_kind_of Cursor, @@coll.find().sort(:a, 1)
assert_equal 0, @@coll.find().sort(:a, 1).next_object["a"] assert_equal 0, @@coll.find().sort(:a, 1).next_document["a"]
assert_equal 4, @@coll.find().sort(:a, -1).next_object["a"] assert_equal 4, @@coll.find().sort(:a, -1).next_document["a"]
assert_equal 0, @@coll.find().sort([["a", :asc]]).next_object["a"] assert_equal 0, @@coll.find().sort([["a", :asc]]).next_document["a"]
assert_kind_of Cursor, @@coll.find().sort([[:a, -1], [:b, 1]]) assert_kind_of Cursor, @@coll.find().sort([[:a, -1], [:b, 1]])
assert_equal 4, @@coll.find().sort(:a, 1).sort(:a, -1).next_object["a"] assert_equal 4, @@coll.find().sort(:a, 1).sort(:a, -1).next_document["a"]
assert_equal 0, @@coll.find().sort(:a, -1).sort(:a, 1).next_object["a"] assert_equal 0, @@coll.find().sort(:a, -1).sort(:a, 1).next_document["a"]
cursor = @@coll.find() cursor = @@coll.find()
cursor.next_object() cursor.next_document
assert_raise InvalidOperation do assert_raise InvalidOperation do
cursor.sort(["a"]) cursor.sort(["a"])
end end
assert_raise InvalidSortValueError do assert_raise InvalidSortValueError do
@@coll.find().sort(:a, 25).next_object @@coll.find().sort(:a, 25).next_document
end end
assert_raise InvalidSortValueError do assert_raise InvalidSortValueError do
@@coll.find().sort(25).next_object @@coll.find().sort(25).next_document
end end
end end
@ -106,7 +113,7 @@ class CursorTest < Test::Unit::TestCase
end end
cursor = @@coll.find() cursor = @@coll.find()
firstResult = cursor.next_object() firstResult = cursor.next_document
assert_raise InvalidOperation, "Cannot modify the query once it has been run or closed." do assert_raise InvalidOperation, "Cannot modify the query once it has been run or closed." do
cursor.limit(1) cursor.limit(1)
end end
@ -140,7 +147,7 @@ class CursorTest < Test::Unit::TestCase
end end
cursor = @@coll.find() cursor = @@coll.find()
firstResult = cursor.next_object() firstResult = cursor.next_document
assert_raise InvalidOperation, "Cannot modify the query once it has been run or closed." do assert_raise InvalidOperation, "Cannot modify the query once it has been run or closed." do
cursor.skip(1) cursor.skip(1)
end end
@ -233,7 +240,7 @@ class CursorTest < Test::Unit::TestCase
def test_close_after_query_sent def test_close_after_query_sent
begin begin
cursor = @@coll.find('a' => 1) cursor = @@coll.find('a' => 1)
cursor.next_object cursor.next_document
cursor.close cursor.close
assert cursor.closed? assert cursor.closed?
rescue => ex rescue => ex
@ -267,7 +274,7 @@ class CursorTest < Test::Unit::TestCase
10.times do |i| 10.times do |i|
a = @@coll.find() a = @@coll.find()
a.next_object() a.next_document
a.close() a.close()
end end
@ -277,7 +284,7 @@ class CursorTest < Test::Unit::TestCase
@@db.command("cursorInfo" => 1)["byLocation_size"]) @@db.command("cursorInfo" => 1)["byLocation_size"])
a = @@coll.find() a = @@coll.find()
a.next_object() a.next_document
assert_not_equal(client_cursors, assert_not_equal(client_cursors,
@@db.command("cursorInfo" => 1)["clientCursors_size"]) @@db.command("cursorInfo" => 1)["clientCursors_size"])
@ -291,7 +298,7 @@ class CursorTest < Test::Unit::TestCase
assert_equal(by_location, assert_equal(by_location,
@@db.command("cursorInfo" => 1)["byLocation_size"]) @@db.command("cursorInfo" => 1)["byLocation_size"])
a = @@coll.find({}, :limit => 10).next_object() a = @@coll.find({}, :limit => 10).next_document
assert_equal(client_cursors, assert_equal(client_cursors,
@@db.command("cursorInfo" => 1)["clientCursors_size"]) @@db.command("cursorInfo" => 1)["clientCursors_size"])
@ -299,7 +306,7 @@ class CursorTest < Test::Unit::TestCase
@@db.command("cursorInfo" => 1)["byLocation_size"]) @@db.command("cursorInfo" => 1)["byLocation_size"])
@@coll.find() do |cursor| @@coll.find() do |cursor|
cursor.next_object() cursor.next_document
end end
assert_equal(client_cursors, assert_equal(client_cursors,
@ -308,7 +315,7 @@ class CursorTest < Test::Unit::TestCase
@@db.command("cursorInfo" => 1)["byLocation_size"]) @@db.command("cursorInfo" => 1)["byLocation_size"])
@@coll.find() { |cursor| @@coll.find() { |cursor|
cursor.next_object() cursor.next_document
} }
assert_equal(client_cursors, assert_equal(client_cursors,

View File

@ -32,7 +32,7 @@ class DBTest < Test::Unit::TestCase
end end
should "create the proper cursor" do should "create the proper cursor" do
@cursor = mock(:next_object => {"ok" => 1}) @cursor = mock(:next_document => {"ok" => 1})
Cursor.expects(:new).with(@collection, :admin => true, Cursor.expects(:new).with(@collection, :admin => true,
:limit => -1, :selector => {:buildinfo => 1}, :socket => nil).returns(@cursor) :limit => -1, :selector => {:buildinfo => 1}, :socket => nil).returns(@cursor)
command = {:buildinfo => 1} command = {:buildinfo => 1}
@ -40,7 +40,7 @@ class DBTest < Test::Unit::TestCase
end end
should "raise an error when the command fails" do should "raise an error when the command fails" do
@cursor = mock(:next_object => {"ok" => 0}) @cursor = mock(:next_document => {"ok" => 0})
Cursor.expects(:new).with(@collection, :admin => true, Cursor.expects(:new).with(@collection, :admin => true,
:limit => -1, :selector => {:buildinfo => 1}, :socket => nil).returns(@cursor) :limit => -1, :selector => {:buildinfo => 1}, :socket => nil).returns(@cursor)
assert_raise OperationFailure do assert_raise OperationFailure do