mongo 1.1.2 → 1.1.3
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.md +8 -4
- data/Rakefile +2 -2
- data/docs/FAQ.md +112 -0
- data/docs/GridFS.md +158 -0
- data/docs/HISTORY.md +12 -0
- data/docs/REPLICA_SETS.md +77 -0
- data/docs/TUTORIAL.md +1 -1
- data/docs/WRITE_CONCERN.md +28 -0
- data/lib/mongo.rb +2 -1
- data/lib/mongo/collection.rb +98 -44
- data/lib/mongo/connection.rb +140 -146
- data/lib/mongo/cursor.rb +27 -15
- data/lib/mongo/db.rb +5 -0
- data/lib/mongo/gridfs/grid.rb +1 -1
- data/lib/mongo/util/pool.rb +15 -3
- data/test/bson/object_id_test.rb +5 -0
- data/test/collection_test.rb +70 -1
- data/test/connection_test.rb +10 -16
- data/test/cursor_test.rb +4 -8
- data/test/db_api_test.rb +5 -17
- data/test/replica_sets/query_secondaries.rb +40 -0
- data/test/replica_sets/query_test.rb +1 -1
- data/test/rs.rb +20 -0
- data/test/safe_test.rb +27 -1
- data/test/unit/collection_test.rb +49 -0
- data/test/unit/connection_test.rb +24 -21
- data/test/unit/pool_test.rb +9 -0
- metadata +95 -85
data/lib/mongo/cursor.rb
CHANGED
@@ -23,7 +23,7 @@ module Mongo
|
|
23
23
|
|
24
24
|
attr_reader :collection, :selector, :fields,
|
25
25
|
:order, :hint, :snapshot, :timeout,
|
26
|
-
:full_collection_name
|
26
|
+
:full_collection_name
|
27
27
|
|
28
28
|
# Create a new cursor.
|
29
29
|
#
|
@@ -50,20 +50,26 @@ module Mongo
|
|
50
50
|
@explain = options[:explain]
|
51
51
|
@socket = options[:socket]
|
52
52
|
@tailable = options[:tailable] || false
|
53
|
+
@closed = false
|
54
|
+
@query_run = false
|
53
55
|
batch_size(options[:batch_size] || 0)
|
54
56
|
|
55
57
|
@full_collection_name = "#{@collection.db.name}.#{@collection.name}"
|
56
58
|
@cache = []
|
57
|
-
@closed = false
|
58
|
-
@query_run = false
|
59
59
|
@returned = 0
|
60
|
+
|
61
|
+
if @collection.name =~ /^\$cmd/ || @collection.name =~ /^system/
|
62
|
+
@command = true
|
63
|
+
else
|
64
|
+
@command = false
|
65
|
+
end
|
60
66
|
end
|
61
67
|
|
62
68
|
# Get the next document specified the cursor options.
|
63
69
|
#
|
64
70
|
# @return [Hash, Nil] the next document or Nil if no documents remain.
|
65
71
|
def next_document
|
66
|
-
refresh if @cache.length == 0
|
72
|
+
refresh if @cache.length == 0
|
67
73
|
doc = @cache.shift
|
68
74
|
|
69
75
|
if doc && doc['$err']
|
@@ -104,14 +110,21 @@ module Mongo
|
|
104
110
|
|
105
111
|
# Get the size of the result set for this query.
|
106
112
|
#
|
107
|
-
# @
|
108
|
-
#
|
113
|
+
# @param [Boolean] whether of not to take notice of skip and limit
|
114
|
+
#
|
115
|
+
# @return [Integer] the number of objects in the result set for this query.
|
109
116
|
#
|
110
117
|
# @raise [OperationFailure] on a database error.
|
111
|
-
def count
|
112
|
-
command = BSON::OrderedHash["count", @collection.name,
|
113
|
-
|
114
|
-
|
118
|
+
def count(skip_and_limit = false)
|
119
|
+
command = BSON::OrderedHash["count", @collection.name, "query", @selector]
|
120
|
+
|
121
|
+
if skip_and_limit
|
122
|
+
command.merge!(BSON::OrderedHash["limit", @limit]) if @limit != 0
|
123
|
+
command.merge!(BSON::OrderedHash["skip", @skip]) if @skip != 0
|
124
|
+
end
|
125
|
+
|
126
|
+
command.merge!(BSON::OrderedHash["fields", @fields])
|
127
|
+
|
115
128
|
response = @db.command(command)
|
116
129
|
return response['n'].to_i if Mongo::Support.ok?(response)
|
117
130
|
return 0 if response['errmsg'] == "ns missing"
|
@@ -156,7 +169,6 @@ module Mongo
|
|
156
169
|
def limit(number_to_return=nil)
|
157
170
|
return @limit unless number_to_return
|
158
171
|
check_modifiable
|
159
|
-
raise ArgumentError, "limit requires an integer" unless number_to_return.is_a? Integer
|
160
172
|
|
161
173
|
@limit = number_to_return
|
162
174
|
self
|
@@ -174,7 +186,6 @@ module Mongo
|
|
174
186
|
def skip(number_to_skip=nil)
|
175
187
|
return @skip unless number_to_skip
|
176
188
|
check_modifiable
|
177
|
-
raise ArgumentError, "skip requires an integer" unless number_to_skip.is_a? Integer
|
178
189
|
|
179
190
|
@skip = number_to_skip
|
180
191
|
self
|
@@ -354,8 +365,8 @@ module Mongo
|
|
354
365
|
# Cursor id.
|
355
366
|
message.put_long(@cursor_id)
|
356
367
|
@logger.debug("MONGODB cursor.refresh() for cursor #{@cursor_id}") if @logger
|
357
|
-
results, @n_received, @cursor_id = @connection.receive_message(
|
358
|
-
|
368
|
+
results, @n_received, @cursor_id = @connection.receive_message(
|
369
|
+
Mongo::Constants::OP_GET_MORE, message, nil, @socket, @command)
|
359
370
|
@returned += @n_received
|
360
371
|
@cache += results
|
361
372
|
close_cursor_if_query_complete
|
@@ -368,7 +379,8 @@ module Mongo
|
|
368
379
|
else
|
369
380
|
message = construct_query_message
|
370
381
|
@logger.debug query_log_message if @logger
|
371
|
-
results, @n_received, @cursor_id = @connection.receive_message(
|
382
|
+
results, @n_received, @cursor_id = @connection.receive_message(
|
383
|
+
Mongo::Constants::OP_QUERY, message, nil, @socket, @command)
|
372
384
|
@returned += @n_received
|
373
385
|
@cache += results
|
374
386
|
@query_run = true
|
data/lib/mongo/db.rb
CHANGED
@@ -51,6 +51,9 @@ module Mongo
|
|
51
51
|
# The Mongo::Connection instance connecting to the MongoDB server.
|
52
52
|
attr_reader :connection
|
53
53
|
|
54
|
+
# The length of time that Collection.ensure_index should cache index calls
|
55
|
+
attr_accessor :cache_time
|
56
|
+
|
54
57
|
# Instances of DB are normally obtained by calling Mongo#db.
|
55
58
|
#
|
56
59
|
# @param [String] db_name the database name.
|
@@ -70,6 +73,7 @@ module Mongo
|
|
70
73
|
# value is provided, the default value set on this instance's Connection object will be used. This
|
71
74
|
# default can be overridden upon instantiation of any collection by explicity setting a :safe value
|
72
75
|
# on initialization
|
76
|
+
# @option options [Integer] :cache_time (300) Set the time that all ensure_index calls should cache the command.
|
73
77
|
#
|
74
78
|
# @core databases constructor_details
|
75
79
|
def initialize(db_name, connection, options={})
|
@@ -78,6 +82,7 @@ module Mongo
|
|
78
82
|
@strict = options[:strict]
|
79
83
|
@pk_factory = options[:pk]
|
80
84
|
@safe = options.has_key?(:safe) ? options[:safe] : @connection.safe
|
85
|
+
@cache_time = options[:cache_time] || 300 #5 minutes.
|
81
86
|
end
|
82
87
|
|
83
88
|
# Authenticate with the given username and password. Note that mongod
|
data/lib/mongo/gridfs/grid.rb
CHANGED
@@ -65,7 +65,7 @@ module Mongo
|
|
65
65
|
#
|
66
66
|
# @return [Mongo::ObjectId] the file's id.
|
67
67
|
def put(data, opts={})
|
68
|
-
filename = opts
|
68
|
+
filename = opts.delete :filename
|
69
69
|
opts.merge!(default_grid_io_opts)
|
70
70
|
file = GridIO.new(@files, @chunks, filename, 'w', opts=opts)
|
71
71
|
file.write(data)
|
data/lib/mongo/util/pool.rb
CHANGED
@@ -18,11 +18,17 @@
|
|
18
18
|
module Mongo
|
19
19
|
class Pool
|
20
20
|
|
21
|
-
|
21
|
+
attr_accessor :host, :port, :size, :timeout, :safe, :checked_out
|
22
|
+
|
23
|
+
# Create a new pool of connections.
|
24
|
+
#
|
25
|
+
def initialize(connection, host, port, options={})
|
26
|
+
@connection = connection
|
27
|
+
|
22
28
|
@host, @port = host, port
|
23
29
|
|
24
30
|
# Pool size and timeout.
|
25
|
-
@size = options[:
|
31
|
+
@size = options[:size] || 1
|
26
32
|
@timeout = options[:timeout] || 5.0
|
27
33
|
|
28
34
|
# Mutex for synchronizing pool access
|
@@ -43,6 +49,12 @@ module Mongo
|
|
43
49
|
end
|
44
50
|
|
45
51
|
def close
|
52
|
+
@sockets.each do |sock|
|
53
|
+
sock.close
|
54
|
+
end
|
55
|
+
@host = @port = nil
|
56
|
+
@sockets.clear
|
57
|
+
@checked_out.clear
|
46
58
|
end
|
47
59
|
|
48
60
|
# Return a socket to the pool.
|
@@ -84,7 +96,7 @@ module Mongo
|
|
84
96
|
# pool size has not been exceeded. Otherwise, wait for the next
|
85
97
|
# available socket.
|
86
98
|
def checkout
|
87
|
-
connect if
|
99
|
+
@connection.connect if !@connection.connected?
|
88
100
|
start_time = Time.now
|
89
101
|
loop do
|
90
102
|
if (Time.now - start_time) > @timeout
|
data/test/bson/object_id_test.rb
CHANGED
data/test/collection_test.rb
CHANGED
@@ -67,6 +67,17 @@ class TestCollection < Test::Unit::TestCase
|
|
67
67
|
assert_equal 5, @@db.collection("test.foo").find_one()["x"]
|
68
68
|
end
|
69
69
|
|
70
|
+
def test_rename_collection
|
71
|
+
@@db.drop_collection('foo1')
|
72
|
+
@@db.drop_collection('bar1')
|
73
|
+
|
74
|
+
@col = @@db.create_collection('foo1')
|
75
|
+
assert_equal 'foo1', @col.name
|
76
|
+
|
77
|
+
@col.rename('bar1')
|
78
|
+
assert_equal 'bar1', @col.name
|
79
|
+
end
|
80
|
+
|
70
81
|
def test_nil_id
|
71
82
|
assert_equal 5, @@test.insert({"_id" => 5, "foo" => "bar"}, {:safe => true})
|
72
83
|
assert_equal 5, @@test.save({"_id" => 5, "foo" => "baz"}, {:safe => true})
|
@@ -555,8 +566,40 @@ class TestCollection < Test::Unit::TestCase
|
|
555
566
|
assert_equal 1, x
|
556
567
|
end
|
557
568
|
|
569
|
+
|
570
|
+
def test_ensure_index
|
571
|
+
@@test.drop_indexes
|
572
|
+
@@test.insert("x" => "hello world")
|
573
|
+
assert_equal 1, @@test.index_information.keys.count #default index
|
574
|
+
|
575
|
+
@@test.ensure_index([["x", Mongo::DESCENDING]], {})
|
576
|
+
assert_equal 2, @@test.index_information.keys.count
|
577
|
+
assert @@test.index_information.keys.include? "x_-1"
|
578
|
+
|
579
|
+
@@test.ensure_index([["x", Mongo::ASCENDING]])
|
580
|
+
assert @@test.index_information.keys.include? "x_1"
|
581
|
+
|
582
|
+
@@test.drop_index("x_1")
|
583
|
+
assert_equal 2, @@test.index_information.keys.count
|
584
|
+
@@test.drop_index("x_-1")
|
585
|
+
assert_equal 1, @@test.index_information.keys.count
|
586
|
+
|
587
|
+
@@test.ensure_index([["x", Mongo::DESCENDING]], {}) #should work as not cached.
|
588
|
+
assert_equal 2, @@test.index_information.keys.count
|
589
|
+
assert @@test.index_information.keys.include? "x_-1"
|
590
|
+
|
591
|
+
# Make sure that drop_index expires cache properly
|
592
|
+
@@test.ensure_index([['a', 1]])
|
593
|
+
assert @@test.index_information.keys.include?("a_1")
|
594
|
+
@@test.drop_index("a_1")
|
595
|
+
assert !@@test.index_information.keys.include?("a_1")
|
596
|
+
@@test.ensure_index([['a', 1]])
|
597
|
+
assert @@test.index_information.keys.include?("a_1")
|
598
|
+
@@test.drop_index("a_1")
|
599
|
+
end
|
600
|
+
|
558
601
|
context "Grouping" do
|
559
|
-
setup do
|
602
|
+
setup do
|
560
603
|
@@test.remove
|
561
604
|
@@test.save("a" => 1)
|
562
605
|
@@test.save("b" => 1)
|
@@ -576,6 +619,23 @@ class TestCollection < Test::Unit::TestCase
|
|
576
619
|
end
|
577
620
|
end
|
578
621
|
|
622
|
+
context "Grouping with key" do
|
623
|
+
setup do
|
624
|
+
@@test.remove
|
625
|
+
@@test.save("a" => 1, "pop" => 100)
|
626
|
+
@@test.save("a" => 1, "pop" => 100)
|
627
|
+
@@test.save("a" => 2, "pop" => 100)
|
628
|
+
@@test.save("a" => 2, "pop" => 100)
|
629
|
+
@initial = {"count" => 0, "foo" => 1}
|
630
|
+
@reduce_function = "function (obj, prev) { prev.count += obj.pop; }"
|
631
|
+
end
|
632
|
+
|
633
|
+
should "group" do
|
634
|
+
result = @@test.group([:a], {}, @initial, @reduce_function, nil)
|
635
|
+
assert result.all? { |r| r['count'] == 200 }
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
579
639
|
context "Grouping with a key function" do
|
580
640
|
setup do
|
581
641
|
@@test.remove
|
@@ -699,6 +759,15 @@ class TestCollection < Test::Unit::TestCase
|
|
699
759
|
@collection.create_index([['b', 1], ['a', 1]])
|
700
760
|
end
|
701
761
|
|
762
|
+
should "allow multiple calls to create_index" do
|
763
|
+
|
764
|
+
end
|
765
|
+
|
766
|
+
should "allow creation of multiple indexes" do
|
767
|
+
assert @collection.create_index([['a', 1]])
|
768
|
+
assert @collection.create_index([['a', 1]])
|
769
|
+
end
|
770
|
+
|
702
771
|
context "with an index created" do
|
703
772
|
setup do
|
704
773
|
@collection.create_index([['b', 1], ['a', 1]])
|
data/test/connection_test.rb
CHANGED
@@ -24,8 +24,8 @@ class TestConnection < Test::Unit::TestCase
|
|
24
24
|
|
25
25
|
def test_connection_uri
|
26
26
|
con = Connection.from_uri("mongodb://#{host_port}")
|
27
|
-
assert_equal mongo_host, con.host
|
28
|
-
assert_equal mongo_port, con.port
|
27
|
+
assert_equal mongo_host, con.primary_pool.host
|
28
|
+
assert_equal mongo_port, con.primary_pool.port
|
29
29
|
end
|
30
30
|
|
31
31
|
def test_server_version
|
@@ -44,8 +44,8 @@ class TestConnection < Test::Unit::TestCase
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def test_replica_set_connection_name
|
47
|
-
|
48
|
-
standard_connection(:
|
47
|
+
assert_raise Mongo::ReplicaSetConnectionError do
|
48
|
+
standard_connection(:rs_name => "replica-set-wrong-name")
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -144,12 +144,6 @@ class TestConnection < Test::Unit::TestCase
|
|
144
144
|
assert_equal ['bar', 27018], nodes[1]
|
145
145
|
end
|
146
146
|
|
147
|
-
def test_slave_ok_with_multiple_nodes
|
148
|
-
assert_raise MongoArgumentError do
|
149
|
-
Connection.multi([['foo', 27017], ['bar', 27018]], :connect => false, :slave_ok => true)
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
147
|
def test_fsync_lock
|
154
148
|
assert !@conn.locked?
|
155
149
|
@conn.lock!
|
@@ -211,29 +205,29 @@ class TestConnection < Test::Unit::TestCase
|
|
211
205
|
|
212
206
|
should "release connection if an exception is raised on send_message" do
|
213
207
|
@con.stubs(:send_message_on_socket).raises(ConnectionFailure)
|
214
|
-
assert_equal 0, @con.checked_out.size
|
208
|
+
assert_equal 0, @con.primary_pool.checked_out.size
|
215
209
|
assert_raise ConnectionFailure do
|
216
210
|
@coll.insert({:test => "insert"})
|
217
211
|
end
|
218
|
-
assert_equal 0, @con.checked_out.size
|
212
|
+
assert_equal 0, @con.primary_pool.checked_out.size
|
219
213
|
end
|
220
214
|
|
221
215
|
should "release connection if an exception is raised on send_with_safe_check" do
|
222
216
|
@con.stubs(:receive).raises(ConnectionFailure)
|
223
|
-
assert_equal 0, @con.checked_out.size
|
217
|
+
assert_equal 0, @con.primary_pool.checked_out.size
|
224
218
|
assert_raise ConnectionFailure do
|
225
219
|
@coll.insert({:test => "insert"}, :safe => true)
|
226
220
|
end
|
227
|
-
assert_equal 0, @con.checked_out.size
|
221
|
+
assert_equal 0, @con.primary_pool.checked_out.size
|
228
222
|
end
|
229
223
|
|
230
224
|
should "release connection if an exception is raised on receive_message" do
|
231
225
|
@con.stubs(:receive).raises(ConnectionFailure)
|
232
|
-
assert_equal 0, @con.checked_out.size
|
226
|
+
assert_equal 0, @con.primary_pool.checked_out.size
|
233
227
|
assert_raise ConnectionFailure do
|
234
228
|
@coll.find.to_a
|
235
229
|
end
|
236
|
-
assert_equal 0, @con.checked_out.size
|
230
|
+
assert_equal 0, @con.primary_pool.checked_out.size
|
237
231
|
end
|
238
232
|
end
|
239
233
|
end
|
data/test/cursor_test.rb
CHANGED
@@ -46,6 +46,10 @@ class CursorTest < Test::Unit::TestCase
|
|
46
46
|
assert_equal 10, @@coll.find({}, :limit => 5).count()
|
47
47
|
assert_equal 10, @@coll.find({}, :skip => 5).count()
|
48
48
|
|
49
|
+
assert_equal 5, @@coll.find({}, :limit => 5).count(true)
|
50
|
+
assert_equal 5, @@coll.find({}, :skip => 5).count(true)
|
51
|
+
assert_equal 2, @@coll.find({}, :skip => 5, :limit => 2).count(true)
|
52
|
+
|
49
53
|
assert_equal 1, @@coll.find({"x" => 1}).count()
|
50
54
|
assert_equal 5, @@coll.find({"x" => {"$lt" => 5}}).count()
|
51
55
|
|
@@ -182,10 +186,6 @@ class CursorTest < Test::Unit::TestCase
|
|
182
186
|
end
|
183
187
|
|
184
188
|
def test_limit_exceptions
|
185
|
-
assert_raise ArgumentError do
|
186
|
-
cursor = @@coll.find().limit('not-an-integer')
|
187
|
-
end
|
188
|
-
|
189
189
|
cursor = @@coll.find()
|
190
190
|
firstResult = cursor.next_document
|
191
191
|
assert_raise InvalidOperation, "Cannot modify the query once it has been run or closed." do
|
@@ -216,10 +216,6 @@ class CursorTest < Test::Unit::TestCase
|
|
216
216
|
end
|
217
217
|
|
218
218
|
def test_skip_exceptions
|
219
|
-
assert_raise ArgumentError do
|
220
|
-
cursor = @@coll.find().skip('not-an-integer')
|
221
|
-
end
|
222
|
-
|
223
219
|
cursor = @@coll.find()
|
224
220
|
firstResult = cursor.next_document
|
225
221
|
assert_raise InvalidOperation, "Cannot modify the query once it has been run or closed." do
|
data/test/db_api_test.rb
CHANGED
@@ -364,15 +364,12 @@ class DBAPITest < Test::Unit::TestCase
|
|
364
364
|
end
|
365
365
|
|
366
366
|
def test_array
|
367
|
-
@@coll
|
367
|
+
@@coll.remove
|
368
|
+
@@coll.insert({'b' => [1, 2, 3]})
|
369
|
+
@@coll.insert({'b' => [1, 2, 3]})
|
368
370
|
rows = @@coll.find({}, {:fields => ['b']}).to_a
|
369
|
-
|
370
|
-
|
371
|
-
assert_equal [1, 2, 3], rows[0]['b']
|
372
|
-
else
|
373
|
-
assert_equal 2, rows.length
|
374
|
-
assert_equal [1, 2, 3], rows[1]['b']
|
375
|
-
end
|
371
|
+
assert_equal 2, rows.length
|
372
|
+
assert_equal [1, 2, 3], rows[1]['b']
|
376
373
|
end
|
377
374
|
|
378
375
|
def test_regex
|
@@ -713,16 +710,7 @@ class DBAPITest < Test::Unit::TestCase
|
|
713
710
|
|
714
711
|
a.rename("bar")
|
715
712
|
|
716
|
-
assert_equal 0, a.count()
|
717
|
-
assert_equal 2, b.count()
|
718
|
-
|
719
|
-
assert_equal 1, b.find().to_a()[0]["x"]
|
720
|
-
assert_equal 2, b.find().to_a()[1]["x"]
|
721
|
-
|
722
|
-
b.rename(:foo)
|
723
|
-
|
724
713
|
assert_equal 2, a.count()
|
725
|
-
assert_equal 0, b.count()
|
726
714
|
end
|
727
715
|
|
728
716
|
# doesn't really test functionality, just that the option is set correctly
|
@@ -0,0 +1,40 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require 'mongo'
|
3
|
+
require 'test/unit'
|
4
|
+
require './test/test_helper'
|
5
|
+
|
6
|
+
# NOTE: This test expects a replica set of three nodes to be running
|
7
|
+
# on the local host.
|
8
|
+
class ReplicaSetQuerySecondariesTest < Test::Unit::TestCase
|
9
|
+
include Mongo
|
10
|
+
|
11
|
+
def setup
|
12
|
+
@conn = Mongo::Connection.multi([['localhost', 27018]], :read_secondaries => true)
|
13
|
+
@db = @conn.db(MONGO_TEST_DB)
|
14
|
+
@db.drop_collection("test-sets")
|
15
|
+
@coll = @db.collection("test-sets", :safe => {:w => 2, :wtimeout => 100})
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_query
|
19
|
+
@coll.save({:a => 20})
|
20
|
+
@coll.save({:a => 30})
|
21
|
+
@coll.save({:a => 40})
|
22
|
+
results = []
|
23
|
+
@coll.find.each {|r| results << r["a"]}
|
24
|
+
assert results.include?(20)
|
25
|
+
assert results.include?(30)
|
26
|
+
assert results.include?(40)
|
27
|
+
|
28
|
+
puts "Please disconnect the current master."
|
29
|
+
gets
|
30
|
+
|
31
|
+
results = []
|
32
|
+
rescue_connection_failure do
|
33
|
+
@coll.find.each {|r| results << r}
|
34
|
+
[20, 30, 40].each do |a|
|
35
|
+
assert results.any? {|r| r['a'] == a}, "Could not find record for a => #{a}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|