You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

259 lines
6.5 KiB

  1. /*!
  2. * Module dependencies.
  3. */
  4. var MongooseCollection = require('../../collection'),
  5. Collection = require('mongodb').Collection,
  6. utils = require('../../utils');
  7. /**
  8. * A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) collection implementation.
  9. *
  10. * All methods methods from the [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) driver are copied and wrapped in queue management.
  11. *
  12. * @inherits Collection
  13. * @api private
  14. */
  15. function NativeCollection() {
  16. this.collection = null;
  17. MongooseCollection.apply(this, arguments);
  18. }
  19. /*!
  20. * Inherit from abstract Collection.
  21. */
  22. NativeCollection.prototype.__proto__ = MongooseCollection.prototype;
  23. /**
  24. * Called when the connection opens.
  25. *
  26. * @api private
  27. */
  28. NativeCollection.prototype.onOpen = function() {
  29. var _this = this;
  30. // always get a new collection in case the user changed host:port
  31. // of parent db instance when re-opening the connection.
  32. if (!_this.opts.capped.size) {
  33. // non-capped
  34. return _this.conn.db.collection(_this.name, callback);
  35. }
  36. // capped
  37. return _this.conn.db.collection(_this.name, function(err, c) {
  38. if (err) return callback(err);
  39. // discover if this collection exists and if it is capped
  40. _this.conn.db.listCollections({name: _this.name}).toArray(function(err, docs) {
  41. if (err) {
  42. return callback(err);
  43. }
  44. var doc = docs[0];
  45. var exists = !!doc;
  46. if (exists) {
  47. if (doc.options && doc.options.capped) {
  48. callback(null, c);
  49. } else {
  50. var msg = 'A non-capped collection exists with the name: ' + _this.name + '\n\n'
  51. + ' To use this collection as a capped collection, please '
  52. + 'first convert it.\n'
  53. + ' http://www.mongodb.org/display/DOCS/Capped+Collections#CappedCollections-Convertingacollectiontocapped';
  54. err = new Error(msg);
  55. callback(err);
  56. }
  57. } else {
  58. // create
  59. var opts = utils.clone(_this.opts.capped);
  60. opts.capped = true;
  61. _this.conn.db.createCollection(_this.name, opts, callback);
  62. }
  63. });
  64. });
  65. function callback(err, collection) {
  66. if (err) {
  67. // likely a strict mode error
  68. _this.conn.emit('error', err);
  69. } else {
  70. _this.collection = collection;
  71. MongooseCollection.prototype.onOpen.call(_this);
  72. }
  73. }
  74. };
  75. /**
  76. * Called when the connection closes
  77. *
  78. * @api private
  79. */
  80. NativeCollection.prototype.onClose = function() {
  81. MongooseCollection.prototype.onClose.call(this);
  82. };
  83. /*!
  84. * Copy the collection methods and make them subject to queues
  85. */
  86. function iter(i) {
  87. NativeCollection.prototype[i] = function() {
  88. if (this.buffer) {
  89. this.addQueue(i, arguments);
  90. return;
  91. }
  92. var collection = this.collection,
  93. args = arguments,
  94. _this = this,
  95. debug = _this.conn.base.options.debug;
  96. if (debug) {
  97. if (typeof debug === 'function') {
  98. debug.apply(debug,
  99. [_this.name, i].concat(utils.args(args, 0, args.length - 1)));
  100. } else {
  101. this.$print(_this.name, i, args);
  102. }
  103. }
  104. try {
  105. return collection[i].apply(collection, args);
  106. } catch (error) {
  107. // Collection operation may throw because of max bson size, catch it here
  108. // See gh-3906
  109. if (args.length > 0 &&
  110. typeof args[args.length - 1] === 'function') {
  111. args[args.length - 1](error);
  112. } else {
  113. throw error;
  114. }
  115. }
  116. };
  117. }
  118. for (var i in Collection.prototype) {
  119. // Janky hack to work around gh-3005 until we can get rid of the mongoose
  120. // collection abstraction
  121. try {
  122. if (typeof Collection.prototype[i] !== 'function') {
  123. continue;
  124. }
  125. } catch (e) {
  126. continue;
  127. }
  128. iter(i);
  129. }
  130. /**
  131. * Debug print helper
  132. *
  133. * @api public
  134. * @method $print
  135. */
  136. NativeCollection.prototype.$print = function(name, i, args) {
  137. var moduleName = '\x1B[0;36mMongoose:\x1B[0m ';
  138. var functionCall = [name, i].join('.');
  139. var _args = [];
  140. for (var j = args.length - 1; j >= 0; --j) {
  141. if (this.$format(args[j]) || _args.length) {
  142. _args.unshift(this.$format(args[j]));
  143. }
  144. }
  145. var params = '(' + _args.join(', ') + ')';
  146. console.error(moduleName + functionCall + params);
  147. };
  148. /**
  149. * Formatter for debug print args
  150. *
  151. * @api public
  152. * @method $format
  153. */
  154. NativeCollection.prototype.$format = function(arg) {
  155. var type = typeof arg;
  156. if (type === 'function' || type === 'undefined') return '';
  157. return format(arg);
  158. };
  159. /*!
  160. * Debug print helper
  161. */
  162. function map(o) {
  163. return format(o, true);
  164. }
  165. function formatObjectId(x, key) {
  166. var representation = 'ObjectId("' + x[key].toHexString() + '")';
  167. x[key] = {inspect: function() { return representation; }};
  168. }
  169. function formatDate(x, key) {
  170. var representation = 'new Date("' + x[key].toUTCString() + '")';
  171. x[key] = {inspect: function() { return representation; }};
  172. }
  173. function format(obj, sub) {
  174. var x = utils.clone(obj, {retainKeyOrder: 1});
  175. var representation;
  176. if (x != null) {
  177. if (x.constructor.name === 'Binary') {
  178. x = '[object Buffer]';
  179. } else if (x.constructor.name === 'ObjectID') {
  180. representation = 'ObjectId("' + x.toHexString() + '")';
  181. x = {inspect: function() { return representation; }};
  182. } else if (x.constructor.name === 'Date') {
  183. representation = 'new Date("' + x.toUTCString() + '")';
  184. x = {inspect: function() { return representation; }};
  185. } else if (x.constructor.name === 'Object') {
  186. var keys = Object.keys(x);
  187. var numKeys = keys.length;
  188. var key;
  189. for (var i = 0; i < numKeys; ++i) {
  190. key = keys[i];
  191. if (x[key]) {
  192. if (x[key].constructor.name === 'Binary') {
  193. x[key] = '[object Buffer]';
  194. } else if (x[key].constructor.name === 'Object') {
  195. x[key] = format(x[key], true);
  196. } else if (x[key].constructor.name === 'ObjectID') {
  197. formatObjectId(x, key);
  198. } else if (x[key].constructor.name === 'Date') {
  199. formatDate(x, key);
  200. } else if (Array.isArray(x[key])) {
  201. x[key] = x[key].map(map);
  202. }
  203. }
  204. }
  205. }
  206. if (sub) return x;
  207. }
  208. return require('util')
  209. .inspect(x, false, 10, true)
  210. .replace(/\n/g, '')
  211. .replace(/\s{2,}/g, ' ');
  212. }
  213. /**
  214. * Retreives information about this collections indexes.
  215. *
  216. * @param {Function} callback
  217. * @method getIndexes
  218. * @api public
  219. */
  220. NativeCollection.prototype.getIndexes = NativeCollection.prototype.indexInformation;
  221. /*!
  222. * Module exports.
  223. */
  224. module.exports = NativeCollection;