monga 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -143,7 +143,11 @@ puts "We have got #{docs.size} documents in this pretty array"
143
143
  * [ ] check maxBsonSize / validate
144
144
  * [x] cmd
145
145
  * [x] eval
146
- * [ ] aggregation
146
+ * [x] aggregate
147
+ * [x] distinct
148
+ * [x] group
149
+ * [x] mapReduce
150
+ * [x] text
147
151
  * [ ] gridfs?
148
152
 
149
153
  ### Collection
@@ -1,86 +1,191 @@
1
1
  require 'benchmark'
2
- require 'mongo'
3
- require 'moped'
4
- require File.expand_path('../../lib/monga', __FILE__)
5
2
 
6
- include Mongo
3
+ TOTAL_INSERTS = 10000
4
+ TOTAL_READS = 50
7
5
 
8
- Benchmark.bm do |x|
9
- total = 10000
10
- fetch = 50
11
- mongo_collection = MongoClient.new.db("dbTest").collection("testCollection")
12
- monga_collection = Monga::Client.new(type: :block).get_database("dbTest").get_collection("testCollection")
13
- Monga.logger.level = Logger::ERROR
14
- moped_session = Moped::Session.new([ "127.0.0.1:27017" ])
15
- moped_session.use "dbTest"
6
+ chars = ('a'..'z').to_a
7
+ DOCS = [10, 100, 1000, 10000].map do |size|
8
+ doc = {}
9
+ doc[:title] = "Title"
10
+ doc[:body] = size.times.map{ chars.sample }.join
11
+ [size, doc]
12
+ end
16
13
 
17
- document = {}
18
- document[:title] = "Some title"
19
- chars = ('a'..'z').to_a
20
- document[:body] = 100.times.map{ chars.sample } * ""
14
+ # Mongo Ruby Driver
21
15
 
22
- sleep 0.5
16
+ fork do
17
+ require 'mongo'
18
+ include Mongo
23
19
 
24
- GC.start
20
+ Benchmark.bm do |x|
21
+ collection = MongoClient.new.db("dbTest").collection("testCollection")
25
22
 
26
- x.report("Inserting with mongo") do
27
- total.times do |i|
28
- mongo_collection.insert(document.dup)
29
- end
30
- end
23
+ DOCS.each do |size, doc|
24
+ GC.start
25
+ x.report("Mongo: Inserting #{size}b document") do
26
+ TOTAL_INSERTS.times do
27
+ collection.insert(doc.dup)
28
+ end
29
+ end
31
30
 
32
- GC.start
31
+ GC.start
32
+ x.report("Mongo: Reading #{size}b documents") do
33
+ TOTAL_READS.times do
34
+ collection.find.to_a
35
+ end
36
+ end
33
37
 
34
- x.report("Fetching with mongo") do
35
- fetch.times do
36
- mongo_collection.find.to_a
38
+ collection.drop
37
39
  end
38
40
  end
39
- mongo_collection.drop
41
+ end
40
42
 
41
- sleep 0.5
43
+ Process.waitall
44
+ puts "---"*5
42
45
 
43
- GC.start
46
+ # Monga Driver (blocking mode)
44
47
 
45
- x.report("Inserting with monga") do
46
- total.times do |i|
47
- monga_collection.safe_insert(document.dup)
48
- end
49
- end
48
+ fork do
49
+ require File.expand_path('../../lib/monga', __FILE__)
50
+
51
+ Benchmark.bm do |x|
52
+ collection = Monga::Client.new.get_database("dbTest").get_collection("testCollection")
53
+
54
+ DOCS.each do |size, doc|
55
+ GC.start
56
+ x.report("Monga: Inserting #{size}b document") do
57
+ TOTAL_INSERTS.times do
58
+ collection.safe_insert(doc)
59
+ end
60
+ end
50
61
 
51
- GC.start
62
+ GC.start
63
+ x.report("Monga: Reading #{size}b documents") do
64
+ TOTAL_READS.times do
65
+ collection.find.all
66
+ end
67
+ end
52
68
 
53
- x.report("Fetching with monga") do
54
- fetch.times do
55
- monga_collection.find.all
69
+ collection.drop
56
70
  end
57
71
  end
72
+ end
58
73
 
59
- monga_collection.drop
74
+ Process.waitall
75
+ puts "---"*5
60
76
 
61
- sleep 0.5
77
+ # Moped
62
78
 
63
- GC.start
79
+ fork do
80
+ require 'moped'
64
81
 
65
- x.report("Inserting with moped") do
66
- moped_session.with(safe: true) do |safe|
67
- total.times do |i|
68
- safe[:testCollection].insert(document)
82
+ Benchmark.bm do |x|
83
+ session = Moped::Session.new([ "127.0.0.1:27017" ])
84
+ session.use("dbTest")
85
+
86
+ DOCS.each do |size, doc|
87
+ GC.start
88
+ x.report("Moped: Inserting #{size}b document") do
89
+ session.with(safe: true) do |safe|
90
+ TOTAL_INSERTS.times do
91
+ safe[:testCollection].insert(doc)
92
+ end
93
+ end
69
94
  end
70
- end
71
- end
72
95
 
73
- GC.start
96
+ GC.start
97
+ x.report("Moped: Reading #{size}b documents") do
98
+ TOTAL_READS.times do
99
+ session[:testCollection].find.to_a
100
+ end
101
+ end
74
102
 
