mongo 1.0 → 1.1.5
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/LICENSE.txt +1 -13
- data/{README.rdoc → README.md} +129 -149
- data/Rakefile +94 -58
- data/bin/mongo_console +21 -0
- data/docs/1.0_UPGRADE.md +21 -0
- data/docs/CREDITS.md +123 -0
- data/docs/FAQ.md +112 -0
- data/docs/GridFS.md +158 -0
- data/docs/HISTORY.md +185 -0
- data/docs/REPLICA_SETS.md +75 -0
- data/docs/TUTORIAL.md +247 -0
- data/docs/WRITE_CONCERN.md +28 -0
- data/lib/mongo/collection.rb +225 -105
- data/lib/mongo/connection.rb +374 -315
- data/lib/mongo/cursor.rb +122 -77
- data/lib/mongo/db.rb +109 -85
- data/lib/mongo/exceptions.rb +6 -0
- data/lib/mongo/gridfs/grid.rb +19 -11
- data/lib/mongo/gridfs/grid_ext.rb +36 -9
- data/lib/mongo/gridfs/grid_file_system.rb +15 -9
- data/lib/mongo/gridfs/grid_io.rb +49 -16
- data/lib/mongo/gridfs/grid_io_fix.rb +38 -0
- data/lib/mongo/repl_set_connection.rb +290 -0
- data/lib/mongo/util/conversions.rb +3 -1
- data/lib/mongo/util/core_ext.rb +17 -4
- data/lib/mongo/util/pool.rb +125 -0
- data/lib/mongo/util/server_version.rb +2 -0
- data/lib/mongo/util/support.rb +12 -0
- data/lib/mongo/util/uri_parser.rb +71 -0
- data/lib/mongo.rb +23 -7
- data/{mongo-ruby-driver.gemspec → mongo.gemspec} +9 -7
- data/test/auxillary/1.4_features.rb +2 -2
- data/test/auxillary/authentication_test.rb +1 -1
- data/test/auxillary/autoreconnect_test.rb +1 -1
- data/test/{slave_connection_test.rb → auxillary/slave_connection_test.rb} +6 -6
- data/test/bson/binary_test.rb +15 -0
- data/test/bson/bson_test.rb +537 -0
- data/test/bson/byte_buffer_test.rb +190 -0
- data/test/bson/hash_with_indifferent_access_test.rb +38 -0
- data/test/bson/json_test.rb +17 -0
- data/test/bson/object_id_test.rb +141 -0
- data/test/bson/ordered_hash_test.rb +197 -0
- data/test/collection_test.rb +195 -15
- data/test/connection_test.rb +93 -56
- data/test/conversions_test.rb +1 -1
- data/test/cursor_fail_test.rb +75 -0
- data/test/cursor_message_test.rb +43 -0
- data/test/cursor_test.rb +93 -32
- data/test/db_api_test.rb +28 -55
- data/test/db_connection_test.rb +2 -3
- data/test/db_test.rb +45 -40
- data/test/grid_file_system_test.rb +14 -6
- data/test/grid_io_test.rb +36 -7
- data/test/grid_test.rb +54 -10
- data/test/replica_sets/connect_test.rb +84 -0
- data/test/replica_sets/count_test.rb +35 -0
- data/test/{replica → replica_sets}/insert_test.rb +17 -14
- data/test/replica_sets/pooled_insert_test.rb +55 -0
- data/test/replica_sets/query_secondaries.rb +80 -0
- data/test/replica_sets/query_test.rb +41 -0
- data/test/replica_sets/replication_ack_test.rb +64 -0
- data/test/replica_sets/rs_test_helper.rb +29 -0
- data/test/safe_test.rb +68 -0
- data/test/support/hash_with_indifferent_access.rb +199 -0
- data/test/support/keys.rb +45 -0
- data/test/support_test.rb +19 -0
- data/test/test_helper.rb +53 -15
- data/test/threading/{test_threading_large_pool.rb → threading_with_large_pool_test.rb} +2 -2
- data/test/threading_test.rb +2 -2
- data/test/tools/repl_set_manager.rb +241 -0
- data/test/tools/test.rb +13 -0
- data/test/unit/collection_test.rb +70 -7
- data/test/unit/connection_test.rb +18 -39
- data/test/unit/cursor_test.rb +7 -8
- data/test/unit/db_test.rb +14 -17
- data/test/unit/grid_test.rb +49 -0
- data/test/unit/pool_test.rb +9 -0
- data/test/unit/repl_set_connection_test.rb +82 -0
- data/test/unit/safe_test.rb +125 -0
- metadata +132 -51
- data/bin/bson_benchmark.rb +0 -59
- data/bin/fail_if_no_c.rb +0 -11
- data/examples/admin.rb +0 -43
- data/examples/capped.rb +0 -22
- data/examples/cursor.rb +0 -48
- data/examples/gridfs.rb +0 -44
- data/examples/index_test.rb +0 -126
- data/examples/info.rb +0 -31
- data/examples/queries.rb +0 -70
- data/examples/simple.rb +0 -24
- data/examples/strict.rb +0 -35
- data/examples/types.rb +0 -36
- data/test/replica/count_test.rb +0 -34
- data/test/replica/pooled_insert_test.rb +0 -54
- data/test/replica/query_test.rb +0 -39
data/lib/mongo/collection.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
1
3
|
# --
|
|
2
4
|
# Copyright (C) 2008-2010 10gen Inc.
|
|
3
5
|
#
|
|
@@ -12,20 +14,27 @@
|
|
|
12
14
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
15
|
# See the License for the specific language governing permissions and
|
|
14
16
|
# limitations under the License.
|
|
15
|
-
# ++
|
|
16
17
|
|
|
17
18
|
module Mongo
|
|
18
19
|
|
|
19
20
|
# A named collection of documents in a database.
|
|
20
21
|
class Collection
|
|
21
22
|
|
|
22
|
-
attr_reader :db, :name, :pk_factory, :hint
|
|
23
|
+
attr_reader :db, :name, :pk_factory, :hint, :safe
|
|
23
24
|
|
|
24
25
|
# Initialize a collection object.
|
|
25
26
|
#
|
|
26
27
|
# @param [DB] db a MongoDB database instance.
|
|
27
28
|
# @param [String, Symbol] name the name of the collection.
|
|
28
29
|
#
|
|
30
|
+
# @option options [:create_pk] :pk (BSON::ObjectId) A primary key factory to use
|
|
31
|
+
# other than the default BSON::ObjectId.
|
|
32
|
+
#
|
|
33
|
+
# @option options [Boolean, Hash] :safe (false) Set the default safe-mode options
|
|
34
|
+
# for insert, update, and remove method called on this Collection instance. If no
|
|
35
|
+
# value is provided, the default value set on this instance's DB will be used. This
|
|
36
|
+
# default can be overridden for any invocation of insert, update, or remove.
|
|
37
|
+
#
|
|
29
38
|
# @raise [InvalidNSName]
|
|
30
39
|
# if collection name is empty, contains '$', or starts or ends with '.'
|
|
31
40
|
#
|
|
@@ -35,7 +44,7 @@ module Mongo
|
|
|
35
44
|
# @return [Collection]
|
|
36
45
|
#
|
|
37
46
|
# @core collections constructor_details
|
|
38
|
-
def initialize(db, name,
|
|
47
|
+
def initialize(db, name, options={})
|
|
39
48
|
case name
|
|
40
49
|
when Symbol, String
|
|
41
50
|
else
|
|
@@ -54,9 +63,23 @@ module Mongo
|
|
|
54
63
|
raise Mongo::InvalidNSName, "collection names must not start or end with '.'"
|
|
55
64
|
end
|
|
56
65
|
|
|
66
|
+
if options.respond_to?(:create_pk) || !options.is_a?(Hash)
|
|
67
|
+
warn "The method for specifying a primary key factory on a Collection has changed.\n" +
|
|
68
|
+
"Please specify it as an option (e.g., :pk => PkFactory)."
|
|
69
|
+
pk_factory = options
|
|
70
|
+
else
|
|
71
|
+
pk_factory = nil
|
|
72
|
+
end
|
|
73
|
+
|
|
57
74
|
@db, @name = db, name
|
|
58
75
|
@connection = @db.connection
|
|
59
|
-
@
|
|
76
|
+
@logger = @connection.logger
|
|
77
|
+
@cache_time = @db.cache_time
|
|
78
|
+
@cache = Hash.new(0)
|
|
79
|
+
unless pk_factory
|
|
80
|
+
@safe = options.has_key?(:safe) ? options[:safe] : @db.safe
|
|
81
|
+
end
|
|
82
|
+
@pk_factory = pk_factory || options[:pk] || BSON::ObjectId
|
|
60
83
|
@hint = nil
|
|
61
84
|
end
|
|
62
85
|
|
|
@@ -105,7 +128,10 @@ module Mongo
|
|
|
105
128
|
#
|
|
106
129
|
# @param [Hash] selector
|
|
107
130
|
# a document specifying elements which must be present for a
|
|
108
|
-
# document to be included in the result set.
|
|
131
|
+
# document to be included in the result set. Note that in rare cases,
|
|
132
|
+
# (e.g., with $near queries), the order of keys will matter. To preserve
|
|
133
|
+
# key order on a selector, use an instance of BSON::OrderedHash (only applies
|
|
134
|
+
# to Ruby 1.8).
|
|
109
135
|
#
|
|
110
136
|
# @option opts [Array, Hash] :fields field names that should be returned in the result
|
|
111
137
|
# set ("_id" will always be included). By limiting results to a certain subset of fields,
|
|
@@ -117,12 +143,12 @@ module Mongo
|
|
|
117
143
|
# @option opts [Array] :sort an array of [key, direction] pairs to sort by. Direction should
|
|
118
144
|
# be specified as Mongo::ASCENDING (or :ascending / :asc) or Mongo::DESCENDING (or :descending / :desc)
|
|
119
145
|
# @option opts [String, Array, OrderedHash] :hint hint for query optimizer, usually not necessary if using MongoDB > 1.1
|
|
120
|
-
# @option opts [Boolean] :snapshot (
|
|
146
|
+
# @option opts [Boolean] :snapshot (false) if true, snapshot mode will be used for this query.
|
|
121
147
|
# Snapshot mode assures no duplicates are returned, or objects missed, which were preset at both the start and
|
|
122
148
|
# end of the query's execution. For details see http://www.mongodb.org/display/DOCS/How+to+do+Snapshotting+in+the+Mongo+Database
|
|
123
149
|
# @option opts [Boolean] :batch_size (100) the number of documents to returned by the database per GETMORE operation. A value of 0
|
|
124
150
|
# will let the database server decide how many results to returns. This option can be ignored for most use cases.
|
|
125
|
-
# @option opts [Boolean] :timeout (
|
|
151
|
+
# @option opts [Boolean] :timeout (true) when +true+, the returned cursor will be subject to
|
|
126
152
|
# the normal cursor timeout behavior of the mongod process. When +false+, the returned cursor will never timeout. Note
|
|
127
153
|
# that disabling timeout will only work when #find is invoked with a block. This is to prevent any inadvertant failure to
|
|
128
154
|
# close the cursor, as the cursor is explicitly closed when block code finishes.
|
|
@@ -141,24 +167,28 @@ module Mongo
|
|
|
141
167
|
limit = opts.delete(:limit) || 0
|
|
142
168
|
sort = opts.delete(:sort)
|
|
143
169
|
hint = opts.delete(:hint)
|
|
144
|
-
snapshot
|
|
170
|
+
snapshot = opts.delete(:snapshot)
|
|
145
171
|
batch_size = opts.delete(:batch_size)
|
|
146
|
-
|
|
147
|
-
|
|
172
|
+
timeout = (opts.delete(:timeout) == false) ? false : true
|
|
173
|
+
|
|
174
|
+
if timeout == false && !block_given?
|
|
175
|
+
raise ArgumentError, "Collection#find must be invoked with a block when timeout is disabled."
|
|
148
176
|
end
|
|
149
|
-
|
|
177
|
+
|
|
150
178
|
if hint
|
|
151
179
|
hint = normalize_hint_fields(hint)
|
|
152
180
|
else
|
|
153
181
|
hint = @hint # assumed to be normalized already
|
|
154
182
|
end
|
|
183
|
+
|
|
155
184
|
raise RuntimeError, "Unknown options [#{opts.inspect}]" unless opts.empty?
|
|
156
185
|
|
|
157
186
|
cursor = Cursor.new(self, :selector => selector, :fields => fields, :skip => skip, :limit => limit,
|
|
158
|
-
|
|
187
|
+
:order => sort, :hint => hint, :snapshot => snapshot, :timeout => timeout, :batch_size => batch_size)
|
|
188
|
+
|
|
159
189
|
if block_given?
|
|
160
190
|
yield cursor
|
|
161
|
-
cursor.close
|
|
191
|
+
cursor.close
|
|
162
192
|
nil
|
|
163
193
|
else
|
|
164
194
|
cursor
|
|
@@ -170,9 +200,9 @@ module Mongo
|
|
|
170
200
|
# @return [OrderedHash, Nil]
|
|
171
201
|
# a single document or nil if no result is found.
|
|
172
202
|
#
|
|
173
|
-
# @param [Hash,
|
|
203
|
+
# @param [Hash, ObjectId, Nil] spec_or_object_id a hash specifying elements
|
|
174
204
|
# which must be present for a document to be included in the result set or an
|
|
175
|
-
# instance of
|
|
205
|
+
# instance of ObjectId to be used as the value for an _id query.
|
|
176
206
|
# If nil, an empty selector, {}, will be used.
|
|
177
207
|
#
|
|
178
208
|
# @option opts [Hash]
|
|
@@ -184,12 +214,12 @@ module Mongo
|
|
|
184
214
|
spec = case spec_or_object_id
|
|
185
215
|
when nil
|
|
186
216
|
{}
|
|
187
|
-
when BSON::
|
|
217
|
+
when BSON::ObjectId
|
|
188
218
|
{:_id => spec_or_object_id}
|
|
189
219
|
when Hash
|
|
190
220
|
spec_or_object_id
|
|
191
221
|
else
|
|
192
|
-
raise TypeError, "spec_or_object_id must be an instance of
|
|
222
|
+
raise TypeError, "spec_or_object_id must be an instance of ObjectId or Hash, or nil"
|
|
193
223
|
end
|
|
194
224
|
find(spec, opts.merge(:limit => -1)).next_document
|
|
195
225
|
end
|
|
@@ -201,19 +231,24 @@ module Mongo
|
|
|
201
231
|
# then an update (upsert) operation will be performed, and any existing
|
|
202
232
|
# document with that _id is overwritten. Otherwise an insert operation is performed.
|
|
203
233
|
#
|
|
204
|
-
# @return [
|
|
234
|
+
# @return [ObjectId] the _id of the saved document.
|
|
205
235
|
#
|
|
206
|
-
# @option opts [Boolean] :safe (+false+)
|
|
207
|
-
#
|
|
208
|
-
#
|
|
209
|
-
#
|
|
210
|
-
|
|
236
|
+
# @option opts [Boolean, Hash] :safe (+false+)
|
|
237
|
+
# run the operation in safe mode, which run a getlasterror command on the
|
|
238
|
+
# database to report any assertion. In addition, a hash can be provided to
|
|
239
|
+
# run an fsync and/or wait for replication of the save (>= 1.5.1). See the options
|
|
240
|
+
# for DB#error.
|
|
241
|
+
#
|
|
242
|
+
# @raise [OperationFailure] when :safe mode fails.
|
|
243
|
+
#
|
|
244
|
+
# @see DB#remove for options that can be passed to :safe.
|
|
245
|
+
def save(doc, opts={})
|
|
211
246
|
if doc.has_key?(:_id) || doc.has_key?('_id')
|
|
212
247
|
id = doc[:_id] || doc['_id']
|
|
213
|
-
update({:_id => id}, doc, :upsert => true, :safe =>
|
|
248
|
+
update({:_id => id}, doc, :upsert => true, :safe => opts.fetch(:safe, @safe))
|
|
214
249
|
id
|
|
215
250
|
else
|
|
216
|
-
insert(doc, :safe =>
|
|
251
|
+
insert(doc, :safe => opts.fetch(:safe, @safe))
|
|
217
252
|
end
|
|
218
253
|
end
|
|
219
254
|
|
|
@@ -222,20 +257,25 @@ module Mongo
|
|
|
222
257
|
# @param [Hash, Array] doc_or_docs
|
|
223
258
|
# a document (as a hash) or array of documents to be inserted.
|
|
224
259
|
#
|
|
225
|
-
# @return [
|
|
226
|
-
#
|
|
227
|
-
# Note: the object may have been modified by the database's PK factory, if it has one.
|
|
260
|
+
# @return [ObjectId, Array]
|
|
261
|
+
# The _id of the inserted document or a list of _ids of all inserted documents.
|
|
228
262
|
#
|
|
229
|
-
# @option opts [Boolean] :safe (+false+)
|
|
230
|
-
#
|
|
231
|
-
#
|
|
232
|
-
#
|
|
263
|
+
# @option opts [Boolean, Hash] :safe (+false+)
|
|
264
|
+
# run the operation in safe mode, which run a getlasterror command on the
|
|
265
|
+
# database to report any assertion. In addition, a hash can be provided to
|
|
266
|
+
# run an fsync and/or wait for replication of the insert (>= 1.5.1). Safe
|
|
267
|
+
# options provided here will override any safe options set on this collection,
|
|
268
|
+
# its database object, or the current connection. See the options on
|
|
269
|
+
# for DB#get_last_error.
|
|
270
|
+
#
|
|
271
|
+
# @see DB#remove for options that can be passed to :safe.
|
|
233
272
|
#
|
|
234
273
|
# @core insert insert-instance_method
|
|
235
274
|
def insert(doc_or_docs, options={})
|
|
236
275
|
doc_or_docs = [doc_or_docs] unless doc_or_docs.is_a?(Array)
|
|
237
276
|
doc_or_docs.collect! { |doc| @pk_factory.create_pk(doc) }
|
|
238
|
-
|
|
277
|
+
safe = options.has_key?(:safe) ? options[:safe] : @safe
|
|
278
|
+
result = insert_documents(doc_or_docs, @name, true, safe)
|
|
239
279
|
result.size > 1 ? result : result.first
|
|
240
280
|
end
|
|
241
281
|
alias_method :<<, :insert
|
|
@@ -245,8 +285,12 @@ module Mongo
|
|
|
245
285
|
# @param [Hash] selector
|
|
246
286
|
# If specified, only matching documents will be removed.
|
|
247
287
|
#
|
|
248
|
-
# @option opts [Boolean] :safe
|
|
249
|
-
#
|
|
288
|
+
# @option opts [Boolean, Hash] :safe (+false+)
|
|
289
|
+
# run the operation in safe mode, which will run a getlasterror command on the
|
|
290
|
+
# database to report any assertion. In addition, a hash can be provided to
|
|
291
|
+
# run an fsync and/or wait for replication of the remove (>= 1.5.1). Safe
|
|
292
|
+
# options provided here will override any safe options set on this collection,
|
|
293
|
+
# its database, or the current connection. See the options for DB#get_last_error for more details.
|
|
250
294
|
#
|
|
251
295
|
# @example remove all documents from the 'users' collection:
|
|
252
296
|
# users.remove
|
|
@@ -255,32 +299,33 @@ module Mongo
|
|
|
255
299
|
# @example remove only documents that have expired:
|
|
256
300
|
# users.remove({:expire => {"$lte" => Time.now}})
|
|
257
301
|
#
|
|
258
|
-
# @return [
|
|
302
|
+
# @return [Hash, true] Returns a Hash containing the last error object if running in safe mode.
|
|
303
|
+
# Otherwise, returns true.
|
|
259
304
|
#
|
|
260
305
|
# @raise [Mongo::OperationFailure] an exception will be raised iff safe mode is enabled
|
|
261
306
|
# and the operation fails.
|
|
262
307
|
#
|
|
308
|
+
# @see DB#remove for options that can be passed to :safe.
|
|
309
|
+
#
|
|
263
310
|
# @core remove remove-instance_method
|
|
264
311
|
def remove(selector={}, opts={})
|
|
265
312
|
# Initial byte is 0.
|
|
266
|
-
|
|
313
|
+
safe = opts.has_key?(:safe) ? opts[:safe] : @safe
|
|
314
|
+
message = BSON::ByteBuffer.new("\0\0\0\0")
|
|
267
315
|
BSON::BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@name}")
|
|
268
316
|
message.put_int(0)
|
|
269
|
-
message.
|
|
317
|
+
message.put_binary(BSON::BSON_CODER.serialize(selector, false, true).to_s)
|
|
270
318
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
# the return value of send_message_with_safe_check isn't actually meaningful --
|
|
275
|
-
# only the fact that it didn't raise an error is -- so just return true
|
|
276
|
-
true
|
|
319
|
+
@logger.debug("MONGODB #{@db.name}['#{@name}'].remove(#{selector.inspect})") if @logger
|
|
320
|
+
if safe
|
|
321
|
+
@connection.send_message_with_safe_check(Mongo::Constants::OP_DELETE, message, @db.name, nil, safe)
|
|
277
322
|
else
|
|
278
|
-
@connection.send_message(Mongo::Constants::OP_DELETE, message
|
|
279
|
-
|
|
323
|
+
@connection.send_message(Mongo::Constants::OP_DELETE, message)
|
|
324
|
+
true
|
|
280
325
|
end
|
|
281
326
|
end
|
|
282
327
|
|
|
283
|
-
# Update
|
|
328
|
+
# Update one or more documents in this collection.
|
|
284
329
|
#
|
|
285
330
|
# @param [Hash] selector
|
|
286
331
|
# a hash specifying elements which must be present for a document to be updated. Note:
|
|
@@ -297,25 +342,30 @@ module Mongo
|
|
|
297
342
|
# @option opts [Boolean] :safe (+false+)
|
|
298
343
|
# If true, check that the save succeeded. OperationFailure
|
|
299
344
|
# will be raised on an error. Note that a safe check requires an extra
|
|
300
|
-
# round-trip to the database.
|
|
345
|
+
# round-trip to the database. Safe options provided here will override any safe
|
|
346
|
+
# options set on this collection, its database object, or the current collection.
|
|
347
|
+
# See the options for DB#get_last_error for details.
|
|
348
|
+
#
|
|
349
|
+
# @return [Hash, true] Returns a Hash containing the last error object if running in safe mode.
|
|
350
|
+
# Otherwise, returns true.
|
|
301
351
|
#
|
|
302
352
|
# @core update update-instance_method
|
|
303
353
|
def update(selector, document, options={})
|
|
304
354
|
# Initial byte is 0.
|
|
305
|
-
|
|
355
|
+
safe = options.has_key?(:safe) ? options[:safe] : @safe
|
|
356
|
+
message = BSON::ByteBuffer.new("\0\0\0\0")
|
|
306
357
|
BSON::BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@name}")
|
|
307
358
|
update_options = 0
|
|
308
359
|
update_options += 1 if options[:upsert]
|
|
309
360
|
update_options += 2 if options[:multi]
|
|
310
361
|
message.put_int(update_options)
|
|
311
|
-
message.
|
|
312
|
-
message.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
362
|
+
message.put_binary(BSON::BSON_CODER.serialize(selector, false, true).to_s)
|
|
363
|
+
message.put_binary(BSON::BSON_CODER.serialize(document, false, true).to_s)
|
|
364
|
+
@logger.debug("MONGODB #{@db.name}['#{@name}'].update(#{selector.inspect}, #{document.inspect})") if @logger
|
|
365
|
+
if safe
|
|
366
|
+
@connection.send_message_with_safe_check(Mongo::Constants::OP_UPDATE, message, @db.name, nil, safe)
|
|
316
367
|
else
|
|
317
|
-
@connection.send_message(Mongo::Constants::OP_UPDATE, message,
|
|
318
|
-
"#{@db.name}['#{@name}'].update(#{selector.inspect}, #{document.inspect})")
|
|
368
|
+
@connection.send_message(Mongo::Constants::OP_UPDATE, message, nil)
|
|
319
369
|
end
|
|
320
370
|
end
|
|
321
371
|
|
|
@@ -333,13 +383,16 @@ module Mongo
|
|
|
333
383
|
# Also note that it is permissible to create compound indexes that include a geospatial index as
|
|
334
384
|
# long as the geospatial index comes first.
|
|
335
385
|
#
|
|
386
|
+
# If your code calls create_index frequently, you can use Collection#ensure_index to cache these calls
|
|
387
|
+
# and thereby prevent excessive round trips to the database.
|
|
388
|
+
#
|
|
336
389
|
# @option opts [Boolean] :unique (false) if true, this index will enforce a uniqueness constraint.
|
|
337
390
|
# @option opts [Boolean] :background (false) indicate that the index should be built in the background. This
|
|
338
391
|
# feature is only available in MongoDB >= 1.3.2.
|
|
339
|
-
# @option opts [Boolean] :
|
|
392
|
+
# @option opts [Boolean] :drop_dups (nil) If creating a unique index on a collection with pre-existing records,
|
|
340
393
|
# this option will keep the first document the database indexes and drop all subsequent with duplicate values.
|
|
341
|
-
# @option opts [Integer] :min specify the minimum longitude and latitude for a geo index.
|
|
342
|
-
# @option opts [Integer] :max specify the maximum longitude and latitude for a geo index.
|
|
394
|
+
# @option opts [Integer] :min (nil) specify the minimum longitude and latitude for a geo index.
|
|
395
|
+
# @option opts [Integer] :max (nil) specify the maximum longitude and latitude for a geo index.
|
|
343
396
|
#
|
|
344
397
|
# @example Creating a compound index:
|
|
345
398
|
# @posts.create_index([['subject', Mongo::ASCENDING], ['created_at', Mongo::DESCENDING]])
|
|
@@ -359,37 +412,46 @@ module Mongo
|
|
|
359
412
|
#
|
|
360
413
|
# @core indexes create_index-instance_method
|
|
361
414
|
def create_index(spec, opts={})
|
|
362
|
-
opts
|
|
363
|
-
field_spec =
|
|
364
|
-
|
|
365
|
-
field_spec[spec.to_s] = 1
|
|
366
|
-
elsif spec.is_a?(Array) && spec.all? {|field| field.is_a?(Array) }
|
|
367
|
-
spec.each do |f|
|
|
368
|
-
if [Mongo::ASCENDING, Mongo::DESCENDING, Mongo::GEO2D].include?(f[1])
|
|
369
|
-
field_spec[f[0].to_s] = f[1]
|
|
370
|
-
else
|
|
371
|
-
raise MongoArgumentError, "Invalid index field #{f[1].inspect}; " +
|
|
372
|
-
"should be one of Mongo::ASCENDING (1), Mongo::DESCENDING (-1) or Mongo::GEO2D ('2d')."
|
|
373
|
-
end
|
|
374
|
-
end
|
|
375
|
-
else
|
|
376
|
-
raise MongoArgumentError, "Invalid index specification #{spec.inspect}; " +
|
|
377
|
-
"should be either a string, symbol, or an array of arrays."
|
|
378
|
-
end
|
|
415
|
+
opts[:dropDups] = opts.delete(:drop_dups) if opts[:drop_dups]
|
|
416
|
+
field_spec = parse_index_spec(spec)
|
|
417
|
+
name = opts.delete(:name) || generate_index_name(field_spec)
|
|
379
418
|
|
|
380
|
-
name
|
|
419
|
+
generate_indexes(field_spec, name, opts)
|
|
420
|
+
name
|
|
421
|
+
end
|
|
381
422
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
423
|
+
# Calls create_index and sets a flag to not do so again for another X minutes.
|
|
424
|
+
# this time can be specified as an option when initializing a Mongo::DB object as options[:cache_time]
|
|
425
|
+
# Any changes to an index will be propogated through regardless of cache time (e.g., a change of index direction)
|
|
426
|
+
#
|
|
427
|
+
# The parameters and options for this methods are the same as those for Collection#create_index.
|
|
428
|
+
#
|
|
429
|
+
# @example Call sequence:
|
|
430
|
+
# Time t: @posts.ensure_index([['subject', Mongo::ASCENDING]) -- calls create_index and
|
|
431
|
+
# sets the 5 minute cache
|
|
432
|
+
# Time t+2min : @posts.ensure_index([['subject', Mongo::ASCENDING]) -- doesn't do anything
|
|
433
|
+
# Time t+3min : @posts.ensure_index([['something_else', Mongo::ASCENDING]) -- calls create_index
|
|
434
|
+
# and sets 5 minute cache
|
|
435
|
+
# Time t+10min : @posts.ensure_index([['subject', Mongo::ASCENDING]) -- calls create_index and
|
|
436
|
+
# resets the 5 minute counter
|
|
437
|
+
#
|
|
438
|
+
# @return [String] the name of the index.
|
|
439
|
+
def ensure_index(spec, opts={})
|
|
440
|
+
valid = BSON::OrderedHash.new
|
|
441
|
+
now = Time.now.utc.to_i
|
|
442
|
+
field_spec = parse_index_spec(spec)
|
|
443
|
+
|
|
444
|
+
field_spec.each do |key, value|
|
|
445
|
+
cache_key = generate_index_name({key => value})
|
|
446
|
+
timeout = @cache[cache_key] || 0
|
|
447
|
+
valid[key] = value if timeout <= now
|
|
392
448
|
end
|
|
449
|
+
|
|
450
|
+
name = opts.delete(:name) || generate_index_name(valid)
|
|
451
|
+
generate_indexes(valid, name, opts) if valid.any?
|
|
452
|
+
|
|
453
|
+
# Reset the cache here in case there are any errors inserting. Best to be safe.
|
|
454
|
+
@cache[name] = now + @cache_time
|
|
393
455
|
name
|
|
394
456
|
end
|
|
395
457
|
|
|
@@ -399,6 +461,7 @@ module Mongo
|
|
|
399
461
|
#
|
|
400
462
|
# @core indexes
|
|
401
463
|
def drop_index(name)
|
|
464
|
+
@cache[name] = nil
|
|
402
465
|
@db.drop_index(@name, name)
|
|
403
466
|
end
|
|
404
467
|
|
|
@@ -406,6 +469,7 @@ module Mongo
|
|
|
406
469
|
#
|
|
407
470
|
# @core indexes
|
|
408
471
|
def drop_indexes
|
|
472
|
+
@cache = {}
|
|
409
473
|
|
|
410
474
|
# Note: calling drop_indexes with no args will drop them all.
|
|
411
475
|
@db.drop_index(@name, '*')
|
|
@@ -416,11 +480,10 @@ module Mongo
|
|
|
416
480
|
@db.drop_collection(@name)
|
|
417
481
|
end
|
|
418
482
|
|
|
419
|
-
|
|
420
483
|
# Atomically update and return a document using MongoDB's findAndModify command. (MongoDB > 1.3.0)
|
|
421
484
|
#
|
|
422
|
-
# @option opts [Hash] :update (nil) the update operation to perform on the matched document.
|
|
423
485
|
# @option opts [Hash] :query ({}) a query selector document for matching the desired document.
|
|
486
|
+
# @option opts [Hash] :update (nil) the update operation to perform on the matched document.
|
|
424
487
|
# @option opts [Array, String, OrderedHash] :sort ({}) specify a sort option for the query using any
|
|
425
488
|
# of the sort options available for Cursor#sort. Sort order is important if the query will be matching
|
|
426
489
|
# multiple documents since only the first matching document will be updated and returned.
|
|
@@ -432,12 +495,12 @@ module Mongo
|
|
|
432
495
|
#
|
|
433
496
|
# @core findandmodify find_and_modify-instance_method
|
|
434
497
|
def find_and_modify(opts={})
|
|
435
|
-
cmd = OrderedHash.new
|
|
498
|
+
cmd = BSON::OrderedHash.new
|
|
436
499
|
cmd[:findandmodify] = @name
|
|
437
500
|
cmd.merge!(opts)
|
|
438
501
|
cmd[:sort] = Mongo::Support.format_order_clause(opts[:sort]) if opts[:sort]
|
|
439
502
|
|
|
440
|
-
@db.command(cmd
|
|
503
|
+
@db.command(cmd)['value']
|
|
441
504
|
end
|
|
442
505
|
|
|
443
506
|
# Perform a map/reduce operation on the current collection.
|
|
@@ -455,6 +518,8 @@ module Mongo
|
|
|
455
518
|
# @option opts [String] :out (nil) the name of the output collection. If specified, the collection will not be treated as temporary.
|
|
456
519
|
# @option opts [Boolean] :keeptemp (false) if true, the generated collection will be persisted. default is false.
|
|
457
520
|
# @option opts [Boolean ] :verbose (false) if true, provides statistics on job execution time.
|
|
521
|
+
# @option opts [Boolean] :raw (false) if true, return the raw result object from the map_reduce command, and not
|
|
522
|
+
# the instantiated collection that's returned by default.
|
|
458
523
|
#
|
|
459
524
|
# @return [Collection] a collection containing the results of the operation.
|
|
460
525
|
#
|
|
@@ -464,18 +529,24 @@ module Mongo
|
|
|
464
529
|
def map_reduce(map, reduce, opts={})
|
|
465
530
|
map = BSON::Code.new(map) unless map.is_a?(BSON::Code)
|
|
466
531
|
reduce = BSON::Code.new(reduce) unless reduce.is_a?(BSON::Code)
|
|
532
|
+
raw = opts.delete(:raw)
|
|
467
533
|
|
|
468
|
-
hash = OrderedHash.new
|
|
534
|
+
hash = BSON::OrderedHash.new
|
|
469
535
|
hash['mapreduce'] = self.name
|
|
470
536
|
hash['map'] = map
|
|
471
537
|
hash['reduce'] = reduce
|
|
472
538
|
hash.merge! opts
|
|
473
539
|
|
|
474
540
|
result = @db.command(hash)
|
|
475
|
-
unless result
|
|
541
|
+
unless Mongo::Support.ok?(result)
|
|
476
542
|
raise Mongo::OperationFailure, "map-reduce failed: #{result['errmsg']}"
|
|
477
543
|
end
|
|
478
|
-
|
|
544
|
+
|
|
545
|
+
if raw
|
|
546
|
+
result
|
|
547
|
+
else
|
|
548
|
+
@db[result["result"]]
|
|
549
|
+
end
|
|
479
550
|
end
|
|
480
551
|
alias :mapreduce :map_reduce
|
|
481
552
|
|
|
@@ -521,9 +592,9 @@ module Mongo
|
|
|
521
592
|
group_command['group']['finalize'] = finalize
|
|
522
593
|
end
|
|
523
594
|
|
|
524
|
-
result = @db.command
|
|
595
|
+
result = @db.command(group_command)
|
|
525
596
|
|
|
526
|
-
if result
|
|
597
|
+
if Mongo::Support.ok?(result)
|
|
527
598
|
result["retval"]
|
|
528
599
|
else
|
|
529
600
|
raise OperationFailure, "group command failed: #{result['errmsg']}"
|
|
@@ -557,7 +628,7 @@ module Mongo
|
|
|
557
628
|
# @return [Array] an array of distinct values.
|
|
558
629
|
def distinct(key, query=nil)
|
|
559
630
|
raise MongoArgumentError unless [String, Symbol].include?(key.class)
|
|
560
|
-
command = OrderedHash.new
|
|
631
|
+
command = BSON::OrderedHash.new
|
|
561
632
|
command[:distinct] = @name
|
|
562
633
|
command[:key] = key.to_s
|
|
563
634
|
command[:query] = query
|
|
@@ -568,10 +639,12 @@ module Mongo
|
|
|
568
639
|
# Rename this collection.
|
|
569
640
|
#
|
|
570
641
|
# Note: If operating in auth mode, the client must be authorized as an admin to
|
|
571
|
-
# perform this operation.
|
|
642
|
+
# perform this operation.
|
|
572
643
|
#
|
|
573
644
|
# @param [String] new_name the new name for this collection
|
|
574
645
|
#
|
|
646
|
+
# @return [String] the name of the new collection.
|
|
647
|
+
#
|
|
575
648
|
# @raise [Mongo::InvalidNSName] if +new_name+ is an invalid collection name.
|
|
576
649
|
def rename(new_name)
|
|
577
650
|
case new_name
|
|
@@ -593,6 +666,7 @@ module Mongo
|
|
|
593
666
|
end
|
|
594
667
|
|
|
595
668
|
@db.rename_collection(@name, new_name)
|
|
669
|
+
@name = new_name
|
|
596
670
|
end
|
|
597
671
|
|
|
598
672
|
# Get information on the indexes for this collection.
|
|
@@ -639,7 +713,7 @@ module Mongo
|
|
|
639
713
|
when nil
|
|
640
714
|
nil
|
|
641
715
|
else
|
|
642
|
-
h = OrderedHash.new
|
|
716
|
+
h = BSON::OrderedHash.new
|
|
643
717
|
hint.to_a.each { |k| h[k] = 1 }
|
|
644
718
|
h
|
|
645
719
|
end
|
|
@@ -647,20 +721,66 @@ module Mongo
|
|
|
647
721
|
|
|
648
722
|
private
|
|
649
723
|
|
|
724
|
+
def parse_index_spec(spec)
|
|
725
|
+
field_spec = BSON::OrderedHash.new
|
|
726
|
+
if spec.is_a?(String) || spec.is_a?(Symbol)
|
|
727
|
+
field_spec[spec.to_s] = 1
|
|
728
|
+
elsif spec.is_a?(Array) && spec.all? {|field| field.is_a?(Array) }
|
|
729
|
+
spec.each do |f|
|
|
730
|
+
if [Mongo::ASCENDING, Mongo::DESCENDING, Mongo::GEO2D].include?(f[1])
|
|
731
|
+
field_spec[f[0].to_s] = f[1]
|
|
732
|
+
else
|
|
733
|
+
raise MongoArgumentError, "Invalid index field #{f[1].inspect}; " +
|
|
734
|
+
"should be one of Mongo::ASCENDING (1), Mongo::DESCENDING (-1) or Mongo::GEO2D ('2d')."
|
|
735
|
+
end
|
|
736
|
+
end
|
|
737
|
+
else
|
|
738
|
+
raise MongoArgumentError, "Invalid index specification #{spec.inspect}; " +
|
|
739
|
+
"should be either a string, symbol, or an array of arrays."
|
|
740
|
+
end
|
|
741
|
+
field_spec
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
def generate_indexes(field_spec, name, opts)
|
|
745
|
+
selector = {
|
|
746
|
+
:name => name,
|
|
747
|
+
:ns => "#{@db.name}.#{@name}",
|
|
748
|
+
:key => field_spec
|
|
749
|
+
}
|
|
750
|
+
selector.merge!(opts)
|
|
751
|
+
|
|
752
|
+
begin
|
|
753
|
+
insert_documents([selector], Mongo::DB::SYSTEM_INDEX_COLLECTION, false, true)
|
|
754
|
+
|
|
755
|
+
rescue Mongo::OperationFailure => e
|
|
756
|
+
if selector[:dropDups] && e.message =~ /^11000/
|
|
757
|
+
# NOP. If the user is intentionally dropping dups, we can ignore duplicate key errors.
|
|
758
|
+
else
|
|
759
|
+
raise Mongo::OperationFailure, "Failed to create index #{selector.inspect} with the following error: " +
|
|
760
|
+
"#{e.message}"
|
|
761
|
+
end
|
|
762
|
+
end
|
|
763
|
+
|
|
764
|
+
nil
|
|
765
|
+
end
|
|
766
|
+
|
|
650
767
|
# Sends a Mongo::Constants::OP_INSERT message to the database.
|
|
651
768
|
# Takes an array of +documents+, an optional +collection_name+, and a
|
|
652
769
|
# +check_keys+ setting.
|
|
653
770
|
def insert_documents(documents, collection_name=@name, check_keys=true, safe=false)
|
|
654
771
|
# Initial byte is 0.
|
|
655
|
-
message = BSON::ByteBuffer.new(
|
|
772
|
+
message = BSON::ByteBuffer.new("\0\0\0\0")
|
|
656
773
|
BSON::BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{collection_name}")
|
|
657
|
-
documents.each
|
|
774
|
+
documents.each do |doc|
|
|
775
|
+
message.put_binary(BSON::BSON_CODER.serialize(doc, check_keys, true).to_s)
|
|
776
|
+
end
|
|
777
|
+
raise InvalidOperation, "Exceded maximum insert size of 16,000,000 bytes" if message.size > 16_000_000
|
|
778
|
+
|
|
779
|
+
@logger.debug("MONGODB #{@db.name}['#{collection_name}'].insert(#{documents.inspect})") if @logger
|
|
658
780
|
if safe
|
|
659
|
-
@connection.send_message_with_safe_check(Mongo::Constants::OP_INSERT, message, @db.name,
|
|
660
|
-
"#{@db.name}['#{collection_name}'].insert(#{documents.inspect})")
|
|
781
|
+
@connection.send_message_with_safe_check(Mongo::Constants::OP_INSERT, message, @db.name, nil, safe)
|
|
661
782
|
else
|
|
662
|
-
@connection.send_message(Mongo::Constants::OP_INSERT, message,
|
|
663
|
-
"#{@db.name}['#{collection_name}'].insert(#{documents.inspect})")
|
|
783
|
+
@connection.send_message(Mongo::Constants::OP_INSERT, message, nil)
|
|
664
784
|
end
|
|
665
785
|
documents.collect { |o| o[:_id] || o['_id'] }
|
|
666
786
|
end
|