mongo 0.19.1 → 0.19.2

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.
@@ -0,0 +1,36 @@
1
+ # --
2
+ # Copyright (C) 2008-2010 10gen Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
+
17
+ module BSON
18
+
19
+ # JavaScript code to be evaluated by MongoDB.
20
+ class Code < String
21
+
22
+ # Hash mapping identifiers to their values
23
+ attr_accessor :scope
24
+
25
+ # Wrap code to be evaluated by MongoDB.
26
+ #
27
+ # @param [String] code the JavaScript code.
28
+ # @param [Hash] a document mapping identifiers to values, which
29
+ # represent the scope in which the code is to be executed.
30
+ def initialize(code, scope={})
31
+ super(code)
32
+ @scope = scope
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,40 @@
1
+ # --
2
+ # Copyright (C) 2008-2010 10gen Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
+
17
+ module BSON
18
+
19
+ # A reference to another object in a MongoDB database.
20
+ class DBRef
21
+
22
+ attr_reader :namespace, :object_id
23
+
24
+ # Create a DBRef. Use this class in conjunction with DB#dereference.
25
+ #
26
+ # @param [String] a collection name
27
+ # @param [ObjectID] an object id
28
+ #
29
+ # @core dbrefs constructor_details
30
+ def initialize(namespace, object_id)
31
+ @namespace = namespace
32
+ @object_id = object_id
33
+ end
34
+
35
+ def to_s
36
+ "ns: #{namespace}, id: #{object_id}"
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,58 @@
1
+ # --
2
+ # Copyright (C) 2008-2010 10gen Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
+
17
+ module BSON
18
+
19
+ # A class representing the BSON MaxKey type. MaxKey will always compare greater than
20
+ # all other BSON types and values.
21
+ #
22
+ # @example Sorting (assume @numbers is a collection):
23
+ #
24
+ # >> @numbers.save({"n" => Mongo::MaxKey.new})
25
+ # >> @numbers.save({"n" => 0})
26
+ # >> @numbers.save({"n" => 5_000_000})
27
+ # >> @numbers.find.sort("n").to_a
28
+ # => [{"_id"=>4b5a050c238d3bace2000004, "n"=>0},
29
+ # {"_id"=>4b5a04e6238d3bace2000002, "n"=>5_000_000},
30
+ # {"_id"=>4b5a04ea238d3bace2000003, "n"=>#<Mongo::MaxKey:0x1014ef410>},
31
+ # ]
32
+ class MaxKey
33
+
34
+ def ==(obj)
35
+ obj.class == MaxKey
36
+ end
37
+ end
38
+
39
+ # A class representing the BSON MinKey type. MinKey will always compare less than
40
+ # all other BSON types and values.
41
+ #
42
+ # @example Sorting (assume @numbers is a collection):
43
+ #
44
+ # >> @numbers.save({"n" => Mongo::MinKey.new})
45
+ # >> @numbers.save({"n" => -1_000_000})
46
+ # >> @numbers.save({"n" => 1_000_000})
47
+ # >> @numbers.find.sort("n").to_a
48
+ # => [{"_id"=>4b5a050c238d3bace2000004, "n"=>#<Mongo::MinKey:0x1014ef410>},
49
+ # {"_id"=>4b5a04e6238d3bace2000002, "n"=>-1_000_000},
50
+ # {"_id"=>4b5a04ea238d3bace2000003, "n"=>1_000_000},
51
+ # ]
52
+ class MinKey
53
+
54
+ def ==(obj)
55
+ obj.class == MinKey
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,180 @@
1
+ # --
2
+ # Copyright (C) 2008-2010 10gen Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
+
17
+ require 'thread'
18
+ require 'socket'
19
+ require 'digest/md5'
20
+
21
+ module BSON
22
+
23
+ # Generates MongoDB object ids.
24
+ #
25
+ # @core objectids
26
+ class ObjectID
27
+ @@lock = Mutex.new
28
+ @@index = 0
29
+
30
+ # Create a new object id. If no parameter is given, an id corresponding
31
+ # to the ObjectID BSON data type will be created. This is a 12-byte value
32
+ # consisting of a 4-byte timestamp, a 3-byte machine id, a 2-byte process id,
33
+ # and a 3-byte counter.
34
+ #
35
+ # @param [Array] data should be an array of bytes. If you want
36
+ # to generate a standard MongoDB object id, leave this argument blank.
37
+ def initialize(data=nil)
38
+ @data = data || generate
39
+ end
40
+
41
+ # Determine if the supplied string is legal. Legal strings will
42
+ # consist of 24 hexadecimal characters.
43
+ #
44
+ # @param [String] str
45
+ #
46
+ # @return [Boolean]
47
+ def self.legal?(str)
48
+ len = 24
49
+ str =~ /([0-9a-f]+)/i
50
+ match = $1
51
+ str && str.length == len && match == str
52
+ end
53
+
54
+ # Create an object id from the given time. This is useful for doing range
55
+ # queries; it works because MongoDB's object ids begin
56
+ # with a timestamp.
57
+ #
58
+ # @param [Time] time a utc time to encode as an object id.
59
+ #
60
+ # @return [Mongo::ObjectID]
61
+ #
62
+ # @example Return all document created before Jan 1, 2010.
63
+ # time = Time.utc(2010, 1, 1)
64
+ # time_id = ObjectID.from_time(time)
65
+ # collection.find({'_id' => {'$lt' => time_id}})
66
+ def self.from_time(time)
67
+ self.new([time.to_i,0,0].pack("NNN").unpack("C12"))
68
+ end
69
+
70
+ # Adds a primary key to the given document if needed.
71
+ #
72
+ # @param [Hash] doc a document requiring an _id.
73
+ #
74
+ # @return [Mongo::ObjectID, Object] returns a newly-created or
75
+ # current _id for the given document.
76
+ def self.create_pk(doc)
77
+ doc.has_key?(:_id) || doc.has_key?('_id') ? doc : doc.merge!(:_id => self.new)
78
+ end
79
+
80
+ # Check equality of this object id with another.
81
+ #
82
+ # @param [Mongo::ObjectID] object_id
83
+ def eql?(object_id)
84
+ @data == object_id.instance_variable_get("@data")
85
+ end
86
+ alias_method :==, :eql?
87
+
88
+ # Get a unique hashcode for this object.
89
+ # This is required since we've defined an #eql? method.
90
+ #
91
+ # @return [Integer]
92
+ def hash
93
+ @data.hash
94
+ end
95
+
96
+ # Get an array representation of the object id.
97
+ #
98
+ # @return [Array]
99
+ def to_a
100
+ @data.dup
101
+ end
102
+
103
+ # Given a string representation of an ObjectID, return a new ObjectID
104
+ # with that value.
105
+ #
106
+ # @param [String] str
107
+ #
108
+ # @return [Mongo::ObjectID]
109
+ def self.from_string(str)
110
+ raise InvalidObjectID, "illegal ObjectID format" unless legal?(str)
111
+ data = []
112
+ 12.times do |i|
113
+ data[i] = str[i * 2, 2].to_i(16)
114
+ end
115
+ self.new(data)
116
+ end
117
+
118
+ # Get a string representation of this object id.
119
+ #
120
+ # @return [String]
121
+ def to_s
122
+ str = ' ' * 24
123
+ 12.times do |i|
124
+ str[i * 2, 2] = '%02x' % @data[i]
125
+ end
126
+ str
127
+ end
128
+
129
+ def inspect
130
+ "ObjectID('#{to_s}')"
131
+ end
132
+
133
+ # Convert to MongoDB extended JSON format. Since JSON includes type information,
134
+ # but lacks an ObjectID type, this JSON format encodes the type using an $id key.
135
+ #
136
+ # @return [String] the object id represented as MongoDB extended JSON.
137
+ def to_json(escaped=false)
138
+ "{\"$oid\": \"#{to_s}\"}"
139
+ end
140
+
141
+ # Return the UTC time at which this ObjectID was generated. This may
142
+ # be used in lieu of a created_at timestamp since this information
143
+ # is always encoded in the object id.
144
+ #
145
+ # @return [Time] the time at which this object was created.
146
+ def generation_time
147
+ Time.at(@data.pack("C4").unpack("N")[0]).utc
148
+ end
149
+
150
+ private
151
+
152
+ # We need to define this method only if CBson isn't loaded.
153
+ unless defined? CBson
154
+ def generate
155
+ oid = ''
156
+
157
+ # 4 bytes current time
158
+ time = Time.new.to_i
159
+ oid += [time].pack("N")
160
+
161
+ # 3 bytes machine
162
+ oid += Digest::MD5.digest(Socket.gethostname)[0, 3]
163
+
164
+ # 2 bytes pid
165
+ oid += [Process.pid % 0xFFFF].pack("n")
166
+
167
+ # 3 bytes inc
168
+ oid += [get_inc].pack("N")[1, 3]
169
+
170
+ oid.unpack("C12")
171
+ end
172
+ end
173
+
174
+ def get_inc
175
+ @@lock.synchronize do
176
+ @@index = (@@index + 1) % 0xFFFFFF
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,45 @@
1
+ # --
2
+ # Copyright (C) 2008-2010 10gen Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ++
16
+
17
+ module BSON
18
+
19
+ # A Regexp that can hold on to extra options and ignore them. Mongo
20
+ # regexes may contain option characters beyond 'i', 'm', and 'x'. (Note
21
+ # that Mongo only uses those three, but that regexes coming from other
22
+ # languages may store different option characters.)
23
+ #
24
+ # Note that you do not have to use this class at all if you wish to
25
+ # store regular expressions in Mongo. The Mongo and Ruby regex option
26
+ # flags are the same. Storing regexes is discouraged, in any case.
27
+ #
28
+ # @deprecated
29
+ class RegexpOfHolding < Regexp
30
+
31
+ attr_accessor :extra_options_str
32
+
33
+ # @deprecated we're no longer supporting this.
34
+ # +str+ and +options+ are the same as Regexp. +extra_options_str+
35
+ # contains all the other flags that were in Mongo but we do not use or
36
+ # understand.
37
+ def initialize(str, options, extra_options_str)
38
+ warn "RegexpOfHolding is deprecated; the modifiers i, m, and x will be stored automatically as BSON." +
39
+ "If you're only storing the options i, m, and x, you can safely ignore this message."
40
+ super(str, options)
41
+ @extra_options_str = extra_options_str
42
+ end
43
+ end
44
+
45
+ end
@@ -1,7 +1,7 @@
1
1
  $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