75
- x.report("Fetching with moped") do
76
- fetch.times do
77
- moped_session[:testCollection].find.to_a
103
+ session[:testCollection].drop
78
104
  end
79
105
  end
80
-
81
- monga_collection.drop
82
106
  end
83
107
 
108
+ Process.waitall
109
+
110
+ # # require 'moped'
111
+
112
+ # # include Mongo
113
+
114
+ # Benchmark.bm do |x|
115
+ # total = 1000
116
+ # fetch = 50
117
+ # # mongo_collection = MongoClient.new.db("dbTest").collection("testCollection")
118
+ # monga_collection = Monga::Client.new(type: :block).get_database("dbTest").get_collection("testCollection")
119
+ # Monga.logger.level = Logger::ERROR
120
+ # # moped_session = Moped::Session.new([ "127.0.0.1:27017" ])
121
+ # # moped_session.use "dbTest"
122
+
123
+ # document = {}
124
+ # document[:title] = "Some title"
125
+ # document[:body] = 10000.times.map{ chars.sample } * ""
126
+
127
+ # # sleep 0.5
128
+
129
+ # # GC.start
130
+
131
+ # # x.report("Inserting with mongo") do
132
+ # # total.times do |i|
133
+ # # mongo_collection.insert(document.dup)
134
+ # # end
135
+ # # end
136
+
137
+ # # GC.start
138
+
139
+ # # x.report("Fetching with mongo") do
140
+ # # fetch.times do
141
+ # # mongo_collection.find.to_a
142
+ # # end
143
+ # # end
144
+ # # mongo_collection.drop
145
+
146
+ # # sleep 0.5
147
+
148
+ # # GC.start
149
+
150
+ # x.report("Inserting with monga") do
151
+ # total.times do |i|
152
+ # monga_collection.safe_insert(document)
153
+ # end
154
+ # end
155
+
156
+ # GC.start
157
+
158
+ # x.report("Fetching with monga") do
159
+ # fetch.times do
160
+ # monga_collection.find.all
161
+ # end
162
+ # end
163
+
164
+ # monga_collection.drop
165
+
166
+ # # sleep 0.5
167
+
168
+ # # GC.start
169
+
170
+ # # x.report("Inserting with moped") do
171
+ # # moped_session.with(safe: true) do |safe|
172
+ # # total.times do |i|
173
+ # # safe[:testCollection].insert(document)
174
+ # # end
175
+ # # end
176
+ # # end
177
+
178
+ # # GC.start
179
+
180
+ # # x.report("Fetching with moped") do
181
+ # # fetch.times do
182
+ # # moped_session[:testCollection].find.to_a
183
+ # # end
184
+ # # end
185
+
186
+ # # monga_collection.drop
187
+ # end
188
+
84
189
 
85
190
 
86
191
 
@@ -8,7 +8,7 @@ module Monga
8
8
  end
9
9
 
10
10
  # Querying database. It returns cursor.
11
- # Alias to collection#query is colleciotn#find
11
+ # Alias to collection#query is collection#find
12
12
  #
13
13
  # cursor = collection.find(title: "Madonna")
14
14
  # # choose fields to return
@@ -132,6 +132,62 @@ module Monga
132
132
  end
133
133
  end
134
134
 
135
+ def map_reduce(opts, &blk)
136
+ @db.map_reduce(@collection_name, opts) do |err, resp|
137
+ if block_given?
138
+ yield(err, resp)
139
+ else
140
+ raise err if err
141
+ return resp
142
+ end
143
+ end
144
+ end
145
+
146
+ def aggregate(pipeline, &blk)
147
+ @db.aggregate(@collection_name, pipeline) do |err, resp|
148
+ if block_given?
149
+ yield(err, resp)
150
+ else
151
+ raise err if err
152
+ return resp
153
+ end
154
+ end
155
+ end
156
+
157
+ def distinct(opts, &blk)
158
+ @db.distinct(collection_name, opts) do |err, resp|
159
+ if block_given?
160
+ yield(err, resp)
161
+ else
162
+ raise err if err
163
+ return resp
164
+ end
165
+ end
166
+ end
167
+
168
+ def group(opts, &blk)
169
+ @db.group(collection_name, opts) do |err, resp|
170
+ if block_given?
171
+ yield(err, resp)
172
+ else
173
+ raise err if err
174
+ return resp
175
+ end
176
+ end
177
+ end
178
+
179
+ def text(search, opts = {}, &blk)
180
+ opts[:search] = search
181
+ @db.text(collection_name, opts) do |err, resp|
182
+ if block_given?
183
+ yield(err, resp)
184
+ else
185
+ raise err if err
186
+ return resp
187
+ end
188
+ end
189
+ end
190
+
135
191
  # Safe methods
136
192
  [:update, :insert, :delete, :remove, :ensure_index].each do |meth|
137
193
  class_eval <<-EOS
@@ -6,17 +6,6 @@ module Monga
6
6
 
7
7
  attr_reader :type
8
8
 
