mongo 1.1.2 → 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|