mongodb-mongo 0.13 → 0.14
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/lib/mongo/collection.rb +22 -8
- data/lib/mongo/connection.rb +0 -10
- data/lib/mongo/cursor.rb +2 -1
- data/lib/mongo/db.rb +2 -2
- data/lib/mongo/types/objectid.rb +79 -71
- data/mongo-ruby-driver.gemspec +1 -1
- data/test/test_collection.rb +40 -1
- data/test/test_cursor.rb +10 -0
- data/test/test_objectid.rb +36 -29
- metadata +1 -1
data/lib/mongo/collection.rb
CHANGED
@@ -314,10 +314,17 @@ module Mongo
|
|
314
314
|
keys.each do |k|
|
315
315
|
hash[k] = 1
|
316
316
|
end
|
317
|
+
|
318
|
+
case reduce
|
319
|
+
when Code
|
320
|
+
else
|
321
|
+
reduce = Code.new(reduce)
|
322
|
+
end
|
323
|
+
|
317
324
|
result = @db.db_command({"group" =>
|
318
325
|
{
|
319
326
|
"ns" => @name,
|
320
|
-
"$reduce" =>
|
327
|
+
"$reduce" => reduce,
|
321
328
|
"key" => hash,
|
322
329
|
"cond" => condition,
|
323
330
|
"initial" => initial}})
|
@@ -327,6 +334,19 @@ module Mongo
|
|
327
334
|
raise OperationFailure, "group command failed: #{result['errmsg']}"
|
328
335
|
end
|
329
336
|
end
|
337
|
+
|
338
|
+
case reduce
|
339
|
+
when Code
|
340
|
+
scope = reduce.scope
|
341
|
+
else
|
342
|
+
scope = {}
|
343
|
+
end
|
344
|
+
scope.merge!({
|
345
|
+
"ns" => @name,
|
346
|
+
"keys" => keys,
|
347
|
+
"condition" => condition,
|
348
|
+
"initial" => initial })
|
349
|
+
|
330
350
|
group_function = <<EOS
|
331
351
|
function () {
|
332
352
|
var c = db[ns].find(condition);
|
@@ -352,13 +372,7 @@ function () {
|
|
352
372
|
return {"result": map.values()};
|
353
373
|
}
|
354
374
|
EOS
|
355
|
-
return @db.eval(Code.new(group_function,
|
356
|
-
{
|
357
|
-
"ns" => @name,
|
358
|
-
"keys" => keys,
|
359
|
-
"condition" => condition,
|
360
|
-
"initial" => initial
|
361
|
-
}))["result"]
|
375
|
+
return @db.eval(Code.new(group_function, scope))["result"]
|
362
376
|
end
|
363
377
|
|
364
378
|
# Rename this collection.
|
data/lib/mongo/connection.rb
CHANGED
@@ -108,16 +108,6 @@ module Mongo
|
|
108
108
|
database_info.keys
|
109
109
|
end
|
110
110
|
|
111
|
-
# Not implemented.
|
112
|
-
def clone_database(from)
|
113
|
-
raise "not implemented"
|
114
|
-
end
|
115
|
-
|
116
|
-
# Not implemented.
|
117
|
-
def copy_database(from_host, from_db, to_db)
|
118
|
-
raise "not implemented"
|
119
|
-
end
|
120
|
-
|
121
111
|
# Drops the database +name+.
|
122
112
|
def drop_database(name)
|
123
113
|
single_db_command(name, :dropDatabase => 1)
|
data/lib/mongo/cursor.rb
CHANGED
@@ -66,7 +66,8 @@ module Mongo
|
|
66
66
|
# database error.
|
67
67
|
def count
|
68
68
|
command = OrderedHash["count", @collection.name,
|
69
|
-
"query", @query.selector
|
69
|
+
"query", @query.selector,
|
70
|
+
"fields", @query.fields()]
|
70
71
|
response = @db.db_command(command)
|
71
72
|
return response['n'].to_i if response['ok'] == 1
|
72
73
|
return 0 if response['errmsg'] == "ns missing"
|
data/lib/mongo/db.rb
CHANGED
@@ -154,11 +154,11 @@ module Mongo
|
|
154
154
|
is_master = master?
|
155
155
|
@semaphore.lock if semaphore_is_locked
|
156
156
|
|
157
|
-
|
157
|
+
@slave_ok || is_master
|
158
158
|
rescue SocketError, SystemCallError, IOError => ex
|
159
159
|
close if @socket
|
160
|
+
false
|
160
161
|
end
|
161
|
-
@socket
|
162
162
|
}
|
163
163
|
raise "error: failed to connect to any given host:port" unless @socket
|
164
164
|
end
|
data/lib/mongo/types/objectid.rb
CHANGED
@@ -15,61 +15,26 @@
|
|
15
15
|
# ++
|
16
16
|
|
17
17
|
require 'mutex_m'
|
18
|
-
require '
|
18
|
+
require 'socket'
|
19
|
+
require 'digest/md5'
|
19
20
|
|
20
21
|
module Mongo
|
21
22
|
|
22
|
-
#
|
23
|
-
# Mongo, but they make certain operations more efficient.
|
24
|
-
#
|
25
|
-
# The driver does not automatically assign ids to records that are
|
26
|
-
# inserted. (An upcoming feature will allow you to give an id "factory"
|
27
|
-
# to a database and/or a collection.)
|
28
|
-
#
|
29
|
-
# 12 bytes
|
30
|
-
# ---
|
31
|
-
# 0 time
|
32
|
-
# 1
|
33
|
-
# 2
|
34
|
-
# 3
|
35
|
-
# 4 machine
|
36
|
-
# 5
|
37
|
-
# 6
|
38
|
-
# 7 pid
|
39
|
-
# 8
|
40
|
-
# 9 inc
|
41
|
-
# 10
|
42
|
-
# 11
|
23
|
+
# Representation of an ObjectId for Mongo.
|
43
24
|
class ObjectID
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
#
|
49
|
-
#
|
50
|
-
# internal/BSON byte position (the index in BYTE_ORDER) to the
|
51
|
-
# position of the two hex characters representing that byte in the
|
52
|
-
# string representation. For example, the 0th BSON byte corresponds to
|
53
|
-
# the (0-based) 7th pair of hex chars in the string.
|
25
|
+
# This is the legacy byte ordering for Babble. Versions of the Ruby
|
26
|
+
# driver prior to 0.14 used this byte ordering when converting ObjectID
|
27
|
+
# instances to and from strings. If you have string representations of
|
28
|
+
# ObjectIDs using the legacy byte ordering make sure to use the
|
29
|
+
# to_s_legacy and from_string_legacy methods, or convert your strings
|
30
|
+
# with ObjectID#legacy_string_convert
|
54
31
|
BYTE_ORDER = [7, 6, 5, 4, 3, 2, 1, 0, 11, 10, 9, 8]
|
55
32
|
|
56
33
|
LOCK = Object.new
|
57
34
|
LOCK.extend Mutex_m
|
58
35
|
|
59
|
-
@@index_time = Time.new.to_i
|
60
36
|
@@index = 0
|
61
37
|
|
62
|
-
# Given a string representation of an ObjectID, return a new ObjectID
|
63
|
-
# with that value.
|
64
|
-
def self.from_string(str)
|
65
|
-
raise "illegal ObjectID format" unless legal?(str)
|
66
|
-
data = []
|
67
|
-
BYTE_ORDER.each_with_index { |string_position, data_index|
|
68
|
-
data[data_index] = str[string_position * 2, 2].to_i(16)
|
69
|
-
}
|
70
|
-
self.new(data)
|
71
|
-
end
|
72
|
-
|
73
38
|
def self.legal?(str)
|
74
39
|
len = BYTE_ORDER.length * 2
|
75
40
|
str =~ /([0-9a-f]+)/i
|
@@ -78,9 +43,8 @@ module Mongo
|
|
78
43
|
end
|
79
44
|
|
80
45
|
# +data+ is an array of bytes. If nil, a new id will be generated.
|
81
|
-
|
82
|
-
|
83
|
-
@data = data || generate_id(t)
|
46
|
+
def initialize(data=nil)
|
47
|
+
@data = data || generate
|
84
48
|
end
|
85
49
|
|
86
50
|
def eql?(other)
|
@@ -92,7 +56,42 @@ module Mongo
|
|
92
56
|
@data.dup
|
93
57
|
end
|
94
58
|
|
59
|
+
# Given a string representation of an ObjectID, return a new ObjectID
|
60
|
+
# with that value.
|
61
|
+
def self.from_string(str)
|
62
|
+
raise "illegal ObjectID format" unless legal?(str)
|
63
|
+
data = []
|
64
|
+
12.times do |i|
|
65
|
+
data[i] = str[i * 2, 2].to_i(16)
|
66
|
+
end
|
67
|
+
self.new(data)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Create a new ObjectID given a string representation of an ObjectID
|
71
|
+
# using the legacy byte ordering. This method may eventually be
|
72
|
+
# removed. If you are not sure that you need this method you should be
|
73
|
+
# using the regular from_string.
|
74
|
+
def self.from_string_legacy(str)
|
75
|
+
raise "illegal ObjectID format" unless legal?(str)
|
76
|
+
data = []
|
77
|
+
BYTE_ORDER.each_with_index { |string_position, data_index|
|
78
|
+
data[data_index] = str[string_position * 2, 2].to_i(16)
|
79
|
+
}
|
80
|
+
self.new(data)
|
81
|
+
end
|
82
|
+
|
95
83
|
def to_s
|
84
|
+
str = ' ' * 24
|
85
|
+
12.times do |i|
|
86
|
+
str[i * 2, 2] = '%02x' % @data[i]
|
87
|
+
end
|
88
|
+
str
|
89
|
+
end
|
90
|
+
|
91
|
+
# Get a string representation of this ObjectID using the legacy byte
|
92
|
+
# ordering. This method may eventually be removed. If you are not sure
|
93
|
+
# that you need this method you should be using the regular to_s.
|
94
|
+
def to_s_legacy
|
96
95
|
str = ' ' * 24
|
97
96
|
BYTE_ORDER.each_with_index { |string_position, data_index|
|
98
97
|
str[string_position * 2, 2] = '%02x' % @data[data_index]
|
@@ -100,34 +99,43 @@ module Mongo
|
|
100
99
|
str
|
101
100
|
end
|
102
101
|
|
103
|
-
#
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
102
|
+
# Convert a string representation of an ObjectID using the legacy byte
|
103
|
+
# ordering to the proper byte ordering. This method may eventually be
|
104
|
+
# removed. If you are not sure that you need this method it is probably
|
105
|
+
# unnecessary.
|
106
|
+
def self.legacy_string_convert(str)
|
107
|
+
legacy = ' ' * 24
|
108
|
+
BYTE_ORDER.each_with_index do |legacy_pos, pos|
|
109
|
+
legacy[legacy_pos * 2, 2] = str[pos * 2, 2]
|
110
|
+
end
|
111
|
+
legacy
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def generate
|
117
|
+
oid = ''
|
118
|
+
|
119
|
+
# 4 bytes current time
|
120
|
+
time = Time.new.to_i
|
121
|
+
oid += [time].pack("N")
|
122
|
+
|
123
|
+
# 3 bytes machine
|
124
|
+
oid += Digest::MD5.digest(Socket.gethostname)[0, 3]
|
125
|
+
|
126
|
+
# 2 bytes pid
|
127
|
+
oid += [Process.pid % 0xFFFF].pack("n")
|
128
|
+
|
129
|
+
# 3 bytes inc
|
130
|
+
oid += [get_inc].pack("N")[1, 3]
|
131
|
+
|
132
|
+
oid.unpack("C12")
|
117
133
|
end
|
118
134
|
|
119
|
-
|
120
|
-
def index_for_time(t)
|
135
|
+
def get_inc
|
121
136
|
LOCK.mu_synchronize {
|
122
|
-
|
123
|
-
@@index = 0
|
124
|
-
@@index_time = t
|
125
|
-
end
|
126
|
-
retval = @@index
|
127
|
-
@@index += 1
|
128
|
-
retval
|
137
|
+
@@index = (@@index + 1) % 0xFFFFFF
|
129
138
|
}
|
130
139
|
end
|
131
|
-
|
132
140
|
end
|
133
141
|
end
|
data/mongo-ruby-driver.gemspec
CHANGED
@@ -83,7 +83,7 @@ TEST_FILES = ['test/mongo-qa/_common.rb',
|
|
83
83
|
|
84
84
|
Gem::Specification.new do |s|
|
85
85
|
s.name = 'mongo'
|
86
|
-
s.version = '0.
|
86
|
+
s.version = '0.14'
|
87
87
|
s.platform = Gem::Platform::RUBY
|
88
88
|
s.summary = 'Ruby driver for the 10gen Mongo DB'
|
89
89
|
s.description = 'A Ruby driver for the 10gen Mongo DB. For more information about Mongo, see http://www.mongodb.org.'
|
data/test/test_collection.rb
CHANGED
@@ -177,5 +177,44 @@ class TestCollection < Test::Unit::TestCase
|
|
177
177
|
end
|
178
178
|
assert c.closed?
|
179
179
|
end
|
180
|
-
end
|
181
180
|
|
181
|
+
def test_save_symbol_find_string
|
182
|
+
@@test.save(:foo => :mike)
|
183
|
+
|
184
|
+
assert_equal :mike, @@test.find_one(:foo => :mike)["foo"]
|
185
|
+
assert_equal :mike, @@test.find_one("foo" => :mike)["foo"]
|
186
|
+
assert_equal nil, @@test.find_one(:foo => "mike")
|
187
|
+
assert_equal nil, @@test.find_one("foo" => "mike")
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_group_with_scope
|
191
|
+
@@test.save("a" => 1)
|
192
|
+
@@test.save("b" => 1)
|
193
|
+
|
194
|
+
reduce_function = "function (obj, prev) { prev.count += inc_value; }"
|
195
|
+
|
196
|
+
assert_equal 2, @@test.group([], {}, {"count" => 0},
|
197
|
+
Code.new(reduce_function,
|
198
|
+
{"inc_value" => 1}))[0]["count"]
|
199
|
+
|
200
|
+
# TODO enable these tests when SERVER-262 is fixed
|
201
|
+
|
202
|
+
# assert_equal 2, @@test.group([], {}, {"count" => 0},
|
203
|
+
# Code.new(reduce_function,
|
204
|
+
# {"inc_value" => 1}), true)[0]["count"]
|
205
|
+
|
206
|
+
assert_equal 4, @@test.group([], {}, {"count" => 0},
|
207
|
+
Code.new(reduce_function,
|
208
|
+
{"inc_value" => 2}))[0]["count"]
|
209
|
+
# assert_equal 4, @@test.group([], {}, {"count" => 0},
|
210
|
+
# Code.new(reduce_function,
|
211
|
+
# {"inc_value" => 2}), true)[0]["count"]
|
212
|
+
|
213
|
+
assert_equal 1, @@test.group([], {}, {"count" => 0},
|
214
|
+
Code.new(reduce_function,
|
215
|
+
{"inc_value" => 0.5}))[0]["count"]
|
216
|
+
# assert_equal 1, @@test.group([], {}, {"count" => 0},
|
217
|
+
# Code.new(reduce_function,
|
218
|
+
# {"inc_value" => 0.5}), true)[0]["count"]
|
219
|
+
end
|
220
|
+
end
|
data/test/test_cursor.rb
CHANGED
@@ -220,4 +220,14 @@ class CursorTest < Test::Unit::TestCase
|
|
220
220
|
assert_equal(by_location,
|
221
221
|
@@db.db_command("cursorInfo" => 1)["byLocation_size"])
|
222
222
|
end
|
223
|
+
|
224
|
+
def test_count_with_fields
|
225
|
+
@@coll.clear
|
226
|
+
@@coll.save("x" => 1)
|
227
|
+
|
228
|
+
@@coll.find({}, :fields => ["a"]).each do |doc|
|
229
|
+
fail "shouldn't have any results here"
|
230
|
+
end
|
231
|
+
assert_equal(0, @@coll.find({}, :fields => ["a"]).count())
|
232
|
+
end
|
223
233
|
end
|
data/test/test_objectid.rb
CHANGED
@@ -7,41 +7,19 @@ class ObjectIDTest < Test::Unit::TestCase
|
|
7
7
|
include Mongo
|
8
8
|
|
9
9
|
def setup
|
10
|
-
@
|
11
|
-
@o = ObjectID.new(nil, @t)
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_index_for_time
|
15
|
-
t = 99
|
16
|
-
assert_equal 0, @o.index_for_time(t)
|
17
|
-
assert_equal 1, @o.index_for_time(t)
|
18
|
-
assert_equal 2, @o.index_for_time(t)
|
19
|
-
t = 100
|
20
|
-
assert_equal 0, @o.index_for_time(t)
|
21
|
-
end
|
22
|
-
|
23
|
-
def test_time_bytes
|
24
|
-
a = @o.to_a
|
25
|
-
assert_equal @t, a[0]
|
26
|
-
3.times { |i| assert_equal 0, a[i+1] }
|
27
|
-
|
28
|
-
t = 43
|
29
|
-
o = ObjectID.new(nil, t)
|
30
|
-
a = o.to_a
|
31
|
-
assert_equal t, a[0]
|
32
|
-
3.times { |i| assert_equal 0, a[i+1] }
|
33
|
-
assert_equal 1, o.index_for_time(t) # 0 was used for o
|
10
|
+
@o = ObjectID.new()
|
34
11
|
end
|
35
12
|
|
36
13
|
def test_different
|
37
|
-
|
38
|
-
|
14
|
+
a = ObjectID.new
|
15
|
+
b = ObjectID.new
|
16
|
+
assert_not_equal a.to_a, b.to_a
|
17
|
+
assert_not_equal a, b
|
39
18
|
end
|
40
19
|
|
41
20
|
def test_eql?
|
42
21
|
o2 = ObjectID.new(@o.to_a)
|
43
|
-
|
44
|
-
assert @o == o2
|
22
|
+
assert_equal @o, o2
|
45
23
|
end
|
46
24
|
|
47
25
|
def test_to_s
|
@@ -51,6 +29,15 @@ class ObjectIDTest < Test::Unit::TestCase
|
|
51
29
|
assert_equal 24, $1.length
|
52
30
|
end
|
53
31
|
|
32
|
+
def test_to_s_legacy
|
33
|
+
s = @o.to_s_legacy
|
34
|
+
assert_equal 24, s.length
|
35
|
+
s =~ /^([0-9a-f]+)$/
|
36
|
+
assert_equal 24, $1.length
|
37
|
+
|
38
|
+
assert_not_equal s, @o.to_s
|
39
|
+
end
|
40
|
+
|
54
41
|
def test_save_and_restore
|
55
42
|
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
56
43
|
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT
|
@@ -73,6 +60,14 @@ class ObjectIDTest < Test::Unit::TestCase
|
|
73
60
|
assert_equal @o.to_s, o2.to_s
|
74
61
|
end
|
75
62
|
|
63
|
+
def test_from_string_legacy
|
64
|
+
hex_str = @o.to_s_legacy
|
65
|
+
o2 = ObjectID.from_string_legacy(hex_str)
|
66
|
+
assert_equal hex_str, o2.to_s_legacy
|
67
|
+
assert_equal @o, o2
|
68
|
+
assert_equal @o.to_s, o2.to_s
|
69
|
+
end
|
70
|
+
|
76
71
|
def test_legal
|
77
72
|
assert !ObjectID.legal?(nil)
|
78
73
|
assert !ObjectID.legal?("fred")
|
@@ -84,7 +79,7 @@ class ObjectIDTest < Test::Unit::TestCase
|
|
84
79
|
end
|
85
80
|
|
86
81
|
def test_from_string_leading_zeroes
|
87
|
-
hex_str = '
|
82
|
+
hex_str = '000000000000000000000000'
|
88
83
|
o = ObjectID.from_string(hex_str)
|
89
84
|
assert_equal hex_str, o.to_s
|
90
85
|
end
|
@@ -92,7 +87,19 @@ class ObjectIDTest < Test::Unit::TestCase
|
|
92
87
|
def test_byte_order
|
93
88
|
hex_str = '000102030405060708090A0B'
|
94
89
|
o = ObjectID.from_string(hex_str)
|
90
|
+
assert_equal [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b], o.to_a
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_legacy_byte_order
|
94
|
+
hex_str = '000102030405060708090A0B'
|
95
|
+
o = ObjectID.from_string_legacy(hex_str)
|
95
96
|
assert_equal [0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0b, 0x0a, 0x09, 0x08], o.to_a
|
96
97
|
end
|
97
98
|
|
99
|
+
def test_legacy_string_convert
|
100
|
+
l = @o.to_s_legacy
|
101
|
+
s = @o.to_s
|
102
|
+
assert_equal s, ObjectID.legacy_string_convert(l)
|
103
|
+
end
|
104
|
+
|
98
105
|
end
|