9
- CONNECTIONS = {
10
- em: Monga::Connections::EMConnection,
11
- sync: Monga::Connections::FiberedConnection,
12
- block: Monga::Connections::KGIOConnection,
13
- }
14
- PROXY_CONNECTIONS = {
15
- em: Monga::Connections::EMProxyConnection,
16
- sync: Monga::Connections::FiberedProxyConnection,
17
- block: Monga::Connections::ProxyConnection,
18
- }
19
-
20
9
  # Simple connection wrapper.
21
10
  # Accpets
22
11
  # * host/port or server
@@ -35,15 +24,36 @@ module Monga
35
24
  end
36
25
  timeout = opts[:timeout]
37
26
 
38
- conn_type = CONNECTIONS[@type]
39
- raise Monga::Exceptions::WrongConnectionType, "Connection type `#{opts[:type]}` is non valid, choose one of: :em, :sync, or :block" unless conn_type
40
- @connection = conn_type.connect(host, port, timeout)
27
+ @connection = case @type
28
+ when :em
29
+ require File.expand_path("../connections/em_connection", __FILE__)
30
+ Monga::Connections::EMConnection.connect(host, port, timeout)
31
+ when :sync
32
+ require File.expand_path("../connections/em_connection", __FILE__)
33
+ require File.expand_path("../connections/fibered_connection", __FILE__)
34
+ Monga::Connections::FiberedConnection.connect(host, port, timeout)
35
+ when :block
36
+ require File.expand_path("../connections/kgio_connection", __FILE__)
37
+ Monga::Connections::KGIOConnection.connect(host, port, timeout)
38
+ else
39
+ raise Monga::Exceptions::WrongConnectionType, "Connection type `#{opts[:type]}` is non valid, choose one of: :em, :sync, or :block" unless conn_type
40
+ end
41
41
  end
42
-
42
+
43
43
  # Returns name of proxy_connection class
44
44
  def self.proxy_connection_class(type, client)
45
- conn_class = PROXY_CONNECTIONS[type]
46
- conn_class.new(client) if conn_class
45
+ case type
46
+ when :em
47
+ require File.expand_path("../connections/em_proxy_connection", __FILE__)
48
+ Monga::Connections::EMProxyConnection.new(client)
49
+ when :sync
50
+ require File.expand_path("../connections/em_proxy_connection", __FILE__)
51
+ require File.expand_path("../connections/fibered_proxy_connection", __FILE__)
52
+ Monga::Connections::FiberedProxyConnection.new(client)
53
+ when :block
54
+ require File.expand_path("../connections/proxy_connection", __FILE__)
55
+ Monga::Connections::ProxyConnection.new(client)
56
+ end
47
57
  end
48
58
  end
49
59
  end
@@ -1,33 +1,54 @@
1
1
  module Monga::Connections
2
2
  class Buffer
3
- # include Enumerable
3
+ include Enumerable
4
+ EMPTY = ""
4
5
 
5
- attr_reader :buffer, :responses
6
+ attr_reader :buffer, :buffer_size
6
7
 
7
8
  def initialize
8
9
  @buffer = ""
9
10
  @position = 0
10
- @docs = []
11
- @responses = []
11
+ @buffer_size = 0
12
+ @response = []
12
13
  end
13
14
 
14
15
  def append(data)
15
16
  @buffer << data
16
- @buffer_size = @buffer.bytesize
17
- parse
18
- @buffer
17
+ @buffer_size += data.bytesize
18
+ end
19
+
20
+ def each
21
+ while doc = parse_buffer
22
+ yield doc
23
+ end
19
24
  end
20
25
 
21
- def parse
22
- begin
23
- more = parse_meta if @position == 0
24
- parse_doc if @position > 0
25
- end while more && @position == 0
26
+ def parse_buffer
27
+ return if @buffer_size == 0
28
+
29
+ if @position == 0
30
+ parse_doc if parse_meta
31
+ else
32
+ parse_doc
33
+ end
34
+
35
+ if @number_returned == 0
36
+ if @buffer_size == @position
37
+ @buffer.clear
38
+ @buffer_size = 0
39
+ else
40
+ @buffer = @buffer[@position, @buffer_size-@position]
41
+ @buffer_size -= @position
42
+ end
43
+ @position = 0
44
+
45
+ @response
46
+ end
26
47
  end
27
48
 
28
49
  def parse_meta
29
50
  return if @buffer_size < 36
30
- @response = []
51
+ @response.clear
31
52
  @response << ::BinUtils.get_int32_le(@buffer, @position)
32
53
  @response << ::BinUtils.get_int32_le(@buffer, @position += 4)
33
54
  @response << ::BinUtils.get_int32_le(@buffer, @position += 4)
@@ -35,43 +56,36 @@ module Monga::Connections
35
56
  @response << ::BinUtils.get_int32_le(@buffer, @position += 4)
36
57
  @response << ::BinUtils.get_int64_le(@buffer, @position += 4)
37
58
  @response << ::BinUtils.get_int32_le(@buffer, @position += 8)
38
- @response << (@number_returned = ::BinUtils.get_int32_le(@buffer, @position += 4))
59
+ @number_returned = ::BinUtils.get_int32_le(@buffer, @position += 4)
60
+ @response << @number_returned
61
+ @position += 4
39
62
  @response << []
40
63
 
41
- @position += 4
42
64
  end
43
65
 
44
66
  def parse_doc
