mongodb 2.1.0-alpha → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/HISTORY.md +574 -429
  2. package/Makefile +2 -5
  3. package/README.md +108 -15
  4. package/conf.json +17 -13
  5. package/index.js +13 -2
  6. package/lib/admin.js +113 -47
  7. package/lib/aggregation_cursor.js +56 -28
  8. package/lib/apm.js +608 -0
  9. package/lib/bulk/common.js +7 -7
  10. package/lib/bulk/ordered.js +56 -17
  11. package/lib/bulk/unordered.js +52 -14
  12. package/lib/collection.js +671 -212
  13. package/lib/command_cursor.js +60 -32
  14. package/lib/cursor.js +313 -115
  15. package/lib/db.js +264 -105
  16. package/lib/gridfs/chunk.js +26 -29
  17. package/lib/gridfs/grid_store.js +150 -64
  18. package/lib/gridfs-stream/download.js +310 -0
  19. package/lib/gridfs-stream/index.js +335 -0
  20. package/lib/gridfs-stream/upload.js +450 -0
  21. package/lib/metadata.js +64 -0
  22. package/lib/mongo_client.js +69 -39
  23. package/lib/mongos.js +65 -20
  24. package/lib/replset.js +69 -34
  25. package/lib/server.js +35 -1
  26. package/lib/topology_base.js +22 -10
  27. package/lib/url_parser.js +111 -13
  28. package/lib/utils.js +9 -8
  29. package/mongolabs.js +427 -0
  30. package/package.json +8 -6
  31. package/t.js +68 -51
  32. package/test.js +12 -0
  33. package/test_boot/boot.sh +3 -0
  34. package/test_boot/ca.pem +49 -0
  35. package/test_boot/client.pem +48 -0
  36. package/test_boot/client_password.pem +51 -0
  37. package/test_boot/connect.js +29 -0
  38. package/test_boot/data/WiredTiger +2 -0
  39. package/test_boot/data/WiredTiger.lock +1 -0
  40. package/test_boot/data/WiredTiger.turtle +6 -0
  41. package/test_boot/data/WiredTiger.wt +0 -0
  42. package/test_boot/data/WiredTigerLAS.wt +0 -0
  43. package/test_boot/data/_mdb_catalog.wt +0 -0
  44. package/test_boot/data/collection-0-757073248613337118.wt +0 -0
  45. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-44-37Z-00000 +0 -0
  46. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-45-15Z-00000 +0 -0
  47. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-46-31Z-00000 +0 -0
  48. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-47-25Z-00000 +0 -0
  49. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-49-07Z-00000 +0 -0
  50. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-50-41Z-00000 +0 -0
  51. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-50-53Z-00000 +0 -0
  52. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-52-31Z-00000 +0 -0
  53. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-54-53Z-00000 +0 -0
  54. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-55-09Z-00000 +0 -0
  55. package/test_boot/data/diagnostic.data/metrics.2015-10-07T14-55-38Z-00000 +0 -0
  56. package/test_boot/data/index-1-757073248613337118.wt +0 -0
  57. package/test_boot/data/mongod.lock +0 -0
  58. package/test_boot/data/sizeStorer.wt +0 -0
  59. package/test_boot/data/storage.bson +0 -0
  60. package/test_boot/server_password.pem +51 -0
  61. package/.travis.yml +0 -10
  62. package/t1.js +0 -59
  63. package/wercker.yml +0 -19
