mongo 0.18.1 → 0.18.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/mongo/db.rb CHANGED
@@ -48,7 +48,7 @@ module Mongo
48
48
 
49
49
  # The Mongo::Connection instance connecting to the MongoDB server.
50
50
  attr_reader :connection
51
-
51
+
52
52
  # An array of [host, port] pairs.
53
53
  attr_reader :nodes
54
54
 
@@ -132,7 +132,7 @@ module Mongo
132
132
  names.map {|name| name.sub(@name + '.', '')}
133
133
  end
134
134
 
135
- # Retruns an array of Collection instances, one for each collection in this
135
+ # Retuns an array of Collection instances, one for each collection in this
136
136
  # database.
137
137
  def collections
138
138
  collection_names.map do |collection_name|
@@ -316,7 +316,7 @@ module Mongo
316
316
  def create_index(collection_name, field_or_spec, unique=false)
317
317
  self.collection(collection_name).create_index(field_or_spec, unique)
318
318
  end
319
-
319
+
320
320
  # Return +true+ if +doc+ contains an 'ok' field with the value 1.
321
321
  def ok?(doc)
322
322
  ok = doc['ok']
@@ -334,9 +334,9 @@ module Mongo
334
334
  end
335
335
 
336
336
  cursor = Cursor.new(Collection.new(self, SYSTEM_COMMAND_COLLECTION), :admin => use_admin_db, :limit => -1, :selector => selector, :socket => sock)
337
- cursor.next_object
337
+ cursor.next_document
338
338
  end
339
-
339
+
340
340
  # Sends a command to the database.
341
341
  #
342
342
  # :selector (required) :: An OrderedHash, or a standard Hash with just one
@@ -352,12 +352,12 @@ module Mongo
352
352
  # any selector containing more than one key must be an OrderedHash.
353
353
  def command(selector, admin=false, check_response=false, sock=nil)
354
354
  raise MongoArgumentError, "command must be given a selector" unless selector.is_a?(Hash) && !selector.empty?
355
- if selector.class.eql?(Hash) && selector.keys.length > 1
355
+ if selector.class.eql?(Hash) && selector.keys.length > 1
356
356
  raise MongoArgumentError, "DB#command requires an OrderedHash when hash contains multiple keys"
357
357
  end
358
358
 
359
- result = Cursor.new(system_command_collection, :admin => admin,
360
- :limit => -1, :selector => selector, :socket => sock).next_object
359
+ result = Cursor.new(system_command_collection, :admin => admin,
360
+ :limit => -1, :selector => selector, :socket => sock).next_document
361
361
 
362
362
  if check_response && !ok?(result)
363
363
  raise OperationFailure, "Database command '#{selector.keys.first}' failed."
data/lib/mongo/errors.rb CHANGED
@@ -30,16 +30,22 @@ module Mongo
30
30
  # Raised when given a string is not valid utf-8 (Ruby 1.8 only).
31
31
  class InvalidStringEncoding < MongoRubyError; end
32
32
 
33
+ # Raised when attempting to initialize an invalid ObjectID.
34
+ class InvalidObjectID < MongoRubyError; end
35
+
33
36
  # Raised on failures in connection to the database server.
34
37
  class ConnectionError < MongoRubyError; end
35
38
 
36
39
  # Raised on failures in connection to the database server.
37
40
  class ConnectionTimeoutError < MongoRubyError; end
38
41
 
42
+ # Raised when trying to insert a document that exceeds the 4MB limit.
43
+ class InvalidDocument < MongoDBError; end
44
+
39
45
  # Raised when a database operation fails.
40
46
  class OperationFailure < MongoDBError; end
41
-
42
- # Raised when a database operation fails.
47
+
48
+ # Raised when a connection operation fails.
43
49
  class ConnectionFailure < MongoDBError; end
44
50
 
45
51
  # Raised when a client attempts to perform an invalid operation.
