collection.js 6.8 KB

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