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

This commit is contained in:
Adrian Madrid 2008-12-18 13:56:33 -07:00
commit 3aac974f13
13 changed files with 252 additions and 38 deletions

View File

@ -10,7 +10,7 @@ for any production data yet.
= Demo = Demo
$ ruby examples/demo.rb $ ruby examples/simple.rb
Mongo must be running, of course. Mongo must be running, of course.
@ -54,6 +54,8 @@ type
= To Do = To Do
* Tests for update and repsert.
* Add a way to specify a collection of databases on startup (a simple array of * Add a way to specify a collection of databases on startup (a simple array of
IP address/port numbers, perhaps, or a hash or something). The driver would IP address/port numbers, perhaps, or a hash or something). The driver would
then find the master and, on each subsequent command, ask that machine if it then find the master and, on each subsequent command, ask that machine if it
@ -64,8 +66,6 @@ type
* Capped collection support. * Capped collection support.
* More code comments. More text in this file.
* Support more types: REF, SYMBOL, CODE_W_SCOPE, etc. * Support more types: REF, SYMBOL, CODE_W_SCOPE, etc.
* Introduce optional per-database and per-collection PKInjector. * Introduce optional per-database and per-collection PKInjector.
@ -105,5 +105,5 @@ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
details. details.
See http://www.gnu.org/licenses/ for a copy of the GNU Affero General Public See http://www.gnu.org/licenses for a copy of the GNU Affero General Public
License. License.

View File

@ -11,5 +11,5 @@ end
desc "Generate documentation" desc "Generate documentation"
task :rdoc do task :rdoc do
FileUtils.rm_rf('doc') FileUtils.rm_rf('doc')
system "rdoc --main README --inline-source --quiet README `find lib -name '*.rb'`" system "rdoc --main README.rdoc --inline-source --quiet README.rdoc `find lib -name '*.rb'`"
end end

View File

@ -11,14 +11,7 @@ db = Mongo.new(host, port).db('ruby-mongo-examples-simple')
coll = db.collection('test') coll = db.collection('test')
coll.clear coll.clear
doc = {'a' => 1} 3.times { |i| coll.insert({'a' => i+1}) }
coll.insert(doc)
doc = {'a' => 2}
coll.insert(doc)
doc = {'a' => 3}
coll.insert(doc)
puts "There are #{coll.count()} records in the test collection. Here they are:" puts "There are #{coll.count()} records in the test collection. Here they are:"
coll.find().each { |doc| puts doc.inspect } coll.find().each { |doc| puts doc.inspect }

View File