45
- while true
46
- break if done
47
- break if @buffer_size < @position + 4
48
- doc_length = ::BinUtils.get_int32_le(@buffer, @position)
49
- break if @buffer_size < @position + doc_length
67
+ @str_io = nil if @number_returned == @response[7]
50
68
 
51
- doc = @buffer[@position, doc_length]
52
- @response[-1] << CBson.deserialize(doc)
53
- @position += doc_length
69
+ current_pos = @position
70
+ while (l = @buffer_size - @position) > 4
71
+ doc_length = ::BinUtils.get_int32_le(@buffer, @position)
72
+ break if l < doc_length
54
73
  @number_returned -= 1
74
+ @position += doc_length
55
75
  end
56
- end
57
76
 
58
- def each
59
- while resp = @responses.shift
60
- yield resp
77
+ if @str_io
78
+ @str_io << @buffer[current_pos..@position]
79
+ else
80
+ @str_io = StringIO.new @buffer[current_pos..@position]
61
81
  end
62
- end
63
82
 
64
- def done
65
- return false if @number_returned > 0
66
- @responses << @response
67
- @response = nil
68
- if @buffer_size == @position
69
- @buffer.clear
70
- else
71
- @buffer = @buffer[@position, @buffer_size-@position]
83
+ if @number_returned == 0
84
+ @str_io.rewind
85
+ @response[7].times do
86
+ @response[-1] << BSON::Document.from_bson(@str_io)
87
+ end
72
88
  end
73
- @buffer_size = @buffer.bytesize
74
- @position = 0
75
89
  end
76
90
  end
77
91
  end
@@ -1,3 +1,5 @@
1
+ require "eventmachine"
2
+
1
3
  module Monga::Connections
2
4
  class EMConnection < EM::Connection
3
5
  include EM::Deferrable
@@ -1,7 +1,10 @@
1
1
  require 'kgio'
2
- require 'io/nonblock' # ha?
2
+
3
+ # Toy blocking connection over TCP
3
4
  module Monga::Connections
4
5
  class KGIOConnection
6
+ TO_RECV = 512
7
+
5
8
  def self.connect(host, port, timeout)
6
9
  new(host, port, timeout)
7
10
  end
@@ -10,6 +13,7 @@ module Monga::Connections
10
13
  @host, @port, @timout = host, port, timeout
11
14
  @connected = true
12
15
  @buffer = Buffer.new
16
+ @tmp = ""
13
17
  end
14
18
 
15
19
  def connected?
@@ -20,10 +24,10 @@ module Monga::Connections
20
24
  def socket
21
25
  @socket ||= begin
22
26
  sock = Kgio::TCPSocket.new(@host, @port)
27
+ # MacOS doesn't support autopush
23
28
  sock.kgio_autopush = true unless RUBY_PLATFORM['darwin']
24
29
  # check connection
25
30
  sock.kgio_write ""
26
- # Macos doesn't support autopush
27
31
  @connected = true
28
32
  sock
29
33
  end
@@ -31,18 +35,19 @@ module Monga::Connections
31
35
  nil
32
36
  end
33
37
 
34
- # Fake answer, as far as we are blocking
38
+ # Fake answer, as far as we are blocking,
39
+ # but we should support API
35
40
  def responses
36
41
  0
37
42
  end
38
43
 
39
44
  def send_command(msg, request_id=nil, &cb)
40
45
  raise Errno::ECONNREFUSED, "Connection Refused" unless socket
41
- socket.kgio_write msg.to_s
46
+ socket.kgio_write msg
42
47
  if cb
43
48
  read_socket
44
49
 
45
- message = @buffer.responses.shift
50
+ message = @buffer.first
46
51
  rid = message[2]
47
52
 
48
53
  fail "Returned Request Id is not equal to sended one (#{rid} != #{request_id}), #{message}" if rid != request_id
@@ -58,19 +63,24 @@ module Monga::Connections
58
63
  end
59
64
 
60
65
  def read_socket
61
- torecv = 512
62
- length = nil
63
- buf = ''.force_encoding('ASCII-8BIT')
64
- tmp = ''
65
- while torecv > 0
66
- resp = socket.kgio_read(torecv, tmp)
67
- raise Errno::ECONNREFUSED.new "Nil was return. Closing connection" unless resp
68
- buf << resp
69
- size = buf.bytesize
70
- length ||= ::BinUtils.get_int32_le(buf) if size > 4
71
- torecv = length - size if length
66
+ while @buffer.buffer_size < 4
67
+ unless socket.kgio_read(TO_RECV, @tmp)
68
+ raise Errno::ECONNREFUSED.new "Nil was return. Closing connection"
69
+ end
70
+
71
+ @buffer.append(@tmp)
72
+
73
+ size = @buffer.buffer_size
74
+ if size >= 4
75
+ length = ::BinUtils.get_int32_le(@buffer.buffer)
76
+
77
+ torecv = length - size
78
+ if torecv > 0
79
+ socket.read(torecv, @tmp)
80
+ @buffer.append(@tmp)
81
+ end
82
+ end
72
83
  end
73
- @buffer.append(buf)
74
84
  end
75
85
 
76
86
  def primary?
@@ -83,7 +93,7 @@ module Monga::Connections
83
93
  request_id = req.request_id
84
94
  socket.kgio_write command
85
95
  read_socket
86
- message = @buffer.responses.shift
96
+ message = @buffer.first
87
97
  @primary = message.last.first["ismaster"]
