mongo-find_replace 0.18.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/LICENSE.txt +202 -0
- data/README.rdoc +358 -0
- data/Rakefile +133 -0
- data/bin/bson_benchmark.rb +59 -0
- data/bin/fail_if_no_c.rb +11 -0
- data/examples/admin.rb +42 -0
- data/examples/capped.rb +22 -0
- data/examples/cursor.rb +48 -0
- data/examples/gridfs.rb +88 -0
- data/examples/index_test.rb +126 -0
- data/examples/info.rb +31 -0
- data/examples/queries.rb +70 -0
- data/examples/simple.rb +24 -0
- data/examples/strict.rb +35 -0
- data/examples/types.rb +36 -0
- data/lib/mongo.rb +61 -0
- data/lib/mongo/admin.rb +95 -0
- data/lib/mongo/collection.rb +664 -0
- data/lib/mongo/connection.rb +555 -0
- data/lib/mongo/cursor.rb +393 -0
- data/lib/mongo/db.rb +527 -0
- data/lib/mongo/exceptions.rb +60 -0
- data/lib/mongo/gridfs.rb +22 -0
- data/lib/mongo/gridfs/chunk.rb +90 -0
- data/lib/mongo/gridfs/grid_store.rb +555 -0
- data/lib/mongo/types/binary.rb +48 -0
- data/lib/mongo/types/code.rb +36 -0
- data/lib/mongo/types/dbref.rb +38 -0
- data/lib/mongo/types/min_max_keys.rb +58 -0
- data/lib/mongo/types/objectid.rb +219 -0
- data/lib/mongo/types/regexp_of_holding.rb +45 -0
- data/lib/mongo/util/bson_c.rb +18 -0
- data/lib/mongo/util/bson_ruby.rb +595 -0
- data/lib/mongo/util/byte_buffer.rb +222 -0
- data/lib/mongo/util/conversions.rb +97 -0
- data/lib/mongo/util/ordered_hash.rb +135 -0
- data/lib/mongo/util/server_version.rb +69 -0
- data/lib/mongo/util/support.rb +26 -0
- data/lib/mongo/util/xml_to_ruby.rb +112 -0
- data/mongo-ruby-driver.gemspec +28 -0
- data/test/replica/count_test.rb +34 -0
- data/test/replica/insert_test.rb +50 -0
- data/test/replica/pooled_insert_test.rb +54 -0
- data/test/replica/query_test.rb +39 -0
- data/test/test_admin.rb +67 -0
- data/test/test_bson.rb +397 -0
- data/test/test_byte_buffer.rb +81 -0
- data/test/test_chunk.rb +82 -0
- data/test/test_collection.rb +534 -0
- data/test/test_connection.rb +160 -0
- data/test/test_conversions.rb +120 -0
- data/test/test_cursor.rb +386 -0
- data/test/test_db.rb +254 -0
- data/test/test_db_api.rb +783 -0
- data/test/test_db_connection.rb +16 -0
- data/test/test_grid_store.rb +306 -0
- data/test/test_helper.rb +42 -0
- data/test/test_objectid.rb +156 -0
- data/test/test_ordered_hash.rb +168 -0
- data/test/test_round_trip.rb +114 -0
- data/test/test_slave_connection.rb +36 -0
- data/test/test_threading.rb +87 -0
- data/test/threading/test_threading_large_pool.rb +90 -0
- data/test/unit/collection_test.rb +52 -0
- data/test/unit/connection_test.rb +59 -0
- data/test/unit/cursor_test.rb +94 -0
- data/test/unit/db_test.rb +97 -0
- metadata +123 -0
data/examples/info.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
|
3
|
+
require 'mongo'
|
4
|
+
|
5
|
+
include Mongo
|
6
|
+
|
7
|
+
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
8
|
+
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT
|
9
|
+
|
10
|
+
puts "Connecting to #{host}:#{port}"
|
11
|
+
db = Connection.new(host, port).db('ruby-mongo-examples')
|
12
|
+
coll = db.collection('test')
|
13
|
+
|
14
|
+
# Erase all records from collection, if any
|
15
|
+
coll.remove
|
16
|
+
|
17
|
+
# Insert 3 records
|
18
|
+
3.times { |i| coll.insert({'a' => i+1}) }
|
19
|
+
|
20
|
+
# Collection names in database
|
21
|
+
p db.collection_names
|
22
|
+
|
23
|
+
# More information about each collection
|
24
|
+
p db.collections_info
|
25
|
+
|
26
|
+
# Index information
|
27
|
+
db.create_index('test', 'a')
|
28
|
+
p db.index_information('test')
|
29
|
+
|
30
|
+
# Destroy the collection
|
31
|
+
coll.drop
|
data/examples/queries.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
|
3
|
+
require 'mongo'
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
include Mongo
|
7
|
+
|
8
|
+
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
9
|
+
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT
|
10
|
+
|
11
|
+
puts "Connecting to #{host}:#{port}"
|
12
|
+
db = Connection.new(host, port).db('ruby-mongo-examples')
|
13
|
+
coll = db.collection('test')
|
14
|
+
|
15
|
+
# Remove all records, if any
|
16
|
+
coll.remove
|
17
|
+
|
18
|
+
# Insert three records
|
19
|
+
coll.insert('a' => 1)
|
20
|
+
coll.insert('a' => 2)
|
21
|
+
coll.insert('b' => 3)
|
22
|
+
|
23
|
+
# Count.
|
24
|
+
puts "There are #{coll.count()} records."
|
25
|
+
|
26
|
+
# Find all records. find() returns a Cursor.
|
27
|
+
cursor = coll.find()
|
28
|
+
|
29
|
+
# Print them. Note that all records have an _id automatically added by the
|
30
|
+
# database. See pk.rb for an example of how to use a primary key factory to
|
31
|
+
# generate your own values for _id.
|
32
|
+
cursor.each { |row| pp row }
|
33
|
+
|
34
|
+
# Cursor has a to_a method that slurps all records into memory.
|
35
|
+
rows = coll.find().to_a
|
36
|
+
rows.each { |row| pp row }
|
37
|
+
|
38
|
+
# See Collection#find. From now on in this file, we won't be printing the
|
39
|
+
# records we find.
|
40
|
+
coll.find('a' => 1)
|
41
|
+
|
42
|
+
# Find records sort by 'a', skip 1, limit 2 records.
|
43
|
+
# Sort can be single name, array, or hash.
|
44
|
+
coll.find({}, {:skip => 1, :limit => 2, :sort => 'a'})
|
45
|
+
|
46
|
+
# Find all records with 'a' > 1. There is also $lt, $gte, and $lte.
|
47
|
+
coll.find({'a' => {'$gt' => 1}})
|
48
|
+
coll.find({'a' => {'$gt' => 1, '$lte' => 3}})
|
49
|
+
|
50
|
+
# Find all records with 'a' in a set of values.
|
51
|
+
coll.find('a' => {'$in' => [1,2]})
|
52
|
+
|
53
|
+
# Find by regexp
|
54
|
+
coll.find('a' => /[1|2]/)
|
55
|
+
|
56
|
+
# Print query explanation
|
57
|
+
pp coll.find('a' => /[1|2]/).explain()
|
58
|
+
|
59
|
+
# Use a hint with a query. Need an index. Hints can be stored with the
|
60
|
+
# collection, in which case they will be used with all queries, or they can be
|
61
|
+
# specified per query, in which case that hint overrides the hint associated
|
62
|
+
# with the collection if any.
|
63
|
+
coll.create_index('a')
|
64
|
+
coll.hint = 'a'
|
65
|
+
|
66
|
+
# You will see a different explanation now that the hint is in place
|
67
|
+
pp coll.find('a' => /[1|2]/).explain()
|
68
|
+
|
69
|
+
# Override hint for single query
|
70
|
+
coll.find({'a' => 1}, :hint => 'b')
|
data/examples/simple.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
|
3
|
+
require 'mongo'
|
4
|
+
|
5
|
+
include Mongo
|
6
|
+
|
7
|
+
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
8
|
+
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT
|
9
|
+
|
10
|
+
puts "Connecting to #{host}:#{port}"
|
11
|
+
db = Connection.new(host, port).db('ruby-mongo-examples')
|
12
|
+
coll = db.collection('test')
|
13
|
+
|
14
|
+
# Erase all records from collection, if any
|
15
|
+
coll.remove
|
16
|
+
|
17
|
+
# Insert 3 records
|
18
|
+
3.times { |i| coll.insert({'a' => i+1}) }
|
19
|
+
|
20
|
+
puts "There are #{coll.count()} records in the test collection. Here they are:"
|
21
|
+
coll.find().each { |doc| puts doc.inspect }
|
22
|
+
|
23
|
+
# Destroy the collection
|
24
|
+
coll.drop
|
data/examples/strict.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
|
3
|
+
require 'mongo'
|
4
|
+
|
5
|
+
include Mongo
|
6
|
+
|
7
|
+
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
8
|
+
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT
|
9
|
+
|
10
|
+
puts "Connecting to #{host}:#{port}"
|
11
|
+
db = Connection.new(host, port).db('ruby-mongo-examples')
|
12
|
+
|
13
|
+
db.drop_collection('does-not-exist')
|
14
|
+
db.create_collection('test')
|
15
|
+
|
16
|
+
db.strict = true
|
17
|
+
|
18
|
+
begin
|
19
|
+
# Can't reference collection that does not exist
|
20
|
+
db.collection('does-not-exist')
|
21
|
+
puts "error: expected exception"
|
22
|
+
rescue => ex
|
23
|
+
puts "expected exception: #{ex}"
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
# Can't create collection that already exists
|
28
|
+
db.create_collection('test')
|
29
|
+
puts "error: expected exception"
|
30
|
+
rescue => ex
|
31
|
+
puts "expected exception: #{ex}"
|
32
|
+
end
|
33
|
+
|
34
|
+
db.strict = false
|
35
|
+
db.drop_collection('test')
|
data/examples/types.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
|
3
|
+
require 'mongo'
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
include Mongo
|
7
|
+
|
8
|
+
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
9
|
+
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT
|
10
|
+
|
11
|
+
puts "Connecting to #{host}:#{port}"
|
12
|
+
db = Connection.new(host, port).db('ruby-mongo-examples')
|
13
|
+
coll = db.collection('test')
|
14
|
+
|
15
|
+
# Remove all records, if any
|
16
|
+
coll.remove
|
17
|
+
|
18
|
+
# Insert record with all sorts of values
|
19
|
+
coll.insert('array' => [1, 2, 3],
|
20
|
+
'string' => 'hello',
|
21
|
+
'hash' => {'a' => 1, 'b' => 2},
|
22
|
+
'date' => Time.now, # milliseconds only; microseconds are not stored
|
23
|
+
'oid' => ObjectID.new,
|
24
|
+
'binary' => Binary.new([1, 2, 3]),
|
25
|
+
'int' => 42,
|
26
|
+
'float' => 33.33333,
|
27
|
+
'regex' => /foobar/i,
|
28
|
+
'boolean' => true,
|
29
|
+
'where' => Code.new('this.x == 3'),
|
30
|
+
'dbref' => DBRef.new(coll.name, ObjectID.new),
|
31
|
+
'null' => nil,
|
32
|
+
'symbol' => :zildjian)
|
33
|
+
|
34
|
+
pp coll.find().next_document
|
35
|
+
|
36
|
+
coll.remove
|
data/lib/mongo.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
|
3
|
+
module Mongo
|
4
|
+
VERSION = "0.18.3"
|
5
|
+
end
|
6
|
+
|
7
|
+
begin
|
8
|
+
# Need this for running test with and without c ext in Ruby 1.9.
|
9
|
+
raise LoadError if ENV['TEST_MODE'] && !ENV['C_EXT']
|
10
|
+
require 'mongo_ext/cbson'
|
11
|
+
raise LoadError unless defined?(CBson::VERSION) && CBson::VERSION == Mongo::VERSION
|
12
|
+
require 'mongo/util/bson_c'
|
13
|
+
BSON = BSON_C
|
14
|
+
rescue LoadError
|
15
|
+
require 'mongo/util/bson_ruby'
|
16
|
+
BSON = BSON_RUBY
|
17
|
+
warn "\n**Notice: C extension not loaded. This is required for optimum MongoDB Ruby driver performance."
|
18
|
+
warn " You can install the extension as follows:\n gem install mongo_ext\n"
|
19
|
+
warn " If you continue to receive this message after installing, make sure that the"
|
20
|
+
warn " mongo_ext gem is in your load path and that the mongo_ext and mongo gems are of the same version.\n"
|
21
|
+
end
|
22
|
+
|
23
|
+
module Mongo
|
24
|
+
ASCENDING = 1
|
25
|
+
DESCENDING = -1
|
26
|
+
|
27
|
+
module Constants
|
28
|
+
OP_REPLY = 1
|
29
|
+
OP_MSG = 1000
|
30
|
+
OP_UPDATE = 2001
|
31
|
+
OP_INSERT = 2002
|
32
|
+
OP_QUERY = 2004
|
33
|
+
OP_GET_MORE = 2005
|
34
|
+
OP_DELETE = 2006
|
35
|
+
OP_KILL_CURSORS = 2007
|
36
|
+
|
37
|
+
OP_QUERY_SLAVE_OK = 4
|
38
|
+
OP_QUERY_NO_CURSOR_TIMEOUT = 16
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
require 'mongo/types/binary'
|
44
|
+
require 'mongo/types/code'
|
45
|
+
require 'mongo/types/dbref'
|
46
|
+
require 'mongo/types/objectid'
|
47
|
+
require 'mongo/types/regexp_of_holding'
|
48
|
+
require 'mongo/types/min_max_keys'
|
49
|
+
|
50
|
+
require 'mongo/util/support'
|
51
|
+
require 'mongo/util/conversions'
|
52
|
+
require 'mongo/util/server_version'
|
53
|
+
require 'mongo/util/bson_ruby'
|
54
|
+
|
55
|
+
require 'mongo/admin'
|
56
|
+
require 'mongo/collection'
|
57
|
+
require 'mongo/connection'
|
58
|
+
require 'mongo/cursor'
|
59
|
+
require 'mongo/db'
|
60
|
+
require 'mongo/exceptions'
|
61
|
+
require 'mongo/gridfs'
|
data/lib/mongo/admin.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# --
|
2
|
+
# Copyright (C) 2008-2009 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 Mongo
|
18
|
+
|
19
|
+
# @deprecated this class is deprecated. Methods defined here will
|
20
|
+
# henceforth be available in Mongo::DB.
|
21
|
+
class Admin
|
22
|
+
|
23
|
+
def initialize(db)
|
24
|
+
warn "The Admin class has been DEPRECATED. All admin methods now exist in DB."
|
25
|
+
@db = db
|
26
|
+
end
|
27
|
+
|
28
|
+
# Return the current database profiling level.
|
29
|
+
#
|
30
|
+
# @return [Symbol] :off, :slow_only, or :all
|
31
|
+
#
|
32
|
+
# @deprecated please use DB#profiling_level instead.
|
33
|
+
def profiling_level
|
34
|
+
warn "Admin#profiling_level has been DEPRECATED. Please use DB#profiling_level instead."
|
35
|
+
oh = OrderedHash.new
|
36
|
+
oh[:profile] = -1
|
37
|
+
doc = @db.command(oh)
|
38
|
+
raise "Error with profile command: #{doc.inspect}" unless @db.ok?(doc) && doc['was'].kind_of?(Numeric)
|
39
|
+
case doc['was'].to_i
|
40
|
+
when 0
|
41
|
+
:off
|
42
|
+
when 1
|
43
|
+
:slow_only
|
44
|
+
when 2
|
45
|
+
:all
|
46
|
+
else
|
47
|
+
raise "Error: illegal profiling level value #{doc['was']}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Set database profiling level to :off, :slow_only, or :all.
|
52
|
+
#
|
53
|
+
# @deprecated please use DB#profiling_level= instead.
|
54
|
+
def profiling_level=(level)
|
55
|
+
warn "Admin#profiling_level= has been DEPRECATED. Please use DB#profiling_level= instead."
|
56
|
+
oh = OrderedHash.new
|
57
|
+
oh[:profile] = case level
|
58
|
+
when :off
|
59
|
+
0
|
60
|
+
when :slow_only
|
61
|
+
1
|
62
|
+
when :all
|
63
|
+
2
|
64
|
+
else
|
65
|
+
raise "Error: illegal profiling level value #{level}"
|
66
|
+
end
|
67
|
+
doc = @db.command(oh)
|
68
|
+
raise "Error with profile command: #{doc.inspect}" unless @db.ok?(doc)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns an array containing current profiling information.
|
72
|
+
#
|
73
|
+
# @deprecated please use DB#profiling_info instead.
|
74
|
+
def profiling_info
|
75
|
+
warn "Admin#profiling_info has been DEPRECATED. Please use DB#profiling_info instead."
|
76
|
+
Cursor.new(Collection.new(@db, DB::SYSTEM_PROFILE_COLLECTION), :selector => {}).to_a
|
77
|
+
end
|
78
|
+
|
79
|
+
# Validate a named collection by raising an exception if there is a
|
80
|
+
# problem or returning an interesting hash (see especially the
|
81
|
+
# 'result' string value) if all is well.
|
82
|
+
#
|
83
|
+
# @deprecated please use DB#validate_collection instead.
|
84
|
+
def validate_collection(name)
|
85
|
+
warn "Admin#validate_collection has been DEPRECATED. Please use DB#validate_collection instead."
|
86
|
+
doc = @db.command(:validate => name)
|
87
|
+
raise "Error with validate command: #{doc.inspect}" unless @db.ok?(doc)
|
88
|
+
result = doc['result']
|
89
|
+
raise "Error with validation data: #{doc.inspect}" unless result.kind_of?(String)
|
90
|
+
raise "Error: invalid collection #{name}: #{doc.inspect}" if result =~ /\b(exception|corrupt)\b/i
|
91
|
+
doc
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,664 @@
|
|
1
|
+
# --
|
2
|
+
# Copyright (C) 2008-2009 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 Mongo
|
18
|
+
|
19
|
+
# A named collection of documents in a database.
|
20
|
+
class Collection
|
21
|
+
include Mongo::Conversions
|
22
|
+
|
23
|
+
attr_reader :db, :name, :pk_factory, :hint
|
24
|
+
|
25
|
+
# Initialize a collection object.
|
26
|
+
#
|
27
|
+
# @param [DB] db a MongoDB database instance.
|
28
|
+
# @param [String, Symbol] name the name of the collection.
|
29
|
+
#
|
30
|
+
# @raise [InvalidName]
|
31
|
+
# if collection name is empty, contains '$', or starts or ends with '.'
|
32
|
+
#
|
33
|
+
# @raise [TypeError]
|
34
|
+
# if collection name is not a string or symbol
|
35
|
+
#
|
36
|
+
# @return [Collection]
|
37
|
+
def initialize(db, name, pk_factory=nil)
|
38
|
+
case name
|
39
|
+
when Symbol, String
|
40
|
+
else
|
41
|
+
raise TypeError, "new_name must be a string or symbol"
|
42
|
+
end
|
43
|
+
|
44
|
+
name = name.to_s
|
45
|
+
|
46
|
+
if name.empty? or name.include? ".."
|
47
|
+
raise InvalidName, "collection names cannot be empty"
|
48
|
+
end
|
49
|
+
if name.include? "$"
|
50
|
+
raise InvalidName, "collection names must not contain '$'" unless name =~ /((^\$cmd)|(oplog\.\$main))/
|
51
|
+
end
|
52
|
+
if name.match(/^\./) or name.match(/\.$/)
|
53
|
+
raise InvalidName, "collection names must not start or end with '.'"
|
54
|
+
end
|
55
|
+
|
56
|
+
@db, @name = db, name
|
57
|
+
@connection = @db.connection
|
58
|
+
@pk_factory = pk_factory || ObjectID
|
59
|
+
@hint = nil
|
60
|
+
end
|
61
|
+
|
62
|
+
# Return a sub-collection of this collection by name. If 'users' is a collection, then
|
63
|
+
# 'users.comments' is a sub-collection of users.
|
64
|
+
#
|
65
|
+
# @param [String] name
|
66
|
+
# the collection to return
|
67
|
+
#
|
68
|
+
# @raise [InvalidName]
|
69
|
+
# if passed an invalid collection name
|
70
|
+
#
|
71
|
+
# @return [Collection]
|
72
|
+
# the specified sub-collection
|
73
|
+
def [](name)
|
74
|
+
name = "#{self.name}.#{name}"
|
75
|
+
return Collection.new(db, name) if !db.strict? || db.collection_names.include?(name)
|
76
|
+
raise "Collection #{name} doesn't exist. Currently in strict mode."
|
77
|
+
end
|
78
|
+
|
79
|
+
# Set a hint field for query optimizer. Hint may be a single field
|
80
|
+
# name, array of field names, or a hash (preferably an [OrderedHash]).
|
81
|
+
# If using MongoDB > 1.1, you probably don't ever need to set a hint.
|
82
|
+
#
|
83
|
+
# @param [String, Array, OrderedHash] hint a single field, an array of
|
84
|
+
# fields, or a hash specifying fields
|
85
|
+
def hint=(hint=nil)
|
86
|
+
@hint = normalize_hint_fields(hint)
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
# Query the database.
|
91
|
+
#
|
92
|
+
# The +selector+ argument is a prototype document that all results must
|
93
|
+
# match. For example:
|
94
|
+
#
|
95
|
+
# collection.find({"hello" => "world"})
|
96
|
+
#
|
97
|
+
# only matches documents that have a key "hello" with value "world".
|
98
|
+
# Matches can have other keys *in addition* to "hello".
|
99
|
+
#
|
100
|
+
# If given an optional block +find+ will yield a Cursor to that block,
|
101
|
+
# close the cursor, and then return nil. This guarantees that partially
|
102
|
+
# evaluated cursors will be closed. If given no block +find+ returns a
|
103
|
+
# cursor.
|
104
|
+
#
|
105
|
+
# @param [Hash] selector
|
106
|
+
# a document specifying elements which must be present for a
|
107
|
+
# document to be included in the result set.
|
108
|
+
#
|
109
|
+
# @option opts [Array] :fields field names that should be returned in the result
|
110
|
+
# set ("_id" will always be included). By limiting results to a certain subset of fields,
|
111
|
+
# you can cut down on network traffic and decoding time.
|
112
|
+
# @option opts [Integer] :skip number of documents to skip from the beginning of the result set
|
113
|
+
# @option opts [Integer] :limit maximum number of documents to return
|
114
|
+
# @option opts [Array] :sort an array of [key, direction] pairs to sort by. Direction should
|
115
|
+
# be specified as Mongo::ASCENDING (or :ascending / :asc) or Mongo::DESCENDING (or :descending / :desc)
|
116
|
+
# @option opts [String, Array, OrderedHash] :hint hint for query optimizer, usually not necessary if using MongoDB > 1.1
|
117
|
+
# @option opts [Boolean] :snapshot ('false') if true, snapshot mode will be used for this query.
|
118
|
+
# Snapshot mode assures no duplicates are returned, or objects missed, which were preset at both the start and
|
119
|
+
# end of the query's execution. For details see http://www.mongodb.org/display/DOCS/How+to+do+Snapshotting+in+the+Mongo+Database
|
120
|
+
# @option opts [Boolean] :timeout ('true') when +true+, the returned cursor will be subject to
|
121
|
+
# the normal cursor timeout behavior of the mongod process. When +false+, the returned cursor will never timeout. Note
|
122
|
+
# that disabling timeout will only work when #find is invoked with a block. This is to prevent any inadvertant failure to
|
123
|
+
# close the cursor, as the cursor is explicitly closed when block code finishes.
|
124
|
+
#
|
125
|
+
# @raise [ArgumentError]
|
126
|
+
# if timeout is set to false and find is not invoked in a block
|
127
|
+
#
|
128
|
+
# @raise [RuntimeError]
|
129
|
+
# if given unknown options
|
130
|
+
def find(selector={}, opts={})
|
131
|
+
fields = opts.delete(:fields)
|
132
|
+
fields = ["_id"] if fields && fields.empty?
|
133
|
+
skip = opts.delete(:skip) || skip || 0
|
134
|
+
limit = opts.delete(:limit) || 0
|
135
|
+
sort = opts.delete(:sort)
|
136
|
+
hint = opts.delete(:hint)
|
137
|
+
snapshot = opts.delete(:snapshot)
|
138
|
+
if opts[:timeout] == false && !block_given?
|
139
|
+
raise ArgumentError, "Timeout can be set to false only when #find is invoked with a block."
|
140
|
+
end
|
141
|
+
timeout = block_given? ? (opts.delete(:timeout) || true) : true
|
142
|
+
if hint
|
143
|
+
hint = normalize_hint_fields(hint)
|
144
|
+
else
|
145
|
+
hint = @hint # assumed to be normalized already
|
146
|
+
end
|
147
|
+
raise RuntimeError, "Unknown options [#{opts.inspect}]" unless opts.empty?
|
148
|
+
|
149
|
+
cursor = Cursor.new(self, :selector => selector, :fields => fields, :skip => skip, :limit => limit,
|
150
|
+
:order => sort, :hint => hint, :snapshot => snapshot, :timeout => timeout)
|
151
|
+
if block_given?
|
152
|
+
yield cursor
|
153
|
+
cursor.close()
|
154
|
+
nil
|
155
|
+
else
|
156
|
+
cursor
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Find and Modify (or Remove)
|
161
|
+
# This command is useful to atomically change an object and then get back the results
|
162
|
+
#
|
163
|
+
# @option opts [Hash] :query ({}) a query selector document, like what's passed to #find, to limit
|
164
|
+
# the operation to a subset of the collection.
|
165
|
+
# @option opts [Array] :sort ([]) an array of [key, direction] pairs to sort by. Direction should
|
166
|
+
# be specified as Mongo::ASCENDING (or :ascending / :asc) or Mongo::DESCENDING (or :descending / :desc)
|
167
|
+
# @option opts [Boolean] :remove (false) set to a true to remove the object before returning
|
168
|
+
# @option opts [Hash] :update ({}) a modifier object that updates the document
|
169
|
+
# @option opts [Boolean] :new (false) if true, return the modified object rather than the original. Ignored for remove.
|
170
|
+
#
|
171
|
+
# @return [Hash] The document from the database
|
172
|
+
#
|
173
|
+
# @see http://www.mongodb.org/display/DOCS/findandmodify+Command
|
174
|
+
def find_modify(opts={})
|
175
|
+
hash = OrderedHash.new
|
176
|
+
hash['findandmodify'] = self.name
|
177
|
+
|
178
|
+
sort = opts.delete(:sort)
|
179
|
+
hash[:sort] = formatted_sort_clause(sort) if sort
|
180
|
+
|
181
|
+
hash.merge! opts
|
182
|
+
|
183
|
+
result = @db.command(hash)
|
184
|
+
unless result["ok"] == 1
|
185
|
+
raise Mongo::OperationFailure, "find_modify failed: #{result['errmsg']}"
|
186
|
+
end
|
187
|
+
|
188
|
+
document = result['value']
|
189
|
+
end
|
190
|
+
|
191
|
+
# Return a single object from the database.
|
192
|
+
#
|
193
|
+
# @return [OrderedHash, Nil]
|
194
|
+
# a single document or nil if no result is found.
|
195
|
+
#
|
196
|
+
# @param [Hash, ObjectID, Nil] spec_or_object_id a hash specifying elements
|
197
|
+
# which must be present for a document to be included in the result set or an
|
198
|
+
# instance of ObjectID to be used as the value for an _id query.
|
199
|
+
# If nil, an empty selector, {}, will be used.
|
200
|
+
#
|
201
|
+
# @option opts [Hash]
|
202
|
+
# any valid options that can be send to Collection#find
|
203
|
+
#
|
204
|
+
# @raise [TypeError]
|
205
|
+
# if the argument is of an improper type.
|
206
|
+
def find_one(spec_or_object_id=nil, opts={})
|
207
|
+
spec = case spec_or_object_id
|
208
|
+
when nil
|
209
|
+
{}
|
210
|
+
when ObjectID
|
211
|
+
{:_id => spec_or_object_id}
|
212
|
+
when Hash
|
213
|
+
spec_or_object_id
|
214
|
+
else
|
215
|
+
raise TypeError, "spec_or_object_id must be an instance of ObjectID or Hash, or nil"
|
216
|
+
end
|
217
|
+
find(spec, opts.merge(:limit => -1)).next_document
|
218
|
+
end
|
219
|
+
|
220
|
+
# Save a document to this collection.
|
221
|
+
#
|
222
|
+
# @param [Hash] doc
|
223
|
+
# the document to be saved. If the document already has an '_id' key,
|
224
|
+
# then an update (upsert) operation will be performed, and any existing
|
225
|
+
# document with that _id is overwritten. Otherwise an insert operation is performed.
|
226
|
+
#
|
227
|
+
# @return [ObjectID] the _id of the saved document.
|
228
|
+
#
|
229
|
+
# @option opts [Boolean] :safe (+false+)
|
230
|
+
# If true, check that the save succeeded. OperationFailure
|
231
|
+
# will be raised on an error. Note that a safe check requires an extra
|
232
|
+
# round-trip to the database.
|
233
|
+
def save(doc, options={})
|
234
|
+
if doc.has_key?(:_id) || doc.has_key?('_id')
|
235
|
+
id = doc[:_id] || doc['_id']
|
236
|
+
update({:_id => id}, doc, :upsert => true, :safe => options.delete(:safe))
|
237
|
+
id
|
238
|
+
else
|
239
|
+
insert(doc, :safe => options.delete(:safe))
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
# Insert one or more documents into the collection.
|
244
|
+
#
|
245
|
+
# @param [Hash, Array] doc_or_docs
|
246
|
+
# a document (as a hash) or array of documents to be inserted.
|
247
|
+
#
|
248
|
+
# @return [ObjectID, Array]
|
249
|
+
# the _id of the inserted document or a list of _ids of all inserted documents.
|
250
|
+
# Note: the object may have been modified by the database's PK factory, if it has one.
|
251
|
+
#
|
252
|
+
# @option opts [Boolean] :safe (+false+)
|
253
|
+
# If true, check that the save succeeded. OperationFailure
|
254
|
+
# will be raised on an error. Note that a safe check requires an extra
|
255
|
+
# round-trip to the database.
|
256
|
+
def insert(doc_or_docs, options={})
|
257
|
+
doc_or_docs = [doc_or_docs] unless doc_or_docs.is_a?(Array)
|
258
|
+
doc_or_docs.collect! { |doc| @pk_factory.create_pk(doc) }
|
259
|
+
result = insert_documents(doc_or_docs, @name, true, options[:safe])
|
260
|
+
result.size > 1 ? result : result.first
|
261
|
+
end
|
262
|
+
alias_method :<<, :insert
|
263
|
+
|
264
|
+
# Remove all documents from this collection.
|
265
|
+
#
|
266
|
+
# @param [Hash] selector
|
267
|
+
# If specified, only matching documents will be removed.
|
268
|
+
#
|
269
|
+
# @option opts [Boolean] :safe [false] run the operation in safe mode, which
|
270
|
+
# will call :getlasterror on the database and report any assertions.
|
271
|
+
#
|
272
|
+
# @example remove all documents from the 'users' collection:
|
273
|
+
# users.remove
|
274
|
+
# users.remove({})
|
275
|
+
#
|
276
|
+
# @example remove only documents that have expired:
|
277
|
+
# users.remove({:expire => {"$lte" => Time.now}})
|
278
|
+
#
|
279
|
+
# @return [True]
|
280
|
+
#
|
281
|
+
# @raise [Mongo::OperationFailure] an exception will be raised iff safe mode is enabled
|
282
|
+
# and the operation fails.
|
283
|
+
def remove(selector={}, opts={})
|
284
|
+
# Initial byte is 0.
|
285
|
+
message = ByteBuffer.new([0, 0, 0, 0])
|
286
|
+
BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@name}")
|
287
|
+
message.put_int(0)
|
288
|
+
message.put_array(BSON.serialize(selector, false).to_a)
|
289
|
+
|
290
|
+
if opts[:safe]
|
291
|
+
@connection.send_message_with_safe_check(Mongo::Constants::OP_DELETE, message,
|
292
|
+
"db.#{@db.name}.remove(#{selector.inspect})")
|
293
|
+
# the return value of send_message_with_safe_check isn't actually meaningful --
|
294
|
+
# only the fact that it didn't raise an error is -- so just return true
|
295
|
+
true
|
296
|
+
else
|
297
|
+
@connection.send_message(Mongo::Constants::OP_DELETE, message,
|
298
|
+
"db.#{@db.name}.remove(#{selector.inspect})")
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
# Update a single document in this collection.
|
303
|
+
#
|
304
|
+
# @param [Hash] selector
|
305
|
+
# a hash specifying elements which must be present for a document to be updated. Note:
|
306
|
+
# the update command currently updates only the first document matching the
|
307
|
+
# given selector. If you want all matching documents to be updated, be sure
|
308
|
+
# to specify :multi => true.
|
309
|
+
# @param [Hash] document
|
310
|
+
# a hash specifying the fields to be changed in the selected document,
|
311
|
+
# or (in the case of an upsert) the document to be inserted
|
312
|
+
#
|
313
|
+
# @option [Boolean] :upsert (+false+) if true, performs an upsert (update or insert)
|
314
|
+
# @option [Boolean] :multi (+false+) update all documents matching the selector, as opposed to
|
315
|
+
# just the first matching document. Note: only works in MongoDB 1.1.3 or later.
|
316
|
+
# @option opts [Boolean] :safe (+false+)
|
317
|
+
# If true, check that the save succeeded. OperationFailure
|
318
|
+
# will be raised on an error. Note that a safe check requires an extra
|
319
|
+
# round-trip to the database.
|
320
|
+
def update(selector, document, options={})
|
321
|
+
# Initial byte is 0.
|
322
|
+
message = ByteBuffer.new([0, 0, 0, 0])
|
323
|
+
BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@name}")
|
324
|
+
update_options = 0
|
325
|
+
update_options += 1 if options[:upsert]
|
326
|
+
update_options += 2 if options[:multi]
|
327
|
+
message.put_int(update_options)
|
328
|
+
message.put_array(BSON.serialize(selector, false).to_a)
|
329
|
+
message.put_array(BSON.serialize(document, false).to_a)
|
330
|
+
if options[:safe]
|
331
|
+
@connection.send_message_with_safe_check(Mongo::Constants::OP_UPDATE, message, @db.name,
|
332
|
+
"db.#{@name}.update(#{selector.inspect}, #{document.inspect})")
|
333
|
+
else
|
334
|
+
@connection.send_message(Mongo::Constants::OP_UPDATE, message,
|
335
|
+
"db.#{@name}.update(#{selector.inspect}, #{document.inspect})")
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
# Create a new index.
|
340
|
+
#
|
341
|
+
# @param [String, Array] field_or_spec
|
342
|
+
# should be either a single field name or an array of
|
343
|
+
# [field name, direction] pairs. Directions should be specified as Mongo::ASCENDING or Mongo::DESCENDING.
|
344
|
+
#
|
345
|
+
# @param [Boolean] unique if true, this index will enforce a uniqueness constraint.
|
346
|
+
#
|
347
|
+
# @return [String] the name of the index created.
|
348
|
+
def create_index(field_or_spec, unique=false)
|
349
|
+
field_h = OrderedHash.new
|
350
|
+
if field_or_spec.is_a?(String) || field_or_spec.is_a?(Symbol)
|
351
|
+
field_h[field_or_spec.to_s] = 1
|
352
|
+
else
|
353
|
+
field_or_spec.each { |f| field_h[f[0].to_s] = f[1] }
|
354
|
+
end
|
355
|
+
name = generate_index_names(field_h)
|
356
|
+
sel = {
|
357
|
+
:name => name,
|
358
|
+
:ns => "#{@db.name}.#{@name}",
|
359
|
+
:key => field_h,
|
360
|
+
:unique => unique }
|
361
|
+
insert_documents([sel], Mongo::DB::SYSTEM_INDEX_COLLECTION, false)
|
362
|
+
name
|
363
|
+
end
|
364
|
+
|
365
|
+
# Drop a specified index.
|
366
|
+
#
|
367
|
+
# @param [String] name
|
368
|
+
def drop_index(name)
|
369
|
+
@db.drop_index(@name, name)
|
370
|
+
end
|
371
|
+
|
372
|
+
# Drop all indexes.
|
373
|
+
def drop_indexes
|
374
|
+
|
375
|
+
# Note: calling drop_indexes with no args will drop them all.
|
376
|
+
@db.drop_index(@name, '*')
|
377
|
+
|
378
|
+
end
|
379
|
+
|
380
|
+
# Drop the entire collection. USE WITH CAUTION.
|
381
|
+
def drop
|
382
|
+
@db.drop_collection(@name)
|
383
|
+
end
|
384
|
+
|
385
|
+
# Perform a map/reduce operation on the current collection.
|
386
|
+
#
|
387
|
+
# @param [String, Code] map a map function, written in JavaScript.
|
388
|
+
# @param [String, Code] reduce a reduce function, written in JavaScript.
|
389
|
+
#
|
390
|
+
# @option opts [Hash] :query ({}) a query selector document, like what's passed to #find, to limit
|
391
|
+
# the operation to a subset of the collection.
|
392
|
+
# @option opts [Array] :sort ([]) an array of [key, direction] pairs to sort by. Direction should
|
393
|
+
# be specified as Mongo::ASCENDING (or :ascending / :asc) or Mongo::DESCENDING (or :descending / :desc)
|
394
|
+
# @option opts [Integer] :limit (nil) if passing a query, number of objects to return from the collection.
|
395
|
+
# @option opts [String, Code] :finalize (nil) a javascript function to apply to the result set after the
|
396
|
+
# map/reduce operation has finished.
|
397
|
+
# @option opts [String] :out (nil) the name of the output collection. If specified, the collection will not be treated as temporary.
|
398
|
+
# @option opts [Boolean] :keeptemp (false) if true, the generated collection will be persisted. default is false.
|
399
|
+
# @option opts [Boolean ] :verbose (false) if true, provides statistics on job execution time.
|
400
|
+
#
|
401
|
+
# @return [Collection] a collection containing the results of the operation.
|
402
|
+
#
|
403
|
+
# @see http://www.mongodb.org/display/DOCS/MapReduce Offical MongoDB map/reduce documentation.
|
404
|
+
def map_reduce(map, reduce, opts={})
|
405
|
+
map = Code.new(map) unless map.is_a?(Code)
|
406
|
+
reduce = Code.new(reduce) unless reduce.is_a?(Code)
|
407
|
+
|
408
|
+
hash = OrderedHash.new
|
409
|
+
hash['mapreduce'] = self.name
|
410
|
+
hash['map'] = map
|
411
|
+
hash['reduce'] = reduce
|
412
|
+
hash.merge! opts
|
413
|
+
|
414
|
+
result = @db.command(hash)
|
415
|
+
unless result["ok"] == 1
|
416
|
+
raise Mongo::OperationFailure, "map-reduce failed: #{result['errmsg']}"
|
417
|
+
end
|
418
|
+
@db[result["result"]]
|
419
|
+
end
|
420
|
+
alias :mapreduce :map_reduce
|
421
|
+
|
422
|
+
# Perform a group aggregation.
|
423
|
+
#
|
424
|
+
# @param [Array, String, Code, Nil] :key either 1) an array of fields to group by,
|
425
|
+
# 2) a javascript function to generate the key object, or 3) nil.
|
426
|
+
# @param [Hash] condition an optional document specifying a query to limit the documents over which group is run.
|
427
|
+
# @param [Hash] initial initial value of the aggregation counter object
|
428
|
+
# @param [String, Code] reduce aggregation function, in JavaScript
|
429
|
+
# @param [String, Code] finalize :: optional. a JavaScript function that receives and modifies
|
430
|
+
# each of the resultant grouped objects. Available only when group is run
|
431
|
+
# with command set to true.
|
432
|
+
# @param [Boolean] command if true, run the group as a command instead of in an
|
433
|
+
# eval. Note: Running group as eval has been DEPRECATED.
|
434
|
+
#
|
435
|
+
# @return [Array] the grouped items.
|
436
|
+
def group(key, condition, initial, reduce, command=false, finalize=nil)
|
437
|
+
|
438
|
+
if command
|
439
|
+
|
440
|
+
reduce = Code.new(reduce) unless reduce.is_a?(Code)
|
441
|
+
|
442
|
+
group_command = {
|
443
|
+
"group" => {
|
444
|
+
"ns" => @name,
|
445
|
+
"$reduce" => reduce,
|
446
|
+
"cond" => condition,
|
447
|
+
"initial" => initial
|
448
|
+
}
|
449
|
+
}
|
450
|
+
|
451
|
+
unless key.nil?
|
452
|
+
if key.is_a? Array
|
453
|
+
key_type = "key"
|
454
|
+
key_value = {}
|
455
|
+
key.each { |k| key_value[k] = 1 }
|
456
|
+
else
|
457
|
+
key_type = "$keyf"
|
458
|
+
key_value = key.is_a?(Code) ? key : Code.new(key)
|
459
|
+
end
|
460
|
+
|
461
|
+
group_command["group"][key_type] = key_value
|
462
|
+
end
|
463
|
+
|
464
|
+
# only add finalize if specified
|
465
|
+
if finalize
|
466
|
+
finalize = Code.new(finalize) unless finalize.is_a?(Code)
|
467
|
+
group_command['group']['finalize'] = finalize
|
468
|
+
end
|
469
|
+
|
470
|
+
result = @db.command group_command
|
471
|
+
|
472
|
+
if result["ok"] == 1
|
473
|
+
return result["retval"]
|
474
|
+
else
|
475
|
+
raise OperationFailure, "group command failed: #{result['errmsg']}"
|
476
|
+
end
|
477
|
+
|
478
|
+
else
|
479
|
+
|
480
|
+
warn "Collection#group must now be run as a command; you can do this by passing 'true' as the command argument."
|
481
|
+
|
482
|
+
raise OperationFailure, ":finalize can be specified only when " +
|
483
|
+
"group is run as a command (set command param to true)" if finalize
|
484
|
+
|
485
|
+
raise OperationFailure, "key must be an array of fields to group by. If you want to pass a key function,
|
486
|
+
run group as a command by passing 'true' as the command argument." unless key.is_a? Array || key.nil?
|
487
|
+
|
488
|
+
case reduce
|
489
|
+
when Code
|
490
|
+
scope = reduce.scope
|
491
|
+
else
|
492
|
+
scope = {}
|
493
|
+
end
|
494
|
+
scope.merge!({
|
495
|
+
"ns" => @name,
|
496
|
+
"keys" => key,
|
497
|
+
"condition" => condition,
|
498
|
+
"initial" => initial })
|
499
|
+
|
500
|
+
group_function = <<EOS
|
501
|
+
function () {
|
502
|
+
var c = db[ns].find(condition);
|
503
|
+
var map = new Map();
|
504
|
+
var reduce_function = #{reduce};
|
505
|
+
while (c.hasNext()) {
|
506
|
+
var obj = c.next();
|
507
|
+
|
508
|
+
var key = {};
|
509
|
+
for (var i = 0; i < keys.length; i++) {
|
510
|
+
var k = keys[i];
|
511
|
+
key[k] = obj[k];
|
512
|
+
}
|
513
|
+
|
514
|
+
var aggObj = map.get(key);
|
515
|
+
if (aggObj == null) {
|
516
|
+
var newObj = Object.extend({}, key);
|
517
|
+
aggObj = Object.extend(newObj, initial);
|
518
|
+
map.put(key, aggObj);
|
519
|
+
}
|
520
|
+
reduce_function(obj, aggObj);
|
521
|
+
}
|
522
|
+
return {"result": map.values()};
|
523
|
+
}
|
524
|
+
EOS
|
525
|
+
@db.eval(Code.new(group_function, scope))["result"]
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
# Return a list of distinct values for +key+ across all
|
530
|
+
# documents in the collection. The key may use dot notation
|
531
|
+
# to reach into an embedded object.
|
532
|
+
#
|
533
|
+
# @param [String, Symbol, OrderedHash] key or hash to group by.
|
534
|
+
# @param [Hash] query a selector for limiting the result set over which to group.
|
535
|
+
#
|
536
|
+
# @example Saving zip codes and ages and returning distinct results.
|
537
|
+
# @collection.save({:zip => 10010, :name => {:age => 27}})
|
538
|
+
# @collection.save({:zip => 94108, :name => {:age => 24}})
|
539
|
+
# @collection.save({:zip => 10010, :name => {:age => 27}})
|
540
|
+
# @collection.save({:zip => 99701, :name => {:age => 24}})
|
541
|
+
# @collection.save({:zip => 94108, :name => {:age => 27}})
|
542
|
+
#
|
543
|
+
# @collection.distinct(:zip)
|
544
|
+
# [10010, 94108, 99701]
|
545
|
+
# @collection.distinct("name.age")
|
546
|
+
# [27, 24]
|
547
|
+
#
|
548
|
+
# # You may also pass a document selector as the second parameter
|
549
|
+
# # to limit the documents over which distinct is run:
|
550
|
+
# @collection.distinct("name.age", {"name.age" => {"$gt" => 24}})
|
551
|
+
# [27]
|
552
|
+
#
|
553
|
+
# @return [Array] an array of distinct values.
|
554
|
+
def distinct(key, query=nil)
|
555
|
+
raise MongoArgumentError unless [String, Symbol].include?(key.class)
|
556
|
+
command = OrderedHash.new
|
557
|
+
command[:distinct] = @name
|
558
|
+
command[:key] = key.to_s
|
559
|
+
command[:query] = query
|
560
|
+
|
561
|
+
@db.command(command)["values"]
|
562
|
+
end
|
563
|
+
|
564
|
+
# Rename this collection.
|
565
|
+
#
|
566
|
+
# Note: If operating in auth mode, the client must be authorized as an admin to
|
567
|
+
# perform this operation.
|
568
|
+
#
|
569
|
+
# @param [String ] new_name the new name for this collection
|
570
|
+
#
|
571
|
+
# @raise [InvalidName] if +new_name+ is an invalid collection name.
|
572
|
+
def rename(new_name)
|
573
|
+
case new_name
|
574
|
+
when Symbol, String
|
575
|
+
else
|
576
|
+
raise TypeError, "new_name must be a string or symbol"
|
577
|
+
end
|
578
|
+
|
579
|
+
new_name = new_name.to_s
|
580
|
+
|
581
|
+
if new_name.empty? or new_name.include? ".."
|
582
|
+
raise InvalidName, "collection names cannot be empty"
|
583
|
+
end
|
584
|
+
if new_name.include? "$"
|
585
|
+
raise InvalidName, "collection names must not contain '$'"
|
586
|
+
end
|
587
|
+
if new_name.match(/^\./) or new_name.match(/\.$/)
|
588
|
+
raise InvalidName, "collection names must not start or end with '.'"
|
589
|
+
end
|
590
|
+
|
591
|
+
@db.rename_collection(@name, new_name)
|
592
|
+
end
|
593
|
+
|
594
|
+
# Get information on the indexes for this collection.
|
595
|
+
#
|
596
|
+
# @return [Hash] a hash where the keys are index names.
|
597
|
+
def index_information
|
598
|
+
@db.index_information(@name)
|
599
|
+
end
|
600
|
+
|
601
|
+
# Return a hash containing options that apply to this collection.
|
602
|
+
# For all possible keys and values, see DB#create_collection.
|
603
|
+
#
|
604
|
+
# @return [Hash] options that apply to this collection.
|
605
|
+
def options
|
606
|
+
@db.collections_info(@name).next_document['options']
|
607
|
+
end
|
608
|
+
|
609
|
+
# Get the number of documents in this collection.
|
610
|
+
#
|
611
|
+
# @return [Integer]
|
612
|
+
def count
|
613
|
+
find().count()
|
614
|
+
end
|
615
|
+
|
616
|
+
alias :size :count
|
617
|
+
|
618
|
+
protected
|
619
|
+
|
620
|
+
def normalize_hint_fields(hint)
|
621
|
+
case hint
|
622
|
+
when String
|
623
|
+
{hint => 1}
|
624
|
+
when Hash
|
625
|
+
hint
|
626
|
+
when nil
|
627
|
+
nil
|
628
|
+
else
|
629
|
+
h = OrderedHash.new
|
630
|
+
hint.to_a.each { |k| h[k] = 1 }
|
631
|
+
h
|
632
|
+
end
|
633
|
+
end
|
634
|
+
|
635
|
+
private
|
636
|
+
|
637
|
+
# Sends a Mongo::Constants::OP_INSERT message to the database.
|
638
|
+
# Takes an array of +documents+, an optional +collection_name+, and a
|
639
|
+
# +check_keys+ setting.
|
640
|
+
def insert_documents(documents, collection_name=@name, check_keys=true, safe=false)
|
641
|
+
# Initial byte is 0.
|
642
|
+
message = ByteBuffer.new([0, 0, 0, 0])
|
643
|
+
BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{collection_name}")
|
644
|
+
documents.each { |doc| message.put_array(BSON.serialize(doc, check_keys).to_a) }
|
645
|
+
if safe
|
646
|
+
@connection.send_message_with_safe_check(Mongo::Constants::OP_INSERT, message, @db.name,
|
647
|
+
"db.#{collection_name}.insert(#{documents.inspect})")
|
648
|
+
else
|
649
|
+
@connection.send_message(Mongo::Constants::OP_INSERT, message,
|
650
|
+
"db.#{collection_name}.insert(#{documents.inspect})")
|
651
|
+
end
|
652
|
+
documents.collect { |o| o[:_id] || o['_id'] }
|
653
|
+
end
|
654
|
+
|
655
|
+
def generate_index_names(spec)
|
656
|
+
indexes = []
|
657
|
+
spec.each_pair do |field, direction|
|
658
|
+
indexes.push("#{field}_#{direction}")
|
659
|
+
end
|
660
|
+
indexes.join("_")
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
end
|