@ -1,3 +1,4 @@
# --
# Copyright (C) 2008 10gen Inc. # Copyright (C) 2008 10gen Inc.
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
@ -11,12 +12,15 @@
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# ++
require 'mongo/query' require 'mongo/query'
module XGen module XGen
module Mongo module Mongo
module Driver module Driver
# A named collection of records in a database.
class Collection class Collection
attr_reader :db, :name attr_reader :db, :name
@ -26,18 +30,23 @@ module XGen
@name = name @name = name
end end
# Return records that match a +selector+ hash. See Mongo docs for
# details.
#
# Options: # Options:
# * <tt>:fields</tt> - Array of collection field names; only those will be returned (plus _id if defined) # :fields :: Array of collection field names; only those will be returned (plus _id if defined)
# * <tt>:offset</tt> - Start at this record when returning records # :offset :: Start at this record when returning records
# * <tt>:limit</tt> - Maximum number of records to return # :limit :: Maximum number of records to return
# * <tt>:sort</tt> - Hash of field names as keys and 1/-1 as values; 1 == ascending, -1 == descending # :sort :: Either hash of field names as keys and 1/-1 as values; 1 ==
# <tt> # ascending, -1 == descending, or array of field names (all
# assumed to be sorted in ascending order).
def find(selector={}, options={}) def find(selector={}, options={})
fields = options.delete(:fields) fields = options.delete(:fields)
fields = nil if fields && fields.empty? fields = nil if fields && fields.empty?
@db.query(@name, Query.new(selector, fields, options[:offset] || 0, options[:limit] || 0, options[:sort])) @db.query(@name, Query.new(selector, fields, options[:offset] || 0, options[:limit] || 0, options[:sort]))
end end
# Insert +objects+, which are hashes.
def insert(*objects) def insert(*objects)
objects = objects.first if objects.size == 1 && objects.first.is_a?(Array) objects = objects.first if objects.size == 1 && objects.first.is_a?(Array)
res = @db.insert_into_db(@name, objects) res = @db.insert_into_db(@name, objects)
@ -45,45 +54,73 @@ module XGen
end end
alias_method :<<, :insert alias_method :<<, :insert
# Remove the records that match +selector+.
def remove(selector={}) def remove(selector={})
@db.remove_from_db(@name, selector) @db.remove_from_db(@name, selector)
end end
# Remove all records.
def clear def clear
remove({}) remove({})
end end
# Update records that match +selector+ by applying +obj+ as an update.
# If no match, inserts (???).
def repsert(selector, obj) def repsert(selector, obj)
@db.repsert_in_db(@name, selector, obj) @db.repsert_in_db(@name, selector, obj)
end end
# Update records that match +selector+ by applying +obj+ as an update.
def replace(selector, obj) def replace(selector, obj)
@db.replace_in_db(@name, selector, obj) @db.replace_in_db(@name, selector, obj)
end end
def modify(selector, modifierObj) # Update records that match +selector+ by applying +obj+ as an update.
raise "no object" unless modifierObj # Both +selector+ and +modifier_obj+ are required.
def modify(selector, modifier_obj)
raise "no object" unless modifier_obj
raise "no selector" unless selector raise "no selector" unless selector
@db.modify_in_db(@name, selector, modifierObj) @db.modify_in_db(@name, selector, modifier_obj)
end end
# Create a new index named +index_name+. +fields+ should be an array
# of field names.
def create_index(name, *fields) def create_index(name, *fields)
@db.create_index(@name, name, fields) @db.create_index(@name, name, fields)
end end
# Drop index +name+.
def drop_index(name) def drop_index(name)
@db.drop_index(@name, name) @db.drop_index(@name, name)
end end
# Drop all indexes.
def drop_indexes def drop_indexes
# just need to call drop indexes with no args; will drop them all # just need to call drop indexes with no args; will drop them all
@db.drop_index(@name, '*') @db.drop_index(@name, '*')
end end
# Return an array of hashes, one for each index. Each hash contains:
#
# :name :: Index name
#
# :keys :: Hash whose keys are the names of the fields that make up
# the key and values are integers.
#
# :ns :: Namespace; same as this collection's name.
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.
# 'create' will be the collection name. For the other possible keys
# and values, see DB#create_collection.
def options
@db.collections_info(@name).next_object()['options']
end
# Return the number of records that match +selector+. If +selector+ is
# +nil+ or an empty hash, returns the count of all records.
def count(selector={}) def count(selector={})
@db.count(@name, selector || {}) @db.count(@name, selector || {})
end end

View File

@ -1,3 +1,4 @@
# --
# Copyright (C) 2008 10gen Inc. # Copyright (C) 2008 10gen Inc.
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
@ -11,6 +12,7 @@
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# ++
require 'mongo/message' require 'mongo/message'
require 'mongo/util/byte_buffer' require 'mongo/util/byte_buffer'
@ -20,6 +22,7 @@ module XGen
module Mongo module Mongo
module Driver module Driver
# A cursor over query results. Returned objects are hashes.
class Cursor class Cursor
include Enumerable include Enumerable
@ -33,10 +36,12 @@ module XGen
read_all read_all
end end
# Return +true+ if there are more records to retrieve.
def more? def more?
num_remaining > 0 num_remaining > 0
end end
# Return the next object. Raises an error 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 = @objects.shift o = @objects.shift
@ -44,12 +49,14 @@ module XGen
o o
end end
# Iterate over each object, yielding it to the given block.
def each def each
while more? while more?
yield next_object() yield next_object()
end end
end end
# Close the cursor.
def close def close
@db.send_to_db(KillCursorMessage(@cursor_id)) if @cursor_id @db.send_to_db(KillCursorMessage(@cursor_id)) if @cursor_id
@objects = [] @objects = []

View File