2
 
3
3
  module Mongo
4
- VERSION = "0.19.1"
4
+ VERSION = "0.19.2"
5
5
  end
6
6
 
7
7
  begin
@@ -21,8 +21,9 @@ begin
21
21
  end
22
22
 
23
23
  module Mongo
24
- ASCENDING = 1
24
+ ASCENDING = 1
25
25
  DESCENDING = -1
26
+ GEO2D = '2d'
26
27
 
27
28
  module Constants
28
29
  OP_REPLY = 1
@@ -48,6 +49,7 @@ require 'mongo/types/regexp_of_holding'
48
49
  require 'mongo/types/min_max_keys'
49
50
 
50
51
  require 'mongo/util/support'
52
+ require 'mongo/util/core_ext'
51
53
  require 'mongo/util/conversions'
52
54
  require 'mongo/util/server_version'
53
55
  require 'mongo/util/bson_ruby'
@@ -265,13 +265,13 @@ module Mongo
265
265
 
266
266
  if opts[:safe]
267
267
  @connection.send_message_with_safe_check(Mongo::Constants::OP_DELETE, message, @db.name,
268
- "db.#{@db.name}.remove(#{selector.inspect})")
268
+ "#{@db.name}['#{@name}'].remove(#{selector.inspect})")
269
269
  # the return value of send_message_with_safe_check isn't actually meaningful --
