jmongo 1.0.3 → 1.1.0
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/Gemfile +8 -0
- data/Gemfile.lock +43 -0
- data/Rakefile +72 -0
- data/jmongo.gemspec +84 -6
- data/lib/jmongo.rb +6 -14
- data/lib/jmongo/collection.rb +196 -114
- data/lib/jmongo/connection.rb +39 -13
- data/lib/jmongo/cursor.rb +161 -63
- data/lib/jmongo/db.rb +119 -30
- data/lib/jmongo/exceptions.rb +39 -0
- data/lib/jmongo/mongo-2.6.5.gb1.jar +0 -0
- data/lib/jmongo/mongo/bson.rb +130 -0
- data/lib/jmongo/mongo/collection.rb +185 -0
- data/lib/jmongo/mongo/connection.rb +45 -0
- data/lib/jmongo/mongo/db.rb +31 -0
- data/lib/jmongo/mongo/jmongo.rb +44 -0
- data/lib/jmongo/mongo/mongo.rb +98 -0
- data/lib/jmongo/mongo/ruby_ext.rb +38 -0
- data/lib/jmongo/mongo/utils.rb +136 -0
- data/lib/jmongo/version.rb +1 -1
- data/test-results.txt +98 -0
- data/test/auxillary/1.4_features.rb +166 -0
- data/test/auxillary/authentication_test.rb +68 -0
- data/test/auxillary/autoreconnect_test.rb +41 -0
- data/test/auxillary/fork_test.rb +30 -0
- data/test/auxillary/repl_set_auth_test.rb +58 -0
- data/test/auxillary/slave_connection_test.rb +36 -0
- data/test/auxillary/threaded_authentication_test.rb +101 -0
- data/test/bson/binary_test.rb +15 -0
- data/test/bson/bson_test.rb +657 -0
- data/test/bson/byte_buffer_test.rb +208 -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 +138 -0
- data/test/bson/ordered_hash_test.rb +245 -0
- data/test/bson/test_helper.rb +46 -0
- data/test/bson/timestamp_test.rb +46 -0
- data/test/collection_test.rb +933 -0
- data/test/connection_test.rb +325 -0
- data/test/conversions_test.rb +121 -0
- data/test/cursor_fail_test.rb +75 -0
- data/test/cursor_message_test.rb +43 -0
- data/test/cursor_test.rb +547 -0
- data/test/data/empty_data +0 -0
- data/test/data/sample_data +0 -0
- data/test/data/sample_file.pdf +0 -0
- data/test/data/small_data.txt +1 -0
- data/test/db_api_test.rb +739 -0
- data/test/db_connection_test.rb +15 -0
- data/test/db_test.rb +325 -0
- data/test/grid_file_system_test.rb +260 -0
- data/test/grid_io_test.rb +210 -0
- data/test/grid_test.rb +259 -0
- data/test/load/thin/config.ru +6 -0
- data/test/load/thin/config.yml.template +6 -0
- data/test/load/thin/load.rb +24 -0
- data/test/load/unicorn/config.ru +6 -0
- data/test/load/unicorn/load.rb +23 -0
- data/test/load/unicorn/unicorn.rb.template +29 -0
- data/test/replica_sets/connect_test.rb +111 -0
- data/test/replica_sets/connection_string_test.rb +29 -0
- data/test/replica_sets/count_test.rb +36 -0
- data/test/replica_sets/insert_test.rb +54 -0
- data/test/replica_sets/pooled_insert_test.rb +58 -0
- data/test/replica_sets/query_secondaries.rb +109 -0
- data/test/replica_sets/query_test.rb +52 -0
- data/test/replica_sets/read_preference_test.rb +43 -0
- data/test/replica_sets/refresh_test.rb +123 -0
- data/test/replica_sets/replication_ack_test.rb +71 -0
- data/test/replica_sets/rs_test_helper.rb +27 -0
- data/test/safe_test.rb +68 -0
- data/test/support/hash_with_indifferent_access.rb +186 -0
- data/test/support/keys.rb +45 -0
- data/test/support_test.rb +19 -0
- data/test/test_helper.rb +111 -0
- data/test/threading/threading_with_large_pool_test.rb +90 -0
- data/test/threading_test.rb +88 -0
- data/test/tools/auth_repl_set_manager.rb +14 -0
- data/test/tools/keyfile.txt +1 -0
- data/test/tools/repl_set_manager.rb +377 -0
- data/test/unit/collection_test.rb +128 -0
- data/test/unit/connection_test.rb +85 -0
- data/test/unit/cursor_test.rb +127 -0
- data/test/unit/db_test.rb +96 -0
- data/test/unit/grid_test.rb +51 -0
- data/test/unit/node_test.rb +73 -0
- data/test/unit/pool_manager_test.rb +47 -0
- data/test/unit/pool_test.rb +9 -0
- data/test/unit/read_test.rb +101 -0
- data/test/unit/safe_test.rb +125 -0
- data/test/uri_test.rb +92 -0
- metadata +170 -99
- data/lib/jmongo/ajrb.rb +0 -189
- data/lib/jmongo/jmongo_jext.rb +0 -302
- data/lib/jmongo/mongo-2.6.3.jar +0 -0
- data/lib/jmongo/utils.rb +0 -61
@@ -0,0 +1,98 @@
|
|
1
|
+
module Mongo
|
2
|
+
ASCENDING = 1
|
3
|
+
DESCENDING = -1
|
4
|
+
GEO2D = '2d'
|
5
|
+
|
6
|
+
DEFAULT_MAX_BSON_SIZE = 1024 * 1024 * 4
|
7
|
+
|
8
|
+
REPLACE = JMongo::MapReduceCommand::OutputType::REPLACE
|
9
|
+
MERGE = JMongo::MapReduceCommand::OutputType::MERGE
|
10
|
+
REDUCE = JMongo::MapReduceCommand::OutputType::REDUCE
|
11
|
+
INLINE = JMongo::MapReduceCommand::OutputType::INLINE
|
12
|
+
|
13
|
+
MapReduceEnumHash = {:replace => REPLACE, :merge => MERGE, :reduce => REDUCE, :inline => INLINE}
|
14
|
+
|
15
|
+
DEFAULT_BATCH_SIZE = 100
|
16
|
+
|
17
|
+
OP_REPLY = 1
|
18
|
+
OP_MSG = 1000
|
19
|
+
OP_UPDATE = 2001
|
20
|
+
OP_INSERT = 2002
|
21
|
+
OP_QUERY = 2004
|
22
|
+
OP_GET_MORE = 2005
|
23
|
+
OP_DELETE = 2006
|
24
|
+
OP_KILL_CURSORS = 2007
|
25
|
+
|
26
|
+
OP_QUERY_TAILABLE = JMongo::Bytes::QUERYOPTION_TAILABLE
|
27
|
+
OP_QUERY_SLAVE_OK = JMongo::Bytes::QUERYOPTION_SLAVEOK
|
28
|
+
OP_QUERY_OPLOG_REPLAY = JMongo::Bytes::QUERYOPTION_OPLOGREPLAY
|
29
|
+
OP_QUERY_NO_CURSOR_TIMEOUT = JMongo::Bytes::QUERYOPTION_NOTIMEOUT
|
30
|
+
OP_QUERY_AWAIT_DATA = JMongo::Bytes::QUERYOPTION_AWAITDATA
|
31
|
+
OP_QUERY_EXHAUST = JMongo::Bytes::QUERYOPTION_EXHAUST
|
32
|
+
|
33
|
+
REPLY_CURSOR_NOT_FOUND = JMongo::Bytes::RESULTFLAG_CURSORNOTFOUND
|
34
|
+
REPLY_QUERY_FAILURE = JMongo::Bytes::RESULTFLAG_ERRSET
|
35
|
+
REPLY_SHARD_CONFIG_STALE = JMongo::Bytes::RESULTFLAG_SHARDCONFIGSTALE
|
36
|
+
REPLY_AWAIT_CAPABLE = JMongo::Bytes::RESULTFLAG_AWAITCAPABLE
|
37
|
+
|
38
|
+
def self.logger(logger=nil)
|
39
|
+
logger ? @logger = logger : @logger
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.result_ok?(result)
|
43
|
+
result['ok'] == 1.0 || result['ok'] == true
|
44
|
+
end
|
45
|
+
|
46
|
+
# Simple class for comparing server versions.
|
47
|
+
class ServerVersion
|
48
|
+
include Comparable
|
49
|
+
|
50
|
+
def initialize(version)
|
51
|
+
@version = version
|
52
|
+
end
|
53
|
+
|
54
|
+
# Implements comparable.
|
55
|
+
def <=>(new)
|
56
|
+
local, new = self.to_a, to_array(new)
|
57
|
+
for n in 0...local.size do
|
58
|
+
break if elements_include_mods?(local[n], new[n])
|
59
|
+
if local[n] < new[n].to_i
|
60
|
+
result = -1
|
61
|
+
break;
|
62
|
+
elsif local[n] > new[n].to_i
|
63
|
+
result = 1
|
64
|
+
break;
|
65
|
+
end
|
66
|
+
end
|
67
|
+
result || 0
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return an array representation of this server version.
|
71
|
+
def to_a
|
72
|
+
to_array(@version)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Return a string representation of this server version.
|
76
|
+
def to_s
|
77
|
+
@version
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# Returns true if any elements include mod symbols (-, +)
|
83
|
+
def elements_include_mods?(*elements)
|
84
|
+
elements.any? { |n| n =~ /[\-\+]/ }
|
85
|
+
end
|
86
|
+
|
87
|
+
# Converts argument to an array of integers,
|
88
|
+
# appending any mods as the final element.
|
89
|
+
def to_array(version)
|
90
|
+
array = version.split(".").map {|n| (n =~ /^\d+$/) ? n.to_i : n }
|
91
|
+
if array.last =~ /(\d+)([\-\+])/
|
92
|
+
array[array.length-1] = $1.to_i
|
93
|
+
array << $2
|
94
|
+
end
|
95
|
+
array
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
class String
|
3
|
+
def to_bson_code
|
4
|
+
BSON::Code.new(self)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class Symbol
|
9
|
+
def to_bson
|
10
|
+
BSON::Symbol.new(self.to_s)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Object
|
15
|
+
def to_bson
|
16
|
+
self
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Hash
|
21
|
+
def to_bson
|
22
|
+
obj = ::JMongo::BasicDBObject.new
|
23
|
+
self.each_pair do |key, val|
|
24
|
+
obj.put( key.to_s, val.to_bson )
|
25
|
+
end
|
26
|
+
obj
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Array
|
31
|
+
def to_bson
|
32
|
+
list = Array.new #Java::ComMongodb::DBObject[ary.length].new
|
33
|
+
self.each_with_index do |ele, i|
|
34
|
+
list[i] = ele.to_bson
|
35
|
+
end
|
36
|
+
list
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# Copyright (C) 2010 Guy Boertje
|
2
|
+
|
3
|
+
module Mongo
|
4
|
+
module JavaImpl
|
5
|
+
|
6
|
+
module NoImplYetClass
|
7
|
+
def raise_not_implemented
|
8
|
+
raise NoMethodError, "This method hasn't been implemented yet."
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Utils
|
13
|
+
def raise_not_implemented
|
14
|
+
raise NoMethodError, "This method hasn't been implemented yet."
|
15
|
+
end
|
16
|
+
|
17
|
+
def trap_raise(ex_class, msg=nil)
|
18
|
+
begin
|
19
|
+
yield
|
20
|
+
rescue => ex
|
21
|
+
raise ex_class, msg ? "#{msg} - #{ex.message}" : ex.message
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def prep_fields(fields)
|
26
|
+
case fields
|
27
|
+
when String, Symbol
|
28
|
+
{fields => 1}
|
29
|
+
when Array
|
30
|
+
fields << "_id" if fields.empty?
|
31
|
+
Hash[fields.zip( [1]*fields.size )]
|
32
|
+
when Hash
|
33
|
+
fields
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def prep_sort(key_or_list=nil, direction=nil)
|
38
|
+
return if key_or_list.nil?
|
39
|
+
if !direction.nil?
|
40
|
+
order = [[key_or_list, direction]]
|
41
|
+
elsif key_or_list.is_a?(String) || key_or_list.is_a?(Symbol)
|
42
|
+
order = [[key_or_list.to_s, 1]]
|
43
|
+
else
|
44
|
+
order = [key_or_list]
|
45
|
+
end
|
46
|
+
hord = {}
|
47
|
+
order.flatten.each_slice(2){|k,v| hord[k] = sort_value(k,v)}
|
48
|
+
to_dbobject(hord)
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_dbobject obj
|
52
|
+
if obj.respond_to?('to_bson')
|
53
|
+
obj.to_bson
|
54
|
+
elsif obj.respond_to?(:merge)
|
55
|
+
hash_to_dbobject(obj)
|
56
|
+
elsif obj.respond_to?(:compact)
|
57
|
+
array_to_dblist(obj)
|
58
|
+
else
|
59
|
+
obj
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def from_dbobject obj
|
64
|
+
# for better upstream compatibility make the objects into ruby hash or array
|
65
|
+
|
66
|
+
case obj
|
67
|
+
when Java::ComMongodb::BasicDBObject, Java::ComMongodb::CommandResult
|
68
|
+
h = obj.hashify
|
69
|
+
Hash[h.keys.zip(h.values.map{|v| from_dbobject(v)})]
|
70
|
+
when Java::ComMongodb::BasicDBList
|
71
|
+
obj.arrayify.map{|v| from_dbobject(v)}
|
72
|
+
when Java::JavaUtil::ArrayList
|
73
|
+
obj.map{|v| from_dbobject(v)}
|
74
|
+
when Java::JavaUtil::Date
|
75
|
+
Time.at(obj.get_time/1000.0)
|
76
|
+
when Java::OrgBsonTypes::Symbol
|
77
|
+
obj.toString.to_sym
|
78
|
+
else
|
79
|
+
obj
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def sort_value(key, value)
|
84
|
+
val = value.to_s.downcase
|
85
|
+
return val if val == '2d'
|
86
|
+
direction = SortingHash[val]
|
87
|
+
return direction if direction != 0
|
88
|
+
raise InvalidSortValueError.new(
|
89
|
+
"for key: #{key}, #{value} was supplied as a sort direction when acceptable values are: " +
|
90
|
+
"Mongo::ASCENDING, 'ascending', 'asc', :ascending, :asc, 1, Mongo::DESCENDING, " +
|
91
|
+
"'descending', 'desc', :descending, :desc, -1.")
|
92
|
+
end
|
93
|
+
|
94
|
+
SortingHash = Hash.new(0).merge!(
|
95
|
+
"ascending" => 1, "asc" => 1, "1" => 1,
|
96
|
+
"descending" => -1, "desc" => -1, "-1" => -1
|
97
|
+
)
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def hash_to_dbobject doc
|
102
|
+
obj = JMongo::BasicDBObject.new
|
103
|
+
doc.each_pair do |key, value|
|
104
|
+
obj.put(key.to_s, to_dbobject(value))
|
105
|
+
end
|
106
|
+
obj
|
107
|
+
end
|
108
|
+
|
109
|
+
def array_to_dblist ary
|
110
|
+
list = [] #Java::ComMongodb::DBObject[ary.length].new
|
111
|
+
ary.each_with_index do |ele, i|
|
112
|
+
list[i] = to_dbobject(ele)
|
113
|
+
end
|
114
|
+
list
|
115
|
+
end
|
116
|
+
|
117
|
+
#@collection.save({:doc => 'foo'}, :safe => nil) ---> NONE = new WriteConcern(-1)
|
118
|
+
#@collection.save({:doc => 'foo'}, :safe => true) ---> NORMAL = new WriteConcern(0)
|
119
|
+
#@collection.save({:doc => 'foo'}, :safe => {:w => 2}) ---> new WriteConcern( 2 , 0 , false)
|
120
|
+
#@collection.save({:doc => 'foo'}, :safe => {:w => 2, :wtimeout => 200}) ---> new WriteConcern( 2 , 200 , false)
|
121
|
+
#@collection.save({:doc => 'foo'}, :safe => {:w => 2, :wtimeout => 200, :fsync => true}) ---> new WriteConcern( 2 , 0 , true)
|
122
|
+
#@collection.save({:doc => 'foo'}, :safe => {:fsync => true}) ---> FSYNC_SAFE = new WriteConcern( 1 , 0 , true)
|
123
|
+
|
124
|
+
def write_concern(safe)
|
125
|
+
return JMongo::WriteConcern.new(-1) if safe.nil?
|
126
|
+
return JMongo::WriteConcern.new(0) if safe.is_a?(FalseClass)
|
127
|
+
return JMongo::WriteConcern.new(1) if safe.is_a?(TrueClass)
|
128
|
+
return JMongo::WriteConcern.new(0) unless safe.is_a?(Hash)
|
129
|
+
w = safe[:w] || 1
|
130
|
+
t = safe[:wtimeout] || 0
|
131
|
+
f = !!(safe[:fsync] || false)
|
132
|
+
JMongo::WriteConcern.new(w, t, f) #dont laugh!
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/lib/jmongo/version.rb
CHANGED
data/test-results.txt
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
Loaded suite ./test/collection_test
|
2
|
+
Started
|
3
|
+
"test_map_reduce_with_raw_response"
|
4
|
+
"function() { emit(this.user_id, 1); }"
|
5
|
+
"function(k,vals) { return 1; }"
|
6
|
+
{
|
7
|
+
"result" => "foo",
|
8
|
+
"timeMillis" => 18,
|
9
|
+
"timing" => {
|
10
|
+
"mapTime" => 0,
|
11
|
+
"emitLoop" => 17,
|
12
|
+
"total" => 18
|
13
|
+
},
|
14
|
+
"counts" => {
|
15
|
+
"input" => 3,
|
16
|
+
"emit" => 3,
|
17
|
+
"output" => 3
|
18
|
+
},
|
19
|
+
"ok" => 1.0
|
20
|
+
}
|
21
|
+
........."test_map_reduce_with_output_collection"
|
22
|
+
"function() { emit(this.user_id, 1); }"
|
23
|
+
"function(k,vals) { return 1; }"
|
24
|
+
{
|
25
|
+
"result" => "test-map-coll",
|
26
|
+
"timeMillis" => 1,
|
27
|
+
"timing" => {
|
28
|
+
"mapTime" => 0,
|
29
|
+
"emitLoop" => 0,
|
30
|
+
"total" => 1
|
31
|
+
},
|
32
|
+
"counts" => {
|
33
|
+
"input" => 3,
|
34
|
+
"emit" => 3,
|
35
|
+
"output" => 3
|
36
|
+
},
|
37
|
+
"ok" => 1.0
|
38
|
+
}
|
39
|
+
....."test_map_reduce_with_code_objects"
|
40
|
+
"function() { emit(this.user_id, 1); }"
|
41
|
+
"function(k,vals) { return 1; }"
|
42
|
+
{
|
43
|
+
"result" => "foo",
|
44
|
+
"timeMillis" => 1,
|
45
|
+
"timing" => {
|
46
|
+
"mapTime" => 0,
|
47
|
+
"emitLoop" => 0,
|
48
|
+
"total" => 1
|
49
|
+
},
|
50
|
+
"counts" => {
|
51
|
+
"input" => 2,
|
52
|
+
"emit" => 2,
|
53
|
+
"output" => 2
|
54
|
+
},
|
55
|
+
"ok" => 1.0
|
56
|
+
}
|
57
|
+
..."test_map_reduce_with_options"
|
58
|
+
"function() { emit(this.user_id, 1); }"
|
59
|
+
"function(k,vals) { return 1; }"
|
60
|
+
{
|
61
|
+
"result" => "foo",
|
62
|
+
"timeMillis" => 1,
|
63
|
+
"timing" => {
|
64
|
+
"mapTime" => 0,
|
65
|
+
"emitLoop" => 0,
|
66
|
+
"total" => 1
|
67
|
+
},
|
68
|
+
"counts" => {
|
69
|
+
"input" => 2,
|
70
|
+
"emit" => 2,
|
71
|
+
"output" => 2
|
72
|
+
},
|
73
|
+
"ok" => 1.0
|
74
|
+
}
|
75
|
+
.."test_map_reduce"
|
76
|
+
"function() { emit(this.user_id, 1); }"
|
77
|
+
"function(k,vals) { return 1; }"
|
78
|
+
{
|
79
|
+
"result" => "foo",
|
80
|
+
"timeMillis" => 1,
|
81
|
+
"timing" => {
|
82
|
+
"mapTime" => 0,
|
83
|
+
"emitLoop" => 0,
|
84
|
+
"total" => 1
|
85
|
+
},
|
86
|
+
"counts" => {
|
87
|
+
"input" => 2,
|
88
|
+
"emit" => 2,
|
89
|
+
"output" => 2
|
90
|
+
},
|
91
|
+
"ok" => 1.0
|
92
|
+
}
|
93
|
+
..............
|
94
|
+
Finished in 0.304000 seconds.
|
95
|
+
|
96
|
+
33 tests, 94 assertions, 0 failures, 0 errors, 0 skips
|
97
|
+
|
98
|
+
Test run options: --seed 45344
|
@@ -0,0 +1,166 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require 'mongo'
|
3
|
+
require 'test/unit'
|
4
|
+
require './test/test_helper'
|
5
|
+
|
6
|
+
# Demonstrate features in MongoDB 1.4
|
7
|
+
class Features14Test < Test::Unit::TestCase
|
8
|
+
|
9
|
+
context "MongoDB 1.4" do
|
10
|
+
setup do
|
11
|
+
@con = Mongo::Connection.new
|
12
|
+
@db = @con['mongo-ruby-test']
|
13
|
+
@col = @db['new-features']
|
14
|
+
end
|
15
|
+
|
16
|
+
teardown do
|
17
|
+
@col.drop
|
18
|
+
end
|
19
|
+
|
20
|
+
context "new query operators: " do
|
21
|
+
|
22
|
+
context "$elemMatch: " do
|
23
|
+
setup do
|
24
|
+
@col.save({:user => 'bob', :updates => [{:date => Time.now.utc, :body => 'skiing', :n => 1},
|
25
|
+
{:date => Time.now.utc, :body => 'biking', :n => 2}]})
|
26
|
+
|
27
|
+
@col.save({:user => 'joe', :updates => [{:date => Time.now.utc, :body => 'skiing', :n => 2},
|
28
|
+
{:date => Time.now.utc, :body => 'biking', :n => 10}]})
|
29
|
+
end
|
30
|
+
|
31
|
+
should "match a document with a matching object element in an array" do
|
32
|
+
doc = @col.find_one({"updates" => {"$elemMatch" => {"body" => "skiing", "n" => 2}}})
|
33
|
+
assert_equal 'joe', doc['user']
|
34
|
+
end
|
35
|
+
|
36
|
+
should "$elemMatch with a conditional operator" do
|
37
|
+
doc1 = @col.find_one({"updates" => {"$elemMatch" => {"body" => "biking", "n" => {"$gt" => 5}}}})
|
38
|
+
assert_equal 'joe', doc1['user']
|
39
|
+
end
|
40
|
+
|
41
|
+
should "note the difference between $elemMatch and a traditional match" do
|
42
|
+
doc = @col.find({"updates.body" => "skiing", "updates.n" => 2}).to_a
|
43
|
+
assert_equal 2, doc.size
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "$all with regexes" do
|
48
|
+
setup do
|
49
|
+
@col.save({:n => 1, :a => 'whale'})
|
50
|
+
@col.save({:n => 2, :a => 'snake'})
|
51
|
+
end
|
52
|
+
|
53
|
+
should "match multiple regexes" do
|
54
|
+
doc = @col.find({:a => {'$all' => [/ha/, /le/]}}).to_a
|
55
|
+
assert_equal 1, doc.size
|
56
|
+
assert_equal 1, doc.first['n']
|
57
|
+
end
|
58
|
+
|
59
|
+
should "not match if not every regex matches" do
|
60
|
+
doc = @col.find({:a => {'$all' => [/ha/, /sn/]}}).to_a
|
61
|
+
assert_equal 0, doc.size
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "the $not operator" do
|
66
|
+
setup do
|
67
|
+
@col.save({:a => ['x']})
|
68
|
+
@col.save({:a => ['x', 'y']})
|
69
|
+
@col.save({:a => ['x', 'y', 'z']})
|
70
|
+
end
|
71
|
+
|
72
|
+
should "negate a standard operator" do
|
73
|
+
results = @col.find({:a => {'$not' => {'$size' => 2}}}).to_a
|
74
|
+
assert_equal 2, results.size
|
75
|
+
results = results.map {|r| r['a']}
|
76
|
+
assert_equal ['x'], results.sort.first
|
77
|
+
assert_equal ['x', 'y', 'z'], results.sort.last
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "new update operators: " do
|
83
|
+
|
84
|
+
context "$addToSet (pushing a unique value)" do
|
85
|
+
setup do
|
86
|
+
@col.save({:username => 'bob', :interests => ['skiing', 'guitar']})
|
87
|
+
end
|
88
|
+
|
89
|
+
should "add an item to a set uniquely ($addToSet)" do
|
90
|
+
@col.update({:username => 'bob'}, {'$addToSet' => {'interests' => 'skiing'}})
|
91
|
+
@col.update({:username => 'bob'}, {'$addToSet' => {'interests' => 'kayaking'}})
|
92
|
+
document = @col.find_one({:username => 'bob'})
|
93
|
+
assert_equal ['guitar', 'kayaking', 'skiing'], document['interests'].sort
|
94
|
+
end
|
95
|
+
|
96
|
+
should "add an array of items uniquely ($addToSet with $each)" do
|
97
|
+
@col.update({:username => 'bob'}, {'$addToSet' => {'interests' => {'$each' => ['skiing', 'kayaking', 'biking']}}})
|
98
|
+
document = @col.find_one({:username => 'bob'})
|
99
|
+
assert_equal ['biking', 'guitar', 'kayaking', 'skiing'], document['interests'].sort
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "the positional operator ($)" do
|
104
|
+
setup do
|
105
|
+
@id1 = @col.insert({:text => 'hello',
|
106
|
+
:comments => [{'by' => 'bob',
|
107
|
+
'text' => 'lol!'},
|
108
|
+
{'by' => 'susie',
|
109
|
+
'text' => 'bye bye!'}]})
|
110
|
+
@id2 = @col.insert({:text => 'goodbye',
|
111
|
+
:comments => [{'by' => 'bob',
|
112
|
+
'text' => 'au revoir'},
|
113
|
+
{'by' => 'susie',
|
114
|
+
'text' => 'bye bye!'}]})
|
115
|
+
end
|
116
|
+
|
117
|
+
should "update a matching array item" do
|
118
|
+
@col.update({"_id" => @id1, "comments.by" => 'bob'}, {'$set' => {'comments.$.text' => 'lmao!'}}, :multi => true)
|
119
|
+
result = @col.find_one({"_id" => @id1})
|
120
|
+
assert_equal 'lmao!', result['comments'][0]['text']
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context "Geoindexing" do
|
126
|
+
setup do
|
127
|
+
@places = @db['places']
|
128
|
+
@places.create_index([['loc', Mongo::GEO2D]])
|
129
|
+
|
130
|
+
@empire_state = ([40.748371, -73.985031])
|
131
|
+
@jfk = ([40.643711, -73.790009])
|
132
|
+
|
133
|
+
@places.insert({'name' => 'Empire State Building', 'loc' => ([40.748371, -73.985031])})
|
134
|
+
@places.insert({'name' => 'Flatiron Building', 'loc' => ([40.741581, -73.987549])})
|
135
|
+
@places.insert({'name' => 'Grand Central', 'loc' => ([40.751678, -73.976562])})
|
136
|
+
@places.insert({'name' => 'Columbia University', 'loc' => ([40.808922, -73.961617])})
|
137
|
+
@places.insert({'name' => 'NYSE', 'loc' => ([40.71455, -74.007124])})
|
138
|
+
@places.insert({'name' => 'JFK', 'loc' => ([40.643711, -73.790009])})
|
139
|
+
end
|
140
|
+
|
141
|
+
teardown do
|
142
|
+
@places.drop
|
143
|
+
end
|
144
|
+
|
145
|
+
should "find the nearest addresses" do
|
146
|
+
results = @places.find({'loc' => {'$near' => @empire_state}}).limit(2).to_a
|
147
|
+
assert_equal 2, results.size
|
148
|
+
assert_equal 'Empire State Building', results[0]['name']
|
149
|
+
assert_equal 'Flatiron Building', results[1]['name']
|
150
|
+
end
|
151
|
+
|
152
|
+
should "use geoNear command to return distances from a point" do
|
153
|
+
cmd = BSON::OrderedHash.new
|
154
|
+
cmd['geoNear'] = 'places'
|
155
|
+
cmd['near'] = @empire_state
|
156
|
+
cmd['num'] = 6
|
157
|
+
r = @db.command(cmd)
|
158
|
+
|
159
|
+
assert_equal 6, r['results'].length
|
160
|
+
r['results'].each do |result|
|
161
|
+
puts result.inspect
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|