@@ -55,7 +55,6 @@ module GridFS
55
55
  def size; @data.size; end
56
56
  alias_method :length, :size
57
57
 
58
- # Erase all data after current position.
59
58
  def truncate
60
59
  if @data.position < @data.length
61
60
  curr_data = @data
@@ -54,6 +54,9 @@ module GridFS
54
54
  # Default is DEFAULT_CONTENT_TYPE
55
55
  attr_accessor :content_type
56
56
 
57
+ # Size of file in bytes
58
+ attr_reader :length
59
+
57
60
  attr_accessor :metadata
58
61
 
59
62
  attr_reader :files_id
@@ -70,7 +73,7 @@ module GridFS
70
73
  class << self
71
74
 
72
75
  def exist?(db, name, root_collection=DEFAULT_ROOT_COLLECTION)
73
- db.collection("#{root_collection}.files").find({'filename' => name}).next_object != nil
76
+ db.collection("#{root_collection}.files").find({'filename' => name}).next_document != nil
74
77
  end
75
78
 
76
79
  def open(db, name, mode, options={})
@@ -91,12 +94,12 @@ module GridFS
91
94
  }
92
95
  end
93
96
 
94
- # List the contains of all GridFS files stored in the given db and
97
+ # List the contents of all GridFS files stored in the given db and
95
98
  # root collection.
96
99
  #
97
100
  # :db :: the database to use
98
101
  #
99
- # :root_collection :: the root collection to use
102
+ # :root_collection :: the root collection to use. If not specified, will use default root collection.
100
103
  def list(db, root_collection=DEFAULT_ROOT_COLLECTION)
