animehunter-mongo 0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +311 -0
- data/Rakefile +62 -0
- data/bin/bson_benchmark.rb +59 -0
- data/bin/mongo_console +21 -0
- data/bin/run_test_script +19 -0
- data/bin/standard_benchmark +109 -0
- data/examples/admin.rb +41 -0
- data/examples/benchmarks.rb +42 -0
- data/examples/blog.rb +76 -0
- data/examples/capped.rb +23 -0
- data/examples/cursor.rb +47 -0
- data/examples/gridfs.rb +87 -0
- data/examples/index_test.rb +125 -0
- data/examples/info.rb +30 -0
- data/examples/queries.rb +69 -0
- data/examples/simple.rb +23 -0
- data/examples/strict.rb +34 -0
- data/examples/types.rb +40 -0
- data/lib/mongo.rb +19 -0
- data/lib/mongo/admin.rb +87 -0
- data/lib/mongo/collection.rb +235 -0
- data/lib/mongo/cursor.rb +227 -0
- data/lib/mongo/db.rb +538 -0
- data/lib/mongo/gridfs.rb +16 -0
- data/lib/mongo/gridfs/chunk.rb +96 -0
- data/lib/mongo/gridfs/grid_store.rb +468 -0
- data/lib/mongo/message.rb +20 -0
- data/lib/mongo/message/get_more_message.rb +37 -0
- data/lib/mongo/message/insert_message.rb +35 -0
- data/lib/mongo/message/kill_cursors_message.rb +36 -0
- data/lib/mongo/message/message.rb +84 -0
- data/lib/mongo/message/message_header.rb +50 -0
- data/lib/mongo/message/msg_message.rb +33 -0
- data/lib/mongo/message/opcodes.rb +32 -0
- data/lib/mongo/message/query_message.rb +77 -0
- data/lib/mongo/message/remove_message.rb +36 -0
- data/lib/mongo/message/update_message.rb +37 -0
- data/lib/mongo/mongo.rb +164 -0
- data/lib/mongo/query.rb +119 -0
- data/lib/mongo/types/binary.rb +42 -0
- data/lib/mongo/types/code.rb +34 -0
- data/lib/mongo/types/dbref.rb +37 -0
- data/lib/mongo/types/objectid.rb +137 -0
- data/lib/mongo/types/regexp_of_holding.rb +44 -0
- data/lib/mongo/types/undefined.rb +31 -0
- data/lib/mongo/util/bson.rb +534 -0
- data/lib/mongo/util/byte_buffer.rb +167 -0
- data/lib/mongo/util/ordered_hash.rb +96 -0
- data/lib/mongo/util/xml_to_ruby.rb +107 -0
- data/mongo-ruby-driver.gemspec +99 -0
- data/tests/mongo-qa/_common.rb +8 -0
- data/tests/mongo-qa/admin +26 -0
- data/tests/mongo-qa/capped +22 -0
- data/tests/mongo-qa/count1 +18 -0
- data/tests/mongo-qa/dbs +22 -0
- data/tests/mongo-qa/find +10 -0
- data/tests/mongo-qa/find1 +15 -0
- data/tests/mongo-qa/gridfs_in +16 -0
- data/tests/mongo-qa/gridfs_out +17 -0
- data/tests/mongo-qa/indices +49 -0
- data/tests/mongo-qa/remove +25 -0
- data/tests/mongo-qa/stress1 +35 -0
- data/tests/mongo-qa/test1 +11 -0
- data/tests/mongo-qa/update +18 -0
- data/tests/test_admin.rb +69 -0
- data/tests/test_bson.rb +246 -0
- data/tests/test_byte_buffer.rb +69 -0
- data/tests/test_chunk.rb +84 -0
- data/tests/test_cursor.rb +121 -0
- data/tests/test_db.rb +160 -0
- data/tests/test_db_api.rb +701 -0
- data/tests/test_db_connection.rb +18 -0
- data/tests/test_grid_store.rb +284 -0
- data/tests/test_message.rb +35 -0
- data/tests/test_mongo.rb +78 -0
- data/tests/test_objectid.rb +98 -0
- data/tests/test_ordered_hash.rb +129 -0
- data/tests/test_round_trip.rb +116 -0
- metadata +133 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
class Exception
|
|
2
|
+
def errmsg
|
|
3
|
+
"%s: %s\n%s" % [self.class, message, (backtrace || []).join("\n") << "\n"]
|
|
4
|
+
end
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
|
8
|
+
require 'mongo'
|
|
9
|
+
|
|
10
|
+
include XGen::Mongo::Driver
|
|
11
|
+
|
|
12
|
+
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
|
13
|
+
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
|
14
|
+
|
|
15
|
+
puts ">> Connecting to #{host}:#{port}"
|
|
16
|
+
db = Mongo.new(host, port).db('ruby-mongo-index_test')
|
|
17
|
+
|
|
18
|
+
puts ">> Dropping collection test"
|
|
19
|
+
begin
|
|
20
|
+
res = db.drop_collection('test')
|
|
21
|
+
puts "dropped : #{res.inspect}"
|
|
22
|
+
rescue => e
|
|
23
|
+
puts "Error: #{e.errmsg}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
puts ">> Creating collection test"
|
|
27
|
+
begin
|
|
28
|
+
coll = db.collection('test')
|
|
29
|
+
puts "created : #{coll.inspect}"
|
|
30
|
+
rescue => e
|
|
31
|
+
puts "Error: #{e.errmsg}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
OBJS_COUNT = 100
|
|
35
|
+
|
|
36
|
+
puts ">> Generating test data"
|
|
37
|
+
msgs = %w{hola hello aloha ciao}
|
|
38
|
+
arr = (0...OBJS_COUNT).collect {|x| { :number => x, :rndm => (rand(5)+1), :msg => msgs[rand(4)] }}
|
|
39
|
+
puts "generated"
|
|
40
|
+
|
|
41
|
+
puts ">> Inserting data (#{arr.size})"
|
|
42
|
+
coll.insert(arr)
|
|
43
|
+
puts "inserted"
|
|
44
|
+
|
|
45
|
+
puts ">> Creating index"
|
|
46
|
+
res = coll.create_index "all", :_id => 1, :number => 1, :rndm => 1, :msg => 1
|
|
47
|
+
# res = coll.create_index "all", '_id' => 1, 'number' => 1, 'rndm' => 1, 'msg' => 1
|
|
48
|
+
puts "created index: #{res.inspect}"
|
|
49
|
+
# ============================ Mongo Log ============================
|
|
50
|
+
# Fri Dec 5 14:45:02 Adding all existing records for ruby-mongo-console.test to new index
|
|
51
|
+
# ***
|
|
52
|
+
# Bad data or size in BSONElement::size()
|
|
53
|
+
# bad type:30
|
|
54
|
+
# totalsize:11 fieldnamesize:4
|
|
55
|
+
# lastrec:
|
|
56
|
+
# Fri Dec 5 14:45:02 ruby-mongo-console.system.indexes Assertion failure false jsobj.cpp a0
|
|
57
|
+
# Fri Dec 5 14:45:02 database: ruby-mongo-console op:7d2 0
|
|
58
|
+
# Fri Dec 5 14:45:02 ns: ruby-mongo-console.system.indexes
|
|
59
|
+
|
|
60
|
+
puts ">> Gathering index information"
|
|
61
|
+
begin
|
|
62
|
+
res = coll.index_information
|
|
63
|
+
puts "index_information : #{res.inspect}"
|
|
64
|
+
rescue => e
|
|
65
|
+
puts "Error: #{e.errmsg}"
|
|
66
|
+
end
|
|
67
|
+
# ============================ Console Output ============================
|
|
68
|
+
# RuntimeError: Keys for index on return from db was nil. Coll = ruby-mongo-console.test
|
|
69
|
+
# from ./bin/../lib/mongo/db.rb:135:in `index_information'
|
|
70
|
+
# from (irb):11:in `collect'
|
|
71
|
+
# from ./bin/../lib/mongo/cursor.rb:47:in `each'
|
|
72
|
+
# from ./bin/../lib/mongo/db.rb:130:in `collect'
|
|
73
|
+
# from ./bin/../lib/mongo/db.rb:130:in `index_information'
|
|
74
|
+
# from ./bin/../lib/mongo/collection.rb:74:in `index_information'
|
|
75
|
+
# from (irb):11
|
|
76
|
+
|
|
77
|
+
puts ">> Dropping index"
|
|
78
|
+
begin
|
|
79
|
+
res = coll.drop_index "all"
|
|
80
|
+
puts "dropped : #{res.inspect}"
|
|
81
|
+
rescue => e
|
|
82
|
+
puts "Error: #{e.errmsg}"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# ============================ Console Output ============================
|
|
86
|
+
# => {"nIndexesWas"=>2.0, "ok"=>1.0}
|
|
87
|
+
# ============================ Mongo Log ============================
|
|
88
|
+
# 0x41802a 0x411549 0x42bac6 0x42c1f6 0x42c55b 0x42e6f7 0x41631e 0x41a89d 0x41ade2 0x41b448 0x4650d2 0x4695ad
|
|
89
|
+
# db/db(_Z12sayDbContextPKc+0x17a) [0x41802a]
|
|
90
|
+
# db/db(_Z8assertedPKcS0_j+0x9) [0x411549]
|
|
91
|
+
# db/db(_ZNK11BSONElement4sizeEv+0x1f6) [0x42bac6]
|
|
92
|
+
# db/db(_ZN7BSONObj8getFieldEPKc+0xa6) [0x42c1f6]
|
|
93
|
+
# db/db(_ZN7BSONObj14getFieldDottedEPKc+0x11b) [0x42c55b]
|
|
94
|
+
# db/db(_ZN7BSONObj19extractFieldsDottedES_R14BSONObjBuilder+0x87) [0x42e6f7]
|
|
95
|
+
# db/db(_ZN12IndexDetails17getKeysFromObjectER7BSONObjRSt3setIS0_St4lessIS0_ESaIS0_EE+0x24e) [0x41631e]
|
|
96
|
+
# db/db(_Z12_indexRecordR12IndexDetailsR7BSONObj7DiskLoc+0x5d) [0x41a89d]
|
|
97
|
+
# db/db(_Z18addExistingToIndexPKcR12IndexDetails+0xb2) [0x41ade2]
|
|
98
|
+
# db/db(_ZN11DataFileMgr6insertEPKcPKvib+0x508) [0x41b448]
|
|
99
|
+
# db/db(_Z14receivedInsertR7MessageRSt18basic_stringstreamIcSt11char_traitsIcESaIcEE+0x112) [0x4650d2]
|
|
100
|
+
# db/db(_Z10connThreadv+0xb4d) [0x4695ad]
|
|
101
|
+
# Fri Dec 5 14:45:02 ruby-mongo-console.system.indexes Caught Assertion insert, continuing
|
|
102
|
+
# Fri Dec 5 14:47:59 CMD: deleteIndexes ruby-mongo-console.test
|
|
103
|
+
# d->nIndexes was 2
|
|
104
|
+
# alpha implementation, space not reclaimed
|
|
105
|
+
|
|
106
|
+
puts ">> Gathering index information"
|
|
107
|
+
begin
|
|
108
|
+
res = coll.index_information
|
|
109
|
+
puts "index_information : #{res.inspect}"
|
|
110
|
+
rescue => e
|
|
111
|
+
puts "Error: #{e.errmsg}"
|
|
112
|
+
end
|
|
113
|
+
# ============================ Console Output ============================
|
|
114
|
+
# RuntimeError: Keys for index on return from db was nil. Coll = ruby-mongo-console.test
|
|
115
|
+
# from ./bin/../lib/mongo/db.rb:135:in `index_information'
|
|
116
|
+
# from (irb):15:in `collect'
|
|
117
|
+
# from ./bin/../lib/mongo/cursor.rb:47:in `each'
|
|
118
|
+
# from ./bin/../lib/mongo/db.rb:130:in `collect'
|
|
119
|
+
# from ./bin/../lib/mongo/db.rb:130:in `index_information'
|
|
120
|
+
# from ./bin/../lib/mongo/collection.rb:74:in `index_information'
|
|
121
|
+
# from (irb):15
|
|
122
|
+
|
|
123
|
+
puts ">> Closing connection"
|
|
124
|
+
db.close
|
|
125
|
+
puts "closed"
|
data/examples/info.rb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
|
2
|
+
require 'mongo'
|
|
3
|
+
|
|
4
|
+
include XGen::Mongo::Driver
|
|
5
|
+
|
|
6
|
+
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
|
7
|
+
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
|
8
|
+
|
|
9
|
+
puts "Connecting to #{host}:#{port}"
|
|
10
|
+
db = Mongo.new(host, port).db('ruby-mongo-examples')
|
|
11
|
+
coll = db.collection('test')
|
|
12
|
+
|
|
13
|
+
# Erase all records from collection, if any
|
|
14
|
+
coll.clear
|
|
15
|
+
|
|
16
|
+
# Insert 3 records
|
|
17
|
+
3.times { |i| coll.insert({'a' => i+1}) }
|
|
18
|
+
|
|
19
|
+
# Collection names in database
|
|
20
|
+
p db.collection_names
|
|
21
|
+
|
|
22
|
+
# More information about each collection
|
|
23
|
+
p db.collections_info
|
|
24
|
+
|
|
25
|
+
# Index information
|
|
26
|
+
db.create_index('test', 'a')
|
|
27
|
+
p db.index_information('test')
|
|
28
|
+
|
|
29
|
+
# Destroy the collection
|
|
30
|
+
coll.drop
|
data/examples/queries.rb
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
|
2
|
+
require 'mongo'
|
|
3
|
+
require 'pp'
|
|
4
|
+
|
|
5
|
+
include XGen::Mongo::Driver
|
|
6
|
+
|
|
7
|
+
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
|
8
|
+
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
|
9
|
+
|
|
10
|
+
puts "Connecting to #{host}:#{port}"
|
|
11
|
+
db = Mongo.new(host, port).db('ruby-mongo-examples')
|
|
12
|
+
coll = db.collection('test')
|
|
13
|
+
|
|
14
|
+
# Remove all records, if any
|
|
15
|
+
coll.clear
|
|
16
|
+
|
|
17
|
+
# Insert three records
|
|
18
|
+
coll.insert('a' => 1)
|
|
19
|
+
coll.insert('a' => 2)
|
|
20
|
+
coll.insert('b' => 3)
|
|
21
|
+
|
|
22
|
+
# Count.
|
|
23
|
+
puts "There are #{coll.count()} records."
|
|
24
|
+
|
|
25
|
+
# Find all records. find() returns a Cursor.
|
|
26
|
+
cursor = coll.find()
|
|
27
|
+
|
|
28
|
+
# Print them. Note that all records have an _id automatically added by the
|
|
29
|
+
# database. See pk.rb for an example of how to use a primary key factory to
|
|
30
|
+
# generate your own values for _id.
|
|
31
|
+
cursor.each { |row| pp row }
|
|
32
|
+
|
|
33
|
+
# Cursor has a to_a method that slurps all records into memory.
|
|
34
|
+
rows = coll.find().to_a
|
|
35
|
+
rows.each { |row| pp row }
|
|
36
|
+
|
|
37
|
+
# See Collection#find. From now on in this file, we won't be printing the
|
|
38
|
+
# records we find.
|
|
39
|
+
coll.find('a' => 1)
|
|
40
|
+
|
|
41
|
+
# Find records sort by 'a', offset 1, limit 2 records.
|
|
42
|
+
# Sort can be single name, array, or hash.
|
|
43
|
+
coll.find({}, {:offset => 1, :limit => 2, :sort => 'a'})
|
|
44
|
+
|
|
45
|
+
# Find all records with 'a' > 1. There is also $lt, $gte, and $lte.
|
|
46
|
+
coll.find({'a' => {'$gt' => 1}})
|
|
47
|
+
coll.find({'a' => {'$gt' => 1, '$lte' => 3}})
|
|
48
|
+
|
|
49
|
+
# Find all records with 'a' in a set of values.
|
|
50
|
+
coll.find('a' => {'$in' => [1,2]})
|
|
51
|
+
|
|
52
|
+
# Find by regexp
|
|
53
|
+
coll.find('a' => /[1|2]/)
|
|
54
|
+
|
|
55
|
+
# Print query explanation
|
|
56
|
+
pp coll.find('a' => /[1|2]/).explain()
|
|
57
|
+
|
|
58
|
+
# Use a hint with a query. Need an index. Hints can be stored with the
|
|
59
|
+
# collection, in which case they will be used with all queries, or they can be
|
|
60
|
+
# specified per query, in which case that hint overrides the hint associated
|
|
61
|
+
# with the collection if any.
|
|
62
|
+
coll.create_index('a')
|
|
63
|
+
coll.hint = 'a'
|
|
64
|
+
|
|
65
|
+
# You will see a different explanation now that the hint is in place
|
|
66
|
+
pp coll.find('a' => /[1|2]/).explain()
|
|
67
|
+
|
|
68
|
+
# Override hint for single query
|
|
69
|
+
coll.find({'a' => 1}, :hint => 'b')
|
data/examples/simple.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
|
2
|
+
require 'mongo'
|
|
3
|
+
|
|
4
|
+
include XGen::Mongo::Driver
|
|
5
|
+
|
|
6
|
+
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
|
7
|
+
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
|
8
|
+
|
|
9
|
+
puts "Connecting to #{host}:#{port}"
|
|
10
|
+
db = Mongo.new(host, port).db('ruby-mongo-examples')
|
|
11
|
+
coll = db.collection('test')
|
|
12
|
+
|
|
13
|
+
# Erase all records from collection, if any
|
|
14
|
+
coll.clear
|
|
15
|
+
|
|
16
|
+
# Insert 3 records
|
|
17
|
+
3.times { |i| coll.insert({'a' => i+1}) }
|
|
18
|
+
|
|
19
|
+
puts "There are #{coll.count()} records in the test collection. Here they are:"
|
|
20
|
+
coll.find().each { |doc| puts doc.inspect }
|
|
21
|
+
|
|
22
|
+
# Destroy the collection
|
|
23
|
+
coll.drop
|
data/examples/strict.rb
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
|
2
|
+
require 'mongo'
|
|
3
|
+
|
|
4
|
+
include XGen::Mongo::Driver
|
|
5
|
+
|
|
6
|
+
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
|
7
|
+
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
|
8
|
+
|
|
9
|
+
puts "Connecting to #{host}:#{port}"
|
|
10
|
+
db = Mongo.new(host, port).db('ruby-mongo-examples')
|
|
11
|
+
|
|
12
|
+
db.drop_collection('does-not-exist')
|
|
13
|
+
db.create_collection('test')
|
|
14
|
+
|
|
15
|
+
db.strict = true
|
|
16
|
+
|
|
17
|
+
begin
|
|
18
|
+
# Can't reference collection that does not exist
|
|
19
|
+
db.collection('does-not-exist')
|
|
20
|
+
puts "error: expected exception"
|
|
21
|
+
rescue => ex
|
|
22
|
+
puts "expected exception: #{ex}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
begin
|
|
26
|
+
# Can't create collection that already exists
|
|
27
|
+
db.create_collection('test')
|
|
28
|
+
puts "error: expected exception"
|
|
29
|
+
rescue => ex
|
|
30
|
+
puts "expected exception: #{ex}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
db.strict = false
|
|
34
|
+
db.drop_collection('test')
|
data/examples/types.rb
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
|
2
|
+
require 'mongo'
|
|
3
|
+
require 'pp'
|
|
4
|
+
|
|
5
|
+
include XGen::Mongo::Driver
|
|
6
|
+
|
|
7
|
+
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
|
8
|
+
port = ENV['MONGO_RUBY_DRIVER_PORT'] || XGen::Mongo::Driver::Mongo::DEFAULT_PORT
|
|
9
|
+
|
|
10
|
+
puts "Connecting to #{host}:#{port}"
|
|
11
|
+
db = Mongo.new(host, port).db('ruby-mongo-examples')
|
|
12
|
+
coll = db.collection('test')
|
|
13
|
+
|
|
14
|
+
# Remove all records, if any
|
|
15
|
+
coll.clear
|
|
16
|
+
|
|
17
|
+
# Insert record with all sorts of values
|
|
18
|
+
coll.insert('array' => [1, 2, 3],
|
|
19
|
+
'string' => 'hello',
|
|
20
|
+
'hash' => {'a' => 1, 'b' => 2},
|
|
21
|
+
'date' => Time.now, # milliseconds only; microseconds are not stored
|
|
22
|
+
'oid' => ObjectID.new,
|
|
23
|
+
'binary' => Binary.new([1, 2, 3]),
|
|
24
|
+
'int' => 42,
|
|
25
|
+
'float' => 33.33333,
|
|
26
|
+
'regex' => /foobar/i,
|
|
27
|
+
'boolean' => true,
|
|
28
|
+
'$where' => Code.new('this.x == 3'),
|
|
29
|
+
'dbref' => DBRef.new(coll.name, ObjectID.new),
|
|
30
|
+
|
|
31
|
+
# NOTE: the undefined type is not saved to the database properly. This is a
|
|
32
|
+
# Mongo bug. However, the undefined type may go away completely.
|
|
33
|
+
# 'undef' => Undefined.new,
|
|
34
|
+
|
|
35
|
+
'null' => nil,
|
|
36
|
+
'symbol' => :zildjian)
|
|
37
|
+
|
|
38
|
+
pp coll.find().next_object
|
|
39
|
+
|
|
40
|
+
coll.clear
|
data/lib/mongo.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'mongo/types/binary'
|
|
2
|
+
require 'mongo/types/dbref'
|
|
3
|
+
require 'mongo/types/objectid'
|
|
4
|
+
require 'mongo/types/regexp_of_holding'
|
|
5
|
+
require 'mongo/types/undefined'
|
|
6
|
+
|
|
7
|
+
require 'mongo/mongo'
|
|
8
|
+
require 'mongo/message'
|
|
9
|
+
require 'mongo/db'
|
|
10
|
+
require 'mongo/cursor'
|
|
11
|
+
require 'mongo/collection'
|
|
12
|
+
require 'mongo/admin'
|
|
13
|
+
|
|
14
|
+
module XGen
|
|
15
|
+
module Mongo
|
|
16
|
+
ASCENDING = 1
|
|
17
|
+
DESCENDING = -1
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/mongo/admin.rb
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# --
|
|
2
|
+
# Copyright (C) 2008-2009 10gen Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
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(Collection.new(@db, 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 an interesting hash (see especially the
|
|
74
|
+
# 'result' string value) if all is well.
|
|
75
|
+
def validate_collection(name)
|
|
76
|
+
doc = @db.db_command(:validate => name)
|
|
77
|
+
raise "Error with validate command: #{doc.inspect}" unless @db.ok?(doc)
|
|
78
|
+
result = doc['result']
|
|
79
|
+
raise "Error with validation data: #{doc.inspect}" unless result.kind_of?(String)
|
|
80
|
+
raise "Error: invalid collection #{name}: #{doc.inspect}" if result =~ /\b(exception|corrupt)\b/i
|
|
81
|
+
doc
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# --
|
|
2
|
+
# Copyright (C) 2008-2009 10gen Inc.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
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, :hint
|
|
27
|
+
|
|
28
|
+
def initialize(db, name)
|
|
29
|
+
@db, @name = db, name
|
|
30
|
+
@hint = nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Set hint fields to use and return +self+. hint may be a single field
|
|
34
|
+
# name, array of field names, or a hash (preferably an OrderedHash).
|
|
35
|
+
# May be +nil+.
|
|
36
|
+
def hint=(hint)
|
|
37
|
+
@hint = normalize_hint_fields(hint)
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Return records that match a +selector+ hash. See Mongo docs for
|
|
42
|
+
# details.
|
|
43
|
+
#
|
|
44
|
+
# Options:
|
|
45
|
+
# :fields :: Array of collection field names; only those will be returned (plus _id if defined)
|
|
46
|
+
# :offset :: Start at this record when returning records
|
|
47
|
+
# :limit :: Maximum number of records to return
|
|
48
|
+
# :sort :: Either hash of field names as keys and 1/-1 as values; 1 ==
|
|
49
|
+
# ascending, -1 == descending, or array of field names (all
|
|
50
|
+
# assumed to be sorted in ascending order).
|
|
51
|
+
# :hint :: See #hint. This option overrides the collection-wide value.
|
|
52
|
+
def find(selector={}, options={})
|
|
53
|
+
fields = options.delete(:fields)
|
|
54
|
+
fields = nil if fields && fields.empty?
|
|
55
|
+
offset = options.delete(:offset) || 0
|
|
56
|
+
limit = options.delete(:limit) || 0
|
|
57
|
+
sort = options.delete(:sort)
|
|
58
|
+
hint = options.delete(:hint)
|
|
59
|
+
if hint
|
|
60
|
+
hint = normalize_hint_fields(hint)
|
|
61
|
+
else
|
|
62
|
+
hint = @hint # assumed to be normalized already
|
|
63
|
+
end
|
|
64
|
+
raise RuntimeError, "Unknown options [#{options.inspect}]" unless options.empty?
|
|
65
|
+
@db.query(self, Query.new(selector, fields, offset, limit, sort, hint))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Find the first record that matches +selector+. See #find.
|
|
69
|
+
def find_first(selector={}, options={})
|
|
70
|
+
h = options.dup
|
|
71
|
+
h[:limit] = 1
|
|
72
|
+
cursor = find(selector, h)
|
|
73
|
+
cursor.next_object # don't need to explicitly close b/c of limit
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Save an updated +object+ to the collection, or insert it if it doesn't exist already.
|
|
77
|
+
def save(object)
|
|
78
|
+
if id = object[:_id] || object['_id']
|
|
79
|
+
repsert({:_id => id}, object)
|
|
80
|
+
else
|
|
81
|
+
insert(object)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Insert +objects+, which are hashes. "<<" is aliased to this method.
|
|
86
|
+
# Returns either the single inserted object or a new array containing
|
|
87
|
+
# +objects+. The object(s) may have been modified by the database's PK
|
|
88
|
+
# factory, if it has one.
|
|
89
|
+
def insert(*objects)
|
|
90
|
+
objects = objects.first if objects.size == 1 && objects.first.is_a?(Array)
|
|
91
|
+
res = @db.insert_into_db(@name, objects)
|
|
92
|
+
res.size > 1 ? res : res.first
|
|
93
|
+
end
|
|
94
|
+
alias_method :<<, :insert
|
|
95
|
+
|
|
96
|
+
# Remove the records that match +selector+.
|
|
97
|
+
def remove(selector={})
|
|
98
|
+
@db.remove_from_db(@name, selector)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Remove all records.
|
|
102
|
+
def clear
|
|
103
|
+
remove({})
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Update records that match +selector+ by applying +obj+ as an update.
|
|
107
|
+
# If no match, inserts (???).
|
|
108
|
+
def repsert(selector, obj)
|
|
109
|
+
@db.repsert_in_db(@name, selector, obj)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Update records that match +selector+ by applying +obj+ as an update.
|
|
113
|
+
def replace(selector, obj)
|
|
114
|
+
@db.replace_in_db(@name, selector, obj)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Update records that match +selector+ by applying +obj+ as an update.
|
|
118
|
+
# Both +selector+ and +modifier_obj+ are required.
|
|
119
|
+
def modify(selector, modifier_obj)
|
|
120
|
+
raise "no object" unless modifier_obj
|
|
121
|
+
raise "no selector" unless selector
|
|
122
|
+
@db.modify_in_db(@name, selector, modifier_obj)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Create a new index. +field_or_spec+
|
|
126
|
+
# should be either a single field name or a Array of [field name,
|
|
127
|
+
# direction] pairs. Directions should be specified as
|
|
128
|
+
# XGen::Mongo::ASCENDING or XGen::Mongo::DESCENDING.
|
|
129
|
+
# +unique+ is an optional boolean indicating whether this index
|
|
130
|
+
# should enforce a uniqueness constraint.
|
|
131
|
+
def create_index(field_or_spec, unique=false)
|
|
132
|
+
@db.create_index(@name, field_or_spec, unique)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Drop index +name+.
|
|
136
|
+
def drop_index(name)
|
|
137
|
+
@db.drop_index(@name, name)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Drop all indexes.
|
|
141
|
+
def drop_indexes
|
|
142
|
+
# just need to call drop indexes with no args; will drop them all
|
|
143
|
+
@db.drop_index(@name, '*')
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Drop the entire collection. USE WITH CAUTION.
|
|
147
|
+
def drop
|
|
148
|
+
@db.drop_collection(@name)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Perform a query similar to an SQL group by operation.
|
|
152
|
+
#
|
|
153
|
+
# Returns an array of grouped items.
|
|
154
|
+
#
|
|
155
|
+
# :keys :: list of fields to group by
|
|
156
|
+
# :condition :: specification of rows to be considered (as a 'find'
|
|
157
|
+
# query specification)
|
|
158
|
+
# :initial :: initial value of the aggregation counter object
|
|
159
|
+
# :reduce :: aggregation function as a JavaScript string
|
|
160
|
+
def group(keys, condition, initial, reduce)
|
|
161
|
+
group_function = <<EOS
|
|
162
|
+
function () {
|
|
163
|
+
var c = db[ns].find(condition);
|
|
164
|
+
var map = new Map();
|
|
165
|
+
var reduce_function = #{reduce};
|
|
166
|
+
while (c.hasNext()) {
|
|
167
|
+
var obj = c.next();
|
|
168
|
+
|
|
169
|
+
var key = {};
|
|
170
|
+
for (var i in keys) {
|
|
171
|
+
key[keys[i]] = obj[keys[i]];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
var aggObj = map.get(key);
|
|
175
|
+
if (aggObj == null) {
|
|
176
|
+
var newObj = Object.extend({}, key);
|
|
177
|
+
aggObj = Object.extend(newObj, initial);
|
|
178
|
+
map.put(key, aggObj);
|
|
179
|
+
}
|
|
180
|
+
reduce_function(obj, aggObj);
|
|
181
|
+
}
|
|
182
|
+
return {"result": map.values()};
|
|
183
|
+
}
|
|
184
|
+
EOS
|
|
185
|
+
return @db.eval(Code.new(group_function,
|
|
186
|
+
{
|
|
187
|
+
"ns" => @name,
|
|
188
|
+
"keys" => keys,
|
|
189
|
+
"condition" => condition,
|
|
190
|
+
"initial" => initial
|
|
191
|
+
}))["result"]
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Get information on the indexes for the collection +collection_name+.
|
|
195
|
+
# Returns a hash where the keys are index names (as returned by
|
|
196
|
+
# Collection#create_index and the values are lists of [key, direction]
|
|
197
|
+
# pairs specifying the index (as passed to Collection#create_index).
|
|
198
|
+
def index_information
|
|
199
|
+
@db.index_information(@name)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Return a hash containing options that apply to this collection.
|
|
203
|
+
# 'create' will be the collection name. For the other possible keys
|
|
204
|
+
# and values, see DB#create_collection.
|
|
205
|
+
def options
|
|
206
|
+
@db.collections_info(@name).next_object()['options']
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Return the number of records that match +selector+. If +selector+ is
|
|
210
|
+
# +nil+ or an empty hash, returns the count of all records.
|
|
211
|
+
def count(selector={})
|
|
212
|
+
@db.count(@name, selector || {})
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
protected
|
|
216
|
+
|
|
217
|
+
def normalize_hint_fields(hint)
|
|
218
|
+
case hint
|
|
219
|
+
when String
|
|
220
|
+
{hint => 1}
|
|
221
|
+
when Hash
|
|
222
|
+
hint
|
|
223
|
+
when nil
|
|
224
|
+
nil
|
|
225
|
+
else
|
|
226
|
+
h = OrderedHash.new
|
|
227
|
+
hint.to_a.each { |k| h[k] = 1 }
|
|
228
|
+
h
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|