@ -1,3 +1,4 @@
# --
# Copyright (C) 2008 10gen Inc. # Copyright (C) 2008 10gen Inc.
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
@ -11,8 +12,10 @@
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# ++
require 'socket' require 'socket'
require 'mongo/mongo'
require 'mongo/collection' require 'mongo/collection'
require 'mongo/message' require 'mongo/message'
require 'mongo/query' require 'mongo/query'
@ -22,42 +25,74 @@ module XGen
module Mongo module Mongo
module Driver module Driver
# A Mongo database.
class DB class DB
SYSTEM_NAMESPACE_COLLECTION = "system.namespaces" SYSTEM_NAMESPACE_COLLECTION = "system.namespaces"
SYSTEM_INDEX_COLLECTION = "system.indexes" SYSTEM_INDEX_COLLECTION = "system.indexes"
SYSTEM_COMMAND_COLLECTION = "$cmd" SYSTEM_COMMAND_COLLECTION = "$cmd"
# Strict mode means that trying to access a collection that does not # Strict mode enforces collection existence checks. When +true+,
# exist will raise an error. Strict mode is off (false) by default. # asking for a collection that does not exist or trying to create a
# collection that already exists raises an error.
#
# Strict mode is off (+false+) by default. Its value can be changed at
# any time.
attr_writer :strict attr_writer :strict
# Returns the value of the +strict+ flag. # Returns the value of the +strict+ flag.
def strict?; @strict; end def strict?; @strict; end
attr_reader :name, :socket # The name of the database.
attr_reader :name
def initialize(db_name, host, port) # The database's socket. For internal use only.
attr_reader :socket
# db_name :: The database name
#
# host :: The database host name or IP address. Defaults to 'localhost'.
#
# port :: The database port number. Defaults to
# XGen::Mongo::Driver::Mongo::DEFAULT_PORT.
#
def initialize(db_name, host='localhost', port=XGen::Mongo::Driver::Mongo::DEFAULT_PORT)
raise "Invalid DB name" if !db_name || (db_name && db_name.length > 0 && db_name.include?(".")) raise "Invalid DB name" if !db_name || (db_name && db_name.length > 0 && db_name.include?("."))
@name, @host, @port = db_name, host, port @name, @host, @port = db_name, host, port
@socket = TCPSocket.new(@host, @port) @socket = TCPSocket.new(@host, @port)
@strict = false @strict = false
end end
# Returns an array of collection names. Each name is of the form
# "database_name.collection_name".
def collection_names def collection_names
names = collections_info.collect { |doc| doc['name'] || '' } names = collections_info.collect { |doc| doc['name'] || '' }
names.delete('') names.delete('')
names names
end end
# Returns a cursor over query result hashes. Each hash contains a
# 'name' string and optionally an 'options' hash. If +coll_name+ is
# specified, an array of length 1 is returned.
def collections_info(coll_name=nil) def collections_info(coll_name=nil)
selector = {} selector = {}
selector[:name] = "#{@name}.#{coll_name}" if coll_name selector[:name] = full_coll_name(coll_name) if coll_name
query(SYSTEM_NAMESPACE_COLLECTION, Query.new(selector)) query(SYSTEM_NAMESPACE_COLLECTION, Query.new(selector))
end end
# Create a collection. If +strict+ is false, will return existing or # Create a collection. If +strict+ is false, will return existing or
# new collection. If +strict+ is true, will raise an error if # new collection. If +strict+ is true, will raise an error if
# collection +name+ does not already exist. # collection +name+ already exists.
#
# Options is an optional hash:
#
# :capped :: Boolean. If not specified, capped is +false+.
#
# :size :: If +capped+ is +true+, specifies the maximum number of
# bytes. If +false+, specifies the initial extent of the
# collection.
#
# :max :: Max number of records in a capped collection. Optional.
def create_collection(name, options={}) def create_collection(name, options={})
# First check existence # First check existence
if collection_names.include?(full_coll_name(name)) if collection_names.include?(full_coll_name(name))
@ -71,7 +106,7 @@ module XGen
# Create new collection # Create new collection
oh = OrderedHash.new oh = OrderedHash.new
oh[:create] = name oh[:create] = name
doc = db_command(oh.merge(options)) doc = db_command(oh.merge(options || {}))
ok = doc['ok'] ok = doc['ok']
return Collection.new(self, name) if ok.kind_of?(Numeric) && (ok.to_i == 1 || ok.to_i == 0) return Collection.new(self, name) if ok.kind_of?(Numeric) && (ok.to_i == 1 || ok.to_i == 0)
raise "Error creating collection: #{doc.inspect}" raise "Error creating collection: #{doc.inspect}"
@ -85,7 +120,7 @@ module XGen
# Return a collection. If +strict+ is false, will return existing or # Return a collection. If +strict+ is false, will return existing or
# new collection. If +strict+ is true, will raise an error if # new collection. If +strict+ is true, will raise an error if
# collection +name+ already exists. # collection +name+ does not already exists.
def collection(name) def collection(name)
return Collection.new(self, name) if collection_names.include?(full_coll_name(name)) return Collection.new(self, name) if collection_names.include?(full_coll_name(name))
if strict? if strict?
@ -95,11 +130,13 @@ module XGen
end end
end end
# Drop collection +name+. Returns +true+ on success or if the
# collection does not exist, +false+ otherwise.
def drop_collection(name) def drop_collection(name)
coll = collection(name) return true unless collection_names.include?(full_coll_name(name))
return true if coll == nil
coll.drop_indexes # Mongo requires that we drop indexes manually
coll = collection(name)
coll.drop_indexes # Mongo requires that we drop indexes manually
ok?(db_command(:drop => name)) ok?(db_command(:drop => name))
end end
@ -111,31 +148,44 @@ module XGen
ok?(doc) && is_master.kind_of?(Numeric) && is_master.to_i == 1 ok?(doc) && is_master.kind_of?(Numeric) && is_master.to_i == 1
end end
# Close the connection to the database.
def close def close
@socket.close @socket.close
end end
# Send a MsgMessage to the database.
def send_message(msg) def send_message(msg)
send_to_db(MsgMessage.new(msg)) send_to_db(MsgMessage.new(msg))
end end
# Send a Query to +collection_name+ and return a Cursor over the
# results.
def query(collection_name, query) def query(collection_name, query)
# TODO synchronize # TODO synchronize
send_to_db(QueryMessage.new(@name, collection_name, query)) send_to_db(QueryMessage.new(@name, collection_name, query))
return Cursor.new(self, collection_name) return Cursor.new(self, collection_name)
end end
# Remove the records that match +selector+ from +collection_name+.
# Normally called by Collection#remove or Collection#clear.
def remove_from_db(collection_name, selector) def remove_from_db(collection_name, selector)
# TODO synchronize # TODO synchronize
send_to_db(RemoveMessage.new(@name, collection_name, selector)) send_to_db(RemoveMessage.new(@name, collection_name, selector))
end end
# Update records in +collection_name+ that match +selector+ by
# applying +obj+ as an update. Normally called by Collection#replace.
def replace_in_db(collection_name, selector, obj) def replace_in_db(collection_name, selector, obj)
# TODO synchronize # TODO synchronize
send_to_db(UpdateMessage.new(@name, collection_name, selector, obj, false)) send_to_db(UpdateMessage.new(@name, collection_name, selector, obj, false))
end end
# Alias for #replace_in_db. Normally called by Collection.modify.
alias_method :modify_in_db, :replace_in_db alias_method :modify_in_db, :replace_in_db
# Update records in +collection_name+ that match +selector+ by
# applying +obj+ as an update. If no match, inserts (???). Normally
# called by Collection#repsert.
def repsert_in_db(collection_name, selector, obj) def repsert_in_db(collection_name, selector, obj)
# TODO if PKInjector, inject # TODO if PKInjector, inject
# TODO synchronize # TODO synchronize
@ -143,15 +193,20 @@ module XGen
obj obj
end end
def count(collection_name, selector) # Return the number of records in +collection_name+ that match
# +selector+. If +selector+ is +nil+ or an empty hash, returns the
# count of all records. Normally called by Collection#count.
def count(collection_name, selector={})
oh = OrderedHash.new oh = OrderedHash.new
oh[:count] = collection_name oh[:count] = collection_name
oh[:query] = selector oh[:query] = selector || {}
doc = db_command(oh) doc = db_command(oh)
return doc['n'].to_i if ok?(doc) return doc['n'].to_i if ok?(doc)
raise "Error with count command: #{doc.inspect}" raise "Error with count command: #{doc.inspect}"
end end
# Drop index +name+ from +collection_name+. Normally called from
# Collection#drop_index or Collection#drop_indexes.
def drop_index(collection_name, name) def drop_index(collection_name, name)
oh = OrderedHash.new oh = OrderedHash.new
oh[:deleteIndexes] = collection_name oh[:deleteIndexes] = collection_name
@ -160,6 +215,15 @@ module XGen
raise "Error with drop_index command: #{doc.inspect}" unless ok?(doc) raise "Error with drop_index command: #{doc.inspect}" unless ok?(doc)
end end
# Return an array of hashes, one for each index on +collection_name+.
# Normally called by Collection#index_information. Each hash contains:
#
# :name :: Index name
#
# :keys :: Hash whose keys are the names of the fields that make up
# the key and values are integers.
#
# :ns :: Namespace; same as +collection_name+.
def index_information(collection_name) def index_information(collection_name)
sel = {:ns => full_coll_name(collection_name)} sel = {:ns => full_coll_name(collection_name)}
# TODO synchronize # TODO synchronize
@ -179,6 +243,9 @@ module XGen
} }
end end
# Create a new index on +collection_name+ named +index_name+. +fields+
# should be an array of field names. Normally called by
# Collection#create_index.
def create_index(collection_name, index_name, fields) def create_index(collection_name, index_name, fields)
sel = {:name => index_name, :ns => full_coll_name(collection_name)} sel = {:name => index_name, :ns => full_coll_name(collection_name)}
field_h = {} field_h = {}
@ -188,6 +255,8 @@ module XGen
send_to_db(InsertMessage.new(@name, SYSTEM_INDEX_COLLECTION, sel)) send_to_db(InsertMessage.new(@name, SYSTEM_INDEX_COLLECTION, sel))
end end
# Insert +objects+ into +collection_name+. Normally called by
# Collection#insert.
def insert_into_db(collection_name, objects) def insert_into_db(collection_name, objects)
# TODO synchronize # TODO synchronize
objects.each { |o| send_to_db(InsertMessage.new(@name, collection_name, o)) } objects.each { |o| send_to_db(InsertMessage.new(@name, collection_name, o)) }
@ -203,6 +272,7 @@ module XGen
protected protected
# Return +true+ if +doc+ contains an 'ok' field with the value 1.
def ok?(doc) def ok?(doc)
ok = doc['ok'] ok = doc['ok']
ok.kind_of?(Numeric) && ok.to_i == 1 ok.kind_of?(Numeric) && ok.to_i == 1