88
98
  yield @primary ? :primary : :secondary
89
99
  rescue => e
@@ -146,12 +146,79 @@ module Monga
146
146
  run_cmd(cmd, blk)
147
147
  end
148
148
 
149
- def map_reduce
150
-
149
+ # Run mapReduce command.
150
+ # Available options:
151
+ #
152
+ # * map - A JavaScript function that associates or “maps” a value with a key and emits the key and value pair.
153
+ # * reduce - A JavaScript function that “reduces” to a single object all the values associated with a particular key.
154
+ # * out - Specifies the location of the result of the map-reduce operation.
155
+ # * query - Specifies the selection criteria.
156
+ # * sort - Sorts the input documents.
157
+ # * limit - Specifies a maximum number of documents to return from the collection
158
+ # * finalize
159
+ # * scope
160
+ # * jsMode
161
+ # * verbose
162
+ #
163
+ # Inline response returned by default.
164
+ #
165
+ def map_reduce(collection_name, opts, &blk)
166
+ cmd = {}
167
+ cmd[:mapReduce] = collection_name
168
+ cmd.merge! opts
169
+ cmd[:out] ||= { inline: 1 }
170
+ run_cmd(cmd, blk)
151
171
  end
152
172
 
153
- def aggregate
154
-
173
+ # Run aggregate command.
174
+ #
175
+ def aggregate(collection_name, pipeline, &blk)
176
+ cmd = {}
177
+ cmd[:aggregate] = collection_name
178
+ cmd[:pipeline] = pipeline
179
+ run_cmd(cmd, blk)
180
+ end
181
+
182
+ # Run distinct command.
183
+ # You should pass collection_name and key.
184
+ # Query option is optional.
185
+ #
186
+ def distinct(collection_name, opts, &blk)
187
+ cmd = {}
188
+ cmd[:distinct] = collection_name
189
+ cmd.merge! opts
190
+ run_cmd(cmd, blk)
191
+ end
192
+
193
+ # Run group command.
194
+ # Available options are:
195
+ # key – Specifies one or more document fields to group
196
+ # $reduce – Specifies an aggregation function that operates on the documents during the grouping operation
197
+ # initial – Initializes the aggregation result document
198
+ # $keyf – Specifies a function that creates a “key object” for use as the grouping key
199
+ # cond – Specifies the selection criteria to determine which documents in the collection to process
200
+ # finalize – Specifies a function that runs each item in the result
201
+ #
202
+ def group(collection_name, opts, &blk)
203
+ cmd = {}
204
+ cmd[:group] = opts
205
+ cmd[:group][:ns] ||= collection_name
206
+ run_cmd(cmd, blk)
207
+ end
208
+
209
+ # Run text command.
210
+ # Available options are:
211
+ # search (string) – A string of terms that MongoDB parses and uses to query the text index
212
+ # filter (document) – A query document to further limit the results of the query using another database field
213
+ # project (document) – Allows you to limit the fields returned by the query to only those specified.
214
+ # limit (number) – Specify the maximum number of documents to include in the response
215
+ # language (string) – Specify the language that determines for the search the list of stop words and the rules for the stemmer and tokenizer
216
+ #
217
+ def text(collection_name, opts, &blk)
218
+ cmd = {}
219
+ cmd[:text] = collection_name
220
+ cmd.merge! opts
221
+ run_cmd(cmd, blk)
155
222
  end
156
223
 
157
224
  # Just helper to show all list of collections
@@ -13,7 +13,7 @@ module Monga::Protocol
13
13
  msg = ::BinUtils.append_int32_le!(nil, 0)
14
14
  msg << full_name << Monga::NULL_BYTE
15
15
  ::BinUtils.append_int32_le!(msg, flags)
16
- msg << BSON::BSON_C.serialize(query).to_s
16
+ msg << query.to_bson
17
17
  msg
18
18
  end
19
19
  end
@@ -15,10 +15,11 @@ module Monga::Protocol
15
15
  case documents
16
16
  when Array
17
17
  documents.each do |doc|
18
- msg << BSON::BSON_C.serialize(doc).to_s
18
+ msg << doc.to_bson
19
19
  end
20
20
  when Hash
21
- msg << BSON::BSON_C.serialize(documents).to_s
21
+ # msg << BSON::BSON_C.serialize(documents).to_s
22
+ msg << documents.to_bson
22
23
  end
23
24
  msg
24
25
  end
@@ -26,8 +26,8 @@ module Monga::Protocol
26
26
  msg = ::BinUtils.append_int32_le!(nil, flags)
27
27
  msg << full_name << Monga::NULL_BYTE
28
28
  ::BinUtils.append_int32_le!(msg, skip, limit)
29
- msg << BSON::BSON_C.serialize(query).to_s
30
- msg << BSON::BSON_C.serialize(selector).to_s if selector.any?
29
+ msg << query.to_bson
30
+ msg << selector.to_bson if selector.any?
31
31
  msg
32
32
  end
33
33
  end
@@ -15,8 +15,8 @@ module Monga::Protocol
15
15
  msg = ::BinUtils.append_int32_le!(nil, 0)
16
16
  msg << full_name << Monga::NULL_BYTE
17
17
  ::BinUtils.append_int32_le!(msg, flags)
