mongo 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +134 -0
- data/Rakefile +86 -0
- data/bin/mongo_console +21 -0
- data/lib/mongo.rb +7 -0
- data/lib/mongo/admin.rb +86 -0
- data/lib/mongo/collection.rb +136 -0
- data/lib/mongo/cursor.rb +180 -0
- data/lib/mongo/db.rb +307 -0
- data/lib/mongo/message.rb +4 -0
- data/lib/mongo/message/get_more_message.rb +21 -0
- data/lib/mongo/message/insert_message.rb +19 -0
- data/lib/mongo/message/kill_cursors_message.rb +20 -0
- data/lib/mongo/message/message.rb +68 -0
- data/lib/mongo/message/message_header.rb +34 -0
- data/lib/mongo/message/msg_message.rb +17 -0
- data/lib/mongo/message/opcodes.rb +16 -0
- data/lib/mongo/message/query_message.rb +45 -0
- data/lib/mongo/message/remove_message.rb +20 -0
- data/lib/mongo/message/update_message.rb +21 -0
- data/lib/mongo/mongo.rb +52 -0
- data/lib/mongo/objectid.rb +107 -0
- data/lib/mongo/query.rb +103 -0
- data/lib/mongo/util/bson.rb +339 -0
- data/lib/mongo/util/byte_buffer.rb +163 -0
- data/lib/mongo/util/ordered_hash.rb +54 -0
- data/tests/test_admin.rb +59 -0
- data/tests/test_bson.rb +79 -0
- data/tests/test_byte_buffer.rb +69 -0
- data/tests/test_db_api.rb +357 -0
- data/tests/test_db_connection.rb +17 -0
- data/tests/test_message.rb +35 -0
- data/tests/test_objectid.rb +68 -0
- data/tests/test_ordered_hash.rb +82 -0
- metadata +85 -0
data/README.rdoc
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
= Introduction
|
2
|
+
|
3
|
+
This is a Ruby driver for the 10gen Mongo DB. For more information about
|
4
|
+
Mongo, see http://www.mongodb.org.
|
5
|
+
|
6
|
+
Note: this driver is still alpha quality. The API will change, as *may* the
|
7
|
+
data saved to the database (especially primary key values). Do *_not_* use
|
8
|
+
this for any production data yet.
|
9
|
+
|
10
|
+
Start by reading the XGen::Mongo::Driver::Mongo and XGen::Mongo::Driver::DB
|
11
|
+
documentation, then move on to XGen::Mongo::Driver::Collection and
|
12
|
+
XGen::Mongo::Driver::Cursor.
|
13
|
+
|
14
|
+
A quick code sample:
|
15
|
+
|
16
|
+
require 'mongo'
|
17
|
+
|
18
|
+
include XGen::Mongo::Driver
|
19
|
+
|
20
|
+
db = Mongo.new('localhost').db('sample-db')
|
21
|
+
coll = db.collection('test')
|
22
|
+
|
23
|
+
coll.clear
|
24
|
+
3.times { |i| coll.insert({'a' => i+1}) }
|
25
|
+
puts "There are #{coll.count()} records. Here they are:"
|
26
|
+
coll.find().each { |doc| puts doc.inspect }
|
27
|
+
|
28
|
+
= Installation
|
29
|
+
|
30
|
+
Install the "mongo" gem by typing
|
31
|
+
|
32
|
+
$ sudo gem install mongo
|
33
|
+
|
34
|
+
The source code is available at http://github.com/jimm/mongo-ruby-driver. You
|
35
|
+
can either clone the git repository or download a tarball or zip file. Once
|
36
|
+
you have the source, you can use it from wherever you downloaded it or you can
|
37
|
+
install it as a gem from the source by typing
|
38
|
+
|
39
|
+
$ rake gem:install
|
40
|
+
|
41
|
+
|
42
|
+
= Demo
|
43
|
+
|
44
|
+
You can see and run the examples if you've downloaded the source. Mongo must
|
45
|
+
be running, of course.
|
46
|
+
|
47
|
+
$ ruby examples/simple.rb
|
48
|
+
|
49
|
+
See also the test code, especially tests/test_db_api.rb.
|
50
|
+
|
51
|
+
|
52
|
+
= Testing
|
53
|
+
|
54
|
+
If you have the source code, you can run the tests.
|
55
|
+
|
56
|
+
$ rake test
|
57
|
+
|
58
|
+
The tests assume that the Mongo database is running on the default port.
|
59
|
+
|
60
|
+
|
61
|
+
= Documentation
|
62
|
+
|
63
|
+
This documentation is available online at http://mongo.rubyforge.org. You can
|
64
|
+
generate the documentation if you have the source by typing
|
65
|
+
|
66
|
+
$ rake rdoc
|
67
|
+
|
68
|
+
Then open the file html/index.html.
|
69
|
+
|
70
|
+
|
71
|
+
= Release Notes
|
72
|
+
|
73
|
+
See the git log comments.
|
74
|
+
|
75
|
+
|
76
|
+
= To Do
|
77
|
+
|
78
|
+
* Add group_by. Need to figure out how we are going to send functions. The
|
79
|
+
current thinking is that Mongo will allow a subset of JavaScript (which we
|
80
|
+
would have to send as a string), but this is still under discussion.
|
81
|
+
|
82
|
+
* Add explain and hint support.
|
83
|
+
|
84
|
+
* Only update message sizes once, not after every write of a value. This will
|
85
|
+
require an explicit call to update_message_length in each message subclass.
|
86
|
+
|
87
|
+
* Tests for update and repsert.
|
88
|
+
|
89
|
+
* Add a way to specify a collection of databases on startup (a simple array of
|
90
|
+
IP address/port numbers, perhaps, or a hash or something). The driver would
|
91
|
+
then find the master and, on each subsequent command, ask that machine if it
|
92
|
+
is the master before proceeding.
|
93
|
+
|
94
|
+
* Tests that prove that this driver's ObjectID and Geir's Java version do the
|
95
|
+
same thing. (I've done so manually.)
|
96
|
+
|
97
|
+
* Support more types: REF, SYMBOL, CODE_W_SCOPE, etc.
|
98
|
+
|
99
|
+
* Introduce optional per-database and per-collection PKInjector.
|
100
|
+
|
101
|
+
* More tests.
|
102
|
+
|
103
|
+
* Study src/main/ed/db/{dbcollection,dbcursor,db}.js and ByteEncoder.java in
|
104
|
+
the Babble code. That's what I should be writing to.
|
105
|
+
|
106
|
+
|
107
|
+
= Credits
|
108
|
+
|
109
|
+
Adrian Madrid, aemadrid@gmail.com
|
110
|
+
* bin/mongo_console
|
111
|
+
* examples/benchmarks.rb
|
112
|
+
* examples/irb.rb
|
113
|
+
* Modifications to examples/simple.rb
|
114
|
+
* Found plenty of bugs and missing features.
|
115
|
+
* Ruby 1.9 support.
|
116
|
+
* Gem support.
|
117
|
+
* Many other code suggestions and improvements.
|
118
|
+
|
119
|
+
|
120
|
+
= License
|
121
|
+
|
122
|
+
Copyright (C) 2008-2009 10gen Inc.
|
123
|
+
|
124
|
+
This program is free software: you can redistribute it and/or modify it under
|
125
|
+
the terms of the GNU Affero General Public License, version 3, as published by
|
126
|
+
the Free Software Foundation.
|
127
|
+
|
128
|
+
This program is distributed in the hope that it will be useful, but WITHOUT
|
129
|
+
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
130
|
+
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
131
|
+
details.
|
132
|
+
|
133
|
+
See http://www.gnu.org/licenses for a copy of the GNU Affero General Public
|
134
|
+
License.
|
data/Rakefile
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rubygems/specification'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/testtask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/contrib/rubyforgepublisher'
|
8
|
+
|
9
|
+
GEM = "mongo"
|
10
|
+
GEM_VERSION = '0.0.1'
|
11
|
+
SUMMARY = 'Simple pure-Ruby driver for the 10gen Mongo DB'
|
12
|
+
DESCRIPTION = 'This is a simple pure-Ruby driver for the 10gen Mongo DB. For more information about Mongo, see http://www.mongodb.org.'
|
13
|
+
AUTHOR = 'Jim Menard'
|
14
|
+
EMAIL = 'jimm@io.com'
|
15
|
+
HOMEPAGE = 'http://www.mongodb.org'
|
16
|
+
RUBYFORGE_USER = 'jimm'
|
17
|
+
|
18
|
+
spec = Gem::Specification.new do |s|
|
19
|
+
s.name = GEM
|
20
|
+
s.version = GEM_VERSION
|
21
|
+
s.platform = Gem::Platform::RUBY
|
22
|
+
s.summary = SUMMARY
|
23
|
+
s.description = DESCRIPTION
|
24
|
+
|
25
|
+
s.require_paths = ['lib']
|
26
|
+
|
27
|
+
s.files = FileList['bin/*', 'lib/**/*.rb', 'tests/**/*.rb', '[A-Z]*'].to_a
|
28
|
+
|
29
|
+
s.bindir = 'bin'
|
30
|
+
s.executables = %w( mongo_console )
|
31
|
+
s.has_rdoc = true
|
32
|
+
|
33
|
+
s.author = AUTHOR
|
34
|
+
s.email = EMAIL
|
35
|
+
s.homepage = HOMEPAGE
|
36
|
+
|
37
|
+
s.rubyforge_project = GEM # GitHub bug, gem isn't being build when this miss
|
38
|
+
end
|
39
|
+
|
40
|
+
# NOTE: some of the tests assume Mongo is running
|
41
|
+
Rake::TestTask.new do |t|
|
42
|
+
t.test_files = FileList['tests/test*.rb']
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "Generate documentation"
|
46
|
+
task :rdoc do
|
47
|
+
FileUtils.rm_rf('html')
|
48
|
+
system "rdoc --main README.rdoc --op html --inline-source --quiet README.rdoc `find lib -name '*.rb'`"
|
49
|
+
end
|
50
|
+
|
51
|
+
desc "Publish documentation to mongo.rubyforge.org"
|
52
|
+
task :publish => [:rdoc] do
|
53
|
+
# Assumes docs are in ./html
|
54
|
+
Rake::RubyForgePublisher.new(GEM, RUBYFORGE_USER).upload
|
55
|
+
end
|
56
|
+
|
57
|
+
namespace :gem do
|
58
|
+
|
59
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
60
|
+
pkg.gem_spec = spec
|
61
|
+
end
|
62
|
+
|
63
|
+
desc "Install the gem locally"
|
64
|
+
task :install => [:package] do
|
65
|
+
sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
|
66
|
+
end
|
67
|
+
|
68
|
+
desc "Install the gem locally with ruby 1.9"
|
69
|
+
task :'install19' => [:package] do
|
70
|
+
sh %{sudo gem19 install pkg/#{GEM}-#{GEM_VERSION}}
|
71
|
+
end
|
72
|
+
|
73
|
+
desc "Create a gemspec file"
|
74
|
+
task :make_spec do
|
75
|
+
File.open("#{GEM}.gemspec", "w") do |file|
|
76
|
+
file.puts spec.to_ruby
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
task :default => :list
|
83
|
+
|
84
|
+
task :list do
|
85
|
+
system 'rake -T'
|
86
|
+
end
|
data/bin/mongo_console
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
org_argv = ARGV.dup
|
3
|
+
ARGV.clear
|
4
|
+
|
5
|
+
require 'irb'
|
6
|
+
|
7
|
+
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
8
|
+
require 'mongo'
|
9
|
+
|
10
|
+
include XGen::Mongo::Driver
|
11
|
+
|
12
|
+
host = org_argv[0] || ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
13
|
+
port = org_argv[1] || ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
14
|
+
dbnm = org_argv[2] || ENV['MONGO_RUBY_DRIVER_DB'] || 'ruby-mongo-console'
|
15
|
+
|
16
|
+
puts "Connecting to #{host}:#{port} (CONN) on with database #{dbnm} (DB)"
|
17
|
+
CONN = Mongo.new(host, port)
|
18
|
+
DB = CONN.db(dbnm)
|
19
|
+
|
20
|
+
puts "Starting IRB session..."
|
21
|
+
IRB.start(__FILE__)
|
data/lib/mongo.rb
ADDED
data/lib/mongo/admin.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# --
|
2
|
+
# Copyright (C) 2008-2009 10gen Inc.
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify it
|
5
|
+
# under the terms of the GNU Affero General Public License, version 3, as
|
6
|
+
# published by the Free Software Foundation.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
9
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
10
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
|
11
|
+
# for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Affero General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
# ++
|
16
|
+
|
17
|
+
require 'mongo/util/ordered_hash'
|
18
|
+
|
19
|
+
module XGen
|
20
|
+
module Mongo
|
21
|
+
module Driver
|
22
|
+
|
23
|
+
# Provide administrative database methods: those having to do with
|
24
|
+
# profiling and validation.
|
25
|
+
class Admin
|
26
|
+
|
27
|
+
def initialize(db)
|
28
|
+
@db = db
|
29
|
+
end
|
30
|
+
|
31
|
+
# Return the current database profiling level.
|
32
|
+
def profiling_level
|
33
|
+
oh = OrderedHash.new
|
34
|
+
oh[:profile] = -1
|
35
|
+
doc = @db.db_command(oh)
|
36
|
+
raise "Error with profile command: #{doc.inspect}" unless @db.ok?(doc) && doc['was'].kind_of?(Numeric)
|
37
|
+
case doc['was'].to_i
|
38
|
+
when 0
|
39
|
+
:off
|
40
|
+
when 1
|
41
|
+
:slow_only
|
42
|
+
when 2
|
43
|
+
:all
|
44
|
+
else
|
45
|
+
raise "Error: illegal profiling level value #{doc['was']}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Set database profiling level to :off, :slow_only, or :all.
|
50
|
+
def profiling_level=(level)
|
51
|
+
oh = OrderedHash.new
|
52
|
+
oh[:profile] = case level
|
53
|
+
when :off
|
54
|
+
0
|
55
|
+
when :slow_only
|
56
|
+
1
|
57
|
+
when :all
|
58
|
+
2
|
59
|
+
else
|
60
|
+
raise "Error: illegal profiling level value #{level}"
|
61
|
+
end
|
62
|
+
doc = @db.db_command(oh)
|
63
|
+
raise "Error with profile command: #{doc.inspect}" unless @db.ok?(doc)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Return an array contining current profiling information from the
|
67
|
+
# database.
|
68
|
+
def profiling_info
|
69
|
+
@db.query(DB::SYSTEM_PROFILE_COLLECTION, Query.new({})).to_a
|
70
|
+
end
|
71
|
+
|
72
|
+
# Validate a named collection by raising an exception if there is a
|
73
|
+
# problem or returning +true+ if all is well.
|
74
|
+
def validate_collection(name)
|
75
|
+
doc = @db.db_command(:validate => name)
|
76
|
+
raise "Error with validate command: #{doc.inspect}" unless @db.ok?(doc)
|
77
|
+
result = doc['result']
|
78
|
+
raise "Error with validation data: #{doc.inspect}" unless result.kind_of?(String)
|
79
|
+
raise "Error: invalid collection #{name}: #{doc.inspect}" if result =~ /\b(exception|corrupt)\b/i
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# --
|
2
|
+
# Copyright (C) 2008-2009 10gen Inc.
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify it
|
5
|
+
# under the terms of the GNU Affero General Public License, version 3, as
|
6
|
+
# published by the Free Software Foundation.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
9
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
10
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
|
11
|
+
# for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Affero General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
# ++
|
16
|
+
|
17
|
+
require 'mongo/query'
|
18
|
+
|
19
|
+
module XGen
|
20
|
+
module Mongo
|
21
|
+
module Driver
|
22
|
+
|
23
|
+
# A named collection of records in a database.
|
24
|
+
class Collection
|
25
|
+
|
26
|
+
attr_reader :db, :name
|
27
|
+
|
28
|
+
def initialize(db, name)
|
29
|
+
@db = db
|
30
|
+
@name = name
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return records that match a +selector+ hash. See Mongo docs for
|
34
|
+
# details.
|
35
|
+
#
|
36
|
+
# Options:
|
37
|
+
# :fields :: Array of collection field names; only those will be returned (plus _id if defined)
|
38
|
+
# :offset :: Start at this record when returning records
|
39
|
+
# :limit :: Maximum number of records to return
|
40
|
+
# :sort :: Either hash of field names as keys and 1/-1 as values; 1 ==
|
41
|
+
# ascending, -1 == descending, or array of field names (all
|
42
|
+
# assumed to be sorted in ascending order).
|
43
|
+
def find(selector={}, options={})
|
44
|
+
fields = options.delete(:fields)
|
45
|
+
fields = nil if fields && fields.empty?
|
46
|
+
offset = options.delete(:offset) || 0
|
47
|
+
limit = options.delete(:limit) || 0
|
48
|
+
sort = options.delete(:sort)
|
49
|
+
raise RuntimeError, "Unknown options [#{options.inspect}]" unless options.empty?
|
50
|
+
@db.query(@name, Query.new(selector, fields, offset, limit, sort))
|
51
|
+
end
|
52
|
+
|
53
|
+
# Insert +objects+, which are hashes. "<<" is aliased to this method.
|
54
|
+
def insert(*objects)
|
55
|
+
objects = objects.first if objects.size == 1 && objects.first.is_a?(Array)
|
56
|
+
res = @db.insert_into_db(@name, objects)
|
57
|
+
res.size > 1 ? res : res.first
|
58
|
+
end
|
59
|
+
alias_method :<<, :insert
|
60
|
+
|
61
|
+
# Remove the records that match +selector+.
|
62
|
+
def remove(selector={})
|
63
|
+
@db.remove_from_db(@name, selector)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Remove all records.
|
67
|
+
def clear
|
68
|
+
remove({})
|
69
|
+
end
|
70
|
+
|
71
|
+
# Update records that match +selector+ by applying +obj+ as an update.
|
72
|
+
# If no match, inserts (???).
|
73
|
+
def repsert(selector, obj)
|
74
|
+
@db.repsert_in_db(@name, selector, obj)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Update records that match +selector+ by applying +obj+ as an update.
|
78
|
+
def replace(selector, obj)
|
79
|
+
@db.replace_in_db(@name, selector, obj)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Update records that match +selector+ by applying +obj+ as an update.
|
83
|
+
# Both +selector+ and +modifier_obj+ are required.
|
84
|
+
def modify(selector, modifier_obj)
|
85
|
+
raise "no object" unless modifier_obj
|
86
|
+
raise "no selector" unless selector
|
87
|
+
@db.modify_in_db(@name, selector, modifier_obj)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Create a new index named +index_name+. +fields+ should be an array
|
91
|
+
# of field names.
|
92
|
+
def create_index(name, *fields)
|
93
|
+
@db.create_index(@name, name, fields)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Drop index +name+.
|
97
|
+
def drop_index(name)
|
98
|
+
@db.drop_index(@name, name)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Drop all indexes.
|
102
|
+
def drop_indexes
|
103
|
+
# just need to call drop indexes with no args; will drop them all
|
104
|
+
@db.drop_index(@name, '*')
|
105
|
+
end
|
106
|
+
|
107
|
+
# Return an array of hashes, one for each index. Each hash contains:
|
108
|
+
#
|
109
|
+
# :name :: Index name
|
110
|
+
#
|
111
|
+
# :keys :: Hash whose keys are the names of the fields that make up
|
112
|
+
# the key and values are integers.
|
113
|
+
#
|
114
|
+
# :ns :: Namespace; same as this collection's name.
|
115
|
+
def index_information
|
116
|
+
@db.index_information(@name)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Return a hash containing options that apply to this collection.
|
120
|
+
# 'create' will be the collection name. For the other possible keys
|
121
|
+
# and values, see DB#create_collection.
|
122
|
+
def options
|
123
|
+
@db.collections_info(@name).next_object()['options']
|
124
|
+
end
|
125
|
+
|
126
|
+
# Return the number of records that match +selector+. If +selector+ is
|
127
|
+
# +nil+ or an empty hash, returns the count of all records.
|
128
|
+
def count(selector={})
|
129
|
+
@db.count(@name, selector || {})
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|