270
270
  # only the fact that it didn't raise an error is -- so just return true
271
271
  true
272
272
  else
273
273
  @connection.send_message(Mongo::Constants::OP_DELETE, message,
274
- "db.#{@db.name}.remove(#{selector.inspect})")
274
+ "#{@db.name}['#{@name}'].remove(#{selector.inspect})")
275
275
  end
276
276
  end
277
277
 
@@ -307,41 +307,82 @@ module Mongo
307
307
  message.put_array(BSON.serialize(document, false, true).to_a)
308
308
  if options[:safe]
309
309
  @connection.send_message_with_safe_check(Mongo::Constants::OP_UPDATE, message, @db.name,
310
- "db.#{@name}.update(#{selector.inspect}, #{document.inspect})")
310
+ "#{@db.name}['#{@name}'].update(#{selector.inspect}, #{document.inspect})")
311
311
  else
312
312
  @connection.send_message(Mongo::Constants::OP_UPDATE, message,
313
- "db.#{@name}.update(#{selector.inspect}, #{document.inspect})")
313
+ "#{@db.name}['#{@name}'].update(#{selector.inspect}, #{document.inspect})")
314
314
  end
315
315
  end
316
316
 
317
317
  # Create a new index.
318
318
  #
319
- # @param [String, Array] field_or_spec
319
+ # @param [String, Array] spec
320
320
  # should be either a single field name or an array of
321
- # [field name, direction] pairs. Directions should be specified as Mongo::ASCENDING or Mongo::DESCENDING.
321
+ # [field name, direction] pairs. Directions should be specified
322
+ # as Mongo::ASCENDING, Mongo::DESCENDING, or Mongo::GEO2D.
322
323
  #