18
- msg << BSON::BSON_C.serialize(query).to_s
19
- msg << BSON::BSON_C.serialize(update).to_s
18
+ msg << query.to_bson
19
+ msg << update.to_bson
20
20
  msg
21
21
  end
22
22
  end
data/lib/monga.rb CHANGED
@@ -1,4 +1,4 @@
1
- require "em-synchrony"
1
+ gem "bson", "2.0.0.beta"
2
2
  require "bson"
3
3
  require "bin_utils"
4
4
  require "logger"
@@ -21,13 +21,6 @@ require File.expand_path("../monga/cursor", __FILE__)
21
21
 
22
22
  require File.expand_path("../monga/clients/single_instance_client", __FILE__)
23
23
  require File.expand_path("../monga/clients/replica_set_client", __FILE__)
24
- require File.expand_path("../monga/connections/em_connection", __FILE__)
25
- require File.expand_path("../monga/connections/fibered_connection", __FILE__)
26
- require File.expand_path("../monga/connections/tcp_connection", __FILE__)
27
- require File.expand_path("../monga/connections/kgio_connection", __FILE__)
28
- require File.expand_path("../monga/connections/em_proxy_connection", __FILE__)
29
- require File.expand_path("../monga/connections/fibered_proxy_connection", __FILE__)
30
- require File.expand_path("../monga/connections/proxy_connection", __FILE__)
31
24
  require File.expand_path("../monga/connections/buffer", __FILE__)
32
25
 
33
26
  require File.expand_path("../monga/client", __FILE__)
data/monga.gemspec CHANGED
@@ -4,11 +4,11 @@ $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.6"
7
+ spec.version = "0.0.7"
8
8
  spec.authors = ["Petr Yanovich"]
9
9
  spec.email = ["fl00r@yandex.ru"]
10
- spec.description = %q{MongoDB Ruby Evented Driver on EventMachine}
11
- spec.summary = %q{MongoDB Ruby Evented Driver on EventMachine}
10
+ spec.description = %q{Yet another MongoDB Ruby Client}
11
+ spec.summary = %q{Yet another MongoDB Ruby Client}
12
12
  spec.homepage = ""
13
13
  spec.license = "MIT"
14
14
 
@@ -22,7 +22,8 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency "kgio"
23
23
  spec.add_development_dependency "em-synchrony"
24
24
 
25
- spec.add_dependency "bson"
26
- spec.add_dependency "bson_ext"
25
+ spec.add_dependency "bson", ["~> 2.0.0.beta"]
26
+ # spec.add_dependency "bson"
27
+ # spec.add_dependency "bson_ext"
27
28
  spec.add_dependency "bin_utils"
28
29
  end
@@ -1,5 +1,6 @@
1
- module Fake
1
+ require 'eventmachine'
2
2
 
3
+ module Fake
3
4
  # Fake Response.
4
5
  # It could be `ok`, or `primary?` reply.
5
6
  class Response
@@ -11,12 +12,10 @@ module Fake
11
12
  def ok(doc = nil)
12
13
  document = doc || { ok: 1.0 }
13
14
  [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)
15
+ b = ::BinUtils.append_int32_le!(nil, flags)
16
+ ::BinUtils.append_int64_le!(b, cursor_id)
17
+ ::BinUtils.append_int32_le!(b, starting_from, number_returned)
18
+ b << document.to_bson
20
19
 
21
20
  header(b) + b.to_s
22
21
  end
@@ -33,12 +32,7 @@ module Fake
33
32
  request_id = 0
34
33
  op_code = 0
35
34
 
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
35
+ h = ::BinUtils.append_int32_le!(nil, flags, request_id, response_to, op_code)
42
36
  end
43
37
 
44
38
  def response_to
@@ -169,4 +169,48 @@ describe Monga::Collection do
169
169
  docs.select{ |d| d["ns"] == "dbTest.testCollection" }.size.must_equal 1
170
170
  end
171
171
  end
172
+
173
+ # MAP/REDUCE
174
+
175
+ describe "map_reduce" do
176
+ before do
177
+ @collection.safe_remove
178
+ 5.times do |i|
179
+ @collection.safe_insert(title: "The Book", count: i)
180
+ end
181
+ end
182
+
183
+ it "should run map reduce" do
184
+ map_func = "function() {
185
+ emit(this.title, this.count);
186
+ };"
187
+ red_func = "function(docTitle, docCount) {
188
+ return Array.sum(docCount);
189
+ };"
190
+ @collection.map_reduce(map: map_func, reduce: red_func, out: {inline: 1} )["results"].first["value"].must_equal 10.0
191
+ end
192
+ end
193
+
194
+ # AGGREGATE, GROUP, DISTINCT
195
+
196
+ describe "aggregate, group, distinct" do
197
+
198
+ end
199
+
200
+ # TEXT SEARCH
201
+
202
+ # Travis.ci doesn't support text search in mongodb
203
+ # describe "text search" do
204
+ # before do
205
+ # @collection.safe_ensure_index(author: "text")
206
+ # @collection.safe_insert(author: "Lady Gaga", track: "No. 1")
207
+ # @collection.safe_insert(author: "Lady Gaga", track: "No. 2")
208
+ # @collection.safe_insert(author: "Madonna", track: "No. 1")
209
+ # end
210
+
211
+ # it "should find some tracks" do
212
+ # @collection.text("Lady")["results"].map{ |r| r["obj"]["track"] }.sort.must_equal ["No. 1", "No. 2"]
213
+ # @collection.text("Madonna")["results"].map{ |r| r["obj"]["track"] }.sort.must_equal ["No. 1"]
214
+ # end
215
+ # end
172
216
  end
