API CHANGE: moving XGen::Mongo::Driver and XGen::Mongo to Mongo and XGen::Mongo::GridFS to GridFS
This commit is contained in:
parent
040ba7c9c2
commit
e65dd99667
18
README.rdoc
18
README.rdoc
@ -7,9 +7,9 @@ many more.
|
|||||||
|
|
||||||
require 'mongo'
|
require 'mongo'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
db = Mongo.new('localhost').db('sample-db')
|
db = Mongo::Mongo.new('localhost').db('sample-db')
|
||||||
coll = db.collection('test')
|
coll = db.collection('test')
|
||||||
|
|
||||||
coll.clear
|
coll.clear
|
||||||
@ -79,8 +79,8 @@ Here is some simple example code:
|
|||||||
require 'rubygems' # not required for Ruby 1.9
|
require 'rubygems' # not required for Ruby 1.9
|
||||||
require 'mongo'
|
require 'mongo'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
db = Mongo.new.db('my-db-name')
|
db = Mongo::Mongo.new.db('my-db-name')
|
||||||
things = db.collection('things')
|
things = db.collection('things')
|
||||||
|
|
||||||
things.clear
|
things.clear
|
||||||
@ -149,8 +149,8 @@ using a PK factory lets you do so.
|
|||||||
You can tell the Ruby Mongo driver how to create primary keys by passing in
|
You can tell the Ruby Mongo driver how to create primary keys by passing in
|
||||||
the :pk option to the Mongo#db method.
|
the :pk option to the Mongo#db method.
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
db = Mongo.new.db('dbname', :pk => MyPKFactory.new)
|
db = Mongo::Mongo.new.db('dbname', :pk => MyPKFactory.new)
|
||||||
|
|
||||||
A primary key factory object must respond to :create_pk, which should take a
|
A primary key factory object must respond to :create_pk, which should take a
|
||||||
hash and return a hash which merges the original hash with any primary key
|
hash and return a hash which merges the original hash with any primary key
|
||||||
@ -164,7 +164,7 @@ Here is a sample primary key factory, taken from the tests:
|
|||||||
|
|
||||||
class TestPKFactory
|
class TestPKFactory
|
||||||
def create_pk(row)
|
def create_pk(row)
|
||||||
row['_id'] ||= XGen::Mongo::Driver::ObjectID.new
|
row['_id'] ||= Mongo::ObjectID.new
|
||||||
row
|
row
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -178,7 +178,7 @@ ActiveRecord-like framework for non-Rails apps) and the AR Mongo adapter code
|
|||||||
def create_pk(row)
|
def create_pk(row)
|
||||||
return row if row[:_id]
|
return row if row[:_id]
|
||||||
row.delete(:_id) # in case it exists but the value is nil
|
row.delete(:_id) # in case it exists but the value is nil
|
||||||
row['_id'] ||= XGen::Mongo::Driver::ObjectID.new
|
row['_id'] ||= Mongo::ObjectID.new
|
||||||
row
|
row
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -205,7 +205,7 @@ completely harmless; strict mode is a programmer convenience only.
|
|||||||
To turn on strict mode, either pass in :strict => true when obtaining a DB
|
To turn on strict mode, either pass in :strict => true when obtaining a DB
|
||||||
object or call the :strict= method:
|
object or call the :strict= method:
|
||||||
|
|
||||||
db = XGen::Mongo::Driver::Mongo.new.db('dbname', :strict => true)
|
db = Mongo::Mongo.new.db('dbname', :strict => true)
|
||||||
# I'm feeling lax
|
# I'm feeling lax
|
||||||
db.strict = false
|
db.strict = false
|
||||||
# No, I'm not!
|
# No, I'm not!
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
||||||
require 'mongo'
|
require 'mongo'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
TRIALS = 100000
|
TRIALS = 100000
|
||||||
|
|
||||||
|
@ -7,14 +7,14 @@ require 'irb'
|
|||||||
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
||||||
require 'mongo'
|
require 'mongo'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
host = org_argv[0] || ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
host = org_argv[0] || ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
port = org_argv[1] || ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
port = org_argv[1] || ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Mongo::DEFAULT_PORT
|
||||||
dbnm = org_argv[2] || ENV['MONGO_RUBY_DRIVER_DB'] || 'ruby-mongo-console'
|
dbnm = org_argv[2] || ENV['MONGO_RUBY_DRIVER_DB'] || 'ruby-mongo-console'
|
||||||
|
|
||||||
puts "Connecting to #{host}:#{port} (CONN) on with database #{dbnm} (DB)"
|
puts "Connecting to #{host}:#{port} (CONN) on with database #{dbnm} (DB)"
|
||||||
CONN = Mongo.new(host, port)
|
CONN = Mongo::Mongo.new(host, port)
|
||||||
DB = CONN.db(dbnm)
|
DB = CONN.db(dbnm)
|
||||||
|
|
||||||
puts "Starting IRB session..."
|
puts "Starting IRB session..."
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
||||||
require 'mongo'
|
require 'mongo'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
TRIALS = 2
|
TRIALS = 2
|
||||||
PER_TRIAL = 5000
|
PER_TRIAL = 5000
|
||||||
@ -50,9 +50,9 @@ def benchmark(str, proc, n, db, coll_name, object, setup=nil)
|
|||||||
end
|
end
|
||||||
|
|
||||||
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Mongo::DEFAULT_PORT
|
||||||
|
|
||||||
connection = Mongo.new(host, port)
|
connection = Mongo::Mongo.new(host, port)
|
||||||
connection.drop_database("benchmark")
|
connection.drop_database("benchmark")
|
||||||
db = connection.db('benchmark')
|
db = connection.db('benchmark')
|
||||||
|
|
||||||
|
@ -2,13 +2,13 @@ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
|||||||
require 'mongo'
|
require 'mongo'
|
||||||
require 'pp'
|
require 'pp'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Mongo::DEFAULT_PORT
|
||||||
|
|
||||||
puts "Connecting to #{host}:#{port}"
|
puts "Connecting to #{host}:#{port}"
|
||||||
db = Mongo.new(host, port).db('ruby-mongo-examples')
|
db = Mongo::Mongo.new(host, port).db('ruby-mongo-examples')
|
||||||
coll = db.create_collection('test')
|
coll = db.create_collection('test')
|
||||||
|
|
||||||
# Erase all records from collection, if any
|
# Erase all records from collection, if any
|
||||||
|
@ -4,10 +4,10 @@ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
|||||||
require 'mongo'
|
require 'mongo'
|
||||||
|
|
||||||
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Mongo::DEFAULT_PORT
|
||||||
|
|
||||||
puts "Connecting to #{host}:#{port}"
|
puts "Connecting to #{host}:#{port}"
|
||||||
db = XGen::Mongo::Driver::Mongo.new(host, port).db('ruby-mongo-examples')
|
db = Mongo::Mongo.new(host, port).db('ruby-mongo-examples')
|
||||||
coll = db.collection('test')
|
coll = db.collection('test')
|
||||||
coll.clear
|
coll.clear
|
||||||
|
|
||||||
|
@ -5,17 +5,17 @@ class Exception
|
|||||||
"%s: %s\n%s" % [self.class, message, (backtrace || []).join("\n") << "\n"]
|
"%s: %s\n%s" % [self.class, message, (backtrace || []).join("\n") << "\n"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
||||||
require 'mongo'
|
require 'mongo'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Mongo::DEFAULT_PORT
|
||||||
|
|
||||||
puts ">> Connecting to #{host}:#{port}"
|
puts ">> Connecting to #{host}:#{port}"
|
||||||
DB = Mongo.new(host, port).db('ruby-mongo-blog')
|
DB = Mongo::Mongo.new(host, port).db('ruby-mongo-blog')
|
||||||
|
|
||||||
LINE_SIZE = 120
|
LINE_SIZE = 120
|
||||||
puts "=" * LINE_SIZE
|
puts "=" * LINE_SIZE
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
||||||
require 'mongo'
|
require 'mongo'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Mongo::DEFAULT_PORT
|
||||||
|
|
||||||
puts "Connecting to #{host}:#{port}"
|
puts "Connecting to #{host}:#{port}"
|
||||||
db = Mongo.new(host, port).db('ruby-mongo-examples')
|
db = Mongo::Mongo.new(host, port).db('ruby-mongo-examples')
|
||||||
db.drop_collection('test')
|
db.drop_collection('test')
|
||||||
|
|
||||||
# A capped collection has a max size and optionally a max number of records.
|
# A capped collection has a max size and optionally a max number of records.
|
||||||
|
@ -2,13 +2,13 @@ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
|||||||
require 'mongo'
|
require 'mongo'
|
||||||
require 'pp'
|
require 'pp'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Mongo::DEFAULT_PORT
|
||||||
|
|
||||||
puts "Connecting to #{host}:#{port}"
|
puts "Connecting to #{host}:#{port}"
|
||||||
db = Mongo.new(host, port).db('ruby-mongo-examples')
|
db = Mongo::Mongo.new(host, port).db('ruby-mongo-examples')
|
||||||
coll = db.collection('test')
|
coll = db.collection('test')
|
||||||
|
|
||||||
# Erase all records from collection, if any
|
# Erase all records from collection, if any
|
||||||
|
@ -2,14 +2,14 @@ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
|||||||
require 'mongo'
|
require 'mongo'
|
||||||
require 'mongo/gridfs'
|
require 'mongo/gridfs'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
include XGen::Mongo::GridFS
|
include GridFS
|
||||||
|
|
||||||
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Mongo::DEFAULT_PORT
|
||||||
|
|
||||||
puts "Connecting to #{host}:#{port}"
|
puts "Connecting to #{host}:#{port}"
|
||||||
db = Mongo.new(host, port).db('ruby-mongo-examples')
|
db = Mongo::Mongo.new(host, port).db('ruby-mongo-examples')
|
||||||
|
|
||||||
def dump(db, fname)
|
def dump(db, fname)
|
||||||
GridStore.open(db, fname, 'r') { |f| puts f.read }
|
GridStore.open(db, fname, 'r') { |f| puts f.read }
|
||||||
|
@ -3,22 +3,22 @@ class Exception
|
|||||||
"%s: %s\n%s" % [self.class, message, (backtrace || []).join("\n") << "\n"]
|
"%s: %s\n%s" % [self.class, message, (backtrace || []).join("\n") << "\n"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
||||||
require 'mongo'
|
require 'mongo'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Mongo::DEFAULT_PORT
|
||||||
|
|
||||||
puts ">> Connecting to #{host}:#{port}"
|
puts ">> Connecting to #{host}:#{port}"
|
||||||
db = Mongo.new(host, port).db('ruby-mongo-index_test')
|
db = Mongo::Mongo.new(host, port).db('ruby-mongo-index_test')
|
||||||
|
|
||||||
puts ">> Dropping collection test"
|
puts ">> Dropping collection test"
|
||||||
begin
|
begin
|
||||||
res = db.drop_collection('test')
|
res = db.drop_collection('test')
|
||||||
puts "dropped : #{res.inspect}"
|
puts "dropped : #{res.inspect}"
|
||||||
rescue => e
|
rescue => e
|
||||||
puts "Error: #{e.errmsg}"
|
puts "Error: #{e.errmsg}"
|
||||||
end
|
end
|
||||||
@ -26,7 +26,7 @@ end
|
|||||||
puts ">> Creating collection test"
|
puts ">> Creating collection test"
|
||||||
begin
|
begin
|
||||||
coll = db.collection('test')
|
coll = db.collection('test')
|
||||||
puts "created : #{coll.inspect}"
|
puts "created : #{coll.inspect}"
|
||||||
rescue => e
|
rescue => e
|
||||||
puts "Error: #{e.errmsg}"
|
puts "Error: #{e.errmsg}"
|
||||||
end
|
end
|
||||||
@ -59,8 +59,8 @@ puts "created index: #{res.inspect}"
|
|||||||
|
|
||||||
puts ">> Gathering index information"
|
puts ">> Gathering index information"
|
||||||
begin
|
begin
|
||||||
res = coll.index_information
|
res = coll.index_information
|
||||||
puts "index_information : #{res.inspect}"
|
puts "index_information : #{res.inspect}"
|
||||||
rescue => e
|
rescue => e
|
||||||
puts "Error: #{e.errmsg}"
|
puts "Error: #{e.errmsg}"
|
||||||
end
|
end
|
||||||
@ -76,7 +76,7 @@ end
|
|||||||
|
|
||||||
puts ">> Dropping index"
|
puts ">> Dropping index"
|
||||||
begin
|
begin
|
||||||
res = coll.drop_index "all"
|
res = coll.drop_index "all_1"
|
||||||
puts "dropped : #{res.inspect}"
|
puts "dropped : #{res.inspect}"
|
||||||
rescue => e
|
rescue => e
|
||||||
puts "Error: #{e.errmsg}"
|
puts "Error: #{e.errmsg}"
|
||||||
@ -105,8 +105,8 @@ end
|
|||||||
|
|
||||||
puts ">> Gathering index information"
|
puts ">> Gathering index information"
|
||||||
begin
|
begin
|
||||||
res = coll.index_information
|
res = coll.index_information
|
||||||
puts "index_information : #{res.inspect}"
|
puts "index_information : #{res.inspect}"
|
||||||
rescue => e
|
rescue => e
|
||||||
puts "Error: #{e.errmsg}"
|
puts "Error: #{e.errmsg}"
|
||||||
end
|
end
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
||||||
require 'mongo'
|
require 'mongo'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Mongo::DEFAULT_PORT
|
||||||
|
|
||||||
puts "Connecting to #{host}:#{port}"
|
puts "Connecting to #{host}:#{port}"
|
||||||
db = Mongo.new(host, port).db('ruby-mongo-examples')
|
db = Mongo::Mongo.new(host, port).db('ruby-mongo-examples')
|
||||||
coll = db.collection('test')
|
coll = db.collection('test')
|
||||||
|
|
||||||
# Erase all records from collection, if any
|
# Erase all records from collection, if any
|
||||||
|
@ -2,13 +2,13 @@ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
|||||||
require 'mongo'
|
require 'mongo'
|
||||||
require 'pp'
|
require 'pp'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Mongo::DEFAULT_PORT
|
||||||
|
|
||||||
puts "Connecting to #{host}:#{port}"
|
puts "Connecting to #{host}:#{port}"
|
||||||
db = Mongo.new(host, port).db('ruby-mongo-examples')
|
db = Mongo::Mongo.new(host, port).db('ruby-mongo-examples')
|
||||||
coll = db.collection('test')
|
coll = db.collection('test')
|
||||||
|
|
||||||
# Remove all records, if any
|
# Remove all records, if any
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
||||||
require 'mongo'
|
require 'mongo'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Mongo::DEFAULT_PORT
|
||||||
|
|
||||||
puts "Connecting to #{host}:#{port}"
|
puts "Connecting to #{host}:#{port}"
|
||||||
db = Mongo.new(host, port).db('ruby-mongo-examples')
|
db = Mongo::Mongo.new(host, port).db('ruby-mongo-examples')
|
||||||
coll = db.collection('test')
|
coll = db.collection('test')
|
||||||
|
|
||||||
# Erase all records from collection, if any
|
# Erase all records from collection, if any
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
||||||
require 'mongo'
|
require 'mongo'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Mongo::DEFAULT_PORT
|
||||||
|
|
||||||
puts "Connecting to #{host}:#{port}"
|
puts "Connecting to #{host}:#{port}"
|
||||||
db = Mongo.new(host, port).db('ruby-mongo-examples')
|
db = Mongo::Mongo.new(host, port).db('ruby-mongo-examples')
|
||||||
|
|
||||||
db.drop_collection('does-not-exist')
|
db.drop_collection('does-not-exist')
|
||||||
db.create_collection('test')
|
db.create_collection('test')
|
||||||
|
@ -2,13 +2,13 @@ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
|||||||
require 'mongo'
|
require 'mongo'
|
||||||
require 'pp'
|
require 'pp'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Mongo::DEFAULT_PORT
|
||||||
|
|
||||||
puts "Connecting to #{host}:#{port}"
|
puts "Connecting to #{host}:#{port}"
|
||||||
db = Mongo.new(host, port).db('ruby-mongo-examples')
|
db = Mongo::Mongo.new(host, port).db('ruby-mongo-examples')
|
||||||
coll = db.collection('test')
|
coll = db.collection('test')
|
||||||
|
|
||||||
# Remove all records, if any
|
# Remove all records, if any
|
||||||
@ -25,13 +25,8 @@ coll.insert('array' => [1, 2, 3],
|
|||||||
'float' => 33.33333,
|
'float' => 33.33333,
|
||||||
'regex' => /foobar/i,
|
'regex' => /foobar/i,
|
||||||
'boolean' => true,
|
'boolean' => true,
|
||||||
'$where' => Code.new('this.x == 3'),
|
'where' => Code.new('this.x == 3'),
|
||||||
'dbref' => DBRef.new(coll.name, ObjectID.new),
|
'dbref' => DBRef.new(coll.name, ObjectID.new),
|
||||||
|
|
||||||
# NOTE: the undefined type is not saved to the database properly. This is a
|
|
||||||
# Mongo bug. However, the undefined type may go away completely.
|
|
||||||
# 'undef' => Undefined.new,
|
|
||||||
|
|
||||||
'null' => nil,
|
'null' => nil,
|
||||||
'symbol' => :zildjian)
|
'symbol' => :zildjian)
|
||||||
|
|
||||||
|
@ -275,7 +275,7 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
|
|||||||
case T_STRING:
|
case T_STRING:
|
||||||
{
|
{
|
||||||
if (strcmp(rb_class2name(RBASIC(value)->klass),
|
if (strcmp(rb_class2name(RBASIC(value)->klass),
|
||||||
"XGen::Mongo::Driver::Code") == 0) {
|
"Mongo::Code") == 0) {
|
||||||
int start_position, length_location, length, total_length;
|
int start_position, length_location, length, total_length;
|
||||||
write_name_and_type(buffer, key, 0x0F);
|
write_name_and_type(buffer, key, 0x0F);
|
||||||
|
|
||||||
@ -314,7 +314,7 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
|
|||||||
{
|
{
|
||||||
// TODO there has to be a better way to do these checks...
|
// TODO there has to be a better way to do these checks...
|
||||||
const char* cls = rb_class2name(RBASIC(value)->klass);
|
const char* cls = rb_class2name(RBASIC(value)->klass);
|
||||||
if (strcmp(cls, "XGen::Mongo::Driver::Binary") == 0 ||
|
if (strcmp(cls, "Mongo::Binary") == 0 ||
|
||||||
strcmp(cls, "ByteBuffer") == 0) {
|
strcmp(cls, "ByteBuffer") == 0) {
|
||||||
const char subtype = strcmp(cls, "ByteBuffer") ?
|
const char subtype = strcmp(cls, "ByteBuffer") ?
|
||||||
(const char)FIX2INT(rb_funcall(value, rb_intern("subtype"), 0)) : 2;
|
(const char)FIX2INT(rb_funcall(value, rb_intern("subtype"), 0)) : 2;
|
||||||
@ -333,7 +333,7 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
|
|||||||
buffer_write_bytes(buffer, RSTRING_PTR(string_data), length);
|
buffer_write_bytes(buffer, RSTRING_PTR(string_data), length);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (strcmp(cls, "XGen::Mongo::Driver::ObjectID") == 0) {
|
if (strcmp(cls, "Mongo::ObjectID") == 0) {
|
||||||
VALUE as_array = rb_funcall(value, rb_intern("to_a"), 0);
|
VALUE as_array = rb_funcall(value, rb_intern("to_a"), 0);
|
||||||
int i;
|
int i;
|
||||||
write_name_and_type(buffer, key, 0x07);
|
write_name_and_type(buffer, key, 0x07);
|
||||||
@ -343,7 +343,7 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (strcmp(cls, "XGen::Mongo::Driver::DBRef") == 0) {
|
if (strcmp(cls, "Mongo::DBRef") == 0) {
|
||||||
int start_position, length_location, obj_length;
|
int start_position, length_location, obj_length;
|
||||||
VALUE ns, oid;
|
VALUE ns, oid;
|
||||||
write_name_and_type(buffer, key, 0x03);
|
write_name_and_type(buffer, key, 0x03);
|
||||||
@ -364,7 +364,7 @@ static int write_element_allow_id(VALUE key, VALUE value, VALUE extra, int allow
|
|||||||
memcpy(buffer->buffer + length_location, &obj_length, 4);
|
memcpy(buffer->buffer + length_location, &obj_length, 4);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (strcmp(cls, "XGen::Mongo::Driver::Undefined") == 0) {
|
if (strcmp(cls, "Mongo::Undefined") == 0) {
|
||||||
write_name_and_type(buffer, key, 0x0A); // just use nil type
|
write_name_and_type(buffer, key, 0x0A); // just use nil type
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -736,25 +736,22 @@ static VALUE method_deserialize(VALUE self, VALUE bson) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Init_cbson() {
|
void Init_cbson() {
|
||||||
VALUE driver, CBson;
|
VALUE mongo, CBson;
|
||||||
Time = rb_const_get(rb_cObject, rb_intern("Time"));
|
Time = rb_const_get(rb_cObject, rb_intern("Time"));
|
||||||
|
|
||||||
driver = rb_const_get(rb_const_get(rb_const_get(rb_cObject,
|
mongo = rb_const_get(rb_cObject, rb_intern("Mongo"));
|
||||||
rb_intern("XGen")),
|
|
||||||
rb_intern("Mongo")),
|
|
||||||
rb_intern("Driver"));
|
|
||||||
rb_require("mongo/types/binary");
|
rb_require("mongo/types/binary");
|
||||||
Binary = rb_const_get(driver, rb_intern("Binary"));
|
Binary = rb_const_get(mongo, rb_intern("Binary"));
|
||||||
rb_require("mongo/types/objectid");
|
rb_require("mongo/types/objectid");
|
||||||
ObjectID = rb_const_get(driver, rb_intern("ObjectID"));
|
ObjectID = rb_const_get(mongo, rb_intern("ObjectID"));
|
||||||
rb_require("mongo/types/dbref");
|
rb_require("mongo/types/dbref");
|
||||||
DBRef = rb_const_get(driver, rb_intern("DBRef"));
|
DBRef = rb_const_get(mongo, rb_intern("DBRef"));
|
||||||
rb_require("mongo/types/code");
|
rb_require("mongo/types/code");
|
||||||
Code = rb_const_get(driver, rb_intern("Code"));
|
Code = rb_const_get(mongo, rb_intern("Code"));
|
||||||
rb_require("mongo/types/regexp_of_holding");
|
rb_require("mongo/types/regexp_of_holding");
|
||||||
RegexpOfHolding = rb_const_get(driver, rb_intern("RegexpOfHolding"));
|
RegexpOfHolding = rb_const_get(mongo, rb_intern("RegexpOfHolding"));
|
||||||
rb_require("mongo/errors");
|
rb_require("mongo/errors");
|
||||||
InvalidName = rb_const_get(driver, rb_intern("InvalidName"));
|
InvalidName = rb_const_get(mongo, rb_intern("InvalidName"));
|
||||||
rb_require("mongo/util/ordered_hash");
|
rb_require("mongo/util/ordered_hash");
|
||||||
OrderedHash = rb_const_get(rb_cObject, rb_intern("OrderedHash"));
|
OrderedHash = rb_const_get(rb_cObject, rb_intern("OrderedHash"));
|
||||||
|
|
||||||
|
@ -12,9 +12,7 @@ require 'mongo/cursor'
|
|||||||
require 'mongo/collection'
|
require 'mongo/collection'
|
||||||
require 'mongo/admin'
|
require 'mongo/admin'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
ASCENDING = 1
|
||||||
ASCENDING = 1
|
DESCENDING = -1
|
||||||
DESCENDING = -1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -16,72 +16,68 @@
|
|||||||
|
|
||||||
require 'mongo/util/ordered_hash'
|
require 'mongo/util/ordered_hash'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
# Provide administrative database methods: those having to do with
|
# Provide administrative database methods: those having to do with
|
||||||
# profiling and validation.
|
# profiling and validation.
|
||||||
class Admin
|
class Admin
|
||||||
|
|
||||||
def initialize(db)
|
def initialize(db)
|
||||||
@db = db
|
@db = db
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return the current database profiling level.
|
|
||||||
def profiling_level
|
|
||||||
oh = OrderedHash.new
|
|
||||||
oh[:profile] = -1
|
|
||||||
doc = @db.db_command(oh)
|
|
||||||
raise "Error with profile command: #{doc.inspect}" unless @db.ok?(doc) && doc['was'].kind_of?(Numeric)
|
|
||||||
case doc['was'].to_i
|
|
||||||
when 0
|
|
||||||
:off
|
|
||||||
when 1
|
|
||||||
:slow_only
|
|
||||||
when 2
|
|
||||||
:all
|
|
||||||
else
|
|
||||||
raise "Error: illegal profiling level value #{doc['was']}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Set database profiling level to :off, :slow_only, or :all.
|
|
||||||
def profiling_level=(level)
|
|
||||||
oh = OrderedHash.new
|
|
||||||
oh[:profile] = case level
|
|
||||||
when :off
|
|
||||||
0
|
|
||||||
when :slow_only
|
|
||||||
1
|
|
||||||
when :all
|
|
||||||
2
|
|
||||||
else
|
|
||||||
raise "Error: illegal profiling level value #{level}"
|
|
||||||
end
|
|
||||||
doc = @db.db_command(oh)
|
|
||||||
raise "Error with profile command: #{doc.inspect}" unless @db.ok?(doc)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return an array contining current profiling information from the
|
|
||||||
# database.
|
|
||||||
def profiling_info
|
|
||||||
@db.query(Collection.new(@db, DB::SYSTEM_PROFILE_COLLECTION), Query.new({})).to_a
|
|
||||||
end
|
|
||||||
|
|
||||||
# Validate a named collection by raising an exception if there is a
|
|
||||||
# problem or returning an interesting hash (see especially the
|
|
||||||
# 'result' string value) if all is well.
|
|
||||||
def validate_collection(name)
|
|
||||||
doc = @db.db_command(:validate => name)
|
|
||||||
raise "Error with validate command: #{doc.inspect}" unless @db.ok?(doc)
|
|
||||||
result = doc['result']
|
|
||||||
raise "Error with validation data: #{doc.inspect}" unless result.kind_of?(String)
|
|
||||||
raise "Error: invalid collection #{name}: #{doc.inspect}" if result =~ /\b(exception|corrupt)\b/i
|
|
||||||
doc
|
|
||||||
end
|
|
||||||
|
|
||||||
|
# Return the current database profiling level.
|
||||||
|
def profiling_level
|
||||||
|
oh = OrderedHash.new
|
||||||
|
oh[:profile] = -1
|
||||||
|
doc = @db.db_command(oh)
|
||||||
|
raise "Error with profile command: #{doc.inspect}" unless @db.ok?(doc) && doc['was'].kind_of?(Numeric)
|
||||||
|
case doc['was'].to_i
|
||||||
|
when 0
|
||||||
|
:off
|
||||||
|
when 1
|
||||||
|
:slow_only
|
||||||
|
when 2
|
||||||
|
:all
|
||||||
|
else
|
||||||
|
raise "Error: illegal profiling level value #{doc['was']}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Set database profiling level to :off, :slow_only, or :all.
|
||||||
|
def profiling_level=(level)
|
||||||
|
oh = OrderedHash.new
|
||||||
|
oh[:profile] = case level
|
||||||
|
when :off
|
||||||
|
0
|
||||||
|
when :slow_only
|
||||||
|
1
|
||||||
|
when :all
|
||||||
|
2
|
||||||
|
else
|
||||||
|
raise "Error: illegal profiling level value #{level}"
|
||||||
|
end
|
||||||
|
doc = @db.db_command(oh)
|
||||||
|
raise "Error with profile command: #{doc.inspect}" unless @db.ok?(doc)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return an array contining current profiling information from the
|
||||||
|
# database.
|
||||||
|
def profiling_info
|
||||||
|
@db.query(Collection.new(@db, DB::SYSTEM_PROFILE_COLLECTION), Query.new({})).to_a
|
||||||
|
end
|
||||||
|
|
||||||
|
# Validate a named collection by raising an exception if there is a
|
||||||
|
# problem or returning an interesting hash (see especially the
|
||||||
|
# 'result' string value) if all is well.
|
||||||
|
def validate_collection(name)
|
||||||
|
doc = @db.db_command(:validate => name)
|
||||||
|
raise "Error with validate command: #{doc.inspect}" unless @db.ok?(doc)
|
||||||
|
result = doc['result']
|
||||||
|
raise "Error with validation data: #{doc.inspect}" unless result.kind_of?(String)
|
||||||
|
raise "Error: invalid collection #{name}: #{doc.inspect}" if result =~ /\b(exception|corrupt)\b/i
|
||||||
|
doc
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -16,320 +16,318 @@
|
|||||||
|
|
||||||
require 'mongo/query'
|
require 'mongo/query'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
# A named collection of records in a database.
|
# A named collection of records in a database.
|
||||||
class Collection
|
class Collection
|
||||||
|
|
||||||
attr_reader :db, :name, :hint
|
attr_reader :db, :name, :hint
|
||||||
|
|
||||||
def initialize(db, name)
|
def initialize(db, name)
|
||||||
case name
|
case name
|
||||||
when Symbol, String
|
when Symbol, String
|
||||||
else
|
else
|
||||||
raise TypeError, "new_name must be a string or symbol"
|
raise TypeError, "new_name must be a string or symbol"
|
||||||
end
|
end
|
||||||
|
|
||||||
name = name.to_s
|
name = name.to_s
|
||||||
|
|
||||||
if name.empty? or name.include? ".."
|
if name.empty? or name.include? ".."
|
||||||
raise InvalidName, "collection names cannot be empty"
|
raise InvalidName, "collection names cannot be empty"
|
||||||
end
|
end
|
||||||
if name.include? "$" and not name.match(/^\$cmd/)
|
if name.include? "$" and not name.match(/^\$cmd/)
|
||||||
raise InvalidName, "collection names must not contain '$'"
|
raise InvalidName, "collection names must not contain '$'"
|
||||||
end
|
end
|
||||||
if name.match(/^\./) or name.match(/\.$/)
|
if name.match(/^\./) or name.match(/\.$/)
|
||||||
raise InvalidName, "collection names must not start or end with '.'"
|
raise InvalidName, "collection names must not start or end with '.'"
|
||||||
end
|
end
|
||||||
|
|
||||||
@db, @name = db, name
|
@db, @name = db, name
|
||||||
@hint = nil
|
@hint = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get a sub-collection of this collection by name.
|
||||||
|
#
|
||||||
|
# Raises InvalidName if an invalid collection name is used.
|
||||||
|
#
|
||||||
|
# :name :: the name of the collection to get
|
||||||
|
def [](name)
|
||||||
|
name = "#{self.name}.#{name}"
|
||||||
|
return Collection.new(self, name) if !db.strict? || db.collection_names.include?(name)
|
||||||
|
raise "Collection #{name} doesn't exist. Currently in strict mode."
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set hint fields to use and return +self+. hint may be a single field
|
||||||
|
# name, array of field names, or a hash (preferably an OrderedHash).
|
||||||
|
# May be +nil+.
|
||||||
|
def hint=(hint)
|
||||||
|
@hint = normalize_hint_fields(hint)
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
# Query the database.
|
||||||
|
#
|
||||||
|
# The +selector+ argument is a prototype document that all results must
|
||||||
|
# match. For example:
|
||||||
|
#
|
||||||
|
# collection.find({"hello" => "world"})
|
||||||
|
#
|
||||||
|
# only matches documents that have a key "hello" with value "world".
|
||||||
|
# Matches can have other keys *in addition* to "hello".
|
||||||
|
#
|
||||||
|
# If given an optional block +find+ will yield a Cursor to that block,
|
||||||
|
# close the cursor, and then return nil. This guarantees that partially
|
||||||
|
# evaluated cursors will be closed. If given no block +find+ returns a
|
||||||
|
# cursor.
|
||||||
|
#
|
||||||
|
# :selector :: A document (hash) specifying elements which must be
|
||||||
|
# present for a document to be included in the result set.
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# :fields :: Array of field names that should be returned in the result
|
||||||
|
# set ("_id" will always be included). By limiting results
|
||||||
|
# to a certain subset of fields you can cut down on network
|
||||||
|
# traffic and decoding time.
|
||||||
|
# :offset :: Start at this record when returning records
|
||||||
|
# :limit :: Maximum number of records to return
|
||||||
|
# :sort :: Either hash of field names as keys and 1/-1 as values; 1 ==
|
||||||
|
# ascending, -1 == descending, or array of field names (all
|
||||||
|
# assumed to be sorted in ascending order).
|
||||||
|
# :hint :: See #hint. This option overrides the collection-wide value.
|
||||||
|
# :snapshot :: If true, snapshot mode will be used for this query.
|
||||||
|
# Snapshot mode assures no duplicates are returned, or
|
||||||
|
# objects missed, which were preset at both the start and
|
||||||
|
# end of the query's execution. For details see
|
||||||
|
# http://www.mongodb.org/display/DOCS/How+to+do+Snapshotting+in+the+Mongo+Database
|
||||||
|
def find(selector={}, options={})
|
||||||
|
fields = options.delete(:fields)
|
||||||
|
fields = ["_id"] if fields && fields.empty?
|
||||||
|
offset = options.delete(:offset) || 0
|
||||||
|
limit = options.delete(:limit) || 0
|
||||||
|
sort = options.delete(:sort)
|
||||||
|
hint = options.delete(:hint)
|
||||||
|
snapshot = options.delete(:snapshot)
|
||||||
|
if hint
|
||||||
|
hint = normalize_hint_fields(hint)
|
||||||
|
else
|
||||||
|
hint = @hint # assumed to be normalized already
|
||||||
|
end
|
||||||
|
raise RuntimeError, "Unknown options [#{options.inspect}]" unless options.empty?
|
||||||
|
|
||||||
|
cursor = @db.query(self, Query.new(selector, fields, offset, limit, sort, hint, snapshot))
|
||||||
|
if block_given?
|
||||||
|
yield cursor
|
||||||
|
cursor.close()
|
||||||
|
nil
|
||||||
|
else
|
||||||
|
cursor
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get a single object from the database.
|
||||||
|
#
|
||||||
|
# Raises TypeError if the argument is of an improper type. Returns a
|
||||||
|
# single document (hash), or nil if no result is found.
|
||||||
|
#
|
||||||
|
# :spec_or_object_id :: a hash specifying elements which must be
|
||||||
|
# present for a document to be included in the result set OR an
|
||||||
|
# instance of ObjectID to be used as the value for an _id query.
|
||||||
|
# if nil an empty spec, {}, will be used.
|
||||||
|
# :options :: options, as passed to Collection#find
|
||||||
|
def find_one(spec_or_object_id=nil, options={})
|
||||||
|
spec = case spec_or_object_id
|
||||||
|
when nil
|
||||||
|
{}
|
||||||
|
when ObjectID
|
||||||
|
{:_id => spec_or_object_id}
|
||||||
|
when Hash
|
||||||
|
spec_or_object_id
|
||||||
|
else
|
||||||
|
raise TypeError, "spec_or_object_id must be an instance of ObjectID or Hash, or nil"
|
||||||
|
end
|
||||||
|
find(spec, options.merge(:limit => -1)).next_object
|
||||||
|
end
|
||||||
|
|
||||||
|
# DEPRECATED - use find_one instead
|
||||||
|
#
|
||||||
|
# Find the first record that matches +selector+. See #find.
|
||||||
|
def find_first(selector={}, options={})
|
||||||
|
warn "Collection#find_first is deprecated and will be removed. Please use Collection#find_one instead."
|
||||||
|
find_one(selector, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
# 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. Returns the _id of the
|
||||||
|
# saved document.
|
||||||
|
#
|
||||||
|
# :to_save :: the document (a hash) to be saved
|
||||||
|
#
|
||||||
|
# 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, :safe => options.delete(:safe))
|
||||||
|
id
|
||||||
|
else
|
||||||
|
insert(to_save, :safe => options.delete(:safe))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# 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
|
||||||
|
end
|
||||||
|
res.size > 1 ? res : res.first
|
||||||
|
end
|
||||||
|
alias_method :<<, :insert
|
||||||
|
|
||||||
# Get a sub-collection of this collection by name.
|
# Remove the records that match +selector+.
|
||||||
#
|
def remove(selector={})
|
||||||
# Raises InvalidName if an invalid collection name is used.
|
@db.remove_from_db(@name, selector)
|
||||||
#
|
end
|
||||||
# :name :: the name of the collection to get
|
|
||||||
def [](name)
|
# Remove all records.
|
||||||
name = "#{self.name}.#{name}"
|
def clear
|
||||||
return Collection.new(self, name) if !db.strict? || db.collection_names.include?(name)
|
remove({})
|
||||||
raise "Collection #{name} doesn't exist. Currently in strict mode."
|
end
|
||||||
|
|
||||||
|
# DEPRECATED - use update(... :upsert => true) instead
|
||||||
|
#
|
||||||
|
# Update records that match +selector+ by applying +obj+ as an update.
|
||||||
|
# If no match, inserts (???).
|
||||||
|
def repsert(selector, obj)
|
||||||
|
warn "Collection#repsert is deprecated and will be removed. Please use Collection#update instead."
|
||||||
|
update(selector, obj, :upsert => true)
|
||||||
|
end
|
||||||
|
|
||||||
|
# DEPRECATED - use update(... :upsert => false) instead
|
||||||
|
#
|
||||||
|
# Update records that match +selector+ by applying +obj+ as an update.
|
||||||
|
def replace(selector, obj)
|
||||||
|
warn "Collection#replace is deprecated and will be removed. Please use Collection#update instead."
|
||||||
|
update(selector, obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
# DEPRECATED - use update(... :upsert => false) instead
|
||||||
|
#
|
||||||
|
# Update records that match +selector+ by applying +obj+ as an update.
|
||||||
|
# Both +selector+ and +modifier_obj+ are required.
|
||||||
|
def modify(selector, modifier_obj)
|
||||||
|
warn "Collection#modify is deprecated and will be removed. Please use Collection#update instead."
|
||||||
|
update(selector, modifier_obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update a document(s) in this collection.
|
||||||
|
#
|
||||||
|
# :spec :: a hash specifying elements which must be present for
|
||||||
|
# a document to be updated
|
||||||
|
# :document :: a hash specifying the fields to be changed in the
|
||||||
|
# selected document(s), or (in the case of an upsert) the document to
|
||||||
|
# be inserted
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# :upsert :: if true, perform an upsert operation
|
||||||
|
# :safe :: if true, check that the update succeeded. OperationFailure
|
||||||
|
# will be raised on an error. Checking for safety requires an extra
|
||||||
|
# round-trip to the database
|
||||||
|
def update(spec, document, options={})
|
||||||
|
upsert = options.delete(:upsert)
|
||||||
|
safe = options.delete(:safe)
|
||||||
|
|
||||||
|
if upsert
|
||||||
|
@db.repsert_in_db(@name, spec, document)
|
||||||
|
else
|
||||||
|
@db.replace_in_db(@name, spec, document)
|
||||||
|
end
|
||||||
|
if safe
|
||||||
|
error = @db.error
|
||||||
|
if error
|
||||||
|
raise OperationFailure, error
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Set hint fields to use and return +self+. hint may be a single field
|
# Create a new index. +field_or_spec+
|
||||||
# name, array of field names, or a hash (preferably an OrderedHash).
|
# should be either a single field name or a Array of [field name,
|
||||||
# May be +nil+.
|
# direction] pairs. Directions should be specified as
|
||||||
def hint=(hint)
|
# Mongo::ASCENDING or Mongo::DESCENDING.
|
||||||
@hint = normalize_hint_fields(hint)
|
# +unique+ is an optional boolean indicating whether this index
|
||||||
self
|
# should enforce a uniqueness constraint.
|
||||||
|
def create_index(field_or_spec, unique=false)
|
||||||
|
@db.create_index(@name, field_or_spec, unique)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Drop index +name+.
|
||||||
|
def drop_index(name)
|
||||||
|
@db.drop_index(@name, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Drop all indexes.
|
||||||
|
def drop_indexes
|
||||||
|
# just need to call drop indexes with no args; will drop them all
|
||||||
|
@db.drop_index(@name, '*')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Drop the entire collection. USE WITH CAUTION.
|
||||||
|
def drop
|
||||||
|
@db.drop_collection(@name)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Perform a query similar to an SQL group by operation.
|
||||||
|
#
|
||||||
|
# Returns an array of grouped items.
|
||||||
|
#
|
||||||
|
# :keys :: Array of fields to group by
|
||||||
|
# :condition :: specification of rows to be considered (as a 'find'
|
||||||
|
# query specification)
|
||||||
|
# :initial :: initial value of the aggregation counter object
|
||||||
|
# :reduce :: aggregation function as a JavaScript string
|
||||||
|
# :command :: if true, run the group as a command instead of in an
|
||||||
|
# eval - it is likely that this option will eventually be
|
||||||
|
# deprecated and all groups will be run as commands
|
||||||
|
def group(keys, condition, initial, reduce, command=false)
|
||||||
|
if command
|
||||||
|
hash = {}
|
||||||
|
keys.each do |k|
|
||||||
|
hash[k] = 1
|
||||||
end
|
end
|
||||||
|
result = @db.db_command({"group" =>
|
||||||
# Query the database.
|
{
|
||||||
#
|
"ns" => @name,
|
||||||
# The +selector+ argument is a prototype document that all results must
|
"$reduce" => Code.new(reduce),
|
||||||
# match. For example:
|
"key" => hash,
|
||||||
#
|
"cond" => condition,
|
||||||
# collection.find({"hello" => "world"})
|
"initial" => initial}})
|
||||||
#
|
if result["ok"] == 1
|
||||||
# only matches documents that have a key "hello" with value "world".
|
return result["retval"]
|
||||||
# Matches can have other keys *in addition* to "hello".
|
else
|
||||||
#
|
raise OperationFailure, "group command failed: #{result['errmsg']}"
|
||||||
# If given an optional block +find+ will yield a Cursor to that block,
|
|
||||||
# close the cursor, and then return nil. This guarantees that partially
|
|
||||||
# evaluated cursors will be closed. If given no block +find+ returns a
|
|
||||||
# cursor.
|
|
||||||
#
|
|
||||||
# :selector :: A document (hash) specifying elements which must be
|
|
||||||
# present for a document to be included in the result set.
|
|
||||||
#
|
|
||||||
# Options:
|
|
||||||
# :fields :: Array of field names that should be returned in the result
|
|
||||||
# set ("_id" will always be included). By limiting results
|
|
||||||
# to a certain subset of fields you can cut down on network
|
|
||||||
# traffic and decoding time.
|
|
||||||
# :offset :: Start at this record when returning records
|
|
||||||
# :limit :: Maximum number of records to return
|
|
||||||
# :sort :: Either hash of field names as keys and 1/-1 as values; 1 ==
|
|
||||||
# ascending, -1 == descending, or array of field names (all
|
|
||||||
# assumed to be sorted in ascending order).
|
|
||||||
# :hint :: See #hint. This option overrides the collection-wide value.
|
|
||||||
# :snapshot :: If true, snapshot mode will be used for this query.
|
|
||||||
# Snapshot mode assures no duplicates are returned, or
|
|
||||||
# objects missed, which were preset at both the start and
|
|
||||||
# end of the query's execution. For details see
|
|
||||||
# http://www.mongodb.org/display/DOCS/How+to+do+Snapshotting+in+the+Mongo+Database
|
|
||||||
def find(selector={}, options={})
|
|
||||||
fields = options.delete(:fields)
|
|
||||||
fields = ["_id"] if fields && fields.empty?
|
|
||||||
offset = options.delete(:offset) || 0
|
|
||||||
limit = options.delete(:limit) || 0
|
|
||||||
sort = options.delete(:sort)
|
|
||||||
hint = options.delete(:hint)
|
|
||||||
snapshot = options.delete(:snapshot)
|
|
||||||
if hint
|
|
||||||
hint = normalize_hint_fields(hint)
|
|
||||||
else
|
|
||||||
hint = @hint # assumed to be normalized already
|
|
||||||
end
|
|
||||||
raise RuntimeError, "Unknown options [#{options.inspect}]" unless options.empty?
|
|
||||||
|
|
||||||
cursor = @db.query(self, Query.new(selector, fields, offset, limit, sort, hint, snapshot))
|
|
||||||
if block_given?
|
|
||||||
yield cursor
|
|
||||||
cursor.close()
|
|
||||||
nil
|
|
||||||
else
|
|
||||||
cursor
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
# Get a single object from the database.
|
group_function = <<EOS
|
||||||
#
|
|
||||||
# Raises TypeError if the argument is of an improper type. Returns a
|
|
||||||
# single document (hash), or nil if no result is found.
|
|
||||||
#
|
|
||||||
# :spec_or_object_id :: a hash specifying elements which must be
|
|
||||||
# present for a document to be included in the result set OR an
|
|
||||||
# instance of ObjectID to be used as the value for an _id query.
|
|
||||||
# if nil an empty spec, {}, will be used.
|
|
||||||
# :options :: options, as passed to Collection#find
|
|
||||||
def find_one(spec_or_object_id=nil, options={})
|
|
||||||
spec = case spec_or_object_id
|
|
||||||
when nil
|
|
||||||
{}
|
|
||||||
when ObjectID
|
|
||||||
{:_id => spec_or_object_id}
|
|
||||||
when Hash
|
|
||||||
spec_or_object_id
|
|
||||||
else
|
|
||||||
raise TypeError, "spec_or_object_id must be an instance of ObjectID or Hash, or nil"
|
|
||||||
end
|
|
||||||
find(spec, options.merge(:limit => -1)).next_object
|
|
||||||
end
|
|
||||||
|
|
||||||
# DEPRECATED - use find_one instead
|
|
||||||
#
|
|
||||||
# Find the first record that matches +selector+. See #find.
|
|
||||||
def find_first(selector={}, options={})
|
|
||||||
warn "Collection#find_first is deprecated and will be removed. Please use Collection#find_one instead."
|
|
||||||
find_one(selector, options)
|
|
||||||
end
|
|
||||||
|
|
||||||
# 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. Returns the _id of the
|
|
||||||
# saved document.
|
|
||||||
#
|
|
||||||
# :to_save :: the document (a hash) to be saved
|
|
||||||
#
|
|
||||||
# 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, :safe => options.delete(:safe))
|
|
||||||
id
|
|
||||||
else
|
|
||||||
insert(to_save, :safe => options.delete(:safe))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# Remove the records that match +selector+.
|
|
||||||
def remove(selector={})
|
|
||||||
@db.remove_from_db(@name, selector)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Remove all records.
|
|
||||||
def clear
|
|
||||||
remove({})
|
|
||||||
end
|
|
||||||
|
|
||||||
# DEPRECATED - use update(... :upsert => true) instead
|
|
||||||
#
|
|
||||||
# Update records that match +selector+ by applying +obj+ as an update.
|
|
||||||
# If no match, inserts (???).
|
|
||||||
def repsert(selector, obj)
|
|
||||||
warn "Collection#repsert is deprecated and will be removed. Please use Collection#update instead."
|
|
||||||
update(selector, obj, :upsert => true)
|
|
||||||
end
|
|
||||||
|
|
||||||
# DEPRECATED - use update(... :upsert => false) instead
|
|
||||||
#
|
|
||||||
# Update records that match +selector+ by applying +obj+ as an update.
|
|
||||||
def replace(selector, obj)
|
|
||||||
warn "Collection#replace is deprecated and will be removed. Please use Collection#update instead."
|
|
||||||
update(selector, obj)
|
|
||||||
end
|
|
||||||
|
|
||||||
# DEPRECATED - use update(... :upsert => false) instead
|
|
||||||
#
|
|
||||||
# Update records that match +selector+ by applying +obj+ as an update.
|
|
||||||
# Both +selector+ and +modifier_obj+ are required.
|
|
||||||
def modify(selector, modifier_obj)
|
|
||||||
warn "Collection#modify is deprecated and will be removed. Please use Collection#update instead."
|
|
||||||
update(selector, modifier_obj)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Update a document(s) in this collection.
|
|
||||||
#
|
|
||||||
# :spec :: a hash specifying elements which must be present for
|
|
||||||
# a document to be updated
|
|
||||||
# :document :: a hash specifying the fields to be changed in the
|
|
||||||
# selected document(s), or (in the case of an upsert) the document to
|
|
||||||
# be inserted
|
|
||||||
#
|
|
||||||
# Options:
|
|
||||||
# :upsert :: if true, perform an upsert operation
|
|
||||||
# :safe :: if true, check that the update succeeded. OperationFailure
|
|
||||||
# will be raised on an error. Checking for safety requires an extra
|
|
||||||
# round-trip to the database
|
|
||||||
def update(spec, document, options={})
|
|
||||||
upsert = options.delete(:upsert)
|
|
||||||
safe = options.delete(:safe)
|
|
||||||
|
|
||||||
if upsert
|
|
||||||
@db.repsert_in_db(@name, spec, document)
|
|
||||||
else
|
|
||||||
@db.replace_in_db(@name, spec, document)
|
|
||||||
end
|
|
||||||
if safe
|
|
||||||
error = @db.error
|
|
||||||
if error
|
|
||||||
raise OperationFailure, error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Create a new index. +field_or_spec+
|
|
||||||
# should be either a single field name or a Array of [field name,
|
|
||||||
# direction] pairs. Directions should be specified as
|
|
||||||
# XGen::Mongo::ASCENDING or XGen::Mongo::DESCENDING.
|
|
||||||
# +unique+ is an optional boolean indicating whether this index
|
|
||||||
# should enforce a uniqueness constraint.
|
|
||||||
def create_index(field_or_spec, unique=false)
|
|
||||||
@db.create_index(@name, field_or_spec, unique)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Drop index +name+.
|
|
||||||
def drop_index(name)
|
|
||||||
@db.drop_index(@name, name)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Drop all indexes.
|
|
||||||
def drop_indexes
|
|
||||||
# just need to call drop indexes with no args; will drop them all
|
|
||||||
@db.drop_index(@name, '*')
|
|
||||||
end
|
|
||||||
|
|
||||||
# Drop the entire collection. USE WITH CAUTION.
|
|
||||||
def drop
|
|
||||||
@db.drop_collection(@name)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Perform a query similar to an SQL group by operation.
|
|
||||||
#
|
|
||||||
# Returns an array of grouped items.
|
|
||||||
#
|
|
||||||
# :keys :: Array of fields to group by
|
|
||||||
# :condition :: specification of rows to be considered (as a 'find'
|
|
||||||
# query specification)
|
|
||||||
# :initial :: initial value of the aggregation counter object
|
|
||||||
# :reduce :: aggregation function as a JavaScript string
|
|
||||||
# :command :: if true, run the group as a command instead of in an
|
|
||||||
# eval - it is likely that this option will eventually be
|
|
||||||
# deprecated and all groups will be run as commands
|
|
||||||
def group(keys, condition, initial, reduce, command=false)
|
|
||||||
if command
|
|
||||||
hash = {}
|
|
||||||
keys.each do |k|
|
|
||||||
hash[k] = 1
|
|
||||||
end
|
|
||||||
result = @db.db_command({"group" =>
|
|
||||||
{
|
|
||||||
"ns" => @name,
|
|
||||||
"$reduce" => Code.new(reduce),
|
|
||||||
"key" => hash,
|
|
||||||
"cond" => condition,
|
|
||||||
"initial" => initial}})
|
|
||||||
if result["ok"] == 1
|
|
||||||
return result["retval"]
|
|
||||||
else
|
|
||||||
raise OperationFailure, "group command failed: #{result['errmsg']}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
group_function = <<EOS
|
|
||||||
function () {
|
function () {
|
||||||
var c = db[ns].find(condition);
|
var c = db[ns].find(condition);
|
||||||
var map = new Map();
|
var map = new Map();
|
||||||
@ -354,88 +352,85 @@ function () {
|
|||||||
return {"result": map.values()};
|
return {"result": map.values()};
|
||||||
}
|
}
|
||||||
EOS
|
EOS
|
||||||
return @db.eval(Code.new(group_function,
|
return @db.eval(Code.new(group_function,
|
||||||
{
|
{
|
||||||
"ns" => @name,
|
"ns" => @name,
|
||||||
"keys" => keys,
|
"keys" => keys,
|
||||||
"condition" => condition,
|
"condition" => condition,
|
||||||
"initial" => initial
|
"initial" => initial
|
||||||
}))["result"]
|
}))["result"]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Rename this collection.
|
# Rename this collection.
|
||||||
#
|
#
|
||||||
# If operating in auth mode, client must be authorized as an admin to
|
# If operating in auth mode, client must be authorized as an admin to
|
||||||
# perform this operation. Raises +InvalidName+ if +new_name+ is an invalid
|
# perform this operation. Raises +InvalidName+ if +new_name+ is an invalid
|
||||||
# collection name.
|
# collection name.
|
||||||
#
|
#
|
||||||
# :new_name :: new name for this collection
|
# :new_name :: new name for this collection
|
||||||
def rename(new_name)
|
def rename(new_name)
|
||||||
case new_name
|
case new_name
|
||||||
when Symbol, String
|
when Symbol, String
|
||||||
else
|
else
|
||||||
raise TypeError, "new_name must be a string or symbol"
|
raise TypeError, "new_name must be a string or symbol"
|
||||||
end
|
end
|
||||||
|
|
||||||
new_name = new_name.to_s
|
new_name = new_name.to_s
|
||||||
|
|
||||||
if new_name.empty? or new_name.include? ".."
|
if new_name.empty? or new_name.include? ".."
|
||||||
raise InvalidName, "collection names cannot be empty"
|
raise InvalidName, "collection names cannot be empty"
|
||||||
end
|
end
|
||||||
if new_name.include? "$"
|
if new_name.include? "$"
|
||||||
raise InvalidName, "collection names must not contain '$'"
|
raise InvalidName, "collection names must not contain '$'"
|
||||||
end
|
end
|
||||||
if new_name.match(/^\./) or new_name.match(/\.$/)
|
if new_name.match(/^\./) or new_name.match(/\.$/)
|
||||||
raise InvalidName, "collection names must not start or end with '.'"
|
raise InvalidName, "collection names must not start or end with '.'"
|
||||||
end
|
end
|
||||||
|
|
||||||
@db.rename_collection(@name, new_name)
|
@db.rename_collection(@name, new_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get information on the indexes for the collection +collection_name+.
|
# Get information on the indexes for the collection +collection_name+.
|
||||||
# Returns a hash where the keys are index names (as returned by
|
# Returns a hash where the keys are index names (as returned by
|
||||||
# Collection#create_index and the values are lists of [key, direction]
|
# Collection#create_index and the values are lists of [key, direction]
|
||||||
# pairs specifying the index (as passed to Collection#create_index).
|
# pairs specifying the index (as passed to Collection#create_index).
|
||||||
def index_information
|
def index_information
|
||||||
@db.index_information(@name)
|
@db.index_information(@name)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return a hash containing options that apply to this collection.
|
# Return a hash containing options that apply to this collection.
|
||||||
# '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_object()['options']
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get the number of documents in this collection.
|
# Get the number of documents in this collection.
|
||||||
#
|
#
|
||||||
# Specifying a +selector+ is DEPRECATED and will be removed. Please use
|
# Specifying a +selector+ is DEPRECATED and will be removed. Please use
|
||||||
# find(selector).count() instead.
|
# find(selector).count() instead.
|
||||||
def count(selector=nil)
|
def count(selector=nil)
|
||||||
if selector
|
if selector
|
||||||
warn "specifying a selector for Collection#count is deprecated and will be removed. Please use Collection.find(selector).count instead."
|
warn "specifying a selector for Collection#count is deprecated and will be removed. Please use Collection.find(selector).count instead."
|
||||||
end
|
end
|
||||||
find(selector || {}).count()
|
find(selector || {}).count()
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def normalize_hint_fields(hint)
|
def normalize_hint_fields(hint)
|
||||||
case hint
|
case hint
|
||||||
when String
|
when String
|
||||||
{hint => 1}
|
{hint => 1}
|
||||||
when Hash
|
when Hash
|
||||||
hint
|
hint
|
||||||
when nil
|
when nil
|
||||||
nil
|
nil
|
||||||
else
|
else
|
||||||
h = OrderedHash.new
|
h = OrderedHash.new
|
||||||
hint.to_a.each { |k| h[k] = 1 }
|
hint.to_a.each { |k| h[k] = 1 }
|
||||||
h
|
h
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,232 +18,228 @@ require 'mongo/message'
|
|||||||
require 'mongo/util/byte_buffer'
|
require 'mongo/util/byte_buffer'
|
||||||
require 'mongo/util/bson'
|
require 'mongo/util/bson'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
# A cursor over query results. Returned objects are hashes.
|
# A cursor over query results. Returned objects are hashes.
|
||||||
class Cursor
|
class Cursor
|
||||||
|
|
||||||
include Enumerable
|
include Enumerable
|
||||||
|
|
||||||
RESPONSE_HEADER_SIZE = 20
|
RESPONSE_HEADER_SIZE = 20
|
||||||
|
|
||||||
attr_reader :db, :collection, :query
|
attr_reader :db, :collection, :query
|
||||||
|
|
||||||
def initialize(db, collection, query, admin=false)
|
def initialize(db, collection, query, admin=false)
|
||||||
@db, @collection, @query, @admin = db, collection, query, admin
|
@db, @collection, @query, @admin = db, collection, query, admin
|
||||||
@num_to_return = @query.number_to_return || 0
|
@num_to_return = @query.number_to_return || 0
|
||||||
@cache = []
|
@cache = []
|
||||||
@closed = false
|
@closed = false
|
||||||
@can_call_to_a = true
|
@can_call_to_a = true
|
||||||
@query_run = false
|
@query_run = false
|
||||||
@rows = nil
|
@rows = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def closed?; @closed; end
|
def closed?; @closed; 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. We do not check @num_to_return; #each is
|
# more records to retrieve. We do not check @num_to_return; #each is
|
||||||
# responsible for doing that.
|
# responsible for doing that.
|
||||||
def more?
|
def more?
|
||||||
num_remaining > 0
|
num_remaining > 0
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return the next object or nil if there are no more. Raises an error
|
# Return the next object or nil if there are no more. Raises an error
|
||||||
# if necessary.
|
# if necessary.
|
||||||
def next_object
|
def next_object
|
||||||
refill_via_get_more if num_remaining == 0
|
refill_via_get_more if num_remaining == 0
|
||||||
o = @cache.shift
|
o = @cache.shift
|
||||||
|
|
||||||
if o && o['$err']
|
if o && o['$err']
|
||||||
err = o['$err']
|
err = o['$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. If the db has auto connect option and a pair of
|
||||||
# servers, next request will re-open on master server.
|
# servers, next request will re-open on master server.
|
||||||
@db.close if err == "not master"
|
@db.close if err == "not master"
|
||||||
|
|
||||||
raise err
|
raise err
|
||||||
end
|
|
||||||
|
|
||||||
o
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the size of the results set for this query.
|
|
||||||
#
|
|
||||||
# Returns the number of objects in the results set for this query. Does
|
|
||||||
# not take limit and skip into account. Raises OperationFailure on a
|
|
||||||
# database error.
|
|
||||||
def count
|
|
||||||
command = OrderedHash["count", @collection.name,
|
|
||||||
"query", @query.selector]
|
|
||||||
response = @db.db_command(command)
|
|
||||||
return response['n'].to_i if response['ok'] == 1
|
|
||||||
return 0 if response['errmsg'] == "ns missing"
|
|
||||||
raise OperationFailure, "Count failed: #{response['errmsg']}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Iterate over each object, yielding it to the given block. At most
|
|
||||||
# @num_to_return records are returned (or all of them, if
|
|
||||||
# @num_to_return is 0).
|
|
||||||
#
|
|
||||||
# If #to_a has already been called then this method uses the array
|
|
||||||
# that we store internally. In that case, #each can be called multiple
|
|
||||||
# times because it re-uses that array.
|
|
||||||
#
|
|
||||||
# You can call #each after calling #to_a (multiple times even, because
|
|
||||||
# it will use the internally-stored array), but you can't call #to_a
|
|
||||||
# after calling #each unless you also called it before calling #each.
|
|
||||||
# If you try to do that, an error will be raised.
|
|
||||||
def each
|
|
||||||
if @rows # Already turned into an array
|
|
||||||
@rows.each { |row| yield row }
|
|
||||||
else
|
|
||||||
num_returned = 0
|
|
||||||
while more? && (@num_to_return <= 0 || num_returned < @num_to_return)
|
|
||||||
yield next_object()
|
|
||||||
num_returned += 1
|
|
||||||
end
|
|
||||||
@can_call_to_a = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return all of the rows (up to the +num_to_return+ value specified in
|
|
||||||
# #new) as an array. Calling this multiple times will work fine; it
|
|
||||||
# always returns the same array.
|
|
||||||
#
|
|
||||||
# Don't use this if you're expecting large amounts of data, of course.
|
|
||||||
# All of the returned rows are kept in an array stored in this object
|
|
||||||
# so it can be reused.
|
|
||||||
#
|
|
||||||
# You can call #each after calling #to_a (multiple times even, because
|
|
||||||
# it will use the internally-stored array), but you can't call #to_a
|
|
||||||
# after calling #each unless you also called it before calling #each.
|
|
||||||
# If you try to do that, an error will be raised.
|
|
||||||
def to_a
|
|
||||||
return @rows if @rows
|
|
||||||
raise "can't call Cursor#to_a after calling Cursor#each" unless @can_call_to_a
|
|
||||||
@rows = []
|
|
||||||
num_returned = 0
|
|
||||||
while more? && (@num_to_return <= 0 || num_returned < @num_to_return)
|
|
||||||
@rows << next_object()
|
|
||||||
num_returned += 1
|
|
||||||
end
|
|
||||||
@rows
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns an explain plan record.
|
|
||||||
def explain
|
|
||||||
old_val = @query.explain
|
|
||||||
@query.explain = true
|
|
||||||
|
|
||||||
c = Cursor.new(@db, @collection, @query)
|
|
||||||
explanation = c.next_object
|
|
||||||
c.close
|
|
||||||
|
|
||||||
@query.explain = old_val
|
|
||||||
explanation
|
|
||||||
end
|
|
||||||
|
|
||||||
# Close the cursor.
|
|
||||||
#
|
|
||||||
# Note: if a cursor is read until exhausted (read until OP_QUERY or
|
|
||||||
# OP_GETMORE returns zero for the cursor id), there is no need to
|
|
||||||
# close it by calling this method.
|
|
||||||
def close
|
|
||||||
@db.send_to_db(KillCursorsMessage.new(@cursor_id)) if @cursor_id
|
|
||||||
@cache = []
|
|
||||||
@cursor_id = 0
|
|
||||||
@closed = true
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def read_all
|
|
||||||
read_message_header
|
|
||||||
read_response_header
|
|
||||||
read_objects_off_wire
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_objects_off_wire
|
|
||||||
while doc = next_object_on_wire
|
|
||||||
@cache << doc
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_message_header
|
|
||||||
MessageHeader.new.read_header(@db)
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_response_header
|
|
||||||
header_buf = ByteBuffer.new
|
|
||||||
header_buf.put_array(@db.receive_full(RESPONSE_HEADER_SIZE).unpack("C*"))
|
|
||||||
raise "Short read for DB response header; expected #{RESPONSE_HEADER_SIZE} bytes, saw #{header_buf.length}" unless header_buf.length == RESPONSE_HEADER_SIZE
|
|
||||||
header_buf.rewind
|
|
||||||
@result_flags = header_buf.get_int
|
|
||||||
@cursor_id = header_buf.get_long
|
|
||||||
@starting_from = header_buf.get_int
|
|
||||||
@n_returned = header_buf.get_int
|
|
||||||
@n_remaining = @n_returned
|
|
||||||
end
|
|
||||||
|
|
||||||
def num_remaining
|
|
||||||
refill_via_get_more if @cache.length == 0
|
|
||||||
@cache.length
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def next_object_on_wire
|
|
||||||
send_query_if_needed
|
|
||||||
# if @n_remaining is 0 but we have a non-zero cursor, there are more
|
|
||||||
# to fetch, so do a GetMore operation, but don't do it here - do it
|
|
||||||
# when someone pulls an object out of the cache and it's empty
|
|
||||||
return nil if @n_remaining == 0
|
|
||||||
object_from_stream
|
|
||||||
end
|
|
||||||
|
|
||||||
def refill_via_get_more
|
|
||||||
if send_query_if_needed or @cursor_id == 0
|
|
||||||
return
|
|
||||||
end
|
|
||||||
@db._synchronize {
|
|
||||||
@db.send_to_db(GetMoreMessage.new(@admin ? 'admin' : @db.name, @collection.name, @cursor_id))
|
|
||||||
read_all
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def object_from_stream
|
|
||||||
buf = ByteBuffer.new
|
|
||||||
buf.put_array(@db.receive_full(4).unpack("C*"))
|
|
||||||
buf.rewind
|
|
||||||
size = buf.get_int
|
|
||||||
buf.put_array(@db.receive_full(size - 4).unpack("C*"), 4)
|
|
||||||
@n_remaining -= 1
|
|
||||||
buf.rewind
|
|
||||||
BSON.new.deserialize(buf)
|
|
||||||
end
|
|
||||||
|
|
||||||
def send_query_if_needed
|
|
||||||
# Run query first time we request an object from the wire
|
|
||||||
if @query_run
|
|
||||||
false
|
|
||||||
else
|
|
||||||
@db._synchronize {
|
|
||||||
@db.send_query_message(QueryMessage.new(@admin ? 'admin' : @db.name, @collection.name, @query))
|
|
||||||
@query_run = true
|
|
||||||
read_all
|
|
||||||
}
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
"DBResponse(flags=#@result_flags, cursor_id=#@cursor_id, start=#@starting_from, n_returned=#@n_returned)"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
o
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the size of the results set for this query.
|
||||||
|
#
|
||||||
|
# Returns the number of objects in the results set for this query. Does
|
||||||
|
# not take limit and skip into account. Raises OperationFailure on a
|
||||||
|
# database error.
|
||||||
|
def count
|
||||||
|
command = OrderedHash["count", @collection.name,
|
||||||
|
"query", @query.selector]
|
||||||
|
response = @db.db_command(command)
|
||||||
|
return response['n'].to_i if response['ok'] == 1
|
||||||
|
return 0 if response['errmsg'] == "ns missing"
|
||||||
|
raise OperationFailure, "Count failed: #{response['errmsg']}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Iterate over each object, yielding it to the given block. At most
|
||||||
|
# @num_to_return records are returned (or all of them, if
|
||||||
|
# @num_to_return is 0).
|
||||||
|
#
|
||||||
|
# If #to_a has already been called then this method uses the array
|
||||||
|
# that we store internally. In that case, #each can be called multiple
|
||||||
|
# times because it re-uses that array.
|
||||||
|
#
|
||||||
|
# You can call #each after calling #to_a (multiple times even, because
|
||||||
|
# it will use the internally-stored array), but you can't call #to_a
|
||||||
|
# after calling #each unless you also called it before calling #each.
|
||||||
|
# If you try to do that, an error will be raised.
|
||||||
|
def each
|
||||||
|
if @rows # Already turned into an array
|
||||||
|
@rows.each { |row| yield row }
|
||||||
|
else
|
||||||
|
num_returned = 0
|
||||||
|
while more? && (@num_to_return <= 0 || num_returned < @num_to_return)
|
||||||
|
yield next_object()
|
||||||
|
num_returned += 1
|
||||||
|
end
|
||||||
|
@can_call_to_a = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return all of the rows (up to the +num_to_return+ value specified in
|
||||||
|
# #new) as an array. Calling this multiple times will work fine; it
|
||||||
|
# always returns the same array.
|
||||||
|
#
|
||||||
|
# Don't use this if you're expecting large amounts of data, of course.
|
||||||
|
# All of the returned rows are kept in an array stored in this object
|
||||||
|
# so it can be reused.
|
||||||
|
#
|
||||||
|
# You can call #each after calling #to_a (multiple times even, because
|
||||||
|
# it will use the internally-stored array), but you can't call #to_a
|
||||||
|
# after calling #each unless you also called it before calling #each.
|
||||||
|
# If you try to do that, an error will be raised.
|
||||||
|
def to_a
|
||||||
|
return @rows if @rows
|
||||||
|
raise "can't call Cursor#to_a after calling Cursor#each" unless @can_call_to_a
|
||||||
|
@rows = []
|
||||||
|
num_returned = 0
|
||||||
|
while more? && (@num_to_return <= 0 || num_returned < @num_to_return)
|
||||||
|
@rows << next_object()
|
||||||
|
num_returned += 1
|
||||||
|
end
|
||||||
|
@rows
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns an explain plan record.
|
||||||
|
def explain
|
||||||
|
old_val = @query.explain
|
||||||
|
@query.explain = true
|
||||||
|
|
||||||
|
c = Cursor.new(@db, @collection, @query)
|
||||||
|
explanation = c.next_object
|
||||||
|
c.close
|
||||||
|
|
||||||
|
@query.explain = old_val
|
||||||
|
explanation
|
||||||
|
end
|
||||||
|
|
||||||
|
# Close the cursor.
|
||||||
|
#
|
||||||
|
# Note: if a cursor is read until exhausted (read until OP_QUERY or
|
||||||
|
# OP_GETMORE returns zero for the cursor id), there is no need to
|
||||||
|
# close it by calling this method.
|
||||||
|
def close
|
||||||
|
@db.send_to_db(KillCursorsMessage.new(@cursor_id)) if @cursor_id
|
||||||
|
@cache = []
|
||||||
|
@cursor_id = 0
|
||||||
|
@closed = true
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def read_all
|
||||||
|
read_message_header
|
||||||
|
read_response_header
|
||||||
|
read_objects_off_wire
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_objects_off_wire
|
||||||
|
while doc = next_object_on_wire
|
||||||
|
@cache << doc
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_message_header
|
||||||
|
MessageHeader.new.read_header(@db)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_response_header
|
||||||
|
header_buf = ByteBuffer.new
|
||||||
|
header_buf.put_array(@db.receive_full(RESPONSE_HEADER_SIZE).unpack("C*"))
|
||||||
|
raise "Short read for DB response header; expected #{RESPONSE_HEADER_SIZE} bytes, saw #{header_buf.length}" unless header_buf.length == RESPONSE_HEADER_SIZE
|
||||||
|
header_buf.rewind
|
||||||
|
@result_flags = header_buf.get_int
|
||||||
|
@cursor_id = header_buf.get_long
|
||||||
|
@starting_from = header_buf.get_int
|
||||||
|
@n_returned = header_buf.get_int
|
||||||
|
@n_remaining = @n_returned
|
||||||
|
end
|
||||||
|
|
||||||
|
def num_remaining
|
||||||
|
refill_via_get_more if @cache.length == 0
|
||||||
|
@cache.length
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def next_object_on_wire
|
||||||
|
send_query_if_needed
|
||||||
|
# if @n_remaining is 0 but we have a non-zero cursor, there are more
|
||||||
|
# to fetch, so do a GetMore operation, but don't do it here - do it
|
||||||
|
# when someone pulls an object out of the cache and it's empty
|
||||||
|
return nil if @n_remaining == 0
|
||||||
|
object_from_stream
|
||||||
|
end
|
||||||
|
|
||||||
|
def refill_via_get_more
|
||||||
|
if send_query_if_needed or @cursor_id == 0
|
||||||
|
return
|
||||||
|
end
|
||||||
|
@db._synchronize {
|
||||||
|
@db.send_to_db(GetMoreMessage.new(@admin ? 'admin' : @db.name, @collection.name, @cursor_id))
|
||||||
|
read_all
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def object_from_stream
|
||||||
|
buf = ByteBuffer.new
|
||||||
|
buf.put_array(@db.receive_full(4).unpack("C*"))
|
||||||
|
buf.rewind
|
||||||
|
size = buf.get_int
|
||||||
|
buf.put_array(@db.receive_full(size - 4).unpack("C*"), 4)
|
||||||
|
@n_remaining -= 1
|
||||||
|
buf.rewind
|
||||||
|
BSON.new.deserialize(buf)
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_query_if_needed
|
||||||
|
# Run query first time we request an object from the wire
|
||||||
|
if @query_run
|
||||||
|
false
|
||||||
|
else
|
||||||
|
@db._synchronize {
|
||||||
|
@db.send_query_message(QueryMessage.new(@admin ? 'admin' : @db.name, @collection.name, @query))
|
||||||
|
@query_run = true
|
||||||
|
read_all
|
||||||
|
}
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"DBResponse(flags=#@result_flags, cursor_id=#@cursor_id, start=#@starting_from, n_returned=#@n_returned)"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
1038
lib/mongo/db.rb
1038
lib/mongo/db.rb
File diff suppressed because it is too large
Load Diff
@ -14,14 +14,10 @@
|
|||||||
|
|
||||||
# Exceptions raised by the MongoDB driver.
|
# Exceptions raised by the MongoDB driver.
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
# Raised when a database operation fails.
|
||||||
module Driver
|
class OperationFailure < RuntimeError; end
|
||||||
# Raised when a database operation fails.
|
|
||||||
class OperationFailure < RuntimeError; end
|
|
||||||
|
|
||||||
# Raised when an invalid name is used.
|
# Raised when an invalid name is used.
|
||||||
class InvalidName < RuntimeError; end
|
class InvalidName < RuntimeError; end
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -19,78 +19,74 @@ require 'mongo/util/byte_buffer'
|
|||||||
require 'mongo/util/ordered_hash'
|
require 'mongo/util/ordered_hash'
|
||||||
|
|
||||||
|
|
||||||
module XGen
|
module GridFS
|
||||||
module Mongo
|
|
||||||
module GridFS
|
|
||||||
|
|
||||||
# A chunk stores a portion of GridStore data.
|
# A chunk stores a portion of GridStore data.
|
||||||
class Chunk
|
class Chunk
|
||||||
|
|
||||||
DEFAULT_CHUNK_SIZE = 1024 * 256
|
DEFAULT_CHUNK_SIZE = 1024 * 256
|
||||||
|
|
||||||
attr_reader :object_id, :chunk_number
|
attr_reader :object_id, :chunk_number
|
||||||
attr_accessor :data
|
attr_accessor :data
|
||||||
|
|
||||||
def initialize(file, mongo_object={})
|
def initialize(file, mongo_object={})
|
||||||
@file = file
|
@file = file
|
||||||
@object_id = mongo_object['_id'] || XGen::Mongo::Driver::ObjectID.new
|
@object_id = mongo_object['_id'] || Mongo::ObjectID.new
|
||||||
@chunk_number = mongo_object['n'] || 0
|
@chunk_number = mongo_object['n'] || 0
|
||||||
|
|
||||||
@data = ByteBuffer.new
|
@data = ByteBuffer.new
|
||||||
case mongo_object['data']
|
case mongo_object['data']
|
||||||
when String
|
when String
|
||||||
mongo_object['data'].each_byte { |b| @data.put(b) }
|
mongo_object['data'].each_byte { |b| @data.put(b) }
|
||||||
when ByteBuffer
|
when ByteBuffer
|
||||||
@data.put_array(mongo_object['data'].to_a)
|
@data.put_array(mongo_object['data'].to_a)
|
||||||
when Array
|
when Array
|
||||||
@data.put_array(mongo_object['data'])
|
@data.put_array(mongo_object['data'])
|
||||||
when nil
|
when nil
|
||||||
else
|
else
|
||||||
raise "illegal chunk format; data is #{mongo_object['data'] ? (' ' + mongo_object['data'].class.name) : 'nil'}"
|
raise "illegal chunk format; data is #{mongo_object['data'] ? (' ' + mongo_object['data'].class.name) : 'nil'}"
|
||||||
end
|
end
|
||||||
@data.rewind
|
@data.rewind
|
||||||
end
|
end
|
||||||
|
|
||||||
def pos; @data.position; end
|
def pos; @data.position; end
|
||||||
def pos=(pos); @data.position = pos; end
|
def pos=(pos); @data.position = pos; end
|
||||||
def eof?; !@data.more?; end
|
def eof?; !@data.more?; end
|
||||||
|
|
||||||
def size; @data.size; end
|
def size; @data.size; end
|
||||||
alias_method :length, :size
|
alias_method :length, :size
|
||||||
|
|
||||||
# Erase all data after current position.
|
|
||||||
def truncate
|
|
||||||
if @data.position < @data.length
|
|
||||||
curr_data = @data
|
|
||||||
@data = ByteBuffer.new
|
|
||||||
@data.put_array(curr_data.to_a[0...curr_data.position])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def getc
|
|
||||||
@data.more? ? @data.get : nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def putc(byte)
|
|
||||||
@data.put(byte)
|
|
||||||
end
|
|
||||||
|
|
||||||
def save
|
|
||||||
coll = @file.chunk_collection
|
|
||||||
coll.remove({'_id' => @object_id})
|
|
||||||
coll.insert(to_mongo_object)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_mongo_object
|
|
||||||
h = OrderedHash.new
|
|
||||||
h['_id'] = @object_id
|
|
||||||
h['files_id'] = @file.files_id
|
|
||||||
h['n'] = @chunk_number
|
|
||||||
h['data'] = data
|
|
||||||
h
|
|
||||||
end
|
|
||||||
|
|
||||||
|
# Erase all data after current position.
|
||||||
|
def truncate
|
||||||
|
if @data.position < @data.length
|
||||||
|
curr_data = @data
|
||||||
|
@data = ByteBuffer.new
|
||||||
|
@data.put_array(curr_data.to_a[0...curr_data.position])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def getc
|
||||||
|
@data.more? ? @data.get : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def putc(byte)
|
||||||
|
@data.put(byte)
|
||||||
|
end
|
||||||
|
|
||||||
|
def save
|
||||||
|
coll = @file.chunk_collection
|
||||||
|
coll.remove({'_id' => @object_id})
|
||||||
|
coll.insert(to_mongo_object)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_mongo_object
|
||||||
|
h = OrderedHash.new
|
||||||
|
h['_id'] = @object_id
|
||||||
|
h['files_id'] = @file.files_id
|
||||||
|
h['n'] = @chunk_number
|
||||||
|
h['data'] = data
|
||||||
|
h
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -18,451 +18,447 @@ require 'mongo/types/objectid'
|
|||||||
require 'mongo/util/ordered_hash'
|
require 'mongo/util/ordered_hash'
|
||||||
require 'mongo/gridfs/chunk'
|
require 'mongo/gridfs/chunk'
|
||||||
|
|
||||||
module XGen
|
module GridFS
|
||||||
module Mongo
|
|
||||||
module GridFS
|
|
||||||
|
|
||||||
# GridStore is an IO-like object that provides input and output for
|
# GridStore is an IO-like object that provides input and output for
|
||||||
# streams of data to Mongo. See Mongo's documentation about GridFS for
|
# streams of data to Mongo. See Mongo's documentation about GridFS for
|
||||||
# storage implementation details.
|
# storage implementation details.
|
||||||
|
#
|
||||||
|
# Example code:
|
||||||
|
#
|
||||||
|
# require 'mongo/gridfs'
|
||||||
|
# GridStore.open(database, 'filename', 'w') { |f|
|
||||||
|
# f.puts "Hello, world!"
|
||||||
|
# }
|
||||||
|
# GridStore.open(database, 'filename, 'r') { |f|
|
||||||
|
# puts f.read # => Hello, world!\n
|
||||||
|
# }
|
||||||
|
# GridStore.open(database, 'filename', 'w+') { |f|
|
||||||
|
# f.puts "But wait, there's more!"
|
||||||
|
# }
|
||||||
|
# GridStore.open(database, 'filename, 'r') { |f|
|
||||||
|
# puts f.read # => Hello, world!\nBut wait, there's more!\n
|
||||||
|
# }
|
||||||
|
class GridStore
|
||||||
|
|
||||||
|
DEFAULT_ROOT_COLLECTION = 'fs'
|
||||||
|
DEFAULT_CONTENT_TYPE = 'text/plain'
|
||||||
|
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
attr_accessor :filename
|
||||||
|
|
||||||
|
# Array of strings; may be +nil+
|
||||||
|
attr_accessor :aliases
|
||||||
|
|
||||||
|
# Default is DEFAULT_CONTENT_TYPE
|
||||||
|
attr_accessor :content_type
|
||||||
|
|
||||||
|
attr_accessor :metadata
|
||||||
|
|
||||||
|
attr_reader :files_id
|
||||||
|
|
||||||
|
# Time that the file was first saved.
|
||||||
|
attr_reader :upload_date
|
||||||
|
|
||||||
|
attr_reader :chunk_size
|
||||||
|
|
||||||
|
attr_accessor :lineno
|
||||||
|
|
||||||
|
attr_reader :md5
|
||||||
|
|
||||||
|
class << self
|
||||||
|
|
||||||
|
def exist?(db, name, root_collection=DEFAULT_ROOT_COLLECTION)
|
||||||
|
db.collection("#{root_collection}.files").find({'filename' => name}).next_object != nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def open(db, name, mode, options={})
|
||||||
|
gs = self.new(db, name, mode, options)
|
||||||
|
result = nil
|
||||||
|
begin
|
||||||
|
result = yield gs if block_given?
|
||||||
|
ensure
|
||||||
|
gs.close
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def read(db, name, length=nil, offset=nil)
|
||||||
|
GridStore.open(db, name, 'r') { |gs|
|
||||||
|
gs.seek(offset) if offset
|
||||||
|
gs.read(length)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# List the contains of all GridFS files stored in the given db and
|
||||||
|
# root collection.
|
||||||
#
|
#
|
||||||
# Example code:
|
# :db :: the database to use
|
||||||
#
|
#
|
||||||
# require 'mongo/gridfs'
|
# :root_collection :: the root collection to use
|
||||||
# GridStore.open(database, 'filename', 'w') { |f|
|
def list(db, root_collection=DEFAULT_ROOT_COLLECTION)
|
||||||
# f.puts "Hello, world!"
|
db.collection("#{root_collection}.files").find().map { |f|
|
||||||
# }
|
f['filename']
|
||||||
# GridStore.open(database, 'filename, 'r') { |f|
|
}
|
||||||
# puts f.read # => Hello, world!\n
|
end
|
||||||
# }
|
|
||||||
# GridStore.open(database, 'filename', 'w+') { |f|
|
|
||||||
# f.puts "But wait, there's more!"
|
|
||||||
# }
|
|
||||||
# GridStore.open(database, 'filename, 'r') { |f|
|
|
||||||
# puts f.read # => Hello, world!\nBut wait, there's more!\n
|
|
||||||
# }
|
|
||||||
class GridStore
|
|
||||||
|
|
||||||
DEFAULT_ROOT_COLLECTION = 'fs'
|
def readlines(db, name, separator=$/)
|
||||||
DEFAULT_CONTENT_TYPE = 'text/plain'
|
GridStore.open(db, name, 'r') { |gs|
|
||||||
|
gs.readlines(separator)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
include Enumerable
|
def unlink(db, *names)
|
||||||
|
names.each { |name|
|
||||||
|
gs = GridStore.new(db, name)
|
||||||
|
gs.send(:delete_chunks)
|
||||||
|
gs.collection.remove('_id' => gs.files_id)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
alias_method :delete, :unlink
|
||||||
|
|
||||||
attr_accessor :filename
|
end
|
||||||
|
|
||||||
# Array of strings; may be +nil+
|
#---
|
||||||
attr_accessor :aliases
|
# ================================================================
|
||||||
|
#+++
|
||||||
|
|
||||||
# Default is DEFAULT_CONTENT_TYPE
|
# Mode may only be 'r', 'w', or 'w+'.
|
||||||
attr_accessor :content_type
|
#
|
||||||
|
# Options. Descriptions start with a list of the modes for which that
|
||||||
|
# option is legitimate.
|
||||||
|
#
|
||||||
|
# :root :: (r, w, w+) Name of root collection to use, instead of
|
||||||
|
# DEFAULT_ROOT_COLLECTION.
|
||||||
|
#
|
||||||
|
# :metadata:: (w, w+) A hash containing any data you want persisted as
|
||||||
|
# this file's metadata. See also metadata=
|
||||||
|
#
|
||||||
|
# :chunk_size :: (w) Sets chunk size for files opened for writing
|
||||||
|
# See also chunk_size= which may only be called before
|
||||||
|
# any data is written.
|
||||||
|
#
|
||||||
|
# :content_type :: (w) Default value is DEFAULT_CONTENT_TYPE. See
|
||||||
|
# also #content_type=
|
||||||
|
def initialize(db, name, mode='r', options={})
|
||||||
|
@db, @filename, @mode = db, name, mode
|
||||||
|
@root = options[:root] || DEFAULT_ROOT_COLLECTION
|
||||||
|
|
||||||
attr_accessor :metadata
|
doc = collection.find({'filename' => @filename}).next_object
|
||||||
|
if doc
|
||||||
|
@files_id = doc['_id']
|
||||||
|
@content_type = doc['contentType']
|
||||||
|
@chunk_size = doc['chunkSize']
|
||||||
|
@upload_date = doc['uploadDate']
|
||||||
|
@aliases = doc['aliases']
|
||||||
|
@length = doc['length']
|
||||||
|
@metadata = doc['metadata']
|
||||||
|
@md5 = doc['md5']
|
||||||
|
else
|
||||||
|
@files_id = Mongo::ObjectID.new
|
||||||
|
@content_type = DEFAULT_CONTENT_TYPE
|
||||||
|
@chunk_size = Chunk::DEFAULT_CHUNK_SIZE
|
||||||
|
@length = 0
|
||||||
|
end
|
||||||
|
|
||||||
attr_reader :files_id
|
case mode
|
||||||
|
when 'r'
|
||||||
|
@curr_chunk = nth_chunk(0)
|
||||||
|
@position = 0
|
||||||
|
when 'w'
|
||||||
|
chunk_collection.create_index([['files_id', Mongo::ASCENDING], ['n', Mongo::ASCENDING]])
|
||||||
|
delete_chunks
|
||||||
|
@curr_chunk = Chunk.new(self, 'n' => 0)
|
||||||
|
@content_type = options[:content_type] if options[:content_type]
|
||||||
|
@chunk_size = options[:chunk_size] if options[:chunk_size]
|
||||||
|
@metadata = options[:metadata] if options[:metadata]
|
||||||
|
@position = 0
|
||||||
|
when 'w+'
|
||||||
|
chunk_collection.create_index([['files_id', Mongo::ASCENDING], ['n', Mongo::ASCENDING]])
|
||||||
|
@curr_chunk = nth_chunk(last_chunk_number) || Chunk.new(self, 'n' => 0) # might be empty
|
||||||
|
@curr_chunk.pos = @curr_chunk.data.length if @curr_chunk
|
||||||
|
@metadata = options[:metadata] if options[:metadata]
|
||||||
|
@position = @length
|
||||||
|
else
|
||||||
|
raise "error: illegal mode #{mode}"
|
||||||
|
end
|
||||||
|
|
||||||
# Time that the file was first saved.
|
@lineno = 0
|
||||||
attr_reader :upload_date
|
@pushback_byte = nil
|
||||||
|
end
|
||||||
|
|
||||||
attr_reader :chunk_size
|
def collection
|
||||||
|
@db.collection("#{@root}.files")
|
||||||
|
end
|
||||||
|
|
||||||
attr_accessor :lineno
|
# Returns collection used for storing chunks. Depends on value of
|
||||||
|
# @root.
|
||||||
|
def chunk_collection
|
||||||
|
@db.collection("#{@root}.chunks")
|
||||||
|
end
|
||||||
|
|
||||||
attr_reader :md5
|
# Change chunk size. Can only change if the file is opened for write
|
||||||
|
# and no data has yet been written.
|
||||||
|
def chunk_size=(size)
|
||||||
|
unless @mode[0] == ?w && @position == 0 && @upload_date == nil
|
||||||
|
raise "error: can only change chunk size if open for write and no data written."
|
||||||
|
end
|
||||||
|
@chunk_size = size
|
||||||
|
end
|
||||||
|
|
||||||
class << self
|
#---
|
||||||
|
# ================ reading ================
|
||||||
def exist?(db, name, root_collection=DEFAULT_ROOT_COLLECTION)
|
#+++
|
||||||
db.collection("#{root_collection}.files").find({'filename' => name}).next_object != nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def open(db, name, mode, options={})
|
|
||||||
gs = self.new(db, name, mode, options)
|
|
||||||
result = nil
|
|
||||||
begin
|
|
||||||
result = yield gs if block_given?
|
|
||||||
ensure
|
|
||||||
gs.close
|
|
||||||
end
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
def read(db, name, length=nil, offset=nil)
|
|
||||||
GridStore.open(db, name, 'r') { |gs|
|
|
||||||
gs.seek(offset) if offset
|
|
||||||
gs.read(length)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
# List the contains of all GridFS files stored in the given db and
|
|
||||||
# root collection.
|
|
||||||
#
|
|
||||||
# :db :: the database to use
|
|
||||||
#
|
|
||||||
# :root_collection :: the root collection to use
|
|
||||||
def list(db, root_collection=DEFAULT_ROOT_COLLECTION)
|
|
||||||
db.collection("#{root_collection}.files").find().map { |f|
|
|
||||||
f['filename']
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def readlines(db, name, separator=$/)
|
|
||||||
GridStore.open(db, name, 'r') { |gs|
|
|
||||||
gs.readlines(separator)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def unlink(db, *names)
|
|
||||||
names.each { |name|
|
|
||||||
gs = GridStore.new(db, name)
|
|
||||||
gs.send(:delete_chunks)
|
|
||||||
gs.collection.remove('_id' => gs.files_id)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
alias_method :delete, :unlink
|
|
||||||
|
|
||||||
|
def getc
|
||||||
|
if @pushback_byte
|
||||||
|
byte = @pushback_byte
|
||||||
|
@pushback_byte = nil
|
||||||
|
@position += 1
|
||||||
|
byte
|
||||||
|
elsif eof?
|
||||||
|
nil
|
||||||
|
else
|
||||||
|
if @curr_chunk.eof?
|
||||||
|
@curr_chunk = nth_chunk(@curr_chunk.chunk_number + 1)
|
||||||
end
|
end
|
||||||
|
@position += 1
|
||||||
#---
|
@curr_chunk.getc
|
||||||
# ================================================================
|
|
||||||
#+++
|
|
||||||
|
|
||||||
# Mode may only be 'r', 'w', or 'w+'.
|
|
||||||
#
|
|
||||||
# Options. Descriptions start with a list of the modes for which that
|
|
||||||
# option is legitimate.
|
|
||||||
#
|
|
||||||
# :root :: (r, w, w+) Name of root collection to use, instead of
|
|
||||||
# DEFAULT_ROOT_COLLECTION.
|
|
||||||
#
|
|
||||||
# :metadata:: (w, w+) A hash containing any data you want persisted as
|
|
||||||
# this file's metadata. See also metadata=
|
|
||||||
#
|
|
||||||
# :chunk_size :: (w) Sets chunk size for files opened for writing
|
|
||||||
# See also chunk_size= which may only be called before
|
|
||||||
# any data is written.
|
|
||||||
#
|
|
||||||
# :content_type :: (w) Default value is DEFAULT_CONTENT_TYPE. See
|
|
||||||
# also #content_type=
|
|
||||||
def initialize(db, name, mode='r', options={})
|
|
||||||
@db, @filename, @mode = db, name, mode
|
|
||||||
@root = options[:root] || DEFAULT_ROOT_COLLECTION
|
|
||||||
|
|
||||||
doc = collection.find({'filename' => @filename}).next_object
|
|
||||||
if doc
|
|
||||||
@files_id = doc['_id']
|
|
||||||
@content_type = doc['contentType']
|
|
||||||
@chunk_size = doc['chunkSize']
|
|
||||||
@upload_date = doc['uploadDate']
|
|
||||||
@aliases = doc['aliases']
|
|
||||||
@length = doc['length']
|
|
||||||
@metadata = doc['metadata']
|
|
||||||
@md5 = doc['md5']
|
|
||||||
else
|
|
||||||
@files_id = XGen::Mongo::Driver::ObjectID.new
|
|
||||||
@content_type = DEFAULT_CONTENT_TYPE
|
|
||||||
@chunk_size = Chunk::DEFAULT_CHUNK_SIZE
|
|
||||||
@length = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
case mode
|
|
||||||
when 'r'
|
|
||||||
@curr_chunk = nth_chunk(0)
|
|
||||||
@position = 0
|
|
||||||
when 'w'
|
|
||||||
chunk_collection.create_index([['files_id', XGen::Mongo::ASCENDING], ['n', XGen::Mongo::ASCENDING]])
|
|
||||||
delete_chunks
|
|
||||||
@curr_chunk = Chunk.new(self, 'n' => 0)
|
|
||||||
@content_type = options[:content_type] if options[:content_type]
|
|
||||||
@chunk_size = options[:chunk_size] if options[:chunk_size]
|
|
||||||
@metadata = options[:metadata] if options[:metadata]
|
|
||||||
@position = 0
|
|
||||||
when 'w+'
|
|
||||||
chunk_collection.create_index([['files_id', XGen::Mongo::ASCENDING], ['n', XGen::Mongo::ASCENDING]])
|
|
||||||
@curr_chunk = nth_chunk(last_chunk_number) || Chunk.new(self, 'n' => 0) # might be empty
|
|
||||||
@curr_chunk.pos = @curr_chunk.data.length if @curr_chunk
|
|
||||||
@metadata = options[:metadata] if options[:metadata]
|
|
||||||
@position = @length
|
|
||||||
else
|
|
||||||
raise "error: illegal mode #{mode}"
|
|
||||||
end
|
|
||||||
|
|
||||||
@lineno = 0
|
|
||||||
@pushback_byte = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def collection
|
|
||||||
@db.collection("#{@root}.files")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns collection used for storing chunks. Depends on value of
|
|
||||||
# @root.
|
|
||||||
def chunk_collection
|
|
||||||
@db.collection("#{@root}.chunks")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Change chunk size. Can only change if the file is opened for write
|
|
||||||
# and no data has yet been written.
|
|
||||||
def chunk_size=(size)
|
|
||||||
unless @mode[0] == ?w && @position == 0 && @upload_date == nil
|
|
||||||
raise "error: can only change chunk size if open for write and no data written."
|
|
||||||
end
|
|
||||||
@chunk_size = size
|
|
||||||
end
|
|
||||||
|
|
||||||
#---
|
|
||||||
# ================ reading ================
|
|
||||||
#+++
|
|
||||||
|
|
||||||
def getc
|
|
||||||
if @pushback_byte
|
|
||||||
byte = @pushback_byte
|
|
||||||
@pushback_byte = nil
|
|
||||||
@position += 1
|
|
||||||
byte
|
|
||||||
elsif eof?
|
|
||||||
nil
|
|
||||||
else
|
|
||||||
if @curr_chunk.eof?
|
|
||||||
@curr_chunk = nth_chunk(@curr_chunk.chunk_number + 1)
|
|
||||||
end
|
|
||||||
@position += 1
|
|
||||||
@curr_chunk.getc
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def gets(separator=$/)
|
|
||||||
str = ''
|
|
||||||
byte = self.getc
|
|
||||||
return nil if byte == nil # EOF
|
|
||||||
while byte != nil
|
|
||||||
s = byte.chr
|
|
||||||
str << s
|
|
||||||
break if s == separator
|
|
||||||
byte = self.getc
|
|
||||||
end
|
|
||||||
@lineno += 1
|
|
||||||
str
|
|
||||||
end
|
|
||||||
|
|
||||||
def read(len=nil, buf=nil)
|
|
||||||
buf ||= ''
|
|
||||||
byte = self.getc
|
|
||||||
while byte != nil && (len == nil || len > 0)
|
|
||||||
buf << byte.chr
|
|
||||||
len -= 1 if len
|
|
||||||
byte = self.getc if (len == nil || len > 0)
|
|
||||||
end
|
|
||||||
buf
|
|
||||||
end
|
|
||||||
|
|
||||||
def readchar
|
|
||||||
byte = self.getc
|
|
||||||
raise EOFError.new if byte == nil
|
|
||||||
byte
|
|
||||||
end
|
|
||||||
|
|
||||||
def readline(separator=$/)
|
|
||||||
line = gets
|
|
||||||
raise EOFError.new if line == nil
|
|
||||||
line
|
|
||||||
end
|
|
||||||
|
|
||||||
def readlines(separator=$/)
|
|
||||||
read.split(separator).collect { |line| "#{line}#{separator}" }
|
|
||||||
end
|
|
||||||
|
|
||||||
def each
|
|
||||||
line = gets
|
|
||||||
while line
|
|
||||||
yield line
|
|
||||||
line = gets
|
|
||||||
end
|
|
||||||
end
|
|
||||||
alias_method :each_line, :each
|
|
||||||
|
|
||||||
def each_byte
|
|
||||||
byte = self.getc
|
|
||||||
while byte
|
|
||||||
yield byte
|
|
||||||
byte = self.getc
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def ungetc(byte)
|
|
||||||
@pushback_byte = byte
|
|
||||||
@position -= 1
|
|
||||||
end
|
|
||||||
|
|
||||||
#---
|
|
||||||
# ================ writing ================
|
|
||||||
#+++
|
|
||||||
|
|
||||||
def putc(byte)
|
|
||||||
if @curr_chunk.pos == @chunk_size
|
|
||||||
prev_chunk_number = @curr_chunk.chunk_number
|
|
||||||
@curr_chunk.save
|
|
||||||
@curr_chunk = Chunk.new(self, 'n' => prev_chunk_number + 1)
|
|
||||||
end
|
|
||||||
@position += 1
|
|
||||||
@curr_chunk.putc(byte)
|
|
||||||
end
|
|
||||||
|
|
||||||
def print(*objs)
|
|
||||||
objs = [$_] if objs == nil || objs.empty?
|
|
||||||
objs.each { |obj|
|
|
||||||
str = obj.to_s
|
|
||||||
str.each_byte { |byte| self.putc(byte) }
|
|
||||||
}
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def puts(*objs)
|
|
||||||
if objs == nil || objs.empty?
|
|
||||||
self.putc(10)
|
|
||||||
else
|
|
||||||
print(*objs.collect{ |obj|
|
|
||||||
str = obj.to_s
|
|
||||||
str << "\n" unless str =~ /\n$/
|
|
||||||
str
|
|
||||||
})
|
|
||||||
end
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def <<(obj)
|
|
||||||
write(obj.to_s)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Writes +string+ as bytes and returns the number of bytes written.
|
|
||||||
def write(string)
|
|
||||||
raise "#@filename not opened for write" unless @mode[0] == ?w
|
|
||||||
count = 0
|
|
||||||
string.each_byte { |byte|
|
|
||||||
self.putc byte
|
|
||||||
count += 1
|
|
||||||
}
|
|
||||||
count
|
|
||||||
end
|
|
||||||
|
|
||||||
# A no-op.
|
|
||||||
def flush
|
|
||||||
end
|
|
||||||
|
|
||||||
#---
|
|
||||||
# ================ status ================
|
|
||||||
#+++
|
|
||||||
|
|
||||||
def eof
|
|
||||||
raise IOError.new("stream not open for reading") unless @mode[0] == ?r
|
|
||||||
@position >= @length
|
|
||||||
end
|
|
||||||
alias_method :eof?, :eof
|
|
||||||
|
|
||||||
#---
|
|
||||||
# ================ positioning ================
|
|
||||||
#+++
|
|
||||||
|
|
||||||
def rewind
|
|
||||||
if @curr_chunk.chunk_number != 0
|
|
||||||
if @mode[0] == ?w
|
|
||||||
delete_chunks
|
|
||||||
@curr_chunk = Chunk.new(self, 'n' => 0)
|
|
||||||
else
|
|
||||||
@curr_chunk == nth_chunk(0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@curr_chunk.pos = 0
|
|
||||||
@lineno = 0
|
|
||||||
@position = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def seek(pos, whence=IO::SEEK_SET)
|
|
||||||
target_pos = case whence
|
|
||||||
when IO::SEEK_CUR
|
|
||||||
@position + pos
|
|
||||||
when IO::SEEK_END
|
|
||||||
@length + pos
|
|
||||||
when IO::SEEK_SET
|
|
||||||
pos
|
|
||||||
end
|
|
||||||
|
|
||||||
new_chunk_number = (target_pos / @chunk_size).to_i
|
|
||||||
if new_chunk_number != @curr_chunk.chunk_number
|
|
||||||
@curr_chunk.save if @mode[0] == ?w
|
|
||||||
@curr_chunk = nth_chunk(new_chunk_number)
|
|
||||||
end
|
|
||||||
@position = target_pos
|
|
||||||
@curr_chunk.pos = @position % @chunk_size
|
|
||||||
0
|
|
||||||
end
|
|
||||||
|
|
||||||
def tell
|
|
||||||
@position
|
|
||||||
end
|
|
||||||
|
|
||||||
#---
|
|
||||||
# ================ closing ================
|
|
||||||
#+++
|
|
||||||
|
|
||||||
def close
|
|
||||||
if @mode[0] == ?w
|
|
||||||
if @curr_chunk
|
|
||||||
@curr_chunk.truncate
|
|
||||||
@curr_chunk.save if @curr_chunk.pos > 0
|
|
||||||
end
|
|
||||||
files = collection
|
|
||||||
if @upload_date
|
|
||||||
files.remove('_id' => @files_id)
|
|
||||||
else
|
|
||||||
@upload_date = Time.now
|
|
||||||
end
|
|
||||||
files.insert(to_mongo_object)
|
|
||||||
end
|
|
||||||
@db = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def closed?
|
|
||||||
@db == nil
|
|
||||||
end
|
|
||||||
|
|
||||||
#---
|
|
||||||
# ================ protected ================
|
|
||||||
#+++
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def to_mongo_object
|
|
||||||
h = OrderedHash.new
|
|
||||||
h['_id'] = @files_id
|
|
||||||
h['filename'] = @filename
|
|
||||||
h['contentType'] = @content_type
|
|
||||||
h['length'] = @curr_chunk ? @curr_chunk.chunk_number * @chunk_size + @curr_chunk.pos : 0
|
|
||||||
h['chunkSize'] = @chunk_size
|
|
||||||
h['uploadDate'] = @upload_date
|
|
||||||
h['aliases'] = @aliases
|
|
||||||
h['metadata'] = @metadata
|
|
||||||
md5_command = OrderedHash.new
|
|
||||||
md5_command['filemd5'] = @files_id
|
|
||||||
md5_command['root'] = @root
|
|
||||||
h['md5'] = @db.db_command(md5_command)['md5']
|
|
||||||
h
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete_chunks
|
|
||||||
chunk_collection.remove({'files_id' => @files_id}) if @files_id
|
|
||||||
@curr_chunk = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def nth_chunk(n)
|
|
||||||
mongo_chunk = chunk_collection.find({'files_id' => @files_id, 'n' => n}).next_object
|
|
||||||
Chunk.new(self, mongo_chunk || {})
|
|
||||||
end
|
|
||||||
|
|
||||||
def last_chunk_number
|
|
||||||
(@length / @chunk_size).to_i
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def gets(separator=$/)
|
||||||
|
str = ''
|
||||||
|
byte = self.getc
|
||||||
|
return nil if byte == nil # EOF
|
||||||
|
while byte != nil
|
||||||
|
s = byte.chr
|
||||||
|
str << s
|
||||||
|
break if s == separator
|
||||||
|
byte = self.getc
|
||||||
|
end
|
||||||
|
@lineno += 1
|
||||||
|
str
|
||||||
|
end
|
||||||
|
|
||||||
|
def read(len=nil, buf=nil)
|
||||||
|
buf ||= ''
|
||||||
|
byte = self.getc
|
||||||
|
while byte != nil && (len == nil || len > 0)
|
||||||
|
buf << byte.chr
|
||||||
|
len -= 1 if len
|
||||||
|
byte = self.getc if (len == nil || len > 0)
|
||||||
|
end
|
||||||
|
buf
|
||||||
|
end
|
||||||
|
|
||||||
|
def readchar
|
||||||
|
byte = self.getc
|
||||||
|
raise EOFError.new if byte == nil
|
||||||
|
byte
|
||||||
|
end
|
||||||
|
|
||||||
|
def readline(separator=$/)
|
||||||
|
line = gets
|
||||||
|
raise EOFError.new if line == nil
|
||||||
|
line
|
||||||
|
end
|
||||||
|
|
||||||
|
def readlines(separator=$/)
|
||||||
|
read.split(separator).collect { |line| "#{line}#{separator}" }
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
line = gets
|
||||||
|
while line
|
||||||
|
yield line
|
||||||
|
line = gets
|
||||||
|
end
|
||||||
|
end
|
||||||
|
alias_method :each_line, :each
|
||||||
|
|
||||||
|
def each_byte
|
||||||
|
byte = self.getc
|
||||||
|
while byte
|
||||||
|
yield byte
|
||||||
|
byte = self.getc
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def ungetc(byte)
|
||||||
|
@pushback_byte = byte
|
||||||
|
@position -= 1
|
||||||
|
end
|
||||||
|
|
||||||
|
#---
|
||||||
|
# ================ writing ================
|
||||||
|
#+++
|
||||||
|
|
||||||
|
def putc(byte)
|
||||||
|
if @curr_chunk.pos == @chunk_size
|
||||||
|
prev_chunk_number = @curr_chunk.chunk_number
|
||||||
|
@curr_chunk.save
|
||||||
|
@curr_chunk = Chunk.new(self, 'n' => prev_chunk_number + 1)
|
||||||
|
end
|
||||||
|
@position += 1
|
||||||
|
@curr_chunk.putc(byte)
|
||||||
|
end
|
||||||
|
|
||||||
|
def print(*objs)
|
||||||
|
objs = [$_] if objs == nil || objs.empty?
|
||||||
|
objs.each { |obj|
|
||||||
|
str = obj.to_s
|
||||||
|
str.each_byte { |byte| self.putc(byte) }
|
||||||
|
}
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def puts(*objs)
|
||||||
|
if objs == nil || objs.empty?
|
||||||
|
self.putc(10)
|
||||||
|
else
|
||||||
|
print(*objs.collect{ |obj|
|
||||||
|
str = obj.to_s
|
||||||
|
str << "\n" unless str =~ /\n$/
|
||||||
|
str
|
||||||
|
})
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def <<(obj)
|
||||||
|
write(obj.to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Writes +string+ as bytes and returns the number of bytes written.
|
||||||
|
def write(string)
|
||||||
|
raise "#@filename not opened for write" unless @mode[0] == ?w
|
||||||
|
count = 0
|
||||||
|
string.each_byte { |byte|
|
||||||
|
self.putc byte
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
count
|
||||||
|
end
|
||||||
|
|
||||||
|
# A no-op.
|
||||||
|
def flush
|
||||||
|
end
|
||||||
|
|
||||||
|
#---
|
||||||
|
# ================ status ================
|
||||||
|
#+++
|
||||||
|
|
||||||
|
def eof
|
||||||
|
raise IOError.new("stream not open for reading") unless @mode[0] == ?r
|
||||||
|
@position >= @length
|
||||||
|
end
|
||||||
|
alias_method :eof?, :eof
|
||||||
|
|
||||||
|
#---
|
||||||
|
# ================ positioning ================
|
||||||
|
#+++
|
||||||
|
|
||||||
|
def rewind
|
||||||
|
if @curr_chunk.chunk_number != 0
|
||||||
|
if @mode[0] == ?w
|
||||||
|
delete_chunks
|
||||||
|
@curr_chunk = Chunk.new(self, 'n' => 0)
|
||||||
|
else
|
||||||
|
@curr_chunk == nth_chunk(0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@curr_chunk.pos = 0
|
||||||
|
@lineno = 0
|
||||||
|
@position = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def seek(pos, whence=IO::SEEK_SET)
|
||||||
|
target_pos = case whence
|
||||||
|
when IO::SEEK_CUR
|
||||||
|
@position + pos
|
||||||
|
when IO::SEEK_END
|
||||||
|
@length + pos
|
||||||
|
when IO::SEEK_SET
|
||||||
|
pos
|
||||||
|
end
|
||||||
|
|
||||||
|
new_chunk_number = (target_pos / @chunk_size).to_i
|
||||||
|
if new_chunk_number != @curr_chunk.chunk_number
|
||||||
|
@curr_chunk.save if @mode[0] == ?w
|
||||||
|
@curr_chunk = nth_chunk(new_chunk_number)
|
||||||
|
end
|
||||||
|
@position = target_pos
|
||||||
|
@curr_chunk.pos = @position % @chunk_size
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
def tell
|
||||||
|
@position
|
||||||
|
end
|
||||||
|
|
||||||
|
#---
|
||||||
|
# ================ closing ================
|
||||||
|
#+++
|
||||||
|
|
||||||
|
def close
|
||||||
|
if @mode[0] == ?w
|
||||||
|
if @curr_chunk
|
||||||
|
@curr_chunk.truncate
|
||||||
|
@curr_chunk.save if @curr_chunk.pos > 0
|
||||||
|
end
|
||||||
|
files = collection
|
||||||
|
if @upload_date
|
||||||
|
files.remove('_id' => @files_id)
|
||||||
|
else
|
||||||
|
@upload_date = Time.now
|
||||||
|
end
|
||||||
|
files.insert(to_mongo_object)
|
||||||
|
end
|
||||||
|
@db = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def closed?
|
||||||
|
@db == nil
|
||||||
|
end
|
||||||
|
|
||||||
|
#---
|
||||||
|
# ================ protected ================
|
||||||
|
#+++
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def to_mongo_object
|
||||||
|
h = OrderedHash.new
|
||||||
|
h['_id'] = @files_id
|
||||||
|
h['filename'] = @filename
|
||||||
|
h['contentType'] = @content_type
|
||||||
|
h['length'] = @curr_chunk ? @curr_chunk.chunk_number * @chunk_size + @curr_chunk.pos : 0
|
||||||
|
h['chunkSize'] = @chunk_size
|
||||||
|
h['uploadDate'] = @upload_date
|
||||||
|
h['aliases'] = @aliases
|
||||||
|
h['metadata'] = @metadata
|
||||||
|
md5_command = OrderedHash.new
|
||||||
|
md5_command['filemd5'] = @files_id
|
||||||
|
md5_command['root'] = @root
|
||||||
|
h['md5'] = @db.db_command(md5_command)['md5']
|
||||||
|
h
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_chunks
|
||||||
|
chunk_collection.remove({'files_id' => @files_id}) if @files_id
|
||||||
|
@curr_chunk = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def nth_chunk(n)
|
||||||
|
mongo_chunk = chunk_collection.find({'files_id' => @files_id, 'n' => n}).next_object
|
||||||
|
Chunk.new(self, mongo_chunk || {})
|
||||||
|
end
|
||||||
|
|
||||||
|
def last_chunk_number
|
||||||
|
(@length / @chunk_size).to_i
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -17,21 +17,16 @@
|
|||||||
require 'mongo/message/message'
|
require 'mongo/message/message'
|
||||||
require 'mongo/message/opcodes'
|
require 'mongo/message/opcodes'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
class GetMoreMessage < Message
|
class GetMoreMessage < Message
|
||||||
|
|
||||||
def initialize(db_name, collection_name, cursor)
|
def initialize(db_name, collection_name, cursor)
|
||||||
super(OP_GET_MORE)
|
super(OP_GET_MORE)
|
||||||
write_int(0)
|
write_int(0)
|
||||||
write_string("#{db_name}.#{collection_name}")
|
write_string("#{db_name}.#{collection_name}")
|
||||||
write_int(0) # num to return; leave it up to the db for now
|
write_int(0) # num to return; leave it up to the db for now
|
||||||
write_long(cursor)
|
write_long(cursor)
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -17,19 +17,15 @@
|
|||||||
require 'mongo/message/message'
|
require 'mongo/message/message'
|
||||||
require 'mongo/message/opcodes'
|
require 'mongo/message/opcodes'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
class InsertMessage < Message
|
class InsertMessage < Message
|
||||||
|
|
||||||
def initialize(db_name, collection_name, check_keys=true, *objs)
|
def initialize(db_name, collection_name, check_keys=true, *objs)
|
||||||
super(OP_INSERT)
|
super(OP_INSERT)
|
||||||
write_int(0)
|
write_int(0)
|
||||||
write_string("#{db_name}.#{collection_name}")
|
write_string("#{db_name}.#{collection_name}")
|
||||||
objs.each { |o| write_doc(o, check_keys) }
|
objs.each { |o| write_doc(o, check_keys) }
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -17,20 +17,15 @@
|
|||||||
require 'mongo/message/message'
|
require 'mongo/message/message'
|
||||||
require 'mongo/message/opcodes'
|
require 'mongo/message/opcodes'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
class KillCursorsMessage < Message
|
class KillCursorsMessage < Message
|
||||||
|
|
||||||
def initialize(*cursors)
|
def initialize(*cursors)
|
||||||
super(OP_KILL_CURSORS)
|
super(OP_KILL_CURSORS)
|
||||||
write_int(0)
|
write_int(0)
|
||||||
write_int(cursors.length)
|
write_int(cursors.length)
|
||||||
cursors.each { |c| write_long c }
|
cursors.each { |c| write_long c }
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -17,68 +17,64 @@
|
|||||||
require 'mongo/util/bson'
|
require 'mongo/util/bson'
|
||||||
require 'mongo/util/byte_buffer'
|
require 'mongo/util/byte_buffer'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
class Message
|
class Message
|
||||||
|
|
||||||
HEADER_SIZE = 16 # size, id, response_to, opcode
|
HEADER_SIZE = 16 # size, id, response_to, opcode
|
||||||
|
|
||||||
@@class_req_id = 0
|
@@class_req_id = 0
|
||||||
|
|
||||||
attr_reader :buf # for testing
|
attr_reader :buf # for testing
|
||||||
|
|
||||||
def initialize(op)
|
def initialize(op)
|
||||||
@op = op
|
@op = op
|
||||||
@message_length = HEADER_SIZE
|
@message_length = HEADER_SIZE
|
||||||
@data_length = 0
|
@data_length = 0
|
||||||
@request_id = (@@class_req_id += 1)
|
@request_id = (@@class_req_id += 1)
|
||||||
@response_id = 0
|
@response_id = 0
|
||||||
@buf = ByteBuffer.new
|
@buf = ByteBuffer.new
|
||||||
|
|
||||||
@buf.put_int(16) # holder for length
|
@buf.put_int(16) # holder for length
|
||||||
@buf.put_int(@request_id)
|
@buf.put_int(@request_id)
|
||||||
@buf.put_int(0) # response_to
|
@buf.put_int(0) # response_to
|
||||||
@buf.put_int(op)
|
@buf.put_int(op)
|
||||||
end
|
|
||||||
|
|
||||||
def write_int(i)
|
|
||||||
@buf.put_int(i)
|
|
||||||
update_message_length
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_long(i)
|
|
||||||
@buf.put_long(i)
|
|
||||||
update_message_length
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_string(s)
|
|
||||||
BSON.serialize_cstr(@buf, s)
|
|
||||||
update_message_length
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_doc(hash, check_keys=false)
|
|
||||||
@buf.put_array(BSON.new.serialize(hash, check_keys).to_a)
|
|
||||||
update_message_length
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_a
|
|
||||||
@buf.to_a
|
|
||||||
end
|
|
||||||
|
|
||||||
def dump
|
|
||||||
@buf.dump
|
|
||||||
end
|
|
||||||
|
|
||||||
# Do not call. Private, but kept public for testing.
|
|
||||||
def update_message_length
|
|
||||||
pos = @buf.position
|
|
||||||
@buf.put_int(@buf.size, 0)
|
|
||||||
@buf.position = pos
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def write_int(i)
|
||||||
|
@buf.put_int(i)
|
||||||
|
update_message_length
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_long(i)
|
||||||
|
@buf.put_long(i)
|
||||||
|
update_message_length
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_string(s)
|
||||||
|
BSON.serialize_cstr(@buf, s)
|
||||||
|
update_message_length
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_doc(hash, check_keys=false)
|
||||||
|
@buf.put_array(BSON.new.serialize(hash, check_keys).to_a)
|
||||||
|
update_message_length
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_a
|
||||||
|
@buf.to_a
|
||||||
|
end
|
||||||
|
|
||||||
|
def dump
|
||||||
|
@buf.dump
|
||||||
|
end
|
||||||
|
|
||||||
|
# Do not call. Private, but kept public for testing.
|
||||||
|
def update_message_length
|
||||||
|
pos = @buf.position
|
||||||
|
@buf.put_int(@buf.size, 0)
|
||||||
|
@buf.position = pos
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -16,35 +16,30 @@
|
|||||||
|
|
||||||
require 'mongo/util/byte_buffer'
|
require 'mongo/util/byte_buffer'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
class MessageHeader
|
class MessageHeader
|
||||||
|
|
||||||
HEADER_SIZE = 16
|
HEADER_SIZE = 16
|
||||||
|
|
||||||
def initialize()
|
def initialize()
|
||||||
@buf = ByteBuffer.new
|
@buf = ByteBuffer.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_header(db)
|
def read_header(db)
|
||||||
@buf.rewind
|
@buf.rewind
|
||||||
@buf.put_array(db.receive_full(HEADER_SIZE).unpack("C*"))
|
@buf.put_array(db.receive_full(HEADER_SIZE).unpack("C*"))
|
||||||
raise "Short read for DB response header: expected #{HEADER_SIZE} bytes, saw #{@buf.size}" unless @buf.size == HEADER_SIZE
|
raise "Short read for DB response header: expected #{HEADER_SIZE} bytes, saw #{@buf.size}" unless @buf.size == HEADER_SIZE
|
||||||
@buf.rewind
|
@buf.rewind
|
||||||
@size = @buf.get_int
|
@size = @buf.get_int
|
||||||
@request_id = @buf.get_int
|
@request_id = @buf.get_int
|
||||||
@response_to = @buf.get_int
|
@response_to = @buf.get_int
|
||||||
@op = @buf.get_int
|
@op = @buf.get_int
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
def dump
|
def dump
|
||||||
@buf.dump
|
@buf.dump
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -17,17 +17,13 @@
|
|||||||
require 'mongo/message/message'
|
require 'mongo/message/message'
|
||||||
require 'mongo/message/opcodes'
|
require 'mongo/message/opcodes'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
class MsgMessage < Message
|
class MsgMessage < Message
|
||||||
|
|
||||||
def initialize(msg)
|
def initialize(msg)
|
||||||
super(OP_MSG)
|
super(OP_MSG)
|
||||||
write_string(msg)
|
write_string(msg)
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -14,19 +14,14 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
# ++
|
# ++
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
OP_REPLY = 1 # reply. responseTo is set.
|
||||||
module Driver
|
OP_MSG = 1000 # generic msg command followed by a string
|
||||||
OP_REPLY = 1 # reply. responseTo is set.
|
OP_UPDATE = 2001 # update object
|
||||||
OP_MSG = 1000 # generic msg command followed by a string
|
OP_INSERT = 2002
|
||||||
OP_UPDATE = 2001 # update object
|
# GET_BY_OID = 2003
|
||||||
OP_INSERT = 2002
|
OP_QUERY = 2004
|
||||||
# GET_BY_OID = 2003
|
OP_GET_MORE = 2005
|
||||||
OP_QUERY = 2004
|
OP_DELETE = 2006
|
||||||
OP_GET_MORE = 2005
|
OP_KILL_CURSORS = 2007
|
||||||
OP_DELETE = 2006
|
|
||||||
OP_KILL_CURSORS = 2007
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,60 +18,56 @@ require 'mongo/message/message'
|
|||||||
require 'mongo/message/opcodes'
|
require 'mongo/message/opcodes'
|
||||||
require 'mongo/util/ordered_hash'
|
require 'mongo/util/ordered_hash'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
class QueryMessage < Message
|
class QueryMessage < Message
|
||||||
|
|
||||||
attr_reader :query
|
attr_reader :query
|
||||||
|
|
||||||
def initialize(db_name, collection_name, query)
|
def initialize(db_name, collection_name, query)
|
||||||
super(OP_QUERY)
|
super(OP_QUERY)
|
||||||
@query = query
|
@query = query
|
||||||
write_int(0)
|
write_int(0)
|
||||||
write_string("#{db_name}.#{collection_name}")
|
write_string("#{db_name}.#{collection_name}")
|
||||||
write_int(query.number_to_skip)
|
write_int(query.number_to_skip)
|
||||||
write_int(-query.number_to_return) # Negative means hard limit
|
write_int(-query.number_to_return) # Negative means hard limit
|
||||||
sel = query.selector
|
sel = query.selector
|
||||||
if query.contains_special_fields
|
if query.contains_special_fields
|
||||||
sel = OrderedHash.new
|
sel = OrderedHash.new
|
||||||
sel['query'] = query.selector
|
sel['query'] = query.selector
|
||||||
if query.order_by && query.order_by.length > 0
|
if query.order_by && query.order_by.length > 0
|
||||||
sel['orderby'] = case query.order_by
|
sel['orderby'] = case query.order_by
|
||||||
|
when String
|
||||||
|
{query.order_by => 1}
|
||||||
|
when Array
|
||||||
|
h = OrderedHash.new
|
||||||
|
query.order_by.each { |ob|
|
||||||
|
case ob
|
||||||
when String
|
when String
|
||||||
{query.order_by => 1}
|
h[ob] = 1
|
||||||
when Array
|
when Hash # should have one entry; will handle all
|
||||||
h = OrderedHash.new
|
ob.each { |k,v| h[k] = v }
|
||||||
query.order_by.each { |ob|
|
|
||||||
case ob
|
|
||||||
when String
|
|
||||||
h[ob] = 1
|
|
||||||
when Hash # should have one entry; will handle all
|
|
||||||
ob.each { |k,v| h[k] = v }
|
|
||||||
else
|
|
||||||
raise "illegal query order_by value #{query.order_by.inspect}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
h
|
|
||||||
when Hash # Should be an ordered hash, but this message doesn't care
|
|
||||||
query.order_by
|
|
||||||
else
|
else
|
||||||
raise "illegal order_by: is a #{query.order_by.class.name}, must be String, Array, Hash, or OrderedHash"
|
raise "illegal query order_by value #{query.order_by.inspect}"
|
||||||
end
|
end
|
||||||
end
|
}
|
||||||
sel['$hint'] = query.hint if query.hint && query.hint.length > 0
|
h
|
||||||
sel['$explain'] = true if query.explain
|
when Hash # Should be an ordered hash, but this message doesn't care
|
||||||
sel['$snapshot'] = true if query.snapshot
|
query.order_by
|
||||||
end
|
else
|
||||||
write_doc(sel)
|
raise "illegal order_by: is a #{query.order_by.class.name}, must be String, Array, Hash, or OrderedHash"
|
||||||
write_doc(query.fields) if query.fields
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def first_key(key)
|
|
||||||
@first_key = key
|
|
||||||
end
|
end
|
||||||
|
sel['$hint'] = query.hint if query.hint && query.hint.length > 0
|
||||||
|
sel['$explain'] = true if query.explain
|
||||||
|
sel['$snapshot'] = true if query.snapshot
|
||||||
end
|
end
|
||||||
|
write_doc(sel)
|
||||||
|
write_doc(query.fields) if query.fields
|
||||||
|
end
|
||||||
|
|
||||||
|
def first_key(key)
|
||||||
|
@first_key = key
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -17,20 +17,16 @@
|
|||||||
require 'mongo/message/message'
|
require 'mongo/message/message'
|
||||||
require 'mongo/message/opcodes'
|
require 'mongo/message/opcodes'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
class RemoveMessage < Message
|
class RemoveMessage < Message
|
||||||
|
|
||||||
def initialize(db_name, collection_name, sel)
|
def initialize(db_name, collection_name, sel)
|
||||||
super(OP_DELETE)
|
super(OP_DELETE)
|
||||||
write_int(0)
|
write_int(0)
|
||||||
write_string("#{db_name}.#{collection_name}")
|
write_string("#{db_name}.#{collection_name}")
|
||||||
write_int(0) # flags?
|
write_int(0) # flags?
|
||||||
write_doc(sel)
|
write_doc(sel)
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -17,21 +17,17 @@
|
|||||||
require 'mongo/message/message'
|
require 'mongo/message/message'
|
||||||
require 'mongo/message/opcodes'
|
require 'mongo/message/opcodes'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
class UpdateMessage < Message
|
class UpdateMessage < Message
|
||||||
|
|
||||||
def initialize(db_name, collection_name, sel, obj, repsert)
|
def initialize(db_name, collection_name, sel, obj, repsert)
|
||||||
super(OP_UPDATE)
|
super(OP_UPDATE)
|
||||||
write_int(0)
|
write_int(0)
|
||||||
write_string("#{db_name}.#{collection_name}")
|
write_string("#{db_name}.#{collection_name}")
|
||||||
write_int(repsert ? 1 : 0) # 1 if a repsert operation (upsert)
|
write_int(repsert ? 1 : 0) # 1 if a repsert operation (upsert)
|
||||||
write_doc(sel)
|
write_doc(sel)
|
||||||
write_doc(obj)
|
write_doc(obj)
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -16,149 +16,144 @@
|
|||||||
|
|
||||||
require 'mongo/db'
|
require 'mongo/db'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
# Represents a Mongo database server.
|
# Represents a Mongo database server.
|
||||||
class Mongo
|
class Mongo
|
||||||
|
|
||||||
DEFAULT_PORT = 27017
|
DEFAULT_PORT = 27017
|
||||||
|
|
||||||
# Create a Mongo database server instance. You specify either one or a
|
# Create a Mongo database server instance. You specify either one or a
|
||||||
# pair of servers. If one, you also say if connecting to a slave is
|
# pair of servers. If one, you also say if connecting to a slave is
|
||||||
# OK. In either case, the host default is "localhost" and port default
|
# OK. In either case, the host default is "localhost" and port default
|
||||||
# is DEFAULT_PORT.
|
# is DEFAULT_PORT.
|
||||||
#
|
#
|
||||||
# If you specify a pair, pair_or_host is a hash with two keys :left
|
# If you specify a pair, pair_or_host is a hash with two keys :left
|
||||||
# and :right. Each key maps to either
|
# and :right. Each key maps to either
|
||||||
# * a server name, in which case port is DEFAULT_PORT
|
# * a server name, in which case port is DEFAULT_PORT
|
||||||
# * a port number, in which case server is "localhost"
|
# * a port number, in which case server is "localhost"
|
||||||
# * an array containing a server name and a port number in that order
|
# * an array containing a server name and a port number in that order
|
||||||
#
|
#
|
||||||
# +options+ are passed on to each DB instance:
|
# +options+ are passed on to each DB instance:
|
||||||
#
|
#
|
||||||
# :slave_ok :: Only used if one host is specified. If false, when
|
# :slave_ok :: Only used if one host is specified. If false, when
|
||||||
# connecting to that host/port a DB object will check to
|
# connecting to that host/port a DB object will check to
|
||||||
# see if the server is the master. If it is not, an error
|
# see if the server is the master. If it is not, an error
|
||||||
# is thrown.
|
# is thrown.
|
||||||
#
|
#
|
||||||
# :auto_reconnect :: If a DB connection gets closed (for example, we
|
# :auto_reconnect :: If a DB connection gets closed (for example, we
|
||||||
# have a server pair and saw the "not master"
|
# have a server pair and saw the "not master"
|
||||||
# error, which closes the connection), then
|
# error, which closes the connection), then
|
||||||
# automatically try to reconnect to the master or
|
# automatically try to reconnect to the master or
|
||||||
# to the single server we have been given. Defaults
|
# to the single server we have been given. Defaults
|
||||||
# to +false+.
|
# to +false+.
|
||||||
#
|
#
|
||||||
# Since that's so confusing, here are a few examples:
|
# Since that's so confusing, here are a few examples:
|
||||||
#
|
#
|
||||||
# Mongo.new # localhost, DEFAULT_PORT, !slave
|
# Mongo.new # localhost, DEFAULT_PORT, !slave
|
||||||
# Mongo.new("localhost") # localhost, DEFAULT_PORT, !slave
|
# Mongo.new("localhost") # localhost, DEFAULT_PORT, !slave
|
||||||
# Mongo.new("localhost", 3000) # localhost, 3000, slave not ok
|
# Mongo.new("localhost", 3000) # localhost, 3000, slave not ok
|
||||||
# # localhost, 3000, slave ok
|
# # localhost, 3000, slave ok
|
||||||
# Mongo.new("localhost", 3000, :slave_ok => true)
|
# Mongo.new("localhost", 3000, :slave_ok => true)
|
||||||
# # localhost, DEFAULT_PORT, auto reconnect
|
# # localhost, DEFAULT_PORT, auto reconnect
|
||||||
# Mongo.new(nil, nil, :auto_reconnect => true)
|
# Mongo.new(nil, nil, :auto_reconnect => true)
|
||||||
#
|
#
|
||||||
# # A pair of servers. DB will always talk to the master. On socket
|
# # A pair of servers. DB will always talk to the master. On socket
|
||||||
# # error or "not master" error, we will auto-reconnect to the
|
# # error or "not master" error, we will auto-reconnect to the
|
||||||
# # current master.
|
# # current master.
|
||||||
# Mongo.new({:left => ["db1.example.com", 3000],
|
# Mongo.new({:left => ["db1.example.com", 3000],
|
||||||
# :right => "db2.example.com"}, # DEFAULT_PORT
|
# :right => "db2.example.com"}, # DEFAULT_PORT
|
||||||
# nil, :auto_reconnect => true)
|
# nil, :auto_reconnect => true)
|
||||||
#
|
#
|
||||||
# # Here, :right is localhost/DEFAULT_PORT. No auto-reconnect.
|
# # Here, :right is localhost/DEFAULT_PORT. No auto-reconnect.
|
||||||
# Mongo.new({:left => ["db1.example.com", 3000]})
|
# Mongo.new({:left => ["db1.example.com", 3000]})
|
||||||
#
|
#
|
||||||
# When a DB object first connects to a pair, it will find the master
|
# When a DB object first connects to a pair, it will find the master
|
||||||
# instance and connect to that one.
|
# instance and connect to that one.
|
||||||
def initialize(pair_or_host=nil, port=nil, options={})
|
def initialize(pair_or_host=nil, port=nil, options={})
|
||||||
@pair = case pair_or_host
|
@pair = case pair_or_host
|
||||||
when String
|
when String
|
||||||
[[pair_or_host, port ? port.to_i : DEFAULT_PORT]]
|
[[pair_or_host, port ? port.to_i : DEFAULT_PORT]]
|
||||||
when Hash
|
when Hash
|
||||||
connections = []
|
connections = []
|
||||||
connections << pair_val_to_connection(pair_or_host[:left])
|
connections << pair_val_to_connection(pair_or_host[:left])
|
||||||
connections << pair_val_to_connection(pair_or_host[:right])
|
connections << pair_val_to_connection(pair_or_host[:right])
|
||||||
connections
|
connections
|
||||||
when nil
|
when nil
|
||||||
[['localhost', DEFAULT_PORT]]
|
[['localhost', DEFAULT_PORT]]
|
||||||
end
|
end
|
||||||
@options = options
|
@options = options
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return the XGen::Mongo::Driver::DB named +db_name+. The slave_ok and
|
# Return the Mongo::DB named +db_name+. The slave_ok and
|
||||||
# auto_reconnect options passed in via #new may be overridden here.
|
# auto_reconnect options passed in via #new may be overridden here.
|
||||||
# See DB#new for other options you can pass in.
|
# See DB#new for other options you can pass in.
|
||||||
def db(db_name, options={})
|
def db(db_name, options={})
|
||||||
XGen::Mongo::Driver::DB.new(db_name, @pair, @options.merge(options))
|
DB.new(db_name, @pair, @options.merge(options))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a hash containing database names as keys and disk space for
|
# Returns a hash containing database names as keys and disk space for
|
||||||
# each as values.
|
# each as values.
|
||||||
def database_info
|
def database_info
|
||||||
doc = single_db_command('admin', :listDatabases => 1)
|
doc = single_db_command('admin', :listDatabases => 1)
|
||||||
h = {}
|
h = {}
|
||||||
doc['databases'].each { |db|
|
doc['databases'].each { |db|
|
||||||
h[db['name']] = db['sizeOnDisk'].to_i
|
h[db['name']] = db['sizeOnDisk'].to_i
|
||||||
}
|
}
|
||||||
h
|
h
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns an array of database names.
|
# Returns an array of database names.
|
||||||
def database_names
|
def database_names
|
||||||
database_info.keys
|
database_info.keys
|
||||||
end
|
end
|
||||||
|
|
||||||
# Not implemented.
|
# Not implemented.
|
||||||
def clone_database(from)
|
def clone_database(from)
|
||||||
raise "not implemented"
|
raise "not implemented"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Not implemented.
|
# Not implemented.
|
||||||
def copy_database(from_host, from_db, to_db)
|
def copy_database(from_host, from_db, to_db)
|
||||||
raise "not implemented"
|
raise "not implemented"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Drops the database +name+.
|
# Drops the database +name+.
|
||||||
def drop_database(name)
|
def drop_database(name)
|
||||||
single_db_command(name, :dropDatabase => 1)
|
single_db_command(name, :dropDatabase => 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
# Turns an array containing a host name string and a
|
|
||||||
# port number integer into a [host, port] pair array.
|
|
||||||
def pair_val_to_connection(a)
|
|
||||||
case a
|
|
||||||
when nil
|
|
||||||
['localhost', DEFAULT_PORT]
|
|
||||||
when String
|
|
||||||
[a, DEFAULT_PORT]
|
|
||||||
when Integer
|
|
||||||
['localhost', a]
|
|
||||||
when Array
|
|
||||||
a
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Send cmd (a hash, possibly ordered) to the admin database and return
|
|
||||||
# the answer. Raises an error unless the return is "ok" (DB#ok?
|
|
||||||
# returns +true+).
|
|
||||||
def single_db_command(db_name, cmd)
|
|
||||||
db = nil
|
|
||||||
begin
|
|
||||||
db = db(db_name)
|
|
||||||
doc = db.db_command(cmd)
|
|
||||||
raise "error retrieving database info: #{doc.inspect}" unless db.ok?(doc)
|
|
||||||
doc
|
|
||||||
ensure
|
|
||||||
db.close if db
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
# Turns an array containing a host name string and a
|
||||||
|
# port number integer into a [host, port] pair array.
|
||||||
|
def pair_val_to_connection(a)
|
||||||
|
case a
|
||||||
|
when nil
|
||||||
|
['localhost', DEFAULT_PORT]
|
||||||
|
when String
|
||||||
|
[a, DEFAULT_PORT]
|
||||||
|
when Integer
|
||||||
|
['localhost', a]
|
||||||
|
when Array
|
||||||
|
a
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Send cmd (a hash, possibly ordered) to the admin database and return
|
||||||
|
# the answer. Raises an error unless the return is "ok" (DB#ok?
|
||||||
|
# returns +true+).
|
||||||
|
def single_db_command(db_name, cmd)
|
||||||
|
db = nil
|
||||||
|
begin
|
||||||
|
db = db(db_name)
|
||||||
|
doc = db.db_command(cmd)
|
||||||
|
raise "error retrieving database info: #{doc.inspect}" unless db.ok?(doc)
|
||||||
|
doc
|
||||||
|
ensure
|
||||||
|
db.close if db
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,102 +18,98 @@ require 'mongo/collection'
|
|||||||
require 'mongo/message'
|
require 'mongo/message'
|
||||||
require 'mongo/types/code'
|
require 'mongo/types/code'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
# A query against a collection. A query's selector is a hash. See the
|
# A query against a collection. A query's selector is a hash. See the
|
||||||
# Mongo documentation for query details.
|
# Mongo documentation for query details.
|
||||||
class Query
|
class Query
|
||||||
|
|
||||||
attr_accessor :number_to_skip, :number_to_return, :order_by, :snapshot
|
attr_accessor :number_to_skip, :number_to_return, :order_by, :snapshot
|
||||||
# If true, $explain will be set in QueryMessage that uses this query.
|
# If true, $explain will be set in QueryMessage that uses this query.
|
||||||
attr_accessor :explain
|
attr_accessor :explain
|
||||||
# Either +nil+ or a hash (preferably an OrderedHash).
|
# Either +nil+ or a hash (preferably an OrderedHash).
|
||||||
attr_accessor :hint
|
attr_accessor :hint
|
||||||
attr_reader :selector # writer defined below
|
attr_reader :selector # writer defined below
|
||||||
|
|
||||||
# sel :: A hash describing the query. See the Mongo docs for details.
|
# sel :: A hash describing the query. See the Mongo docs for details.
|
||||||
#
|
#
|
||||||
# return_fields :: If not +nil+, a single field name or an array of
|
# return_fields :: If not +nil+, a single field name or an array of
|
||||||
# field names. Only those fields will be returned.
|
# field names. Only those fields will be returned.
|
||||||
# (Called :fields in calls to Collection#find.)
|
# (Called :fields in calls to Collection#find.)
|
||||||
#
|
#
|
||||||
# number_to_skip :: Number of records to skip before returning
|
# number_to_skip :: Number of records to skip before returning
|
||||||
# records. (Called :offset in calls to
|
# records. (Called :offset in calls to
|
||||||
# Collection#find.) Default is 0.
|
# Collection#find.) Default is 0.
|
||||||
#
|
#
|
||||||
# number_to_return :: Max number of records to return. (Called :limit
|
# number_to_return :: Max number of records to return. (Called :limit
|
||||||
# in calls to Collection#find.) Default is 0 (all
|
# in calls to Collection#find.) Default is 0 (all
|
||||||
# records).
|
# records).
|
||||||
#
|
#
|
||||||
# order_by :: If not +nil+, specifies record sort order. May be a
|
# order_by :: If not +nil+, specifies record sort order. May be a
|
||||||
# String, Hash, OrderedHash, or Array. If a string, the
|
# String, Hash, OrderedHash, or Array. If a string, the
|
||||||
# results will be ordered by that field in ascending
|
# results will be ordered by that field in ascending
|
||||||
# order. If an array, it should be an array of field names
|
# order. If an array, it should be an array of field names
|
||||||
# which will all be sorted in ascending order. If a hash,
|
# which will all be sorted in ascending order. If a hash,
|
||||||
# it may be either a regular Hash or an OrderedHash. The
|
# it may be either a regular Hash or an OrderedHash. The
|
||||||
# keys should be field names, and the values should be 1
|
# keys should be field names, and the values should be 1
|
||||||
# (ascending) or -1 (descending). Note that if it is a
|
# (ascending) or -1 (descending). Note that if it is a
|
||||||
# regular Hash then sorting by more than one field
|
# regular Hash then sorting by more than one field
|
||||||
# probably will not be what you intend because key order
|
# probably will not be what you intend because key order
|
||||||
# is not preserved. (order_by is called :sort in calls to
|
# is not preserved. (order_by is called :sort in calls to
|
||||||
# Collection#find.)
|
# Collection#find.)
|
||||||
#
|
#
|
||||||
# hint :: If not +nil+, specifies query hint fields. Must be either
|
# hint :: If not +nil+, specifies query hint fields. Must be either
|
||||||
# +nil+ or a hash (preferably an OrderedHash). See
|
# +nil+ or a hash (preferably an OrderedHash). See
|
||||||
# Collection#hint.
|
# Collection#hint.
|
||||||
def initialize(sel={}, return_fields=nil, number_to_skip=0, number_to_return=0, order_by=nil, hint=nil, snapshot=nil)
|
def initialize(sel={}, return_fields=nil, number_to_skip=0, number_to_return=0, order_by=nil, hint=nil, snapshot=nil)
|
||||||
@number_to_skip, @number_to_return, @order_by, @hint, @snapshot =
|
@number_to_skip, @number_to_return, @order_by, @hint, @snapshot =
|
||||||
number_to_skip, number_to_return, order_by, hint, snapshot
|
number_to_skip, number_to_return, order_by, hint, snapshot
|
||||||
@explain = nil
|
@explain = nil
|
||||||
self.selector = sel
|
self.selector = sel
|
||||||
self.fields = return_fields
|
self.fields = return_fields
|
||||||
end
|
end
|
||||||
|
|
||||||
# Set query selector hash. If sel is Code/string, it will be used as a
|
# Set query selector hash. If sel is Code/string, it will be used as a
|
||||||
# $where clause. (See Mongo docs for details.)
|
# $where clause. (See Mongo docs for details.)
|
||||||
def selector=(sel)
|
def selector=(sel)
|
||||||
@selector = case sel
|
@selector = case sel
|
||||||
when nil
|
when nil
|
||||||
{}
|
{}
|
||||||
when Code
|
when Code
|
||||||
{"$where" => sel}
|
{"$where" => sel}
|
||||||
when String
|
when String
|
||||||
{"$where" => Code.new(sel)}
|
{"$where" => Code.new(sel)}
|
||||||
when Hash
|
when Hash
|
||||||
sel
|
sel
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Set fields to return. If +val+ is +nil+ or empty, all fields will be
|
# Set fields to return. If +val+ is +nil+ or empty, all fields will be
|
||||||
# returned.
|
# returned.
|
||||||
def fields=(val)
|
def fields=(val)
|
||||||
@fields = val
|
@fields = val
|
||||||
@fields = nil if @fields && @fields.empty?
|
@fields = nil if @fields && @fields.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
def fields
|
def fields
|
||||||
case @fields
|
case @fields
|
||||||
when String
|
when String
|
||||||
{@fields => 1}
|
{@fields => 1}
|
||||||
when Array
|
when Array
|
||||||
if @fields.length == 0
|
if @fields.length == 0
|
||||||
nil
|
nil
|
||||||
else
|
else
|
||||||
h = {}
|
h = {}
|
||||||
@fields.each { |field| h[field] = 1 }
|
@fields.each { |field| h[field] = 1 }
|
||||||
h
|
h
|
||||||
end
|
|
||||||
else # nil, anything else
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def contains_special_fields
|
|
||||||
(@order_by != nil && @order_by.length > 0) || @explain || @hint || @snapshot
|
|
||||||
end
|
end
|
||||||
|
else # nil, anything else
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def contains_special_fields
|
||||||
|
(@order_by != nil && @order_by.length > 0) || @explain || @hint || @snapshot
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -16,27 +16,23 @@
|
|||||||
|
|
||||||
require 'mongo/util/byte_buffer'
|
require 'mongo/util/byte_buffer'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
# An array of binary bytes with a Mongo subtype value.
|
# An array of binary bytes with a Mongo subtype value.
|
||||||
class Binary < ByteBuffer
|
class Binary < ByteBuffer
|
||||||
|
|
||||||
SUBTYPE_BYTES = 0x02
|
SUBTYPE_BYTES = 0x02
|
||||||
SUBTYPE_UUID = 0x03
|
SUBTYPE_UUID = 0x03
|
||||||
SUBTYPE_MD5 = 0x05
|
SUBTYPE_MD5 = 0x05
|
||||||
SUBTYPE_USER_DEFINED = 0x80
|
SUBTYPE_USER_DEFINED = 0x80
|
||||||
|
|
||||||
# One of the SUBTYPE_* constants. Default is SUBTYPE_BYTES.
|
# One of the SUBTYPE_* constants. Default is SUBTYPE_BYTES.
|
||||||
attr_accessor :subtype
|
attr_accessor :subtype
|
||||||
|
|
||||||
def initialize(initial_data=[], subtype=SUBTYPE_BYTES)
|
def initialize(initial_data=[], subtype=SUBTYPE_BYTES)
|
||||||
super(initial_data)
|
super(initial_data)
|
||||||
@subtype = subtype
|
@subtype = subtype
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -14,21 +14,17 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
# ++
|
# ++
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
def initialize(code, scope={})
|
def initialize(code, scope={})
|
||||||
super(code)
|
super(code)
|
||||||
@scope = scope
|
@scope = scope
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -14,24 +14,20 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
# ++
|
# ++
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
class DBRef
|
class DBRef
|
||||||
|
|
||||||
attr_reader :namespace, :object_id
|
attr_reader :namespace, :object_id
|
||||||
|
|
||||||
def initialize(namespace, object_id)
|
def initialize(namespace, object_id)
|
||||||
@namespace, @object_id =
|
@namespace, @object_id =
|
||||||
namespace, object_id
|
namespace, object_id
|
||||||
end
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
"ns: #{namespace}, id: #{object_id}"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"ns: #{namespace}, id: #{object_id}"
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -17,121 +17,117 @@
|
|||||||
require 'mutex_m'
|
require 'mutex_m'
|
||||||
require 'mongo/util/byte_buffer'
|
require 'mongo/util/byte_buffer'
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
# Implementation of the Babble OID. Object ids are not required by
|
# Implementation of the Babble OID. Object ids are not required by
|
||||||
# Mongo, but they make certain operations more efficient.
|
# Mongo, but they make certain operations more efficient.
|
||||||
#
|
#
|
||||||
# The driver does not automatically assign ids to records that are
|
# The driver does not automatically assign ids to records that are
|
||||||
# inserted. (An upcoming feature will allow you to give an id "factory"
|
# inserted. (An upcoming feature will allow you to give an id "factory"
|
||||||
# to a database and/or a collection.)
|
# to a database and/or a collection.)
|
||||||
#
|
#
|
||||||
# 12 bytes
|
# 12 bytes
|
||||||
# ---
|
# ---
|
||||||
# 0 time
|
# 0 time
|
||||||
# 1
|
# 1
|
||||||
# 2
|
# 2
|
||||||
# 3
|
# 3
|
||||||
# 4 machine
|
# 4 machine
|
||||||
# 5
|
# 5
|
||||||
# 6
|
# 6
|
||||||
# 7 pid
|
# 7 pid
|
||||||
# 8
|
# 8
|
||||||
# 9 inc
|
# 9 inc
|
||||||
# 10
|
# 10
|
||||||
# 11
|
# 11
|
||||||
class ObjectID
|
class ObjectID
|
||||||
|
|
||||||
MACHINE = ( val = rand(0x1000000); [val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff] )
|
MACHINE = ( val = rand(0x1000000); [val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff] )
|
||||||
PID = ( val = rand(0x10000); [val & 0xff, (val >> 8) & 0xff]; )
|
PID = ( val = rand(0x10000); [val & 0xff, (val >> 8) & 0xff]; )
|
||||||
|
|
||||||
# The string representation of an OID is different than its internal
|
# The string representation of an OID is different than its internal
|
||||||
# and BSON byte representations. The BYTE_ORDER here maps
|
# and BSON byte representations. The BYTE_ORDER here maps
|
||||||
# internal/BSON byte position (the index in BYTE_ORDER) to the
|
# internal/BSON byte position (the index in BYTE_ORDER) to the
|
||||||
# position of the two hex characters representing that byte in the
|
# position of the two hex characters representing that byte in the
|
||||||
# string representation. For example, the 0th BSON byte corresponds to
|
# string representation. For example, the 0th BSON byte corresponds to
|
||||||
# the (0-based) 7th pair of hex chars in the string.
|
# the (0-based) 7th pair of hex chars in the string.
|
||||||
BYTE_ORDER = [7, 6, 5, 4, 3, 2, 1, 0, 11, 10, 9, 8]
|
BYTE_ORDER = [7, 6, 5, 4, 3, 2, 1, 0, 11, 10, 9, 8]
|
||||||
|
|
||||||
LOCK = Object.new
|
LOCK = Object.new
|
||||||
LOCK.extend Mutex_m
|
LOCK.extend Mutex_m
|
||||||
|
|
||||||
@@index_time = Time.new.to_i
|
@@index_time = Time.new.to_i
|
||||||
@@index = 0
|
@@index = 0
|
||||||
|
|
||||||
# Given a string representation of an ObjectID, return a new ObjectID
|
# Given a string representation of an ObjectID, return a new ObjectID
|
||||||
# with that value.
|
# with that value.
|
||||||
def self.from_string(str)
|
def self.from_string(str)
|
||||||
raise "illegal ObjectID format" unless legal?(str)
|
raise "illegal ObjectID format" unless legal?(str)
|
||||||
data = []
|
data = []
|
||||||
BYTE_ORDER.each_with_index { |string_position, data_index|
|
BYTE_ORDER.each_with_index { |string_position, data_index|
|
||||||
data[data_index] = str[string_position * 2, 2].to_i(16)
|
data[data_index] = str[string_position * 2, 2].to_i(16)
|
||||||
}
|
}
|
||||||
self.new(data)
|
self.new(data)
|
||||||
end
|
|
||||||
|
|
||||||
def self.legal?(str)
|
|
||||||
len = BYTE_ORDER.length * 2
|
|
||||||
str =~ /([0-9a-f]+)/i
|
|
||||||
match = $1
|
|
||||||
str && str.length == len && match == str
|
|
||||||
end
|
|
||||||
|
|
||||||
# +data+ is an array of bytes. If nil, a new id will be generated.
|
|
||||||
# The time +t+ is only used for testing; leave it nil.
|
|
||||||
def initialize(data=nil, t=nil)
|
|
||||||
@data = data || generate_id(t)
|
|
||||||
end
|
|
||||||
|
|
||||||
def eql?(other)
|
|
||||||
@data == other.instance_variable_get("@data")
|
|
||||||
end
|
|
||||||
alias_method :==, :eql?
|
|
||||||
|
|
||||||
def to_a
|
|
||||||
@data.dup
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
str = ' ' * 24
|
|
||||||
BYTE_ORDER.each_with_index { |string_position, data_index|
|
|
||||||
str[string_position * 2, 2] = '%02x' % @data[data_index]
|
|
||||||
}
|
|
||||||
str
|
|
||||||
end
|
|
||||||
|
|
||||||
# (Would normally be private, but isn't so we can test it.)
|
|
||||||
def generate_id(t=nil)
|
|
||||||
t ||= Time.new.to_i
|
|
||||||
buf = ByteBuffer.new
|
|
||||||
buf.put_int(t & 0xffffffff)
|
|
||||||
buf.put_array(MACHINE)
|
|
||||||
buf.put_array(PID)
|
|
||||||
i = index_for_time(t)
|
|
||||||
buf.put(i & 0xff)
|
|
||||||
buf.put((i >> 8) & 0xff)
|
|
||||||
buf.put((i >> 16) & 0xff)
|
|
||||||
|
|
||||||
buf.rewind
|
|
||||||
buf.to_a.dup
|
|
||||||
end
|
|
||||||
|
|
||||||
# (Would normally be private, but isn't so we can test it.)
|
|
||||||
def index_for_time(t)
|
|
||||||
LOCK.mu_synchronize {
|
|
||||||
if t != @@index_time
|
|
||||||
@@index = 0
|
|
||||||
@@index_time = t
|
|
||||||
end
|
|
||||||
retval = @@index
|
|
||||||
@@index += 1
|
|
||||||
retval
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.legal?(str)
|
||||||
|
len = BYTE_ORDER.length * 2
|
||||||
|
str =~ /([0-9a-f]+)/i
|
||||||
|
match = $1
|
||||||
|
str && str.length == len && match == str
|
||||||
|
end
|
||||||
|
|
||||||
|
# +data+ is an array of bytes. If nil, a new id will be generated.
|
||||||
|
# The time +t+ is only used for testing; leave it nil.
|
||||||
|
def initialize(data=nil, t=nil)
|
||||||
|
@data = data || generate_id(t)
|
||||||
|
end
|
||||||
|
|
||||||
|
def eql?(other)
|
||||||
|
@data == other.instance_variable_get("@data")
|
||||||
|
end
|
||||||
|
alias_method :==, :eql?
|
||||||
|
|
||||||
|
def to_a
|
||||||
|
@data.dup
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
str = ' ' * 24
|
||||||
|
BYTE_ORDER.each_with_index { |string_position, data_index|
|
||||||
|
str[string_position * 2, 2] = '%02x' % @data[data_index]
|
||||||
|
}
|
||||||
|
str
|
||||||
|
end
|
||||||
|
|
||||||
|
# (Would normally be private, but isn't so we can test it.)
|
||||||
|
def generate_id(t=nil)
|
||||||
|
t ||= Time.new.to_i
|
||||||
|
buf = ByteBuffer.new
|
||||||
|
buf.put_int(t & 0xffffffff)
|
||||||
|
buf.put_array(MACHINE)
|
||||||
|
buf.put_array(PID)
|
||||||
|
i = index_for_time(t)
|
||||||
|
buf.put(i & 0xff)
|
||||||
|
buf.put((i >> 8) & 0xff)
|
||||||
|
buf.put((i >> 16) & 0xff)
|
||||||
|
|
||||||
|
buf.rewind
|
||||||
|
buf.to_a.dup
|
||||||
|
end
|
||||||
|
|
||||||
|
# (Would normally be private, but isn't so we can test it.)
|
||||||
|
def index_for_time(t)
|
||||||
|
LOCK.mu_synchronize {
|
||||||
|
if t != @@index_time
|
||||||
|
@@index = 0
|
||||||
|
@@index_time = t
|
||||||
|
end
|
||||||
|
retval = @@index
|
||||||
|
@@index += 1
|
||||||
|
retval
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -14,31 +14,27 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
# ++
|
# ++
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
# A Regexp that can hold on to extra options and ignore them. Mongo
|
# A Regexp that can hold on to extra options and ignore them. Mongo
|
||||||
# regexes may contain option characters beyond 'i', 'm', and 'x'. (Note
|
# regexes may contain option characters beyond 'i', 'm', and 'x'. (Note
|
||||||
# that Mongo only uses those three, but that regexes coming from other
|
# that Mongo only uses those three, but that regexes coming from other
|
||||||
# languages may store different option characters.)
|
# languages may store different option characters.)
|
||||||
#
|
#
|
||||||
# Note that you do not have to use this class at all if you wish to
|
# Note that you do not have to use this class at all if you wish to
|
||||||
# store regular expressions in Mongo. The Mongo and Ruby regex option
|
# store regular expressions in Mongo. The Mongo and Ruby regex option
|
||||||
# flags are the same. Storing regexes is discouraged, in any case.
|
# flags are the same. Storing regexes is discouraged, in any case.
|
||||||
class RegexpOfHolding < Regexp
|
class RegexpOfHolding < Regexp
|
||||||
|
|
||||||
attr_accessor :extra_options_str
|
attr_accessor :extra_options_str
|
||||||
|
|
||||||
# +str+ and +options+ are the same as Regexp. +extra_options_str+
|
|
||||||
# contains all the other flags that were in Mongo but we do not use or
|
|
||||||
# understand.
|
|
||||||
def initialize(str, options, extra_options_str)
|
|
||||||
super(str, options)
|
|
||||||
@extra_options_str = extra_options_str
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
# +str+ and +options+ are the same as Regexp. +extra_options_str+
|
||||||
|
# contains all the other flags that were in Mongo but we do not use or
|
||||||
|
# understand.
|
||||||
|
def initialize(str, options, extra_options_str)
|
||||||
|
super(str, options)
|
||||||
|
@extra_options_str = extra_options_str
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -14,19 +14,15 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
# ++
|
# ++
|
||||||
|
|
||||||
module XGen
|
module Mongo
|
||||||
module Mongo
|
|
||||||
module Driver
|
|
||||||
|
|
||||||
# DEPRECATED - the ruby driver converts the BSON undefined type to nil,
|
# DEPRECATED - the ruby driver converts the BSON undefined type to nil,
|
||||||
# and saves this type as nil
|
# and saves this type as nil
|
||||||
class Undefined < Object
|
class Undefined < Object
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
super
|
super
|
||||||
warn "the Undefined type is deprecated and will be removed - BSON undefineds get implicitely converted to nil now"
|
warn "the Undefined type is deprecated and will be removed - BSON undefineds get implicitely converted to nil now"
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -26,7 +26,7 @@ require 'mongo/types/undefined'
|
|||||||
# A BSON seralizer/deserializer.
|
# A BSON seralizer/deserializer.
|
||||||
class BSON
|
class BSON
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
MINKEY = -1
|
MINKEY = -1
|
||||||
EOO = 0
|
EOO = 0
|
||||||
|
@ -21,7 +21,7 @@ require 'mongo'
|
|||||||
# an OrderedHash.
|
# an OrderedHash.
|
||||||
class XMLToRuby
|
class XMLToRuby
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
def xml_to_ruby(io)
|
def xml_to_ruby(io)
|
||||||
doc = REXML::Document.new(io)
|
doc = REXML::Document.new(io)
|
||||||
|
@ -5,4 +5,4 @@ DEFAULT_HOST = '127.0.0.1'
|
|||||||
DEFAULT_PORT = 27017
|
DEFAULT_PORT = 27017
|
||||||
DEFAULT_DB = 'driver_test_framework'
|
DEFAULT_DB = 'driver_test_framework'
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
require File.join(File.dirname(__FILE__), '_common.rb')
|
require File.join(File.dirname(__FILE__), '_common.rb')
|
||||||
|
|
||||||
require 'mongo/gridfs'
|
require 'mongo/gridfs'
|
||||||
include XGen::Mongo::GridFS
|
include GridFS
|
||||||
|
|
||||||
db = Mongo.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
|
db = Mongo::Mongo.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
|
||||||
|
|
||||||
input_file = ARGV[0]
|
input_file = ARGV[0]
|
||||||
|
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
require File.join(File.dirname(__FILE__), '_common.rb')
|
require File.join(File.dirname(__FILE__), '_common.rb')
|
||||||
|
|
||||||
require 'mongo/gridfs'
|
require 'mongo/gridfs'
|
||||||
include XGen::Mongo::GridFS
|
include GridFS
|
||||||
|
|
||||||
db = Mongo.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
|
db = Mongo::Mongo.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
|
||||||
|
|
||||||
input_file = ARGV[0]
|
input_file = ARGV[0]
|
||||||
output_file = ARGV[1]
|
output_file = ARGV[1]
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
require File.join(File.dirname(__FILE__), '_common.rb')
|
require File.join(File.dirname(__FILE__), '_common.rb')
|
||||||
|
|
||||||
include XGen::Mongo
|
include Mongo
|
||||||
|
|
||||||
db = Mongo.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
|
db = Mongo::Mongo.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
|
||||||
x = db.collection('x')
|
x = db.collection('x')
|
||||||
y = db.collection('y')
|
y = db.collection('y')
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ require 'test/unit'
|
|||||||
# NOTE: assumes Mongo is running
|
# NOTE: assumes Mongo is running
|
||||||
class AdminTest < Test::Unit::TestCase
|
class AdminTest < Test::Unit::TestCase
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
@@db = Mongo.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
@@db = Mongo.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
||||||
ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT).db('ruby-mongo-test')
|
ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT).db('ruby-mongo-test')
|
||||||
|
@ -5,7 +5,7 @@ require 'test/unit'
|
|||||||
|
|
||||||
class BSONTest < Test::Unit::TestCase
|
class BSONTest < Test::Unit::TestCase
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
# We don't pass a DB to the constructor, even though we are about to test
|
# We don't pass a DB to the constructor, even though we are about to test
|
||||||
@ -85,7 +85,7 @@ class BSONTest < Test::Unit::TestCase
|
|||||||
assert_equal doc, doc2
|
assert_equal doc, doc2
|
||||||
|
|
||||||
r = doc2['doc']
|
r = doc2['doc']
|
||||||
assert_kind_of XGen::Mongo::Driver::RegexpOfHolding, r
|
assert_kind_of RegexpOfHolding, r
|
||||||
assert_equal '', r.extra_options_str
|
assert_equal '', r.extra_options_str
|
||||||
|
|
||||||
r.extra_options_str << 'zywcab'
|
r.extra_options_str << 'zywcab'
|
||||||
@ -99,7 +99,7 @@ class BSONTest < Test::Unit::TestCase
|
|||||||
assert_equal doc, doc2
|
assert_equal doc, doc2
|
||||||
|
|
||||||
r = doc2['doc']
|
r = doc2['doc']
|
||||||
assert_kind_of XGen::Mongo::Driver::RegexpOfHolding, r
|
assert_kind_of RegexpOfHolding, r
|
||||||
assert_equal 'abcwyz', r.extra_options_str # must be sorted
|
assert_equal 'abcwyz', r.extra_options_str # must be sorted
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -5,8 +5,8 @@ require 'mongo/gridfs'
|
|||||||
|
|
||||||
class ChunkTest < Test::Unit::TestCase
|
class ChunkTest < Test::Unit::TestCase
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
include XGen::Mongo::GridFS
|
include GridFS
|
||||||
|
|
||||||
@@db = Mongo.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
@@db = Mongo.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
||||||
ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT).db('ruby-mongo-utils-test')
|
ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT).db('ruby-mongo-utils-test')
|
||||||
|
@ -20,8 +20,7 @@ require 'test/unit'
|
|||||||
|
|
||||||
# NOTE: assumes Mongo is running
|
# NOTE: assumes Mongo is running
|
||||||
class TestCollection < Test::Unit::TestCase
|
class TestCollection < Test::Unit::TestCase
|
||||||
include XGen::Mongo
|
include Mongo
|
||||||
include XGen::Mongo::Driver
|
|
||||||
|
|
||||||
@@db = Mongo.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
@@db = Mongo.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
||||||
ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT).db('ruby-mongo-test')
|
ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT).db('ruby-mongo-test')
|
||||||
|
@ -5,7 +5,7 @@ require 'test/unit'
|
|||||||
# NOTE: assumes Mongo is running
|
# NOTE: assumes Mongo is running
|
||||||
class CursorTest < Test::Unit::TestCase
|
class CursorTest < Test::Unit::TestCase
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
@@db = Mongo.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
@@db = Mongo.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
||||||
ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT).db('ruby-mongo-test')
|
ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT).db('ruby-mongo-test')
|
||||||
|
@ -5,7 +5,7 @@ require 'test/unit'
|
|||||||
|
|
||||||
class TestPKFactory
|
class TestPKFactory
|
||||||
def create_pk(row)
|
def create_pk(row)
|
||||||
row['_id'] ||= XGen::Mongo::Driver::ObjectID.new
|
row['_id'] ||= Mongo::ObjectID.new
|
||||||
row
|
row
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -13,7 +13,7 @@ end
|
|||||||
# NOTE: assumes Mongo is running
|
# NOTE: assumes Mongo is running
|
||||||
class DBTest < Test::Unit::TestCase
|
class DBTest < Test::Unit::TestCase
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
@@host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
@@host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
@@port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT
|
@@port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT
|
||||||
@ -85,7 +85,7 @@ class DBTest < Test::Unit::TestCase
|
|||||||
assert_not_nil oid
|
assert_not_nil oid
|
||||||
assert_equal insert_id, oid
|
assert_equal insert_id, oid
|
||||||
|
|
||||||
oid = XGen::Mongo::Driver::ObjectID.new
|
oid = ObjectID.new
|
||||||
data = {'_id' => oid, 'name' => 'Barney', 'age' => 41}
|
data = {'_id' => oid, 'name' => 'Barney', 'age' => 41}
|
||||||
coll.insert(data)
|
coll.insert(data)
|
||||||
row = coll.find_one({'name' => data['name']})
|
row = coll.find_one({'name' => data['name']})
|
||||||
|
@ -4,8 +4,7 @@ require 'test/unit'
|
|||||||
|
|
||||||
# NOTE: assumes Mongo is running
|
# NOTE: assumes Mongo is running
|
||||||
class DBAPITest < Test::Unit::TestCase
|
class DBAPITest < Test::Unit::TestCase
|
||||||
include XGen::Mongo
|
include Mongo
|
||||||
include XGen::Mongo::Driver
|
|
||||||
|
|
||||||
@@db = Mongo.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
@@db = Mongo.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
||||||
ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT).db('ruby-mongo-test')
|
ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT).db('ruby-mongo-test')
|
||||||
@ -817,18 +816,4 @@ class DBAPITest < Test::Unit::TestCase
|
|||||||
@@db.collection("test").find({}, :snapshot => true, :sort => 'a').to_a
|
@@db.collection("test").find({}, :snapshot => true, :sort => 'a').to_a
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO this test fails with error message "Undefed Before end of object"
|
|
||||||
# That is a database error. The undefined type may go away.
|
|
||||||
|
|
||||||
# def test_insert_undefined
|
|
||||||
# doc = {'undef' => Undefined.new}
|
|
||||||
# @@coll.clear
|
|
||||||
# @@coll.insert(doc)
|
|
||||||
# p @@db.error # DEBUG
|
|
||||||
# assert_equal 1, @@coll.count
|
|
||||||
# row = @@coll.find().next_object
|
|
||||||
# assert_not_nil row
|
|
||||||
# end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -5,7 +5,7 @@ require 'test/unit'
|
|||||||
# NOTE: assumes Mongo is running
|
# NOTE: assumes Mongo is running
|
||||||
class DBConnectionTest < Test::Unit::TestCase
|
class DBConnectionTest < Test::Unit::TestCase
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
def test_no_exceptions
|
def test_no_exceptions
|
||||||
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
|
@ -5,8 +5,8 @@ require 'mongo/gridfs'
|
|||||||
|
|
||||||
class GridStoreTest < Test::Unit::TestCase
|
class GridStoreTest < Test::Unit::TestCase
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
include XGen::Mongo::GridFS
|
include GridFS
|
||||||
|
|
||||||
@@db = Mongo.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
@@db = Mongo.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
|
||||||
ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT).db('ruby-mongo-test')
|
ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT).db('ruby-mongo-test')
|
||||||
|
@ -4,7 +4,7 @@ require 'test/unit'
|
|||||||
|
|
||||||
class MessageTest < Test::Unit::TestCase
|
class MessageTest < Test::Unit::TestCase
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
@msg = Message.new(42)
|
@msg = Message.new(42)
|
||||||
|
@ -5,7 +5,7 @@ require 'test/unit'
|
|||||||
# NOTE: assumes Mongo is running
|
# NOTE: assumes Mongo is running
|
||||||
class MongoTest < Test::Unit::TestCase
|
class MongoTest < Test::Unit::TestCase
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
@host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
@host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
|
@ -4,7 +4,7 @@ require 'test/unit'
|
|||||||
|
|
||||||
class ObjectIDTest < Test::Unit::TestCase
|
class ObjectIDTest < Test::Unit::TestCase
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
@t = 42
|
@t = 42
|
||||||
|
@ -13,7 +13,7 @@ require 'test/unit'
|
|||||||
# of this project), then we find the BSON test files there and use those, too.
|
# of this project), then we find the BSON test files there and use those, too.
|
||||||
class RoundTripTest < Test::Unit::TestCase
|
class RoundTripTest < Test::Unit::TestCase
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
@@ruby = nil
|
@@ruby = nil
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ require 'test/unit'
|
|||||||
|
|
||||||
class TestThreading < Test::Unit::TestCase
|
class TestThreading < Test::Unit::TestCase
|
||||||
|
|
||||||
include XGen::Mongo::Driver
|
include Mongo
|
||||||
|
|
||||||
@@host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
@@host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
||||||
@@port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT
|
@@port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT
|
||||||
|
Loading…
Reference in New Issue
Block a user