323
- # @param [Boolean] unique if true, this index will enforce a uniqueness constraint.
324
+ # Note that geospatial indexing only works with versions of MongoDB >= 1.3.3+. Keep in mind, too,
325
+ # that in order to geo-index a given field, that field must reference either an array or a sub-object
326
+ # where the first two values represent x- and y-coordinates. Examples can be seen below.
327
+ #
328
+ # Also note that it is permissible to create compound indexes that include a geospatial index as
329
+ # long as the geospatial index comes first.
330
+ #
331
+ # @param [Boolean] unique if true, this index will enforce a uniqueness constraint. DEPRECATED. Future
332
+ # versions of this driver will specify the uniqueness constraint using a hash param.
333
+ #
334
+ # @option opts [Boolean] :unique (false) if true, this index will enforce a uniqueness constraint.
335
+ # @option opts [Boolean] :background (false) indicate that the index should be built in the background. This
336
+ # feature is only available in MongoDB >= 1.3.2.
337
+ # @option opts [Boolean] :dropDups If creating a unique index on a collection with pre-existing records,
338
+ # this option will keep the first document the database indexes and drop all subsequent with duplicate values.
339
+ # @option opts [Integer] :min specify the minimum longitude and latitude for a geo index.
340
+ # @option opts [Integer] :max specify the maximum longitude and latitude for a geo index.
341
+ #
342
+ # @example Creating a compound index:
343
+ # @posts.create_index([['subject', Mongo::ASCENDING], ['created_at', Mongo::DESCENDING]])
344
+ #
345
+ # @example Creating a geospatial index:
346
+ # @restaurants.create_index([['location', Mongo::GEO2D]])
347
+ #
348
+ # # Note that this will work only if 'location' represents x,y coordinates:
349
+ # {'location': [0, 50]}
350
+ # {'location': {'x' => 0, 'y' => 50}}
351
+ # {'location': {'latitude' => 0, 'longitude' => 50}}
352
+ #
353
+ # @example A geospatial index with alternate longitude and latitude:
354
+ # @restaurants.create_index([['location', Mongo::GEO2D]], :min => 500, :max => 500)
324
355
  #
325
356
  # @return [String] the name of the index created.
326
357
  #
327
358
  # @core indexes create_index-instance_method
328
- def create_index(field_or_spec, unique=false)
329
- field_h = OrderedHash.new
330
- if field_or_spec.is_a?(String) || field_or_spec.is_a?(Symbol)
331
- field_h[field_or_spec.to_s] = 1
359
+ def create_index(spec, opts={})
360
+ opts.assert_valid_keys(:min, :max, :background, :unique, :dropDups) if opts.is_a?(Hash)
361
+ field_spec = OrderedHash.new
362
+ if spec.is_a?(String) || spec.is_a?(Symbol)
363
+ field_spec[spec.to_s] = 1
364
+ elsif spec.is_a?(Array) && spec.all? {|field| field.is_a?(Array) }
365
+ spec.each { |f| field_spec[f[0].to_s] = f[1] }
332
366
  else
333
- field_or_spec.each { |f| field_h[f[0].to_s] = f[1] }
367
+ raise MongoArgumentError, "Invalid index specification #{spec.inspect}; " +
368
+ "should be either a string, symbol, or an array of arrays."
369
+ end
370
+
371
+ name = generate_index_name(field_spec)
372
+ if opts == true || opts == false
373
+ warn "For Collection#create_index, the method for specifying a unique index has changed." +
374
+ "Please pass :unique => true to the method instead."
334
375
  end
335
- name = generate_index_names(field_h)
336
376
  sel = {
337
377
  :name => name,
338
378
  :ns => "#{@db.name}.#{@name}",
339
- :key => field_h,
340
- :unique => unique }
379
+ :key => field_spec,
380
+ :unique => (opts == true ? true : false) }
381
+ sel.merge!(opts) if opts.is_a?(Hash)
341
382
  begin
342
- insert_documents([sel], Mongo::DB::SYSTEM_INDEX_COLLECTION, false, true)
383
+ response = insert_documents([sel], Mongo::DB::SYSTEM_INDEX_COLLECTION, false, true)
343
384
  rescue Mongo::OperationFailure
344
- raise Mongo::OperationFailure, "Failed to create index #{sel.inspect}."
385
+ raise Mongo::OperationFailure, "Failed to create index #{sel.inspect} with the following errors: #{response}"
345
386
  end
346
387
  name
347
388
  end
@@ -593,15 +634,15 @@ module Mongo
593
634
  documents.each { |doc| message.put_array(BSON.serialize(doc, check_keys, true).to_a) }
594
635
  if safe
595
636
  @connection.send_message_with_safe_check(Mongo::Constants::OP_INSERT, message, @db.name,
596
- "db.#{collection_name}.insert(#{documents.inspect})")
637
+ "#{@db.name}['#{collection_name}'].insert(#{documents.inspect})")
597
638
  else
598
639
  @connection.send_message(Mongo::Constants::OP_INSERT, message,
599
- "db.#{collection_name}.insert(#{documents.inspect})")
640
+ "#{@db.name}['#{collection_name}'].insert(#{documents.inspect})")
600
641
  end
601
642
  documents.collect { |o| o[:_id] || o['_id'] }
602
643
  end
603
644
 
604
- def generate_index_names(spec)
645
+ def generate_index_name(spec)
605
646
  indexes = []
606
647
  spec.each_pair do |field, direction|
607
648
  indexes.push("#{field}_#{direction}")