@@ -305,4 +305,74 @@ describe Monga::Collection do
305
305
  end
306
306
  end
307
307
  end
308
+
309
+ # MAP/REDUCE
310
+
311
+ describe "map_reduce" do
312
+ before do
313
+ EM.run do
314
+ @collection.safe_remove do
315
+ docs = []
316
+ 5.times do |i|
317
+ docs << { title: "The Book", count: i }
318
+ end
319
+ @collection.safe_insert(docs) do
320
+ EM.stop
321
+ end
322
+ end
323
+ end
324
+ end
325
+
326
+ it "should run map reduce" do
327
+ EM.run do
328
+ map_func = "function() {
329
+ emit(this.title, this.count);
330
+ };"
331
+ red_func = "function(docTitle, docCount) {
332
+ return Array.sum(docCount);
333
+ };"
334
+ @collection.map_reduce(map: map_func, reduce: red_func, out: {inline: 1} ) do |err, res|
335
+ res["results"].first["value"].must_equal 10.0
336
+ EM.stop
337
+ end
338
+ end
339
+ end
340
+ end
341
+
342
+ # AGGREGATE, GROUP, DISTINCT
343
+
344
+ describe "aggregate, group, distinct" do
345
+
346
+ end
347
+
348
+ # TEXT SEARCH
349
+
350
+ # Travis.ci doesn't support text search in mongodb
351
+ # describe "text search" do
352
+ # before do
353
+ # EM.run do
354
+ # @collection.safe_ensure_index(author: "text") do
355
+ # @collection.safe_insert([
356
+ # { author: "Lady Gaga", track: "No. 1" },
357
+ # { author: "Lady Gaga", track: "No. 2" },
358
+ # { author: "Madonna", track: "No. 1" }
359
+ # ]) do
360
+ # EM.stop
361
+ # end
362
+ # end
363
+ # end
364
+ # end
365
+
366
+ # it "should find some tracks" do
367
+ # EM.run do
368
+ # @collection.text("Lady") do |err, res|
369
+ # res["results"].map{ |r| r["obj"]["track"] }.sort.must_equal ["No. 1", "No. 2"]
370
+ # @collection.text("Madonna") do |err, res|
371
+ # res["results"].map{ |r| r["obj"]["track"] }.sort.must_equal ["No. 1"]
372
+ # EM.stop
373
+ # end
374
+ # end
375
+ # end
376
+ # end
377
+ # end
308
378
  end
@@ -244,4 +244,60 @@ describe Monga::Collection do
244
244
  end
245
245
  end
246
246
  end
247
+
248
+ # MAP/REDUCE
249
+
250
+ describe "map_reduce" do
251
+ before do
252
+ EM.synchrony do
253
+ @collection.safe_remove
254
+ 5.times do |i|
255
+ @collection.safe_insert(title: "The Book", count: i)
256
+ end
257
+ EM.stop
258
+ end
259
+ end
260
+
261
+ it "should run map reduce" do
262
+ EM.synchrony do
263
+ map_func = "function() {
264
+ emit(this.title, this.count);
265
+ };"
266
+ red_func = "function(docTitle, docCount) {
267
+ return Array.sum(docCount);
268
+ };"
269
+ @collection.map_reduce(map: map_func, reduce: red_func, out: {inline: 1} )["results"].first["value"].must_equal 10.0
270
+ EM.stop
271
+ end
272
+ end
273
+ end
274
+
275
+ # AGGREGATE, GROUP, DISTINCT
276
+
277
+ describe "aggregate, group, distinct" do
278
+
279
+ end
280
+
281
+ # TEXT SEARCH
282
+
283
+ # Travis.ci doesn't support text search in mongodb
284
+ # describe "text search" do
285
+ # before do
286
+ # EM.synchrony do
287
+ # @collection.safe_ensure_index(author: "text")
288
+ # @collection.safe_insert(author: "Lady Gaga", track: "No. 1")
289
+ # @collection.safe_insert(author: "Lady Gaga", track: "No. 2")
290
+ # @collection.safe_insert(author: "Madonna", track: "No. 1")
291
+ # EM.stop
292
+ # end
293
+ # end
294
+
295
+ # it "should find some tracks" do
296
+ # EM.synchrony do
297
+ # @collection.text("Lady")["results"].map{ |r| r["obj"]["track"] }.sort.must_equal ["No. 1", "No. 2"]
298
+ # @collection.text("Madonna")["results"].map{ |r| r["obj"]["track"] }.sort.must_equal ["No. 1"]
299
+ # EM.stop
300
+ # end
301
+ # end
302
+ # end
247
303
  end
data/spec/spec_helper.rb CHANGED
@@ -4,6 +4,8 @@ require LIB_PATH
4
4
  require 'minitest/spec'
5
5
  require 'minitest/autorun'
6
6
  require 'minitest/reporters'
7
+ require 'em-synchrony'
8
+
7
9
  MiniTest::Reporters.use! MiniTest::Reporters::SpecReporter.new