View File

@ -1,3 +1,4 @@
# --
# Copyright (C) 2008 10gen Inc. # Copyright (C) 2008 10gen Inc.
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
@ -11,6 +12,7 @@
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# ++
require 'mongo/db' require 'mongo/db'
@ -18,22 +20,27 @@ module XGen
module Mongo module Mongo
module Driver module Driver
# Represents a Mongo database server.
class Mongo class Mongo
DEFAULT_PORT = 27017 DEFAULT_PORT = 27017
# Host default is 'localhost', port default is DEFAULT_PORT.
def initialize(host='localhost', port=DEFAULT_PORT) def initialize(host='localhost', port=DEFAULT_PORT)
@host, @port = host, port @host, @port = host, port
end end
# Return the XGen::Mongo::Driver::DB named +db_name+.
def db(db_name) def db(db_name)
XGen::Mongo::Driver::DB.new(db_name, @host, @port) XGen::Mongo::Driver::DB.new(db_name, @host, @port)
end end
# Not implemented.
def clone_database(from) def clone_database(from)
raise "not implemented" raise "not implemented"
end end
# 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

View File

@ -1,3 +1,4 @@
# --
# Copyright (C) 2008 10gen Inc. # Copyright (C) 2008 10gen Inc.
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
@ -11,6 +12,7 @@
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# ++
require 'mutex_m' require 'mutex_m'
require 'mongo/util/byte_buffer' require 'mongo/util/byte_buffer'
@ -19,7 +21,12 @@ module XGen
module Mongo module Mongo
module Driver module Driver
# Implementation of the Babble OID. # Implementation of the Babble OID. Object ids are not required by
# Mongo, but they make certain operations more efficient.
#
# The driver does not automatically assign ids to records that are
# inserted. (An upcoming feature will allow you to give an id "factory"
# to a database and/or a collection.)
# #
# 12 bytes # 12 bytes
# --- # ---
@ -65,6 +72,7 @@ module XGen
@data.collect { |b| '%02x' % b }.join @data.collect { |b| '%02x' % b }.join
end end
# (Would normally be private, but isn't so we can test it.)
def generate_id(t=nil) def generate_id(t=nil)
t ||= Time.new.to_i t ||= Time.new.to_i
buf = ByteBuffer.new buf = ByteBuffer.new
@ -80,6 +88,7 @@ module XGen
buf.to_a.dup buf.to_a.dup
end end
# (Would normally be private, but isn't so we can test it.)
def index_for_time(t) def index_for_time(t)
LOCK.mu_synchronize { LOCK.mu_synchronize {
if t != @@index_time if t != @@index_time

View File

@ -1,3 +1,4 @@
# --
# Copyright (C) 2008 10gen Inc. # Copyright (C) 2008 10gen Inc.
# #
# This program is free software: you can redistribute it and/or modify it # This program is free software: you can redistribute it and/or modify it
@ -11,6 +12,7 @@
# #
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# ++
require 'socket' require 'socket'
require 'mongo/collection' require 'mongo/collection'
@ -20,28 +22,52 @@ module XGen
module Mongo module Mongo
module Driver module Driver
# A query against a collection. A query's selector is a hash. See the
# Mongo documentation for query details.
class Query class Query
attr_accessor :number_to_skip, :number_to_return, :order_by attr_accessor :number_to_skip, :number_to_return, :order_by
attr_reader :selector, :fields # writers defined below attr_reader :selector, :fields # writers defined below
# sel :: A hash describing the query. See the Mongo docs for details.
#
# return_fields :: If not +nil+, an array of field names. Only those
# fields will be returned. (Called :fields in calls
# to Collection#find.)
#
# number_to_skip :: Number of records to skip before returning
# records. (Called :offset in calls to
# Collection#find.)
#
# number_to_return :: Max number of records to return. (Called :limit
# in calls to Collection#find.)
#
# order_by :: If not +nil+, specifies record return order. 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). (Called
# :sort in calls to Collection#find.)
def initialize(sel={}, return_fields=nil, number_to_skip=0, number_to_return=0, order_by=nil) def initialize(sel={}, return_fields=nil, number_to_skip=0, number_to_return=0, order_by=nil)
@number_to_skip, @number_to_return, @order_by = number_to_skip, number_to_return, order_by @number_to_skip, @number_to_return, @order_by = number_to_skip, number_to_return, order_by
self.selector = sel self.selector = sel
self.fields = return_fields self.fields = return_fields
end end
# Set query selector hash. If sel is a string, it will be used as a
# $where clause. (See Mongo docs for details.)
def selector=(sel) def selector=(sel)
@selector = case sel @selector = case sel
when nil when nil
{} {}
when String when String
{"$where" => "function() { return #{sel}; }"} {"$where" => 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
# returned.
def fields=(val) def fields=(val)
@fields = val @fields = val
@fields = nil if @fields && @fields.empty? @fields = nil if @fields && @fields.empty?

View File

@ -1,9 +1,24 @@
# --
# Copyright (C) 2008 10gen Inc.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License, version 3, as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
# for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ++
require 'mongo/util/byte_buffer' require 'mongo/util/byte_buffer'
require 'mongo/util/ordered_hash' require 'mongo/util/ordered_hash'
require 'mongo/objectid' require 'mongo/objectid'
# See http://github.com/10gen/mongo/tree/master/db/jsobj.h # A BSON seralizer/deserializer.
class BSON class BSON
MINKEY = -1 MINKEY = -1

View File

@ -1,3 +1,20 @@
# --
# Copyright (C) 2008 10gen Inc.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License, version 3, as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
# for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ++
# A byte buffer.
class ByteBuffer class ByteBuffer
attr_reader :order attr_reader :order

View File

@ -1,3 +1,20 @@
# --
# Copyright (C) 2008 10gen Inc.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License, version 3, as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
# for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# ++
# A hash in which the order of keys are preserved.
class OrderedHash < Hash class OrderedHash < Hash
attr_accessor :ordered_keys attr_accessor :ordered_keys

View File

@ -194,6 +194,22 @@ class DBAPITest < Test::Unit::TestCase
assert_equal @coll.name, row['options']['create'] assert_equal @coll.name, row['options']['create']
end end
def test_collection_options
@db.drop_collection('foobar')
@db.strict = true
begin
coll = @db.create_collection('foobar', :capped => true, :size => 1024)
options = coll.options()
assert_equal 'foobar', options['create']
assert_equal true, options['capped']
assert_equal 1024, options['size']
rescue => ex
@db.drop_collection('foobar')
fail "did not expect exception \"#{ex}\""
end
end
def test_full_coll_name def test_full_coll_name
assert_equal @coll_full_name, @db.full_coll_name(@coll.name) assert_equal @coll_full_name, @db.full_coll_name(@coll.name)
end end