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