monga 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.travis.yml +1 -0
- data/README.md +59 -3
- data/lib/monga/client.rb +51 -6
- data/lib/monga/clients/master_slave_client.rb +0 -5
- data/lib/monga/clients/replica_set_client.rb +32 -71
- data/lib/monga/clients/single_instance_client.rb +53 -0
- data/lib/monga/collection.rb +102 -41
- data/lib/monga/connection.rb +38 -13
- data/lib/monga/connection_pool.rb +6 -17
- data/lib/monga/connections/buffer.rb +33 -0
- data/lib/monga/connections/em_connection.rb +25 -56
- data/lib/monga/connections/em_proxy_connection.rb +80 -0
- data/lib/monga/connections/fibered_connection.rb +26 -0
- data/lib/monga/connections/fibered_proxy_connection.rb +23 -0
- data/lib/monga/connections/proxy_connection.rb +4 -0
- data/lib/monga/connections/tcp_connection.rb +57 -0
- data/lib/monga/cursor.rb +197 -95
- data/lib/monga/database.rb +175 -60
- data/lib/monga/{requests → protocol}/delete.rb +1 -2
- data/lib/monga/{requests → protocol}/get_more.rb +1 -1
- data/lib/monga/{requests → protocol}/insert.rb +1 -2
- data/lib/monga/{requests → protocol}/kill_cursors.rb +1 -1
- data/lib/monga/{requests → protocol}/query.rb +3 -3
- data/lib/monga/{requests → protocol}/update.rb +1 -1
- data/lib/monga/request.rb +27 -23
- data/lib/monga/utils/constants.rb +5 -0
- data/lib/monga/utils/exceptions.rb +11 -0
- data/lib/monga.rb +19 -11
- data/monga.gemspec +2 -2
- data/spec/helpers/mongodb.rb +115 -38
- data/spec/monga/block/collection_spec.rb +172 -0
- data/spec/monga/block/cursor_spec.rb +160 -0
- data/spec/monga/block/database_spec.rb +80 -0
- data/spec/monga/block/single_instance_client_spec.rb +31 -0
- data/spec/monga/em/collection_spec.rb +308 -0
- data/spec/monga/em/cursor_spec.rb +256 -0
- data/spec/monga/em/database_spec.rb +140 -0
- data/spec/monga/em/replica_set_client_spec.rb +86 -0
- data/spec/monga/em/single_instance_client_spec.rb +28 -0
- data/spec/monga/sync/collection_spec.rb +247 -0
- data/spec/monga/sync/cursor_spec.rb +211 -0
- data/spec/monga/sync/database_spec.rb +110 -0
- data/spec/monga/sync/replica_set_client_spec.rb +54 -0
- data/spec/monga/sync/single_instance_client_spec.rb +25 -0
- data/spec/spec_helper.rb +2 -20
- metadata +50 -38
- data/lib/monga/clients/client.rb +0 -24
- data/lib/monga/connections/primary.rb +0 -46
- data/lib/monga/connections/secondary.rb +0 -13
- data/lib/monga/exceptions.rb +0 -9
- data/lib/monga/miner.rb +0 -72
- data/lib/monga/response.rb +0 -11
- data/spec/helpers/truncate.rb +0 -15
- data/spec/monga/collection_spec.rb +0 -448
- data/spec/monga/connection_pool_spec.rb +0 -50
- data/spec/monga/connection_spec.rb +0 -64
- data/spec/monga/cursor_spec.rb +0 -186
- data/spec/monga/database_spec.rb +0 -67
- data/spec/monga/replica_set_client_spec.rb +0 -46
- data/spec/monga/requests/delete_spec.rb +0 -0
- data/spec/monga/requests/insert_spec.rb +0 -0
- data/spec/monga/requests/query_spec.rb +0 -28
data/lib/monga/request.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Monga
|
2
2
|
class Request
|
3
|
-
attr_reader :request_id
|
3
|
+
attr_reader :request_id, :connection
|
4
4
|
|
5
5
|
OP_CODES = {
|
6
6
|
reply: 1,
|
@@ -14,12 +14,13 @@ module Monga
|
|
14
14
|
kill_cursors: 2007,
|
15
15
|
}
|
16
16
|
|
17
|
-
def initialize(
|
18
|
-
@
|
17
|
+
def initialize(connection, db_name, collection_name, options = {})
|
18
|
+
@connection = connection
|
19
|
+
@db_name = db_name
|
19
20
|
@collection_name = collection_name
|
20
21
|
@options = options
|
22
|
+
|
21
23
|
@request_id = self.class.request_id
|
22
|
-
@connection = @db.client.aquire_connection
|
23
24
|
end
|
24
25
|
|
25
26
|
def command
|
@@ -37,23 +38,25 @@ module Monga
|
|
37
38
|
|
38
39
|
# Fire and Forget
|
39
40
|
def perform
|
40
|
-
@connection.send_command(command)
|
41
|
-
|
41
|
+
@connection.send_command(command, @request_id)
|
42
|
+
self
|
42
43
|
end
|
43
44
|
|
44
45
|
# Fire and wait
|
45
46
|
def callback_perform
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
@connection.send_command(command, @request_id) do |data|
|
48
|
+
err, resp = parse_response(data)
|
49
|
+
if block_given?
|
50
|
+
yield(err, resp)
|
51
|
+
else
|
52
|
+
err ? raise(err) : resp
|
50
53
|
end
|
51
54
|
end
|
52
55
|
end
|
53
56
|
|
54
57
|
def parse_response(data)
|
55
58
|
if Exception === data
|
56
|
-
data
|
59
|
+
[data, nil]
|
57
60
|
else
|
58
61
|
flags = data[4]
|
59
62
|
number = data[7]
|
@@ -66,7 +69,7 @@ module Monga
|
|
66
69
|
elsif docs.first && (docs.first["err"] || docs.first["errmsg"])
|
67
70
|
Monga::Exceptions::QueryFailure.new(docs.first)
|
68
71
|
else
|
69
|
-
data
|
72
|
+
[nil, data]
|
70
73
|
end
|
71
74
|
end
|
72
75
|
end
|
@@ -90,7 +93,7 @@ module Monga
|
|
90
93
|
end
|
91
94
|
|
92
95
|
def full_name
|
93
|
-
[@
|
96
|
+
[@db_name, @collection_name] * "."
|
94
97
|
end
|
95
98
|
|
96
99
|
def op_code
|
@@ -98,13 +101,14 @@ module Monga
|
|
98
101
|
end
|
99
102
|
|
100
103
|
def command_length
|
101
|
-
HEADER_SIZE + body.size
|
104
|
+
Monga::HEADER_SIZE + body.size
|
102
105
|
end
|
103
106
|
|
104
107
|
def self.request_id
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
+
@@request_id ||= 0
|
109
|
+
@@request_id += 1
|
110
|
+
@@request_id >= 2**32 ? @@request_id = 1 : @@request_id
|
111
|
+
@@request_id
|
108
112
|
end
|
109
113
|
|
110
114
|
def self.op_name(op = nil)
|
@@ -114,9 +118,9 @@ module Monga
|
|
114
118
|
end
|
115
119
|
|
116
120
|
|
117
|
-
require File.expand_path("../
|
118
|
-
require File.expand_path("../
|
119
|
-
require File.expand_path("../
|
120
|
-
require File.expand_path("../
|
121
|
-
require File.expand_path("../
|
122
|
-
require File.expand_path("../
|
121
|
+
require File.expand_path("../protocol/query", __FILE__)
|
122
|
+
require File.expand_path("../protocol/insert", __FILE__)
|
123
|
+
require File.expand_path("../protocol/delete", __FILE__)
|
124
|
+
require File.expand_path("../protocol/update", __FILE__)
|
125
|
+
require File.expand_path("../protocol/get_more", __FILE__)
|
126
|
+
require File.expand_path("../protocol/kill_cursors", __FILE__)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Monga::Exceptions
|
2
|
+
class InvalidClientOption < StandardError; end
|
3
|
+
class UndefinedSocketType < StandardError; end
|
4
|
+
class WrongConnectionType < StandardError; end
|
5
|
+
class Disconnected < StandardError; end
|
6
|
+
class CouldNotConnect < StandardError; end
|
7
|
+
class CouldNotReconnect < StandardError; end
|
8
|
+
class QueryFailure < StandardError; end
|
9
|
+
class CursorNotFound < StandardError; end
|
10
|
+
class ClosedCursor < StandardError; end
|
11
|
+
end
|
data/lib/monga.rb
CHANGED
@@ -1,12 +1,9 @@
|
|
1
|
-
require "
|
1
|
+
require "em-synchrony"
|
2
2
|
require "bson"
|
3
3
|
require "logger"
|
4
|
+
require "forwardable"
|
4
5
|
|
5
6
|
module Monga
|
6
|
-
DEFAULT_HOST = "127.0.0.1"
|
7
|
-
DEFAULT_PORT = 27017
|
8
|
-
HEADER_SIZE = 16
|
9
|
-
|
10
7
|
extend self
|
11
8
|
|
12
9
|
def logger
|
@@ -18,13 +15,24 @@ module Monga
|
|
18
15
|
end
|
19
16
|
end
|
20
17
|
|
18
|
+
# It is strange, but cursor should be required befor fibered_connection
|
19
|
+
require File.expand_path("../monga/cursor", __FILE__)
|
20
|
+
|
21
|
+
require File.expand_path("../monga/clients/single_instance_client", __FILE__)
|
22
|
+
require File.expand_path("../monga/clients/replica_set_client", __FILE__)
|
23
|
+
require File.expand_path("../monga/connections/em_connection", __FILE__)
|
24
|
+
require File.expand_path("../monga/connections/fibered_connection", __FILE__)
|
25
|
+
require File.expand_path("../monga/connections/tcp_connection", __FILE__)
|
26
|
+
require File.expand_path("../monga/connections/em_proxy_connection", __FILE__)
|
27
|
+
require File.expand_path("../monga/connections/fibered_proxy_connection", __FILE__)
|
28
|
+
require File.expand_path("../monga/connections/proxy_connection", __FILE__)
|
29
|
+
require File.expand_path("../monga/connections/buffer", __FILE__)
|
30
|
+
|
31
|
+
require File.expand_path("../monga/client", __FILE__)
|
21
32
|
require File.expand_path("../monga/connection", __FILE__)
|
22
33
|
require File.expand_path("../monga/connection_pool", __FILE__)
|
23
|
-
require File.expand_path("../monga/client", __FILE__)
|
24
34
|
require File.expand_path("../monga/database", __FILE__)
|
25
35
|
require File.expand_path("../monga/collection", __FILE__)
|
26
|
-
require File.expand_path("../monga/
|
27
|
-
require File.expand_path("../monga/
|
28
|
-
require File.expand_path("../monga/
|
29
|
-
require File.expand_path("../monga/response", __FILE__)
|
30
|
-
require File.expand_path("../monga/request", __FILE__)
|
36
|
+
require File.expand_path("../monga/request", __FILE__)
|
37
|
+
require File.expand_path("../monga/utils/exceptions", __FILE__)
|
38
|
+
require File.expand_path("../monga/utils/constants", __FILE__)
|
data/monga.gemspec
CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "monga"
|
7
|
-
spec.version = "0.0.
|
7
|
+
spec.version = "0.0.3"
|
8
8
|
spec.authors = ["Petr Yanovich"]
|
9
9
|
spec.email = ["fl00r@yandex.ru"]
|
10
10
|
spec.description = %q{MongoDB Ruby Evented Driver on EventMachine}
|
@@ -19,8 +19,8 @@ Gem::Specification.new do |spec|
|
|
19
19
|
|
20
20
|
spec.add_development_dependency "bundler", "~> 1.3"
|
21
21
|
spec.add_development_dependency "rake"
|
22
|
+
spec.add_development_dependency "em-synchrony"
|
22
23
|
|
23
|
-
spec.add_dependency "eventmachine"
|
24
24
|
spec.add_dependency "bson"
|
25
25
|
spec.add_dependency "bson_ext"
|
26
26
|
end
|
data/spec/helpers/mongodb.rb
CHANGED
@@ -1,59 +1,136 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
@
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
1
|
+
module Fake
|
2
|
+
|
3
|
+
# Fake Response.
|
4
|
+
# It could be `ok`, or `primary?` reply.
|
5
|
+
class Response
|
6
|
+
def initialize(data, primary)
|
7
|
+
@data = data
|
8
|
+
@primary = primary
|
9
|
+
end
|
10
|
+
|
11
|
+
def ok(doc = nil)
|
12
|
+
document = doc || { ok: 1.0 }
|
13
|
+
[flags, cursor_id, starting_from, number_returned].pack("LQLL")
|
14
|
+
b = BSON::ByteBuffer.new
|
15
|
+
b.put_int(flags)
|
16
|
+
b.put_long(cursor_id)
|
17
|
+
b.put_int(starting_from)
|
18
|
+
b.put_int(number_returned)
|
19
|
+
b.append!(BSON::BSON_C.serialize(document).to_s)
|
20
|
+
|
21
|
+
header(b) + b.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def primary?
|
25
|
+
doc = { ismaster: @primary }
|
26
|
+
ok(doc)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def header(body)
|
32
|
+
length = 16 + body.to_s.bytesize
|
33
|
+
request_id = 0
|
34
|
+
op_code = 0
|
35
|
+
|
36
|
+
h = BSON::ByteBuffer.new
|
37
|
+
h.put_int(length)
|
38
|
+
h.put_int(request_id)
|
39
|
+
h.put_int(response_to)
|
40
|
+
h.put_int(op_code)
|
41
|
+
h.to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
def response_to
|
45
|
+
@data.unpack("LLLL")[1]
|
15
46
|
end
|
16
47
|
|
17
|
-
def
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
48
|
+
def flags; 0; end
|
49
|
+
def cursor_id; 0; end
|
50
|
+
def starting_from; 0; end
|
51
|
+
def number_returned; 1; end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Fake MongoDB server.
|
55
|
+
# Replies with `ok` message for all 2004 op queries instead `isMaster`.
|
56
|
+
# For other ops it sends no answer.
|
57
|
+
class Node < EM::Connection
|
58
|
+
def initialize(si)
|
59
|
+
@si = si
|
60
|
+
@si.server = self
|
22
61
|
end
|
23
62
|
|
24
|
-
def
|
25
|
-
|
63
|
+
def primary
|
64
|
+
@si.rs.primary == @si if @si.rs
|
65
|
+
end
|
66
|
+
|
67
|
+
def receive_data(data)
|
68
|
+
begin
|
69
|
+
length, req_id, resp_to, op_code = data.unpack("LLLL")
|
70
|
+
piece = data.slice!(0, length)
|
71
|
+
if op_code == 2004
|
72
|
+
if piece["isMaster"]
|
73
|
+
send_data Fake::Response.new(piece, primary).primary?
|
74
|
+
else
|
75
|
+
send_data Fake::Response.new(piece, primary).ok
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end while data != ""
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Single instance binded on one port.
|
83
|
+
# Could be stopped or started.
|
84
|
+
class SingleInstance
|
85
|
+
attr_reader :rs
|
86
|
+
attr_accessor :server
|
87
|
+
|
88
|
+
def initialize(port, rs=nil)
|
89
|
+
@rs = rs
|
90
|
+
@port = port
|
26
91
|
end
|
27
92
|
|
28
93
|
def start
|
29
|
-
|
30
|
-
@
|
31
|
-
sleep(1)
|
94
|
+
@connected = true
|
95
|
+
@sign = EM.start_server '127.0.0.1', @port, Fake::Node, self
|
32
96
|
end
|
33
97
|
|
34
98
|
def stop
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
99
|
+
@server.close_connection
|
100
|
+
EM.stop_server @sign
|
101
|
+
@connected = false
|
102
|
+
end
|
103
|
+
|
104
|
+
def connected?
|
105
|
+
@connected
|
39
106
|
end
|
40
107
|
end
|
41
108
|
|
109
|
+
# Fake Replica set with a number of Single Instances.
|
110
|
+
# You could stop/start primary/secondary.
|
42
111
|
class ReplicaSet
|
43
|
-
def initialize(ports
|
44
|
-
|
45
|
-
|
46
|
-
@instances = {}
|
47
|
-
ports.each.with_index do |prt, i|
|
48
|
-
dbpath = opts[:dbpath] + "-#{i}"
|
49
|
-
o = opts.merge({ port: prt[:port], dbpath: dbpath })
|
50
|
-
@instances[prt[:port]] = Instance.new(o)
|
112
|
+
def initialize(ports)
|
113
|
+
@instances = ports.map do |port|
|
114
|
+
SingleInstance.new(port, self)
|
51
115
|
end
|
52
116
|
end
|
53
117
|
|
118
|
+
def start_all
|
119
|
+
@instances.each(&:start)
|
120
|
+
end
|
121
|
+
|
122
|
+
def vote
|
123
|
+
@primary = @instances.select{|inst| inst.connected? }.sample
|
124
|
+
end
|
125
|
+
|
54
126
|
def primary
|
55
|
-
|
56
|
-
|
127
|
+
@primary ||= begin
|
128
|
+
@instances.select{|inst| inst.connected? }.sample
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def secondaries
|
133
|
+
@instances - [@primary]
|
57
134
|
end
|
58
135
|
end
|
59
136
|
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Monga::Collection do
|
4
|
+
before do
|
5
|
+
@client = Monga::Client.new(type: :block, pool_size: 10)
|
6
|
+
@db = @client["dbTest"]
|
7
|
+
@collection = @db["testCollection"]
|
8
|
+
@collection.safe_remove
|
9
|
+
docs = []
|
10
|
+
10.times do |i|
|
11
|
+
docs << { artist: "Madonna", title: "Track #{i+1}" }
|
12
|
+
docs << { artist: "Radiohead", title: "Track #{i+1}" }
|
13
|
+
end
|
14
|
+
@collection.safe_insert(docs)
|
15
|
+
end
|
16
|
+
|
17
|
+
# QUERY
|
18
|
+
|
19
|
+
describe "query" do
|
20
|
+
it "should fetch all documents" do
|
21
|
+
docs = @collection.find.all
|
22
|
+
docs.size.must_equal 20
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should fetch all docs with skip and limit" do
|
26
|
+
docs = @collection.find.skip(10).limit(4).all
|
27
|
+
docs.size.must_equal 4
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should fetch first" do
|
31
|
+
doc = @collection.first
|
32
|
+
doc.keys.must_equal ["_id", "artist", "title"]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# INSERT
|
37
|
+
|
38
|
+
describe "insert" do
|
39
|
+
before do
|
40
|
+
@collection.safe_ensure_index({ "personal_id" => 1 }, { unique: true, sparse: true })
|
41
|
+
end
|
42
|
+
|
43
|
+
after do
|
44
|
+
@collection.drop_index( personal_id: 1 )
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should insert single doc" do
|
48
|
+
doc = { name: "Peter", age: 18 }
|
49
|
+
@collection.safe_insert(doc)
|
50
|
+
resp = @collection.find(name: "Peter").all
|
51
|
+
resp.size.must_equal 1
|
52
|
+
resp.first["age"].must_equal 18
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should insert batch of docs" do
|
56
|
+
docs = [{ name: "Peter", age: 18 }, {name: "Jhon", age: 18}]
|
57
|
+
@collection.safe_insert(docs)
|
58
|
+
resp = @collection.find(age: 18).all
|
59
|
+
resp.size.must_equal 2
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should fail on uniq index" do
|
63
|
+
docs = [{ name: "Peter", age: 18, personal_id: 20 }, {name: "Jhon", age: 18, personal_id: 20}, {name: "Rebeca", age: 21, personal_id: 5}]
|
64
|
+
proc{ @collection.safe_insert(docs) }.must_raise Monga::Exceptions::QueryFailure
|
65
|
+
@collection.count.must_equal 21
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should continue_on_error" do
|
69
|
+
docs = [{ name: "Peter", age: 18, personal_id: 20 }, {name: "Jhon", age: 18, personal_id: 20}, {name: "Rebeca", age: 21, personal_id: 5}]
|
70
|
+
proc{ @collection.safe_insert(docs, continue_on_error: true) }.must_raise Monga::Exceptions::QueryFailure
|
71
|
+
@collection.count.must_equal 22
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# UPDATE
|
76
|
+
|
77
|
+
describe "update" do
|
78
|
+
it "should make simple update (first matching)" do
|
79
|
+
@collection.safe_update({ artist: "Madonna" }, { "$set" => { country: "USA" } })
|
80
|
+
@collection.count( query: { artist: "Madonna", country: "USA" }).must_equal 1
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should create non existing item (upsert)" do
|
84
|
+
@collection.safe_update({ artist: "Bjork" }, { "$set" => { country: "Iceland" } }, { upsert: true })
|
85
|
+
@collection.count(query: { artist: "Bjork" }).must_equal 1
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should update all matching data (multi_update)" do
|
89
|
+
@collection.safe_update({ artist: "Madonna" }, { "$set" => { country: "USA" } }, {multi_update: true})
|
90
|
+
docs = @collection.find(artist: "Madonna").all
|
91
|
+
docs.each{ |d| d["country"].must_equal "USA" }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# REMOVE
|
96
|
+
|
97
|
+
describe "remove" do
|
98
|
+
it "should delete all matching docs" do
|
99
|
+
@collection.safe_delete(artist: "Madonna")
|
100
|
+
@collection.count(query: { artist: "Madonna" }).must_equal 0
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should delete first matching doc (single_remove)" do
|
104
|
+
@collection.safe_delete({ artist: "Madonna" }, single_remove: true)
|
105
|
+
@collection.count(query: { artist: "Madonna" }).must_equal 9
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# COUNT
|
110
|
+
|
111
|
+
describe "count" do
|
112
|
+
it "should count all docs" do
|
113
|
+
@collection.count.must_equal 20
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should count all docs with query" do
|
117
|
+
@collection.count(query: { artist: "Madonna" }).must_equal 10
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should count all docs with limit" do
|
121
|
+
@collection.count(query: { artist: "Madonna" }, limit: 5).must_equal 5
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should count all docs with limit and skip" do
|
125
|
+
@collection.count(query: { artist: "Madonna" }, limit: 5, skip: 6).must_equal 4
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# ENSURE/DROP INDEX
|
130
|
+
|
131
|
+
describe "ensure_index" do
|
132
|
+
before do
|
133
|
+
@collection.drop_indexes
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should create index" do
|
137
|
+
@collection.safe_ensure_index(title: 1)
|
138
|
+
docs = @collection.get_indexes
|
139
|
+
docs.any?{ |doc| doc["key"] == {"title" => 1}}.must_equal true
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should create sparse index" do
|
143
|
+
@collection.safe_ensure_index({ title: 1 }, sparse: true)
|
144
|
+
docs = @collection.get_indexes
|
145
|
+
docs.any?{ |doc| doc["key"] == {"title" => 1} && doc["sparse"] == true }.must_equal true
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should create unique index" do
|
149
|
+
@collection.safe_ensure_index({ some_field: 1 }, unique: true, sparse: true)
|
150
|
+
docs = @collection.get_indexes
|
151
|
+
docs.any?{ |doc| doc["key"] == {"some_field" => 1} && doc["unique"] == true }.must_equal true
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should drop single index" do
|
155
|
+
@collection.safe_ensure_index(title: 1)
|
156
|
+
docs = @collection.get_indexes
|
157
|
+
docs.any?{ |doc| doc["key"] == {"title" => 1}}.must_equal true
|
158
|
+
@collection.drop_index(title: 1)
|
159
|
+
docs = @collection.get_indexes
|
160
|
+
docs.any?{ |doc| doc["key"] == {"title" => 1}}.must_equal false
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should drop all indexes (except primary on _id)" do
|
164
|
+
@collection.safe_ensure_index(title: 1)
|
165
|
+
docs = @collection.get_indexes
|
166
|
+
docs.any?{ |doc| doc["key"] == {"title" => 1}}.must_equal true
|
167
|
+
@collection.drop_indexes
|
168
|
+
docs = @collection.get_indexes
|
169
|
+
docs.select{ |d| d["ns"] == "dbTest.testCollection" }.size.must_equal 1
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|