@@ -0,0 +1,450 @@
1
+ var core = require('mongodb-core');
2
+ var crypto = require('crypto');
3
+ var shallowClone = require('../utils').shallowClone;
4
+ var stream = require('stream');
5
+ var util = require('util');
6
+
7
+ var ERROR_NAMESPACE_NOT_FOUND = 26;
8
+
9
+ module.exports = GridFSBucketWriteStream;
10
+
11
+ /**
12
+ * A writable stream that enables you to write buffers to GridFS.
13
+ *
14
+ * Do not instantiate this class directly. Use `openUploadStream()` instead.
15
+ *
16
+ * @class
17
+ * @param {GridFSBucket} bucket Handle for this stream's corresponding bucket
18
+ * @param {string} filename The value of the 'filename' key in the files doc
19
+ * @param {object} [options=null] Optional settings.
20
+ * @param {number} [options.chunkSizeBytes=null] The chunk size to use, in bytes
21
+ * @param {number} [options.w=null] The write concern
22
+ * @param {number} [options.wtimeout=null] The write concern timeout
23
+ * @param {number} [options.j=null] The journal write concern
24
+ * @fires GridFSBucketWriteStream#error
25
+ * @fires GridFSBucketWriteStream#finish
26
+ * @return {GridFSBucketWriteStream} a GridFSBucketWriteStream instance.
27
+ */
28
+
29
+ function GridFSBucketWriteStream(bucket, filename, options) {
30
+ this.bucket = bucket;
31
+ this.chunks = bucket.s._chunksCollection;
32
+ this.filename = filename;
33
+ this.files = bucket.s._filesCollection;
34
+ this.options = options;
35
+
36
+ this.id = core.BSON.ObjectId();
37
+ this.chunkSizeBytes = this.options.chunkSizeBytes;
38
+ this.bufToStore = new Buffer(this.chunkSizeBytes);
39
+ this.length = 0;
40
+ this.md5 = crypto.createHash('md5');
41
+ this.n = 0;
42
+ this.pos = 0;
43
+ this.state = {
44
+ streamEnd: false,
45
+ outstandingRequests: 0,
46
+ errored: false
47
+ };
48
+
49
+ if (!this.bucket.s.calledOpenUploadStream) {
50
+ this.bucket.s.calledOpenUploadStream = true;
51
+
52
+ var _this = this;
53
+ checkIndexes(this, function() {
54
+ _this.bucket.s.checkedIndexes = true;
55
+ _this.bucket.emit('index');
56
+ });
57
+ }
58
+ }
59
+
60
+ util.inherits(GridFSBucketWriteStream, stream.Writable);
61
+
62
+ /**
63
+ * An error occurred
64
+ *
65
+ * @event GridFSBucketWriteStream#error
66
+ * @type {Error}
67
+ */
68
+
69
+ /**
70
+ * end() was called and the write stream successfully wrote all chunks to
71
+ * MongoDB.
72
+ *
73
+ * @event GridFSBucketWriteStream#finish
74
+ * @type {object}
75
+ */
76
+
77
+ /**
78
+ * Write a buffer to the stream.
79
+ *
80
+ * @method
81
+ * @param {Buffer} chunk Buffer to write
82
+ * @param {String} encoding Optional encoding for the buffer
83
+ * @param {Function} callback Function to call when the chunk was added to the buffer, or if the entire chunk was persisted to MongoDB if this chunk caused a flush.
84
+ * @return {Boolean} False if this write required flushing a chunk to MongoDB. True otherwise.
85
+ */
86
+
87
+ GridFSBucketWriteStream.prototype.write = function(chunk, encoding, callback) {
88
+ var _this = this;
89
+ return waitForIndexes(this, function() {
90
+ return doWrite(_this, chunk, encoding, callback);
91
+ });
92
+ };
93
+
94
+ /**
95
+ * Tells the stream that no more data will be coming in. The stream will
96
+ * persist the remaining data to MongoDB, write the files document, and
97
+ * then emit a 'finish' event.
98
+ *
99
+ * @method
100
+ * @param {Buffer} chunk Buffer to write
101
+ * @param {String} encoding Optional encoding for the buffer
102
+ * @param {Function} callback Function to call when all files and chunks have been persisted to MongoDB
103
+ */
104
+
105
+ GridFSBucketWriteStream.prototype.end = function(chunk, encoding, callback) {
106
+ var _this = this;
107
+ this.state.streamEnd = true;
108
+
109
+ if (callback) {
110
+ this.once('finish', callback);
111
+ }
112
+
113
+ if (!chunk) {
114
+ waitForIndexes(this, function() {
115
+ writeRemnant(_this);
116
+ });
117
+ return;
118
+ }
119
+
120
+ var _this = this;
121
+ var inputBuf = (Buffer.isBuffer(chunk)) ?
122
+ chunk : new Buffer(chunk, encoding);
123
+
124
+ this.write(chunk, encoding, function() {
125
+ writeRemnant(_this);
126
+ });
127
+ };
128
+
129
+ /**
130
+ * @ignore
131
+ */
132
+
133
+ function __handleError(_this, error, callback) {
134
+ if (_this.state.errored) {
135
+ return;
136
+ }
137
+ _this.state.errored = true;
138
+ if (callback) {
139
+ return callback(error);
140
+ }
141
+ _this.emit('error', error);
142
+ }
143
+
144
+ /**
145
+ * @ignore
146
+ */
147
+
148
+ function createChunkDoc(filesId, n, data) {
149
+ return {
150
+ _id: core.BSON.ObjectId(),
151
+ files_id: filesId,
152
+ n: n,
153
+ data: data
154
+ };
155
+ }
156
+
157
+ /**
158
+ * @ignore
159
+ */
160
+
161
+ function checkChunksIndex(_this, callback) {
162
+ _this.chunks.listIndexes().toArray(function(error, indexes) {
163
+ if (error) {
164
+ // Collection doesn't exist so create index
165
+ if (error.code === ERROR_NAMESPACE_NOT_FOUND) {
166
+ var index = { files_id: 1, n: 1 };
167
+ _this.chunks.createIndex(index, { background: false }, function(error) {
168
+ if (error) {
169
+ return callback(error);
170
+ }
171
+
172
+ callback();
173
+ });
174
+ return;
175
+ }
176
+ return callback(error);
177
+ }
178
+
179
+ var hasChunksIndex = false;
180
+ indexes.forEach(function(index) {
181
+ if (index.key) {
182
+ var keys = Object.keys(index.key);
183
+ if (keys.length === 2 && index.key.files_id === 1 &&
184
+ index.key.n === 1) {
185
+ hasChunksIndex = true;
186
+ }
187
+ }
188
+ });
189
+
190
+ if (hasChunksIndex) {
191
+ callback();
192
+ } else {
193
+ var index = { files_id: 1, n: 1 };
194
+ var indexOptions = getWriteOptions(_this);
195
+
196
+ indexOptions.background = false;
197
+ indexOptions.unique = true;
198
+
199
+ _this.chunks.createIndex(index, indexOptions, function(error) {
200
+ if (error) {
201
+ return callback(error);
202
+ }
203
+
204
+ callback();
205
+ });
206
+ }
207
+ });
208
+ }
209
+
210
+ /**
211
+ * @ignore
212
+ */
213
+
214
+ function checkDone(_this, callback) {
215
+ if (_this.state.streamEnd &&
216
+ _this.state.outstandingRequests === 0 &&
217
+ !_this.state.errored) {
218
+ var filesDoc = createFilesDoc(_this.id, _this.length, _this.chunkSizeBytes,
219
+ _this.md5.digest('hex'), _this.filename, _this.options.contentType,
220
+ _this.options.aliases, _this.options.metadata);
221
+
222
+ _this.files.insert(filesDoc, getWriteOptions(_this), function(error) {
223
+ if (error) {
224
+ return __handleError(_this, error, callback);
225
+ }
226
+ _this.emit('finish', filesDoc);
227
+ });
228
+
229
+ return true;
230
+ }
231
+
232
+ return false;
233
+ }
234
+
235
+ /**
236
+ * @ignore
237
+ */
238
+
239
+ function checkIndexes(_this, callback) {
240
+ _this.files.findOne({}, { _id: 1 }, function(error, doc) {
241
+ if (error) {
242
+ return callback(error);
243
+ }
244
+ if (doc) {
245
+ return callback();
246
+ }
247
+
248
+ _this.files.listIndexes().toArray(function(error, indexes) {
249
+ if (error) {
250
+ // Collection doesn't exist so create index
251
+ if (error.code === ERROR_NAMESPACE_NOT_FOUND) {
252
+ var index = { filename: 1, uploadDate: 1 };
253
+ _this.files.createIndex(index, { background: false }, function(error) {
254
+ if (error) {
255
+ return callback(error);
256
+ }
257
+
258
+ checkChunksIndex(_this, callback);
259
+ });
260
+ return;
261
+ }
262
+ return callback(error);
263
+ }
264
+
265
+ var hasFileIndex = false;
266
+ indexes.forEach(function(index) {
267
+ var keys = Object.keys(index.key);
268
+ if (keys.length === 2 && index.key.filename === 1 &&
269
+ index.key.uploadDate === 1) {
270
+ hasFileIndex = true;
271
+ }
272
+ });
273
+
274
+ if (hasFileIndex) {
275
+ checkChunksIndex(_this, callback);
276
+ } else {
277
+ var index = { filename: 1, uploadDate: 1 };
278
+
279
+ var indexOptions = getWriteOptions(_this);
280
+
281
+ indexOptions.background = false;
282
+
283
+ _this.files.createIndex(index, indexOptions, function(error) {
284
+ if (error) {
285
+ return callback(error);
286
+ }
287
+
288
+ checkChunksIndex(_this, callback);
289
+ });
290
+ }
291
+ });
292
+ });
293
+ }
294
+
295
+ /**
296
+ * @ignore
297
+ */
298
+
299
+ function createFilesDoc(_id, length, chunkSize, md5, filename, contentType,
300
+ aliases, metadata) {
301
+ var ret = {
302
+ _id: _id,
303
+ length: length,
304
+ chunkSize: chunkSize,
305
+ uploadDate: new Date(),
306
+ md5: md5,
307
+ filename: filename
308
+ };
309
+
310
+ if (contentType) {
311
+ ret.contentType = contentType;
312
+ }
313
+
314
+ if (aliases) {
315
+ ret.aliases = aliases;
316
+ }
317
+
318
+ if (metadata) {
319
+ ret.metadata = metadata;
320
+ }
321
+
322
+ return ret;
323
+ }
324
+
325
+ /**
326
+ * @ignore
327
+ */
328
+
329
+ function doWrite(_this, chunk, encoding, callback) {
330
+
331
+ var inputBuf = (Buffer.isBuffer(chunk)) ?
332
+ chunk : new Buffer(chunk, encoding);
333
+
334
+ _this.length += inputBuf.length;
335
+
336
+ // Input is small enough to fit in our buffer
337
+ if (_this.pos + inputBuf.length < _this.chunkSizeBytes) {
338
+ inputBuf.copy(_this.bufToStore, _this.pos);
339
+ _this.pos += inputBuf.length;
340
+
341
+ callback && callback();
342
+
343
+ // Note that we reverse the typical semantics of write's return value
344
+ // to be compatible with node's `.pipe()` function.
345
+ // True means client can keep writing.
346
+ return true;
347
+ }
348
+
349
+ // Otherwise, buffer is too big for current chunk, so we need to flush
350
+ // to MongoDB.
351
+ var inputBufRemaining = inputBuf.length;
352
+ var spaceRemaining = _this.chunkSizeBytes - _this.pos;
353
+ var numToCopy = Math.min(spaceRemaining, inputBuf.length);
354
+ var outstandingRequests = 0;
355
+ while (inputBufRemaining > 0) {
356
+ var inputBufPos = inputBuf.length - inputBufRemaining;
357
+ inputBuf.copy(_this.bufToStore, _this.pos,
358
+ inputBufPos, inputBufPos + numToCopy);
359
+ _this.pos += numToCopy;
360
+ spaceRemaining -= numToCopy;
361
+ if (spaceRemaining === 0) {
362
+ _this.md5.update(_this.bufToStore);
363
+ var doc = createChunkDoc(_this.id, _this.n, _this.bufToStore);
364
+ ++_this.state.outstandingRequests;
365
+ ++outstandingRequests;
366
+
367
+ _this.chunks.insert(doc, getWriteOptions(_this), function(error) {
368
+ if (error) {
369
+ return __handleError(_this, error);
370
+ }
371
+ --_this.state.outstandingRequests;
372
+ --outstandingRequests;
373
+ if (!outstandingRequests) {
374
+ _this.emit('drain', doc);
375
+ callback && callback();
376
+ checkDone(_this);
377
+ }
378
+ });
379
+
380
+ spaceRemaining = _this.chunkSizeBytes;
381
+ _this.pos = 0;
382
+ ++_this.n;
383
+ }
384
+ inputBufRemaining -= numToCopy;
385
+ numToCopy = Math.min(spaceRemaining, inputBufRemaining);
386
+ }
387
+
388
+ // Note that we reverse the typical semantics of write's return value
389
+ // to be compatible with node's `.pipe()` function.
390
+ // False means the client should wait for the 'drain' event.
391
+ return false;
392
+ }
393
+
394
+ /**
395
+ * @ignore
396
+ */
397
+
398
+ function getWriteOptions(_this) {
399
+ var obj = {};
400
+ if (_this.options.writeConcern) {
401
+ obj.w = concern.w;
402
+ obj.wtimeout = concern.wtimeout;
403
+ obj.j = concern.j;
404
+ }
405
+ return obj;
406
+ }
407
+
408
+ /**
409
+ * @ignore
410
+ */
411
+
412
+ function waitForIndexes(_this, callback) {
413
+ if (_this.bucket.s.checkedIndexes) {
414
+ callback(false);
415
+ }
416
+
417
+ _this.bucket.once('index', function() {
418
+ callback(true);
419
+ });
420
+
421
+ return true;
422
+ }
423
+
424
+ /**
425
+ * @ignore
426
+ */
427
+
428
+ function writeRemnant(_this, callback) {
429
+ // Buffer is empty, so don't bother to insert
430
+ if (_this.pos === 0) {
431
+ return checkDone(_this, callback);
432
+ }
433
+
434
+ ++_this.state.outstandingRequests;
435
+
436
+ // Create a new buffer to make sure the buffer isn't bigger than it needs
437
+ // to be.
438
+ var remnant = new Buffer(_this.pos);
439
+ _this.bufToStore.copy(remnant, 0, 0, _this.pos);
440
+ _this.md5.update(remnant);
441
+ var doc = createChunkDoc(_this.id, _this.n, remnant);
442
+
443
+ _this.chunks.insert(doc, getWriteOptions(_this), function(error) {
444
+ if (error) {
445
+ return __handleError(_this, error);
446
+ }
447
+ --_this.state.outstandingRequests;
448
+ checkDone(_this);
449
+ });
450
+ }
@@ -0,0 +1,64 @@
1
+ var f = require('util').format;
2
+
3
+ var Define = function(name, object, stream) {
4
+ this.name = name;
5
+ this.object = object;
6
+ this.stream = typeof stream == 'boolean' ? stream : false;
7
+ this.instrumentations = {};
8
+ }
9
+
10
+ Define.prototype.classMethod = function(name, options) {
11
+ var keys = Object.keys(options).sort();
12
+ var key = generateKey(keys, options);
13
+
14
+ // Add a list of instrumentations
15
+ if(this.instrumentations[key] == null) {
16
+ this.instrumentations[key] = {
17
+ methods: [], options: options
18
+ }
19
+ }
20
+
21
+ // Push to list of method for this instrumentation
22
+ this.instrumentations[key].methods.push(name);
23
+ }
24
+
25
+ var generateKey = function(keys, options) {
26
+ var parts = [];
27
+ for(var i = 0; i < keys.length; i++) {
28
+ parts.push(f('%s=%s', keys[i], options[keys[i]]));
29
+ }
30
+
31
+ return parts.join();
32
+ }
33
+
34
+ Define.prototype.staticMethod = function(name, options) {
35
+ options.static = true;
36
+ var keys = Object.keys(options).sort();
37
+ var key = generateKey(keys, options);
38
+
39
+ // Add a list of instrumentations
40
+ if(this.instrumentations[key] == null) {
41
+ this.instrumentations[key] = {
42
+ methods: [], options: options
43
+ }
44
+ }
45
+
46
+ // Push to list of method for this instrumentation
47
+ this.instrumentations[key].methods.push(name);
48
+ }
49
+
50
+ Define.prototype.generate = function(keys, options) {
51
+ // Generate the return object
52
+ var object = {
53
+ name: this.name, obj: this.object, stream: this.stream,
54
+ instrumentations: []
55
+ }
56
+
57
+ for(var name in this.instrumentations) {
58
+ object.instrumentations.push(this.instrumentations[name]);
59
+ }
60
+
61
+ return object;
62
+ }
63
+
64
+ module.exports = Define;