101
104
  db.collection("#{root_collection}.files").find().map { |f|
102
105
  f['filename']
@@ -145,7 +148,7 @@ module GridFS
145
148
  @db, @filename, @mode = db, name, mode
146
149
  @root = options[:root] || DEFAULT_ROOT_COLLECTION
147
150
 
148
- doc = collection.find({'filename' => @filename}).next_object
151
+ doc = collection.find({'filename' => @filename}).next_document
149
152
  if doc
150
153
  @files_id = doc['_id']
151
154
  @content_type = doc['contentType']
@@ -242,7 +245,7 @@ module GridFS
242
245
  str
243
246
  end
244
247
 
245
- def read(len=nil, buf=nil)
248
+ def old_read(len=nil, buf=nil)
246
249
  buf ||= ''
247
250
  byte = self.getc
248
251
  while byte != nil && (len == nil || len > 0)
@@ -253,6 +256,14 @@ module GridFS
253
256
  buf
254
257
  end
255
258
 
259
+ def read(len=nil, buf=nil)
260
+ if len
261
+ read_partial(len, buf)
262
+ else
263
+ read_all(buf)
264
+ end
265
+ end
266
+
256
267
  def readchar
257
268
  byte = self.getc
258
269
  raise EOFError.new if byte == nil
@@ -331,15 +342,21 @@ module GridFS
331
342
  write(obj.to_s)
332
343
  end
333
344
 
334
- # Writes +string+ as bytes and returns the number of bytes written.
335
345
  def write(string)
336
346
  raise "#@filename not opened for write" unless @mode[0] == ?w
337
- count = 0
338
- string.each_byte { |byte|
339
- self.putc byte
340
- count += 1
341
- }
342
- count
347
+ to_write = string.length
348
+ while (to_write > 0) do
349
+ if @curr_chunk && @curr_chunk.data.position == @chunk_size
350
+ prev_chunk_number = @curr_chunk.chunk_number
351
+ @curr_chunk = GridFS::Chunk.new(self, 'n' => prev_chunk_number + 1)
352
+ end
353
+ chunk_available = @chunk_size - @curr_chunk.data.position
354
+ step_size = (to_write > chunk_available) ? chunk_available : to_write
355
+ @curr_chunk.data.put_array(ByteBuffer.new(string[-to_write,step_size]).to_a)
356
+ to_write -= step_size
357
+ @curr_chunk.save
358
+ end
359
+ string.length - to_write
343
360
  end
344
361
 
345
362
  # A no-op.
@@ -396,7 +413,7 @@ module GridFS
396
413
 
397
414
  def tell
398
415
  @position
399
- end
416
+ end
400
417
 
401
418
  #---
402
419
  # ================ closing ================
@@ -446,13 +463,39 @@ module GridFS
446
463
  h
447
464
  end
448
465
 
466
+ def read_partial(len, buf=nil)
467
+ buf ||= ''
468
+ byte = self.getc
469
+ while byte != nil && (len == nil || len > 0)
470
+ buf << byte.chr
471
+ len -= 1 if len
472
+ byte = self.getc if (len == nil || len > 0)
473
+ end
474
+ buf
475
+ end
476
+
477
+ def read_all(buf=nil)
478
+ buf ||= ''
479
+ while true do
480
+ if (@curr_chunk.pos > 0)
481
+ data = @curr_chunk.data.to_s
482
+ buf += data[@position, data.length]
483
+ else
484
+ buf += @curr_chunk.data.to_s
485
+ end
486
+ break if @curr_chunk.chunk_number == last_chunk_number
487
+ @curr_chunk = nth_chunk(@curr_chunk.chunk_number + 1)
488
+ end
489
+ buf
490
+ end
491
+
449
492
  def delete_chunks
450
493
  chunk_collection.remove({'files_id' => @files_id}) if @files_id
451
494
  @curr_chunk = nil
452
495
  end
453
496
 
454
497
  def nth_chunk(n)
455
- mongo_chunk = chunk_collection.find({'files_id' => @files_id, 'n' => n}).next_object
498
+ mongo_chunk = chunk_collection.find({'files_id' => @files_id, 'n' => n}).next_document
456
499
  Chunk.new(self, mongo_chunk || {})
457
500
  end
458
501
 
@@ -18,6 +18,7 @@ module Mongo
18
18
 
19
19
  # JavaScript code to be evaluated by MongoDB
20
20
  class Code < String
21
+
21
22
  # Hash mapping identifiers to their values
22
23
  attr_accessor :scope
23
24
 
@@ -44,7 +44,7 @@ module Mongo
44
44
 
45
45
  # Adds a primary key to the given document if needed.
46
46
  def self.create_pk(doc)
47
- doc[:_id] || doc['_id'] ? doc : doc.merge!(:_id => self.new)
47
+ doc.has_key?(:_id) || doc.has_key?('_id') ? doc : doc.merge!(:_id => self.new)
48
48
  end
49
49
 
50
50
  # +data+ is an array of bytes. If nil, a new id will be generated.
@@ -70,7 +70,7 @@ module Mongo
70
70
  # Given a string representation of an ObjectID, return a new ObjectID
71
71
  # with that value.
72
72
  def self.from_string(str)
73
- raise "illegal ObjectID format" unless legal?(str)
73
+ raise InvalidObjectID, "illegal ObjectID format" unless legal?(str)
74
74
  data = []
75
75
  12.times do |i|
76
76
  data[i] = str[i * 2, 2].to_i(16)
@@ -83,7 +83,7 @@ module Mongo
83
83
  # removed. If you are not sure that you need this method you should be
84
84
  # using the regular from_string.
85
85
  def self.from_string_legacy(str)
86
- raise "illegal ObjectID format" unless legal?(str)
86
+ raise InvalidObjectID, "illegal ObjectID format" unless legal?(str)
87
87
  data = []
88
88
  BYTE_ORDER.each_with_index { |string_position, data_index|
89
89
  data[data_index] = str[string_position * 2, 2].to_i(16)
@@ -132,9 +132,8 @@ module Mongo
132
132
 
133
133
  private
134
134
 
135
- begin
136
- require 'mongo_ext/cbson'
137
- rescue LoadError
135
+ # We need to define this method only if CBson isn't loaded.
136
+ unless defined? CBson
138
137
  def generate
139
138
  oid = ''
140
139
 
@@ -72,6 +72,11 @@ class BSON_RUBY
72
72
  buf.put_array(to_utf8(val.to_s).unpack("C*") << 0)
73
73
  end
74
74
 
75
+ def self.serialize_key(buf, key)
76
+ raise InvalidDocument, "Key names / regex patterns must not contain the NULL byte" if key.include? "\x00"
77
+ self.serialize_cstr(buf, key)
78
+ end
79
+
75
80
  def to_a
76
81
  @buf.to_a
77
82
  end
@@ -107,6 +112,9 @@ class BSON_RUBY
107
112
  obj.each {|k, v| serialize_key_value(k, v, check_keys) unless k == '_id' || k == :_id }
108
113
 
109
114
  serialize_eoo_element(@buf)
115
+ if @buf.size > 4 * 1024 * 1024
116
+ raise InvalidDocument, "Document is too large (#{@buf.size}). BSON documents are limited to 4MB (#{4 * 1024 * 1024})."
117
+ end
110
118
  @buf.put_int(@buf.size, 0)
111
119
  self
112
120
  end
@@ -362,7 +370,7 @@ class BSON_RUBY
362
370
 
363
371
  def serialize_null_element(buf, key)
364
372
  buf.put(NULL)
365
- self.class.serialize_cstr(buf, key)
373
+ self.class.serialize_key(buf, key)
366
374
  end
367
375
 
368
376
  def serialize_dbref_element(buf, key, val)
@@ -374,7 +382,7 @@ class BSON_RUBY
374
382
 
375
383
  def serialize_binary_element(buf, key, val)
376
384
  buf.put(BINARY)
377
- self.class.serialize_cstr(buf, key)
385
+ self.class.serialize_key(buf, key)
378
386
 
379
387
  bytes = val.to_a
380
388
  num_bytes = bytes.length
@@ -393,13 +401,13 @@ class BSON_RUBY
393
401
 
394
402
  def serialize_boolean_element(buf, key, val)
395
403
  buf.put(BOOLEAN)
396
- self.class.serialize_cstr(buf, key)
404
+ self.class.serialize_key(buf, key)
397
405
  buf.put(val ? 1 : 0)
398
406
  end
399
407
 
400
408
  def serialize_date_element(buf, key, val)
401
409
  buf.put(DATE)
402
- self.class.serialize_cstr(buf, key)
410
+ self.class.serialize_key(buf, key)
403
411
  millisecs = (val.to_f * 1000).to_i
404
412
  buf.put_long(millisecs)
405
413
  end
@@ -407,7 +415,7 @@ class BSON_RUBY
407
415
  def serialize_number_element(buf, key, val, type)
408
416
  if type == NUMBER
409
417
  buf.put(type)
410
- self.class.serialize_cstr(buf, key)
418
+ self.class.serialize_key(buf, key)
411
419
  buf.put_double(val)
412
420
  else
413
421
  if val > 2**64 / 2 - 1 or val < -2**64 / 2
@@ -415,11 +423,11 @@ class BSON_RUBY
415
423
  end
416
424
  if val > 2**32 / 2 - 1 or val < -2**32 / 2
417
425
  buf.put(NUMBER_LONG)
418
- self.class.serialize_cstr(buf, key)
426
+ self.class.serialize_key(buf, key)
419
427
  buf.put_long(val)
420
428
  else
421
429
  buf.put(type)
422
- self.class.serialize_cstr(buf, key)
430
+ self.class.serialize_key(buf, key)
423
431
  buf.put_int(val)
424
432
  end
425
433
  end
@@ -427,7 +435,7 @@ class BSON_RUBY
427
435
 
428
436
  def serialize_object_element(buf, key, val, check_keys, opcode=OBJECT)
429
437
  buf.put(opcode)
430
- self.class.serialize_cstr(buf, key)
438
+ self.class.serialize_key(buf, key)
431
439
  buf.put_array(BSON.new.serialize(val, check_keys).to_a)
432
440
  end
433
441
 
@@ -441,10 +449,12 @@ class BSON_RUBY
441
449
 
442
450
  def serialize_regex_element(buf, key, val)
443
451
  buf.put(REGEX)
444
- self.class.serialize_cstr(buf, key)
452
+ self.class.serialize_key(buf, key)
445
453
 
446
- str = val.to_s.sub(/.*?:/, '')[0..-2] # Turn "(?xxx:yyy)" into "yyy"
447
- self.class.serialize_cstr(buf, str)
454
+ str = val.source
455
+ # We use serialize_key here since regex patterns aren't prefixed with
456
+ # length (can't contain the NULL byte).
457
+ self.class.serialize_key(buf, str)
448
458
 
449
459
  options = val.options
450
460
  options_str = ''
@@ -458,14 +468,14 @@ class BSON_RUBY
458
468
 
459
469
  def serialize_oid_element(buf, key, val)
460
470
  buf.put(OID)
461
- self.class.serialize_cstr(buf, key)
471
+ self.class.serialize_key(buf, key)
462
472
 
463
473
  buf.put_array(val.to_a)
464
474
  end
465
475
 
466
476
  def serialize_string_element(buf, key, val, type)
467
477
  buf.put(type)
468
- self.class.serialize_cstr(buf, key)
478
+ self.class.serialize_key(buf, key)
469
479
 
470
480
  # Make a hole for the length
471
481
  len_pos = buf.position
@@ -485,7 +495,7 @@ class BSON_RUBY
485
495
 
486
496
  def serialize_code_w_scope(buf, key, val)
487
497
  buf.put(CODE_W_SCOPE)
488
- self.class.serialize_cstr(buf, key)
498
+ self.class.serialize_key(buf, key)
489
499
 
490
500
  # Make a hole for the length
491
501
  len_pos = buf.position
@@ -544,7 +554,7 @@ class BSON_RUBY
544
554
  when Symbol
545
555
  SYMBOL
546
556
  else
547
- raise "Unknown type of object: #{o.class.name}"
557
+ raise InvalidDocument, "Unknown type of object: #{o.class.name}"
548
558
  end
549
559
  end
550
560
 
@@ -32,11 +32,19 @@ module Mongo #:nodoc:
32
32
  # <tt>{ "field1" => 1, "field2" => -1}</tt>
33
33
  def array_as_sort_parameters(value)
34
34
  order_by = OrderedHash.new
35
- value.each do |param|
36
- if (param.class.name == "String")
37
- order_by[param] = 1
35
+ if value.first.is_a? Array
36
+ value.each do |param|
37
+ if (param.class.name == "String")
38
+ order_by[param] = 1
39
+ else
40
+ order_by[param[0]] = sort_value(param[1]) unless param[1].nil?
41
+ end
42
+ end
43
+ elsif !value.empty?
44
+ if order_by.size == 1
45
+ order_by[value.first] = 1
38
46
  else
39
- order_by[param[0]] = sort_value(param[1]) unless param[1].nil?
47
+ order_by[value.first] = sort_value(value[1])
40
48
  end
41
49
  end
42
50
  order_by
@@ -111,5 +111,23 @@ class OrderedHash < Hash
111
111
  super
112
112
  @ordered_keys = []
113
113
  end
114
+
115
+ def hash
116
+ code = 17
117
+ each_pair do |key, value|
118
+ code = 37 * code + key.hash
119
+ code = 37 * code + value.hash
120
+ end
121
+ code
122
+ end
123
+
124
+ def eql?(o)
125
+ if o.instance_of? OrderedHash
126
+ self.hash == o.hash
127
+ else
128
+ false
129
+ end
130
+ end
131
+
114
132
  end
115
133
  end