8
10
 
9
11
  Monga.logger.level = Logger::ERROR
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: monga
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-29 00:00:00.000000000 Z
12
+ date: 2013-05-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  requirement: !ruby/object:Gem::Requirement
@@ -78,35 +78,19 @@ dependencies:
78
78
  - !ruby/object:Gem::Dependency
79
79
  requirement: !ruby/object:Gem::Requirement
80
80
  requirements:
81
- - - ! '>='
81
+ - - ~>
82
82
  - !ruby/object:Gem::Version
83
- version: '0'
83
+ version: 2.0.0.beta
84
84
  none: false
85
85
  type: :runtime
86
86
  version_requirements: !ruby/object:Gem::Requirement
87
87
  requirements:
88
- - - ! '>='
88
+ - - ~>
89
89
  - !ruby/object:Gem::Version
90
- version: '0'
90
+ version: 2.0.0.beta
91
91
  none: false
92
92
  prerelease: false
93
93
  name: bson
94
- - !ruby/object:Gem::Dependency
95
- requirement: !ruby/object:Gem::Requirement
96
- requirements:
97
- - - ! '>='
98
- - !ruby/object:Gem::Version
99
- version: '0'
100
- none: false
101
- type: :runtime
102
- version_requirements: !ruby/object:Gem::Requirement
103
- requirements:
104
- - - ! '>='
105
- - !ruby/object:Gem::Version
106
- version: '0'
107
- none: false
108
- prerelease: false
109
- name: bson_ext
110
94
  - !ruby/object:Gem::Dependency
111
95
  requirement: !ruby/object:Gem::Requirement
112
96
  requirements:
@@ -123,7 +107,7 @@ dependencies:
123
107
  none: false
124
108
  prerelease: false
125
109
  name: bin_utils
126
- description: MongoDB Ruby Evented Driver on EventMachine
110
+ description: Yet another MongoDB Ruby Client
127
111
  email:
128
112
  - fl00r@yandex.ru
129
113
  executables: []
@@ -153,7 +137,6 @@ files:
153
137
  - lib/monga/connections/fibered_proxy_connection.rb
154
138
  - lib/monga/connections/kgio_connection.rb
155
139
  - lib/monga/connections/proxy_connection.rb
156
- - lib/monga/connections/tcp_connection.rb
157
140
  - lib/monga/cursor.rb
158
141
  - lib/monga/database.rb
159
142
  - lib/monga/protocol/delete.rb
@@ -194,7 +177,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
194
177
  requirements:
195
178
  - - ! '>='
196
179
  - !ruby/object:Gem::Version
197
- hash: -3962526029351115610
180
+ hash: -862637376897312253
198
181
  version: '0'
199
182
  segments:
200
183
  - 0
@@ -203,7 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
203
186
  requirements:
204
187
  - - ! '>='
205
188
  - !ruby/object:Gem::Version
206
- hash: -3962526029351115610
189
+ hash: -862637376897312253
207
190
  version: '0'
208
191
  segments:
209
192
  - 0
@@ -213,7 +196,7 @@ rubyforge_project:
213
196
  rubygems_version: 1.8.25
214
197
  signing_key:
215
198
  specification_version: 3
216
- summary: MongoDB Ruby Evented Driver on EventMachine
199
+ summary: Yet another MongoDB Ruby Client
217
200
  test_files:
218
201
  - spec/helpers/mongodb.rb
219
202
  - spec/monga/block/collection_spec.rb
@@ -1,59 +0,0 @@
1
- require 'socket'
2
-
3
- # Currently blocking mode is very poor.
4
- # It is working as is.
5
- # Going to support reconnecting and timouts later.
6
- # Use it for tests and prototyping. Not the best choice for production.
7
-
8
- module Monga::Connections
9
- class TCPConnection
10
- def self.connect(host, port, timeout)
11
- new(host, port, timeout)
12
- end
13
-
14
- def initialize(host, port, timeout)
15
- @host, @port, @timout = host, port, timeout
16
- @connected = true
17
- @buffer = Buffer.new
18
- end
19
-
20
- def connected?
21
- @connected
22
- end
23
-
24
- def socket
25
- @socket ||= begin
26
- TCPSocket.new(@host, @port)
27
- end
28
- end
29
-
30
- # Fake answer, as far as we are blocking
31
- def responses
32
- 0
33
- end
34
-
35
- def send_command(msg, request_id=nil, &cb)
36
- socket.send msg.to_s, 0
37
- if cb
38
- length = socket.read(4)
39
- raise Errno::ECONNREFUSED, "Socket returns nothing like it would be closed." unless length
40
- @buffer.append(length)
41
- l = length.unpack("L").first
42
- rest = socket.read(l-4)
43
- @buffer.append(rest)
44
- @buffer.each do |message|
45
- rid = message[2]
46
- fail "Returned Request Id is not equal to sended one" if rid != request_id
47
- cb.call(message)
48
- end
49
- end
50
- rescue Errno::ECONNREFUSED, Errno::EPIPE => e
51
- @connected = false
52
- @socket = nil
53
- if cb
54
- err = Monga::Exceptions::Disconnected.new("Disconnected from #{@host}:#{@port}, #{e.message}")
55
- cb.call(err)
56
- end
57
- end
58
- end
59
- end