model.js 97 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418
  1. /*!
  2. * Module dependencies.
  3. */
  4. var Document = require('./document');
  5. var MongooseError = require('./error');
  6. var VersionError = MongooseError.VersionError;
  7. var DivergentArrayError = MongooseError.DivergentArrayError;
  8. var Query = require('./query');
  9. var Aggregate = require('./aggregate');
  10. var Schema = require('./schema');
  11. var utils = require('./utils');
  12. var hasOwnProperty = utils.object.hasOwnProperty;
  13. var isMongooseObject = utils.isMongooseObject;
  14. var EventEmitter = require('events').EventEmitter;
  15. var util = require('util');
  16. var tick = utils.tick;
  17. var parallel = require('async/parallel');
  18. var PromiseProvider = require('./promise_provider');
  19. var VERSION_WHERE = 1,
  20. VERSION_INC = 2,
  21. VERSION_ALL = VERSION_WHERE | VERSION_INC;
  22. var POJO_TO_OBJECT_OPTIONS = {
  23. depopulate: true,
  24. transform: false,
  25. _skipDepopulateTopLevel: true
  26. };
  27. /**
  28. * Model constructor
  29. *
  30. * Provides the interface to MongoDB collections as well as creates document instances.
  31. *
  32. * @param {Object} doc values with which to create the document
  33. * @inherits Document http://mongoosejs.com/docs/api.html#document-js
  34. * @event `error`: If listening to this event, it is emitted when a document was saved without passing a callback and an `error` occurred. If not listening, the event bubbles to the connection used to create this Model.
  35. * @event `index`: Emitted after `Model#ensureIndexes` completes. If an error occurred it is passed with the event.
  36. * @event `index-single-start`: Emitted when an individual index starts within `Model#ensureIndexes`. The fields and options being used to build the index are also passed with the event.
  37. * @event `index-single-done`: Emitted when an individual index finishes within `Model#ensureIndexes`. If an error occurred it is passed with the event. The fields, options, and index name are also passed.
  38. * @api public
  39. */
  40. function Model(doc, fields, skipId) {
  41. Document.call(this, doc, fields, skipId);
  42. }
  43. /*!
  44. * Inherits from Document.
  45. *
  46. * All Model.prototype features are available on
  47. * top level (non-sub) documents.
  48. */
  49. Model.prototype.__proto__ = Document.prototype;
  50. /**
  51. * Connection the model uses.
  52. *
  53. * @api public
  54. * @property db
  55. */
  56. Model.prototype.db;
  57. /**
  58. * Collection the model uses.
  59. *
  60. * @api public
  61. * @property collection
  62. */
  63. Model.prototype.collection;
  64. /**
  65. * The name of the model
  66. *
  67. * @api public
  68. * @property modelName
  69. */
  70. Model.prototype.modelName;
  71. /**
  72. * If this is a discriminator model, `baseModelName` is the name of
  73. * the base model.
  74. *
  75. * @api public
  76. * @property baseModelName
  77. */
  78. Model.prototype.baseModelName;
  79. Model.prototype.$__handleSave = function(options, callback) {
  80. var _this = this;
  81. if (!options.safe && this.schema.options.safe) {
  82. options.safe = this.schema.options.safe;
  83. }
  84. if (typeof options.safe === 'boolean') {
  85. options.safe = null;
  86. }
  87. if (this.isNew) {
  88. // send entire doc
  89. var toObjectOptions = {};
  90. toObjectOptions.retainKeyOrder = this.schema.options.retainKeyOrder;
  91. toObjectOptions.depopulate = 1;
  92. toObjectOptions._skipDepopulateTopLevel = true;
  93. toObjectOptions.transform = false;
  94. var obj = this.toObject(toObjectOptions);
  95. if (!utils.object.hasOwnProperty(obj || {}, '_id')) {
  96. // documents must have an _id else mongoose won't know
  97. // what to update later if more changes are made. the user
  98. // wouldn't know what _id was generated by mongodb either
  99. // nor would the ObjectId generated my mongodb necessarily
  100. // match the schema definition.
  101. setTimeout(function() {
  102. callback(new Error('document must have an _id before saving'));
  103. }, 0);
  104. return;
  105. }
  106. this.$__version(true, obj);
  107. this.collection.insert(obj, options.safe, function(err, ret) {
  108. if (err) {
  109. _this.isNew = true;
  110. _this.emit('isNew', true);
  111. callback(err);
  112. return;
  113. }
  114. callback(null, ret);
  115. });
  116. this.$__reset();
  117. this.isNew = false;
  118. this.emit('isNew', false);
  119. // Make it possible to retry the insert
  120. this.$__.inserting = true;
  121. } else {
  122. // Make sure we don't treat it as a new object on error,
  123. // since it already exists
  124. this.$__.inserting = false;
  125. var delta = this.$__delta();
  126. if (delta) {
  127. if (delta instanceof Error) {
  128. callback(delta);
  129. return;
  130. }
  131. var where = this.$__where(delta[0]);
  132. if (where instanceof Error) {
  133. callback(where);
  134. return;
  135. }
  136. this.collection.update(where, delta[1], options.safe, function(err, ret) {
  137. if (err) {
  138. callback(err);
  139. return;
  140. }
  141. callback(null, ret);
  142. });
  143. } else {
  144. this.$__reset();
  145. callback();
  146. return;
  147. }
  148. this.emit('isNew', false);
  149. }
  150. };
  151. /*!
  152. * ignore
  153. */
  154. Model.prototype.$__save = function(options, callback) {
  155. var _this = this;
  156. _this.$__handleSave(options, function(error, result) {
  157. if (error) {
  158. return _this.schema.s.hooks.execPost('save:error', _this, [_this], { error: error }, function(error) {
  159. callback(error);
  160. });
  161. }
  162. _this.$__reset();
  163. _this.$__storeShard();
  164. var numAffected = 0;
  165. if (result) {
  166. if (Array.isArray(result)) {
  167. numAffected = result.length;
  168. } else if (result.result && result.result.n !== undefined) {
  169. numAffected = result.result.n;
  170. } else if (result.result && result.result.nModified !== undefined) {
  171. numAffected = result.result.nModified;
  172. } else {
  173. numAffected = result;
  174. }
  175. }
  176. // was this an update that required a version bump?
  177. if (_this.$__.version && !_this.$__.inserting) {
  178. var doIncrement = VERSION_INC === (VERSION_INC & _this.$__.version);
  179. _this.$__.version = undefined;
  180. if (numAffected <= 0) {
  181. // the update failed. pass an error back
  182. var err = new VersionError(_this);
  183. return callback(err);
  184. }
  185. // increment version if was successful
  186. if (doIncrement) {
  187. var key = _this.schema.options.versionKey;
  188. var version = _this.getValue(key) | 0;
  189. _this.setValue(key, version + 1);
  190. }
  191. }
  192. _this.emit('save', _this, numAffected);
  193. callback(null, _this, numAffected);
  194. });
  195. };
  196. /**
  197. * Saves this document.
  198. *
  199. * ####Example:
  200. *
  201. * product.sold = Date.now();
  202. * product.save(function (err, product, numAffected) {
  203. * if (err) ..
  204. * })
  205. *
  206. * The callback will receive three parameters
  207. *
  208. * 1. `err` if an error occurred
  209. * 2. `product` which is the saved `product`
  210. * 3. `numAffected` will be 1 when the document was successfully persisted to MongoDB, otherwise 0. Unless you tweak mongoose's internals, you don't need to worry about checking this parameter for errors - checking `err` is sufficient to make sure your document was properly saved.
  211. *
  212. * As an extra measure of flow control, save will return a Promise.
  213. * ####Example:
  214. * product.save().then(function(product) {
  215. * ...
  216. * });
  217. *
  218. * For legacy reasons, mongoose stores object keys in reverse order on initial
  219. * save. That is, `{ a: 1, b: 2 }` will be saved as `{ b: 2, a: 1 }` in
  220. * MongoDB. To override this behavior, set
  221. * [the `toObject.retainKeyOrder` option](http://mongoosejs.com/docs/api.html#document_Document-toObject)
  222. * to true on your schema.
  223. *
  224. * @param {Object} [options] options optional options
  225. * @param {Object} [options.safe] overrides [schema's safe option](http://mongoosejs.com//docs/guide.html#safe)
  226. * @param {Boolean} [options.validateBeforeSave] set to false to save without validating.
  227. * @param {Function} [fn] optional callback
  228. * @return {Promise} Promise
  229. * @api public
  230. * @see middleware http://mongoosejs.com/docs/middleware.html
  231. */
  232. Model.prototype.save = function(options, fn) {
  233. if (typeof options === 'function') {
  234. fn = options;
  235. options = undefined;
  236. }
  237. if (!options) {
  238. options = {};
  239. }
  240. if (fn) {
  241. fn = this.constructor.$wrapCallback(fn);
  242. }
  243. return this.$__save(options, fn);
  244. };
  245. /*!
  246. * Determines whether versioning should be skipped for the given path
  247. *
  248. * @param {Document} self
  249. * @param {String} path
  250. * @return {Boolean} true if versioning should be skipped for the given path
  251. */
  252. function shouldSkipVersioning(self, path) {
  253. var skipVersioning = self.schema.options.skipVersioning;
  254. if (!skipVersioning) return false;
  255. // Remove any array indexes from the path
  256. path = path.replace(/\.\d+\./, '.');
  257. return skipVersioning[path];
  258. }
  259. /*!
  260. * Apply the operation to the delta (update) clause as
  261. * well as track versioning for our where clause.
  262. *
  263. * @param {Document} self
  264. * @param {Object} where
  265. * @param {Object} delta
  266. * @param {Object} data
  267. * @param {Mixed} val
  268. * @param {String} [operation]
  269. */
  270. function operand(self, where, delta, data, val, op) {
  271. // delta
  272. op || (op = '$set');
  273. if (!delta[op]) delta[op] = {};
  274. delta[op][data.path] = val;
  275. // disabled versioning?
  276. if (self.schema.options.versionKey === false) return;
  277. // path excluded from versioning?
  278. if (shouldSkipVersioning(self, data.path)) return;
  279. // already marked for versioning?
  280. if (VERSION_ALL === (VERSION_ALL & self.$__.version)) return;
  281. switch (op) {
  282. case '$set':
  283. case '$unset':
  284. case '$pop':
  285. case '$pull':
  286. case '$pullAll':
  287. case '$push':
  288. case '$pushAll':
  289. case '$addToSet':
  290. break;
  291. default:
  292. // nothing to do
  293. return;
  294. }
  295. // ensure updates sent with positional notation are
  296. // editing the correct array element.
  297. // only increment the version if an array position changes.
  298. // modifying elements of an array is ok if position does not change.
  299. if (op === '$push' || op === '$pushAll' || op === '$addToSet') {
  300. self.$__.version = VERSION_INC;
  301. } else if (/^\$p/.test(op)) {
  302. // potentially changing array positions
  303. self.increment();
  304. } else if (Array.isArray(val)) {
  305. // $set an array
  306. self.increment();
  307. } else if (/\.\d+\.|\.\d+$/.test(data.path)) {
  308. // now handling $set, $unset
  309. // subpath of array
  310. self.$__.version = VERSION_WHERE;
  311. }
  312. }
  313. /*!
  314. * Compiles an update and where clause for a `val` with _atomics.
  315. *
  316. * @param {Document} self
  317. * @param {Object} where
  318. * @param {Object} delta
  319. * @param {Object} data
  320. * @param {Array} value
  321. */
  322. function handleAtomics(self, where, delta, data, value) {
  323. if (delta.$set && delta.$set[data.path]) {
  324. // $set has precedence over other atomics
  325. return;
  326. }
  327. if (typeof value.$__getAtomics === 'function') {
  328. value.$__getAtomics().forEach(function(atomic) {
  329. var op = atomic[0];
  330. var val = atomic[1];
  331. if (self.schema.options.usePushEach && op === '$pushAll') {
  332. op = '$push';
  333. val = { $each: val };
  334. }
  335. operand(self, where, delta, data, val, op);
  336. });
  337. return;
  338. }
  339. // legacy support for plugins
  340. var atomics = value._atomics,
  341. ops = Object.keys(atomics),
  342. i = ops.length,
  343. val,
  344. op;
  345. if (i === 0) {
  346. // $set
  347. if (isMongooseObject(value)) {
  348. value = value.toObject({depopulate: 1, _isNested: true});
  349. } else if (value.valueOf) {
  350. value = value.valueOf();
  351. }
  352. return operand(self, where, delta, data, value);
  353. }
  354. function iter(mem) {
  355. return isMongooseObject(mem)
  356. ? mem.toObject({depopulate: 1, _isNested: true})
  357. : mem;
  358. }
  359. while (i--) {
  360. op = ops[i];
  361. val = atomics[op];
  362. if (isMongooseObject(val)) {
  363. val = val.toObject({depopulate: true, transform: false, _isNested: true});
  364. } else if (Array.isArray(val)) {
  365. val = val.map(iter);
  366. } else if (val.valueOf) {
  367. val = val.valueOf();
  368. }
  369. if (op === '$addToSet') {
  370. val = {$each: val};
  371. }
  372. operand(self, where, delta, data, val, op);
  373. }
  374. }
  375. /**
  376. * Produces a special query document of the modified properties used in updates.
  377. *
  378. * @api private
  379. * @method $__delta
  380. * @memberOf Model
  381. */
  382. Model.prototype.$__delta = function() {
  383. var dirty = this.$__dirty();
  384. if (!dirty.length && VERSION_ALL !== this.$__.version) return;
  385. var where = {},
  386. delta = {},
  387. len = dirty.length,
  388. divergent = [],
  389. d = 0;
  390. where._id = this._doc._id;
  391. if (where._id.toObject) {
  392. where._id = where._id.toObject({ transform: false, depopulate: true });
  393. }
  394. for (; d < len; ++d) {
  395. var data = dirty[d];
  396. var value = data.value;
  397. var match = checkDivergentArray(this, data.path, value);
  398. if (match) {
  399. divergent.push(match);
  400. continue;
  401. }
  402. var pop = this.populated(data.path, true);
  403. if (!pop && this.$__.selected) {
  404. // If any array was selected using an $elemMatch projection, we alter the path and where clause
  405. // NOTE: MongoDB only supports projected $elemMatch on top level array.
  406. var pathSplit = data.path.split('.');
  407. var top = pathSplit[0];
  408. if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) {
  409. // If the selected array entry was modified
  410. if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') {
  411. where[top] = this.$__.selected[top];
  412. pathSplit[1] = '$';
  413. data.path = pathSplit.join('.');
  414. }
  415. // if the selected array was modified in any other way throw an error
  416. else {
  417. divergent.push(data.path);
  418. continue;
  419. }
  420. }
  421. }
  422. if (divergent.length) continue;
  423. if (undefined === value) {
  424. operand(this, where, delta, data, 1, '$unset');
  425. } else if (value === null) {
  426. operand(this, where, delta, data, null);
  427. } else if (value._path && value._atomics) {
  428. // arrays and other custom types (support plugins etc)
  429. handleAtomics(this, where, delta, data, value);
  430. } else if (value._path && Buffer.isBuffer(value)) {
  431. // MongooseBuffer
  432. value = value.toObject();
  433. operand(this, where, delta, data, value);
  434. } else {
  435. value = utils.clone(value, {depopulate: 1, _isNested: true});
  436. operand(this, where, delta, data, value);
  437. }
  438. }
  439. if (divergent.length) {
  440. return new DivergentArrayError(divergent);
  441. }
  442. if (this.$__.version) {
  443. this.$__version(where, delta);
  444. }
  445. return [where, delta];
  446. };
  447. /*!
  448. * Determine if array was populated with some form of filter and is now
  449. * being updated in a manner which could overwrite data unintentionally.
  450. *
  451. * @see https://github.com/Automattic/mongoose/issues/1334
  452. * @param {Document} doc
  453. * @param {String} path
  454. * @return {String|undefined}
  455. */
  456. function checkDivergentArray(doc, path, array) {
  457. // see if we populated this path
  458. var pop = doc.populated(path, true);
  459. if (!pop && doc.$__.selected) {
  460. // If any array was selected using an $elemMatch projection, we deny the update.
  461. // NOTE: MongoDB only supports projected $elemMatch on top level array.
  462. var top = path.split('.')[0];
  463. if (doc.$__.selected[top + '.$']) {
  464. return top;
  465. }
  466. }
  467. if (!(pop && array && array.isMongooseArray)) return;
  468. // If the array was populated using options that prevented all
  469. // documents from being returned (match, skip, limit) or they
  470. // deselected the _id field, $pop and $set of the array are
  471. // not safe operations. If _id was deselected, we do not know
  472. // how to remove elements. $pop will pop off the _id from the end
  473. // of the array in the db which is not guaranteed to be the
  474. // same as the last element we have here. $set of the entire array
  475. // would be similarily destructive as we never received all
  476. // elements of the array and potentially would overwrite data.
  477. var check = pop.options.match ||
  478. pop.options.options && hasOwnProperty(pop.options.options, 'limit') || // 0 is not permitted
  479. pop.options.options && pop.options.options.skip || // 0 is permitted
  480. pop.options.select && // deselected _id?
  481. (pop.options.select._id === 0 ||
  482. /\s?-_id\s?/.test(pop.options.select));
  483. if (check) {
  484. var atomics = array._atomics;
  485. if (Object.keys(atomics).length === 0 || atomics.$set || atomics.$pop) {
  486. return path;
  487. }
  488. }
  489. }
  490. /**
  491. * Appends versioning to the where and update clauses.
  492. *
  493. * @api private
  494. * @method $__version
  495. * @memberOf Model
  496. */
  497. Model.prototype.$__version = function(where, delta) {
  498. var key = this.schema.options.versionKey;
  499. if (where === true) {
  500. // this is an insert
  501. if (key) this.setValue(key, delta[key] = 0);
  502. return;
  503. }
  504. // updates
  505. // only apply versioning if our versionKey was selected. else
  506. // there is no way to select the correct version. we could fail
  507. // fast here and force them to include the versionKey but
  508. // thats a bit intrusive. can we do this automatically?
  509. if (!this.isSelected(key)) {
  510. return;
  511. }
  512. // $push $addToSet don't need the where clause set
  513. if (VERSION_WHERE === (VERSION_WHERE & this.$__.version)) {
  514. where[key] = this.getValue(key);
  515. }
  516. if (VERSION_INC === (VERSION_INC & this.$__.version)) {
  517. if (!delta.$set || typeof delta.$set[key] === 'undefined') {
  518. delta.$inc || (delta.$inc = {});
  519. delta.$inc[key] = 1;
  520. }
  521. }
  522. };
  523. /**
  524. * Signal that we desire an increment of this documents version.
  525. *
  526. * ####Example:
  527. *
  528. * Model.findById(id, function (err, doc) {
  529. * doc.increment();
  530. * doc.save(function (err) { .. })
  531. * })
  532. *
  533. * @see versionKeys http://mongoosejs.com/docs/guide.html#versionKey
  534. * @api public
  535. */
  536. Model.prototype.increment = function increment() {
  537. this.$__.version = VERSION_ALL;
  538. return this;
  539. };
  540. /**
  541. * Returns a query object which applies shardkeys if they exist.
  542. *
  543. * @api private
  544. * @method $__where
  545. * @memberOf Model
  546. */
  547. Model.prototype.$__where = function _where(where) {
  548. where || (where = {});
  549. var paths,
  550. len;
  551. if (!where._id) {
  552. where._id = this._doc._id;
  553. }
  554. if (this.$__.shardval) {
  555. paths = Object.keys(this.$__.shardval);
  556. len = paths.length;
  557. for (var i = 0; i < len; ++i) {
  558. where[paths[i]] = this.$__.shardval[paths[i]];
  559. }
  560. }
  561. if (this._doc._id == null) {
  562. return new Error('No _id found on document!');
  563. }
  564. return where;
  565. };
  566. /**
  567. * Removes this document from the db.
  568. *
  569. * ####Example:
  570. * product.remove(function (err, product) {
  571. * if (err) return handleError(err);
  572. * Product.findById(product._id, function (err, product) {
  573. * console.log(product) // null
  574. * })
  575. * })
  576. *
  577. *
  578. * As an extra measure of flow control, remove will return a Promise (bound to `fn` if passed) so it could be chained, or hooked to recive errors
  579. *
  580. * ####Example:
  581. * product.remove().then(function (product) {
  582. * ...
  583. * }).onRejected(function (err) {
  584. * assert.ok(err)
  585. * })
  586. *
  587. * @param {function(err,product)} [fn] optional callback
  588. * @return {Promise} Promise
  589. * @api public
  590. */
  591. Model.prototype.remove = function remove(options, fn) {
  592. if (typeof options === 'function') {
  593. fn = options;
  594. options = undefined;
  595. }
  596. if (!options) {
  597. options = {};
  598. }
  599. if (this.$__.removing) {
  600. if (fn) {
  601. this.$__.removing.then(
  602. function(res) { fn(null, res); },
  603. function(err) { fn(err); });
  604. }
  605. return this;
  606. }
  607. var _this = this;
  608. var Promise = PromiseProvider.get();
  609. if (fn) {
  610. fn = this.constructor.$wrapCallback(fn);
  611. }
  612. this.$__.removing = new Promise.ES6(function(resolve, reject) {
  613. var where = _this.$__where();
  614. if (where instanceof Error) {
  615. reject(where);
  616. fn && fn(where);
  617. return;
  618. }
  619. if (!options.safe && _this.schema.options.safe) {
  620. options.safe = _this.schema.options.safe;
  621. }
  622. _this.collection.remove(where, options, function(err) {
  623. if (!err) {
  624. _this.emit('remove', _this);
  625. resolve(_this);
  626. fn && fn(null, _this);
  627. return;
  628. }
  629. reject(err);
  630. fn && fn(err);
  631. });
  632. });
  633. return this.$__.removing;
  634. };
  635. /**
  636. * Returns another Model instance.
  637. *
  638. * ####Example:
  639. *
  640. * var doc = new Tank;
  641. * doc.model('User').findById(id, callback);
  642. *
  643. * @param {String} name model name
  644. * @api public
  645. */
  646. Model.prototype.model = function model(name) {
  647. return this.db.model(name);
  648. };
  649. /**
  650. * Adds a discriminator type.
  651. *
  652. * ####Example:
  653. *
  654. * function BaseSchema() {
  655. * Schema.apply(this, arguments);
  656. *
  657. * this.add({
  658. * name: String,
  659. * createdAt: Date
  660. * });
  661. * }
  662. * util.inherits(BaseSchema, Schema);
  663. *
  664. * var PersonSchema = new BaseSchema();
  665. * var BossSchema = new BaseSchema({ department: String });
  666. *
  667. * var Person = mongoose.model('Person', PersonSchema);
  668. * var Boss = Person.discriminator('Boss', BossSchema);
  669. *
  670. * @param {String} name discriminator model name
  671. * @param {Schema} schema discriminator model schema
  672. * @api public
  673. */
  674. Model.discriminator = function discriminator(name, schema) {
  675. var CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
  676. toJSON: true,
  677. toObject: true,
  678. _id: true,
  679. id: true
  680. };
  681. if (!(schema && schema.instanceOfSchema)) {
  682. throw new Error('You must pass a valid discriminator Schema');
  683. }
  684. if (this.schema.discriminatorMapping && !this.schema.discriminatorMapping.isRoot) {
  685. throw new Error('Discriminator "' + name +
  686. '" can only be a discriminator of the root model');
  687. }
  688. var key = this.schema.options.discriminatorKey;
  689. if (schema.path(key)) {
  690. throw new Error('Discriminator "' + name +
  691. '" cannot have field with name "' + key + '"');
  692. }
  693. function merge(schema, baseSchema) {
  694. utils.merge(schema, baseSchema);
  695. var obj = {};
  696. obj[key] = {
  697. default: name,
  698. set: function(newName) {
  699. if (newName === name) {
  700. return name;
  701. }
  702. throw new Error('Can\'t set discriminator key "' + key + '"');
  703. }
  704. };
  705. obj[key][schema.options.typeKey] = String;
  706. schema.add(obj);
  707. schema.discriminatorMapping = {key: key, value: name, isRoot: false};
  708. if (baseSchema.options.collection) {
  709. schema.options.collection = baseSchema.options.collection;
  710. }
  711. var toJSON = schema.options.toJSON;
  712. var toObject = schema.options.toObject;
  713. var _id = schema.options._id;
  714. var id = schema.options.id;
  715. var keys = Object.keys(schema.options);
  716. schema.options.discriminatorKey = baseSchema.options.discriminatorKey;
  717. for (var i = 0; i < keys.length; ++i) {
  718. var _key = keys[i];
  719. if (!CUSTOMIZABLE_DISCRIMINATOR_OPTIONS[_key]) {
  720. if (!utils.deepEqual(schema.options[_key], baseSchema.options[_key])) {
  721. throw new Error('Can\'t customize discriminator option ' + _key +
  722. ' (can only modify ' +
  723. Object.keys(CUSTOMIZABLE_DISCRIMINATOR_OPTIONS).join(', ') +
  724. ')');
  725. }
  726. }
  727. }
  728. schema.options = utils.clone(baseSchema.options);
  729. if (toJSON) schema.options.toJSON = toJSON;
  730. if (toObject) schema.options.toObject = toObject;
  731. if (typeof _id !== 'undefined') {
  732. schema.options._id = _id;
  733. }
  734. schema.options.id = id;
  735. schema.callQueue = baseSchema.callQueue.concat(schema.callQueue.slice(schema._defaultMiddleware.length));
  736. schema._requiredpaths = undefined; // reset just in case Schema#requiredPaths() was called on either schema
  737. }
  738. // merges base schema into new discriminator schema and sets new type field.
  739. merge(schema, this.schema);
  740. if (!this.discriminators) {
  741. this.discriminators = {};
  742. }
  743. if (!this.schema.discriminatorMapping) {
  744. this.schema.discriminatorMapping = {key: key, value: null, isRoot: true};
  745. }
  746. if (this.discriminators[name]) {
  747. throw new Error('Discriminator with name "' + name + '" already exists');
  748. }
  749. if (this.db.models[name]) {
  750. throw new MongooseError.OverwriteModelError(name);
  751. }
  752. this.discriminators[name] = this.db.model(name, schema, this.collection.name);
  753. this.discriminators[name].prototype.__proto__ = this.prototype;
  754. Object.defineProperty(this.discriminators[name], 'baseModelName', {
  755. value: this.modelName,
  756. configurable: true,
  757. writable: false
  758. });
  759. // apply methods and statics
  760. applyMethods(this.discriminators[name], schema);
  761. applyStatics(this.discriminators[name], schema);
  762. return this.discriminators[name];
  763. };
  764. // Model (class) features
  765. /*!
  766. * Give the constructor the ability to emit events.
  767. */
  768. for (var i in EventEmitter.prototype) {
  769. Model[i] = EventEmitter.prototype[i];
  770. }
  771. /**
  772. * Called when the model compiles.
  773. *
  774. * @api private
  775. */
  776. Model.init = function init() {
  777. if ((this.schema.options.autoIndex) ||
  778. (this.schema.options.autoIndex === null && this.db.config.autoIndex)) {
  779. this.ensureIndexes({ __noPromise: true, _automatic: true });
  780. }
  781. this.schema.emit('init', this);
  782. };
  783. /**
  784. * Sends `ensureIndex` commands to mongo for each index declared in the schema.
  785. *
  786. * ####Example:
  787. *
  788. * Event.ensureIndexes(function (err) {
  789. * if (err) return handleError(err);
  790. * });
  791. *
  792. * After completion, an `index` event is emitted on this `Model` passing an error if one occurred.
  793. *
  794. * ####Example:
  795. *
  796. * var eventSchema = new Schema({ thing: { type: 'string', unique: true }})
  797. * var Event = mongoose.model('Event', eventSchema);
  798. *
  799. * Event.on('index', function (err) {
  800. * if (err) console.error(err); // error occurred during index creation
  801. * })
  802. *
  803. * _NOTE: It is not recommended that you run this in production. Index creation may impact database performance depending on your load. Use with caution._
  804. *
  805. * The `ensureIndex` commands are not sent in parallel. This is to avoid the `MongoError: cannot add index with a background operation in progress` error. See [this ticket](https://github.com/Automattic/mongoose/issues/1365) for more information.
  806. *
  807. * @param {Object} [options] internal options
  808. * @param {Function} [cb] optional callback
  809. * @return {Promise}
  810. * @api public
  811. */
  812. Model.ensureIndexes = function ensureIndexes(options, callback) {
  813. if (typeof options === 'function') {
  814. callback = options;
  815. options = null;
  816. }
  817. if (options && options.__noPromise) {
  818. _ensureIndexes(this, options, callback);
  819. return;
  820. }
  821. if (callback) {
  822. callback = this.$wrapCallback(callback);
  823. }
  824. var _this = this;
  825. var Promise = PromiseProvider.get();
  826. return new Promise.ES6(function(resolve, reject) {
  827. _ensureIndexes(_this, options || {}, function(error) {
  828. if (error) {
  829. callback && callback(error);
  830. reject(error);
  831. }
  832. callback && callback();
  833. resolve();
  834. });
  835. });
  836. };
  837. function _ensureIndexes(model, options, callback) {
  838. var indexes = model.schema.indexes();
  839. if (!indexes.length) {
  840. setImmediate(function() {
  841. callback && callback();
  842. });
  843. return;
  844. }
  845. // Indexes are created one-by-one to support how MongoDB < 2.4 deals
  846. // with background indexes.
  847. var done = function(err) {
  848. if (err && model.schema.options.emitIndexErrors) {
  849. model.emit('error', err);
  850. }
  851. model.emit('index', err);
  852. callback && callback(err);
  853. };
  854. var indexSingleDone = function(err, fields, options, name) {
  855. model.emit('index-single-done', err, fields, options, name);
  856. };
  857. var indexSingleStart = function(fields, options) {
  858. model.emit('index-single-start', fields, options);
  859. };
  860. var create = function() {
  861. var index = indexes.shift();
  862. if (!index) return done();
  863. var indexFields = index[0];
  864. var options = index[1];
  865. _handleSafe(options);
  866. indexSingleStart(indexFields, options);
  867. model.collection.ensureIndex(indexFields, options, tick(function(err, name) {
  868. indexSingleDone(err, indexFields, options, name);
  869. if (err) {
  870. return done(err);
  871. }
  872. create();
  873. }));
  874. };
  875. setImmediate(function() {
  876. // If buffering is off, do this manually.
  877. if (options._automatic && !model.collection.collection) {
  878. model.collection.addQueue(create, []);
  879. } else {
  880. create();
  881. }
  882. });
  883. }
  884. function _handleSafe(options) {
  885. if (options.safe) {
  886. if (typeof options.safe === 'boolean') {
  887. options.w = options.safe;
  888. delete options.safe;
  889. }
  890. if (typeof options.safe === 'object') {
  891. options.w = options.safe.w;
  892. options.j = options.safe.j;
  893. options.wtimeout = options.safe.wtimeout;
  894. delete options.safe;
  895. }
  896. }
  897. }
  898. /**
  899. * Schema the model uses.
  900. *
  901. * @property schema
  902. * @receiver Model
  903. * @api public
  904. */
  905. Model.schema;
  906. /*!
  907. * Connection instance the model uses.
  908. *
  909. * @property db
  910. * @receiver Model
  911. * @api public
  912. */
  913. Model.db;
  914. /*!
  915. * Collection the model uses.
  916. *
  917. * @property collection
  918. * @receiver Model
  919. * @api public
  920. */
  921. Model.collection;
  922. /**
  923. * Base Mongoose instance the model uses.
  924. *
  925. * @property base
  926. * @receiver Model
  927. * @api public
  928. */
  929. Model.base;
  930. /**
  931. * Registered discriminators for this model.
  932. *
  933. * @property discriminators
  934. * @receiver Model
  935. * @api public
  936. */
  937. Model.discriminators;
  938. /**
  939. * Removes documents from the collection.
  940. *
  941. * ####Example:
  942. *
  943. * Comment.remove({ title: 'baby born from alien father' }, function (err) {
  944. *
  945. * });
  946. *
  947. * ####Note:
  948. *
  949. * To remove documents without waiting for a response from MongoDB, do not pass a `callback`, then call `exec` on the returned [Query](#query-js):
  950. *
  951. * var query = Comment.remove({ _id: id });
  952. * query.exec();
  953. *
  954. * ####Note:
  955. *
  956. * This method sends a remove command directly to MongoDB, no Mongoose documents are involved. Because no Mongoose documents are involved, _no middleware (hooks) are executed_.
  957. *
  958. * @param {Object} conditions
  959. * @param {Function} [callback]
  960. * @return {Query}
  961. * @api public
  962. */
  963. Model.remove = function remove(conditions, callback) {
  964. if (typeof conditions === 'function') {
  965. callback = conditions;
  966. conditions = {};
  967. }
  968. // get the mongodb collection object
  969. var mq = new this.Query(conditions, {}, this, this.collection);
  970. if (callback) {
  971. callback = this.$wrapCallback(callback);
  972. }
  973. return mq.remove(callback);
  974. };
  975. /**
  976. * Finds documents
  977. *
  978. * The `conditions` are cast to their respective SchemaTypes before the command is sent.
  979. *
  980. * ####Examples:
  981. *
  982. * // named john and at least 18
  983. * MyModel.find({ name: 'john', age: { $gte: 18 }});
  984. *
  985. * // executes immediately, passing results to callback
  986. * MyModel.find({ name: 'john', age: { $gte: 18 }}, function (err, docs) {});
  987. *
  988. * // name LIKE john and only selecting the "name" and "friends" fields, executing immediately
  989. * MyModel.find({ name: /john/i }, 'name friends', function (err, docs) { })
  990. *
  991. * // passing options
  992. * MyModel.find({ name: /john/i }, null, { skip: 10 })
  993. *
  994. * // passing options and executing immediately
  995. * MyModel.find({ name: /john/i }, null, { skip: 10 }, function (err, docs) {});
  996. *
  997. * // executing a query explicitly
  998. * var query = MyModel.find({ name: /john/i }, null, { skip: 10 })
  999. * query.exec(function (err, docs) {});
  1000. *
  1001. * // using the promise returned from executing a query
  1002. * var query = MyModel.find({ name: /john/i }, null, { skip: 10 });
  1003. * var promise = query.exec();
  1004. * promise.addBack(function (err, docs) {});
  1005. *
  1006. * @param {Object} conditions
  1007. * @param {Object} [projection] optional fields to return (http://bit.ly/1HotzBo)
  1008. * @param {Object} [options] optional
  1009. * @param {Function} [callback]
  1010. * @return {Query}
  1011. * @see field selection #query_Query-select
  1012. * @see promise #promise-js
  1013. * @api public
  1014. */
  1015. Model.find = function find(conditions, projection, options, callback) {
  1016. if (typeof conditions === 'function') {
  1017. callback = conditions;
  1018. conditions = {};
  1019. projection = null;
  1020. options = null;
  1021. } else if (typeof projection === 'function') {
  1022. callback = projection;
  1023. projection = null;
  1024. options = null;
  1025. } else if (typeof options === 'function') {
  1026. callback = options;
  1027. options = null;
  1028. }
  1029. var mq = new this.Query({}, {}, this, this.collection);
  1030. mq.select(projection);
  1031. mq.setOptions(options);
  1032. if (this.schema.discriminatorMapping && mq.selectedInclusively()) {
  1033. mq.select(this.schema.options.discriminatorKey);
  1034. }
  1035. if (callback) {
  1036. callback = this.$wrapCallback(callback);
  1037. }
  1038. return mq.find(conditions, callback);
  1039. };
  1040. /**
  1041. * Finds a single document by its _id field. `findById(id)` is almost*
  1042. * equivalent to `findOne({ _id: id })`. If you want to query by a document's
  1043. * `_id`, use `findById()` instead of `findOne()`.
  1044. *
  1045. * The `id` is cast based on the Schema before sending the command.
  1046. *
  1047. * Note: `findById()` triggers `findOne` hooks.
  1048. *
  1049. * * Except for how it treats `undefined`. If you use `findOne()`, you'll see
  1050. * that `findOne(undefined)` and `findOne({ _id: undefined })` are equivalent
  1051. * to `findOne({})` and return arbitrary documents. However, mongoose
  1052. * translates `findById(undefined)` into `findOne({ _id: null })`.
  1053. *
  1054. * ####Example:
  1055. *
  1056. * // find adventure by id and execute immediately
  1057. * Adventure.findById(id, function (err, adventure) {});
  1058. *
  1059. * // same as above
  1060. * Adventure.findById(id).exec(callback);
  1061. *
  1062. * // select only the adventures name and length
  1063. * Adventure.findById(id, 'name length', function (err, adventure) {});
  1064. *
  1065. * // same as above
  1066. * Adventure.findById(id, 'name length').exec(callback);
  1067. *
  1068. * // include all properties except for `length`
  1069. * Adventure.findById(id, '-length').exec(function (err, adventure) {});
  1070. *
  1071. * // passing options (in this case return the raw js objects, not mongoose documents by passing `lean`
  1072. * Adventure.findById(id, 'name', { lean: true }, function (err, doc) {});
  1073. *
  1074. * // same as above
  1075. * Adventure.findById(id, 'name').lean().exec(function (err, doc) {});
  1076. *
  1077. * @param {Object|String|Number} id value of `_id` to query by
  1078. * @param {Object} [projection] optional fields to return (http://bit.ly/1HotzBo)
  1079. * @param {Object} [options] optional
  1080. * @param {Function} [callback]
  1081. * @return {Query}
  1082. * @see field selection #query_Query-select
  1083. * @see lean queries #query_Query-lean
  1084. * @api public
  1085. */
  1086. Model.findById = function findById(id, projection, options, callback) {
  1087. if (typeof id === 'undefined') {
  1088. id = null;
  1089. }
  1090. if (callback) {
  1091. callback = this.$wrapCallback(callback);
  1092. }
  1093. return this.findOne({_id: id}, projection, options, callback);
  1094. };
  1095. /**
  1096. * Finds one document.
  1097. *
  1098. * The `conditions` are cast to their respective SchemaTypes before the command is sent.
  1099. *
  1100. * *Note:* `conditions` is optional, and if `conditions` is null or undefined,
  1101. * mongoose will send an empty `findOne` command to MongoDB, which will return
  1102. * an arbitrary document. If you're querying by `_id`, use `findById()` instead.
  1103. *
  1104. * ####Example:
  1105. *
  1106. * // find one iphone adventures - iphone adventures??
  1107. * Adventure.findOne({ type: 'iphone' }, function (err, adventure) {});
  1108. *
  1109. * // same as above
  1110. * Adventure.findOne({ type: 'iphone' }).exec(function (err, adventure) {});
  1111. *
  1112. * // select only the adventures name
  1113. * Adventure.findOne({ type: 'iphone' }, 'name', function (err, adventure) {});
  1114. *
  1115. * // same as above
  1116. * Adventure.findOne({ type: 'iphone' }, 'name').exec(function (err, adventure) {});
  1117. *
  1118. * // specify options, in this case lean
  1119. * Adventure.findOne({ type: 'iphone' }, 'name', { lean: true }, callback);
  1120. *
  1121. * // same as above
  1122. * Adventure.findOne({ type: 'iphone' }, 'name', { lean: true }).exec(callback);
  1123. *
  1124. * // chaining findOne queries (same as above)
  1125. * Adventure.findOne({ type: 'iphone' }).select('name').lean().exec(callback);
  1126. *
  1127. * @param {Object} [conditions]
  1128. * @param {Object} [projection] optional fields to return (http://bit.ly/1HotzBo)
  1129. * @param {Object} [options] optional
  1130. * @param {Function} [callback]
  1131. * @return {Query}
  1132. * @see field selection #query_Query-select
  1133. * @see lean queries #query_Query-lean
  1134. * @api public
  1135. */
  1136. Model.findOne = function findOne(conditions, projection, options, callback) {
  1137. if (typeof options === 'function') {
  1138. callback = options;
  1139. options = null;
  1140. } else if (typeof projection === 'function') {
  1141. callback = projection;
  1142. projection = null;
  1143. options = null;
  1144. } else if (typeof conditions === 'function') {
  1145. callback = conditions;
  1146. conditions = {};
  1147. projection = null;
  1148. options = null;
  1149. }
  1150. // get the mongodb collection object
  1151. var mq = new this.Query({}, {}, this, this.collection);
  1152. mq.select(projection);
  1153. mq.setOptions(options);
  1154. if (this.schema.discriminatorMapping && mq.selectedInclusively()) {
  1155. mq.select(this.schema.options.discriminatorKey);
  1156. }
  1157. if (callback) {
  1158. callback = this.$wrapCallback(callback);
  1159. }
  1160. return mq.findOne(conditions, callback);
  1161. };
  1162. /**
  1163. * Counts number of matching documents in a database collection.
  1164. *
  1165. * ####Example:
  1166. *
  1167. * Adventure.count({ type: 'jungle' }, function (err, count) {
  1168. * if (err) ..
  1169. * console.log('there are %d jungle adventures', count);
  1170. * });
  1171. *
  1172. * @param {Object} conditions
  1173. * @param {Function} [callback]
  1174. * @return {Query}
  1175. * @api public
  1176. */
  1177. Model.count = function count(conditions, callback) {
  1178. if (typeof conditions === 'function') {
  1179. callback = conditions;
  1180. conditions = {};
  1181. }
  1182. // get the mongodb collection object
  1183. var mq = new this.Query({}, {}, this, this.collection);
  1184. if (callback) {
  1185. callback = this.$wrapCallback(callback);
  1186. }
  1187. return mq.count(conditions, callback);
  1188. };
  1189. /**
  1190. * Creates a Query for a `distinct` operation.
  1191. *
  1192. * Passing a `callback` immediately executes the query.
  1193. *
  1194. * ####Example
  1195. *
  1196. * Link.distinct('url', { clicks: {$gt: 100}}, function (err, result) {
  1197. * if (err) return handleError(err);
  1198. *
  1199. * assert(Array.isArray(result));
  1200. * console.log('unique urls with more than 100 clicks', result);
  1201. * })
  1202. *
  1203. * var query = Link.distinct('url');
  1204. * query.exec(callback);
  1205. *
  1206. * @param {String} field
  1207. * @param {Object} [conditions] optional
  1208. * @param {Function} [callback]
  1209. * @return {Query}
  1210. * @api public
  1211. */
  1212. Model.distinct = function distinct(field, conditions, callback) {
  1213. // get the mongodb collection object
  1214. var mq = new this.Query({}, {}, this, this.collection);
  1215. if (typeof conditions === 'function') {
  1216. callback = conditions;
  1217. conditions = {};
  1218. }
  1219. if (callback) {
  1220. callback = this.$wrapCallback(callback);
  1221. }
  1222. return mq.distinct(field, conditions, callback);
  1223. };
  1224. /**
  1225. * Creates a Query, applies the passed conditions, and returns the Query.
  1226. *
  1227. * For example, instead of writing:
  1228. *
  1229. * User.find({age: {$gte: 21, $lte: 65}}, callback);
  1230. *
  1231. * we can instead write:
  1232. *
  1233. * User.where('age').gte(21).lte(65).exec(callback);
  1234. *
  1235. * Since the Query class also supports `where` you can continue chaining
  1236. *
  1237. * User
  1238. * .where('age').gte(21).lte(65)
  1239. * .where('name', /^b/i)
  1240. * ... etc
  1241. *
  1242. * @param {String} path
  1243. * @param {Object} [val] optional value
  1244. * @return {Query}
  1245. * @api public
  1246. */
  1247. Model.where = function where(path, val) {
  1248. void val; // eslint
  1249. // get the mongodb collection object
  1250. var mq = new this.Query({}, {}, this, this.collection).find({});
  1251. return mq.where.apply(mq, arguments);
  1252. };
  1253. /**
  1254. * Creates a `Query` and specifies a `$where` condition.
  1255. *
  1256. * Sometimes you need to query for things in mongodb using a JavaScript expression. You can do so via `find({ $where: javascript })`, or you can use the mongoose shortcut method $where via a Query chain or from your mongoose Model.
  1257. *
  1258. * Blog.$where('this.username.indexOf("val") !== -1').exec(function (err, docs) {});
  1259. *
  1260. * @param {String|Function} argument is a javascript string or anonymous function
  1261. * @method $where
  1262. * @memberOf Model
  1263. * @return {Query}
  1264. * @see Query.$where #query_Query-%24where
  1265. * @api public
  1266. */
  1267. Model.$where = function $where() {
  1268. var mq = new this.Query({}, {}, this, this.collection).find({});
  1269. return mq.$where.apply(mq, arguments);
  1270. };
  1271. /**
  1272. * Issues a mongodb findAndModify update command.
  1273. *
  1274. * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes immediately if `callback` is passed else a Query object is returned.
  1275. *
  1276. * ####Options:
  1277. *
  1278. * - `new`: bool - if true, return the modified document rather than the original. defaults to false (changed in 4.0)
  1279. * - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
  1280. * - `fields`: {Object|String} - Field selection. Equivalent to `.select(fields).findOneAndUpdate()`
  1281. * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
  1282. * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
  1283. * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
  1284. * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
  1285. * - `passRawResult`: if true, passes the [raw result from the MongoDB driver as the third callback parameter](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
  1286. *
  1287. *
  1288. * ####Examples:
  1289. *
  1290. * A.findOneAndUpdate(conditions, update, options, callback) // executes
  1291. * A.findOneAndUpdate(conditions, update, options) // returns Query
  1292. * A.findOneAndUpdate(conditions, update, callback) // executes
  1293. * A.findOneAndUpdate(conditions, update) // returns Query
  1294. * A.findOneAndUpdate() // returns Query
  1295. *
  1296. * ####Note:
  1297. *
  1298. * All top level update keys which are not `atomic` operation names are treated as set operations:
  1299. *
  1300. * ####Example:
  1301. *
  1302. * var query = { name: 'borne' };
  1303. * Model.findOneAndUpdate(query, { name: 'jason borne' }, options, callback)
  1304. *
  1305. * // is sent as
  1306. * Model.findOneAndUpdate(query, { $set: { name: 'jason borne' }}, options, callback)
  1307. *
  1308. * This helps prevent accidentally overwriting your document with `{ name: 'jason borne' }`.
  1309. *
  1310. * ####Note:
  1311. *
  1312. * Values are cast to their appropriate types when using the findAndModify helpers.
  1313. * However, the below are never executed.
  1314. *
  1315. * - defaults
  1316. * - setters
  1317. *
  1318. * `findAndModify` helpers support limited defaults and validation. You can
  1319. * enable these by setting the `setDefaultsOnInsert` and `runValidators` options,
  1320. * respectively.
  1321. *
  1322. * If you need full-fledged validation, use the traditional approach of first
  1323. * retrieving the document.
  1324. *
  1325. * Model.findById(id, function (err, doc) {
  1326. * if (err) ..
  1327. * doc.name = 'jason borne';
  1328. * doc.save(callback);
  1329. * });
  1330. *
  1331. * @param {Object} [conditions]
  1332. * @param {Object} [update]
  1333. * @param {Object} [options]
  1334. * @param {Function} [callback]
  1335. * @return {Query}
  1336. * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
  1337. * @api public
  1338. */
  1339. Model.findOneAndUpdate = function(conditions, update, options, callback) {
  1340. if (typeof options === 'function') {
  1341. callback = options;
  1342. options = null;
  1343. } else if (arguments.length === 1) {
  1344. if (typeof conditions === 'function') {
  1345. var msg = 'Model.findOneAndUpdate(): First argument must not be a function.\n\n'
  1346. + ' ' + this.modelName + '.findOneAndUpdate(conditions, update, options, callback)\n'
  1347. + ' ' + this.modelName + '.findOneAndUpdate(conditions, update, options)\n'
  1348. + ' ' + this.modelName + '.findOneAndUpdate(conditions, update)\n'
  1349. + ' ' + this.modelName + '.findOneAndUpdate(update)\n'
  1350. + ' ' + this.modelName + '.findOneAndUpdate()\n';
  1351. throw new TypeError(msg);
  1352. }
  1353. update = conditions;
  1354. conditions = undefined;
  1355. }
  1356. if (callback) {
  1357. callback = this.$wrapCallback(callback);
  1358. }
  1359. var fields;
  1360. if (options && options.fields) {
  1361. fields = options.fields;
  1362. }
  1363. update = utils.clone(update, {depopulate: 1, _isNested: true});
  1364. if (this.schema.options.versionKey && options && options.upsert) {
  1365. if (!update.$setOnInsert) {
  1366. update.$setOnInsert = {};
  1367. }
  1368. update.$setOnInsert[this.schema.options.versionKey] = 0;
  1369. }
  1370. var mq = new this.Query({}, {}, this, this.collection);
  1371. mq.select(fields);
  1372. return mq.findOneAndUpdate(conditions, update, options, callback);
  1373. };
  1374. /**
  1375. * Issues a mongodb findAndModify update command by a document's _id field.
  1376. * `findByIdAndUpdate(id, ...)` is equivalent to `findOneAndUpdate({ _id: id }, ...)`.
  1377. *
  1378. * Finds a matching document, updates it according to the `update` arg,
  1379. * passing any `options`, and returns the found document (if any) to the
  1380. * callback. The query executes immediately if `callback` is passed else a
  1381. * Query object is returned.
  1382. *
  1383. * This function triggers `findOneAndUpdate` middleware.
  1384. *
  1385. * ####Options:
  1386. *
  1387. * - `new`: bool - true to return the modified document rather than the original. defaults to false
  1388. * - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
  1389. * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
  1390. * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
  1391. * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
  1392. * - `select`: sets the document fields to return
  1393. *
  1394. * ####Examples:
  1395. *
  1396. * A.findByIdAndUpdate(id, update, options, callback) // executes
  1397. * A.findByIdAndUpdate(id, update, options) // returns Query
  1398. * A.findByIdAndUpdate(id, update, callback) // executes
  1399. * A.findByIdAndUpdate(id, update) // returns Query
  1400. * A.findByIdAndUpdate() // returns Query
  1401. *
  1402. * ####Note:
  1403. *
  1404. * All top level update keys which are not `atomic` operation names are treated as set operations:
  1405. *
  1406. * ####Example:
  1407. *
  1408. * Model.findByIdAndUpdate(id, { name: 'jason borne' }, options, callback)
  1409. *
  1410. * // is sent as
  1411. * Model.findByIdAndUpdate(id, { $set: { name: 'jason borne' }}, options, callback)
  1412. *
  1413. * This helps prevent accidentally overwriting your document with `{ name: 'jason borne' }`.
  1414. *
  1415. * ####Note:
  1416. *
  1417. * Values are cast to their appropriate types when using the findAndModify helpers.
  1418. * However, the below are never executed.
  1419. *
  1420. * - defaults
  1421. * - setters
  1422. *
  1423. * `findAndModify` helpers support limited defaults and validation. You can
  1424. * enable these by setting the `setDefaultsOnInsert` and `runValidators` options,
  1425. * respectively.
  1426. *
  1427. * If you need full-fledged validation, use the traditional approach of first
  1428. * retrieving the document.
  1429. *
  1430. * Model.findById(id, function (err, doc) {
  1431. * if (err) ..
  1432. * doc.name = 'jason borne';
  1433. * doc.save(callback);
  1434. * });
  1435. *
  1436. * @param {Object|Number|String} id value of `_id` to query by
  1437. * @param {Object} [update]
  1438. * @param {Object} [options]
  1439. * @param {Function} [callback]
  1440. * @return {Query}
  1441. * @see Model.findOneAndUpdate #model_Model.findOneAndUpdate
  1442. * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
  1443. * @api public
  1444. */
  1445. Model.findByIdAndUpdate = function(id, update, options, callback) {
  1446. if (callback) {
  1447. callback = this.$wrapCallback(callback);
  1448. }
  1449. if (arguments.length === 1) {
  1450. if (typeof id === 'function') {
  1451. var msg = 'Model.findByIdAndUpdate(): First argument must not be a function.\n\n'
  1452. + ' ' + this.modelName + '.findByIdAndUpdate(id, callback)\n'
  1453. + ' ' + this.modelName + '.findByIdAndUpdate(id)\n'
  1454. + ' ' + this.modelName + '.findByIdAndUpdate()\n';
  1455. throw new TypeError(msg);
  1456. }
  1457. return this.findOneAndUpdate({_id: id}, undefined);
  1458. }
  1459. // if a model is passed in instead of an id
  1460. if (id instanceof Document) {
  1461. id = id._id;
  1462. }
  1463. return this.findOneAndUpdate.call(this, {_id: id}, update, options, callback);
  1464. };
  1465. /**
  1466. * Issue a mongodb findAndModify remove command.
  1467. *
  1468. * Finds a matching document, removes it, passing the found document (if any) to the callback.
  1469. *
  1470. * Executes immediately if `callback` is passed else a Query object is returned.
  1471. *
  1472. * ####Options:
  1473. *
  1474. * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
  1475. * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
  1476. * - `select`: sets the document fields to return
  1477. *
  1478. * ####Examples:
  1479. *
  1480. * A.findOneAndRemove(conditions, options, callback) // executes
  1481. * A.findOneAndRemove(conditions, options) // return Query
  1482. * A.findOneAndRemove(conditions, callback) // executes
  1483. * A.findOneAndRemove(conditions) // returns Query
  1484. * A.findOneAndRemove() // returns Query
  1485. *
  1486. * Values are cast to their appropriate types when using the findAndModify helpers.
  1487. * However, the below are never executed.
  1488. *
  1489. * - defaults
  1490. * - setters
  1491. *
  1492. * `findAndModify` helpers support limited defaults and validation. You can
  1493. * enable these by setting the `setDefaultsOnInsert` and `runValidators` options,
  1494. * respectively.
  1495. *
  1496. * If you need full-fledged validation, use the traditional approach of first
  1497. * retrieving the document.
  1498. *
  1499. * Model.findById(id, function (err, doc) {
  1500. * if (err) ..
  1501. * doc.name = 'jason borne';
  1502. * doc.save(callback);
  1503. * });
  1504. *
  1505. * @param {Object} conditions
  1506. * @param {Object} [options]
  1507. * @param {Function} [callback]
  1508. * @return {Query}
  1509. * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
  1510. * @api public
  1511. */
  1512. Model.findOneAndRemove = function(conditions, options, callback) {
  1513. if (arguments.length === 1 && typeof conditions === 'function') {
  1514. var msg = 'Model.findOneAndRemove(): First argument must not be a function.\n\n'
  1515. + ' ' + this.modelName + '.findOneAndRemove(conditions, callback)\n'
  1516. + ' ' + this.modelName + '.findOneAndRemove(conditions)\n'
  1517. + ' ' + this.modelName + '.findOneAndRemove()\n';
  1518. throw new TypeError(msg);
  1519. }
  1520. if (typeof options === 'function') {
  1521. callback = options;
  1522. options = undefined;
  1523. }
  1524. if (callback) {
  1525. callback = this.$wrapCallback(callback);
  1526. }
  1527. var fields;
  1528. if (options) {
  1529. fields = options.select;
  1530. options.select = undefined;
  1531. }
  1532. var mq = new this.Query({}, {}, this, this.collection);
  1533. mq.select(fields);
  1534. return mq.findOneAndRemove(conditions, options, callback);
  1535. };
  1536. /**
  1537. * Issue a mongodb findAndModify remove command by a document's _id field. `findByIdAndRemove(id, ...)` is equivalent to `findOneAndRemove({ _id: id }, ...)`.
  1538. *
  1539. * Finds a matching document, removes it, passing the found document (if any) to the callback.
  1540. *
  1541. * Executes immediately if `callback` is passed, else a `Query` object is returned.
  1542. *
  1543. * ####Options:
  1544. *
  1545. * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
  1546. * - `select`: sets the document fields to return
  1547. *
  1548. * ####Examples:
  1549. *
  1550. * A.findByIdAndRemove(id, options, callback) // executes
  1551. * A.findByIdAndRemove(id, options) // return Query
  1552. * A.findByIdAndRemove(id, callback) // executes
  1553. * A.findByIdAndRemove(id) // returns Query
  1554. * A.findByIdAndRemove() // returns Query
  1555. *
  1556. * @param {Object|Number|String} id value of `_id` to query by
  1557. * @param {Object} [options]
  1558. * @param {Function} [callback]
  1559. * @return {Query}
  1560. * @see Model.findOneAndRemove #model_Model.findOneAndRemove
  1561. * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
  1562. */
  1563. Model.findByIdAndRemove = function(id, options, callback) {
  1564. if (arguments.length === 1 && typeof id === 'function') {
  1565. var msg = 'Model.findByIdAndRemove(): First argument must not be a function.\n\n'
  1566. + ' ' + this.modelName + '.findByIdAndRemove(id, callback)\n'
  1567. + ' ' + this.modelName + '.findByIdAndRemove(id)\n'
  1568. + ' ' + this.modelName + '.findByIdAndRemove()\n';
  1569. throw new TypeError(msg);
  1570. }
  1571. if (callback) {
  1572. callback = this.$wrapCallback(callback);
  1573. }
  1574. return this.findOneAndRemove({_id: id}, options, callback);
  1575. };
  1576. /**
  1577. * Shortcut for saving one or more documents to the database.
  1578. * `MyModel.create(docs)` does `new MyModel(doc).save()` for every doc in
  1579. * docs.
  1580. *
  1581. * Hooks Triggered:
  1582. * - `save()`
  1583. *
  1584. * ####Example:
  1585. *
  1586. * // pass individual docs
  1587. * Candy.create({ type: 'jelly bean' }, { type: 'snickers' }, function (err, jellybean, snickers) {
  1588. * if (err) // ...
  1589. * });
  1590. *
  1591. * // pass an array
  1592. * var array = [{ type: 'jelly bean' }, { type: 'snickers' }];
  1593. * Candy.create(array, function (err, candies) {
  1594. * if (err) // ...
  1595. *
  1596. * var jellybean = candies[0];
  1597. * var snickers = candies[1];
  1598. * // ...
  1599. * });
  1600. *
  1601. * // callback is optional; use the returned promise if you like:
  1602. * var promise = Candy.create({ type: 'jawbreaker' });
  1603. * promise.then(function (jawbreaker) {
  1604. * // ...
  1605. * })
  1606. *
  1607. * @param {Array|Object|*} doc(s)
  1608. * @param {Function} [callback] callback
  1609. * @return {Promise}
  1610. * @api public
  1611. */
  1612. Model.create = function create(doc, callback) {
  1613. var args;
  1614. var cb;
  1615. if (Array.isArray(doc)) {
  1616. args = doc;
  1617. cb = callback;
  1618. } else {
  1619. var last = arguments[arguments.length - 1];
  1620. if (typeof last === 'function') {
  1621. cb = last;
  1622. args = utils.args(arguments, 0, arguments.length - 1);
  1623. } else {
  1624. args = utils.args(arguments);
  1625. }
  1626. }
  1627. var Promise = PromiseProvider.get();
  1628. var _this = this;
  1629. if (cb) {
  1630. cb = this.$wrapCallback(cb);
  1631. }
  1632. var promise = new Promise.ES6(function(resolve, reject) {
  1633. if (args.length === 0) {
  1634. setImmediate(function() {
  1635. cb && cb(null);
  1636. resolve(null);
  1637. });
  1638. return;
  1639. }
  1640. var toExecute = [];
  1641. args.forEach(function(doc) {
  1642. toExecute.push(function(callback) {
  1643. var toSave = doc instanceof _this ? doc : new _this(doc);
  1644. var callbackWrapper = function(error, doc) {
  1645. if (error) {
  1646. return callback(error);
  1647. }
  1648. callback(null, doc);
  1649. };
  1650. // Hack to avoid getting a promise because of
  1651. // $__registerHooksFromSchema
  1652. if (toSave.$__original_save) {
  1653. toSave.$__original_save({ __noPromise: true }, callbackWrapper);
  1654. } else {
  1655. toSave.save({ __noPromise: true }, callbackWrapper);
  1656. }
  1657. });
  1658. });
  1659. parallel(toExecute, function(error, savedDocs) {
  1660. if (error) {
  1661. if (cb) {
  1662. cb(error);
  1663. } else {
  1664. reject(error);
  1665. }
  1666. return;
  1667. }
  1668. if (doc instanceof Array) {
  1669. resolve(savedDocs);
  1670. cb && cb.call(_this, null, savedDocs);
  1671. } else {
  1672. resolve.apply(promise, savedDocs);
  1673. if (cb) {
  1674. savedDocs.unshift(null);
  1675. cb.apply(_this, savedDocs);
  1676. }
  1677. }
  1678. });
  1679. });
  1680. return promise;
  1681. };
  1682. /**
  1683. * Shortcut for validating an array of documents and inserting them into
  1684. * MongoDB if they're all valid. This function is faster than `.create()`
  1685. * because it only sends one operation to the server, rather than one for each
  1686. * document.
  1687. *
  1688. * This function does **not** trigger save middleware.
  1689. *
  1690. * ####Example:
  1691. *
  1692. * var arr = [{ name: 'Star Wars' }, { name: 'The Empire Strikes Back' }];
  1693. * Movies.insertMany(arr, function(error, docs) {});
  1694. *
  1695. * @param {Array|Object|*} doc(s)
  1696. * @param {Function} [callback] callback
  1697. * @return {Promise}
  1698. * @api public
  1699. */
  1700. Model.insertMany = function(arr, callback) {
  1701. var _this = this;
  1702. if (callback) {
  1703. callback = this.$wrapCallback(callback);
  1704. }
  1705. var toExecute = [];
  1706. arr.forEach(function(doc) {
  1707. toExecute.push(function(callback) {
  1708. doc = new _this(doc);
  1709. doc.validate({ __noPromise: true }, function(error) {
  1710. if (error) {
  1711. return callback(error);
  1712. }
  1713. callback(null, doc);
  1714. });
  1715. });
  1716. });
  1717. parallel(toExecute, function(error, docs) {
  1718. if (error) {
  1719. callback && callback(error);
  1720. return;
  1721. }
  1722. var docObjects = docs.map(function(doc) {
  1723. if (doc.schema.options.versionKey) {
  1724. doc[doc.schema.options.versionKey] = 0;
  1725. }
  1726. if (doc.initializeTimestamps) {
  1727. return doc.initializeTimestamps().toObject(POJO_TO_OBJECT_OPTIONS);
  1728. }
  1729. return doc.toObject(POJO_TO_OBJECT_OPTIONS);
  1730. });
  1731. _this.collection.insertMany(docObjects, function(error) {
  1732. if (error) {
  1733. callback && callback(error);
  1734. return;
  1735. }
  1736. for (var i = 0; i < docs.length; ++i) {
  1737. docs[i].isNew = false;
  1738. docs[i].emit('isNew', false);
  1739. }
  1740. callback && callback(null, docs);
  1741. });
  1742. });
  1743. };
  1744. /**
  1745. * Shortcut for creating a new Document from existing raw data, pre-saved in the DB.
  1746. * The document returned has no paths marked as modified initially.
  1747. *
  1748. * ####Example:
  1749. *
  1750. * // hydrate previous data into a Mongoose document
  1751. * var mongooseCandy = Candy.hydrate({ _id: '54108337212ffb6d459f854c', type: 'jelly bean' });
  1752. *
  1753. * @param {Object} obj
  1754. * @return {Document}
  1755. * @api public
  1756. */
  1757. Model.hydrate = function(obj) {
  1758. var model = require('./queryhelpers').createModel(this, obj);
  1759. model.init(obj);
  1760. return model;
  1761. };
  1762. /**
  1763. * Updates documents in the database without returning them.
  1764. *
  1765. * ####Examples:
  1766. *
  1767. * MyModel.update({ age: { $gt: 18 } }, { oldEnough: true }, fn);
  1768. * MyModel.update({ name: 'Tobi' }, { ferret: true }, { multi: true }, function (err, raw) {
  1769. * if (err) return handleError(err);
  1770. * console.log('The raw response from Mongo was ', raw);
  1771. * });
  1772. *
  1773. * ####Valid options:
  1774. *
  1775. * - `safe` (boolean) safe mode (defaults to value set in schema (true))
  1776. * - `upsert` (boolean) whether to create the doc if it doesn't match (false)
  1777. * - `multi` (boolean) whether multiple documents should be updated (false)
  1778. * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
  1779. * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
  1780. * - `strict` (boolean) overrides the `strict` option for this update
  1781. * - `overwrite` (boolean) disables update-only mode, allowing you to overwrite the doc (false)
  1782. *
  1783. * All `update` values are cast to their appropriate SchemaTypes before being sent.
  1784. *
  1785. * The `callback` function receives `(err, rawResponse)`.
  1786. *
  1787. * - `err` is the error if any occurred
  1788. * - `rawResponse` is the full response from Mongo
  1789. *
  1790. * ####Note:
  1791. *
  1792. * All top level keys which are not `atomic` operation names are treated as set operations:
  1793. *
  1794. * ####Example:
  1795. *
  1796. * var query = { name: 'borne' };
  1797. * Model.update(query, { name: 'jason borne' }, options, callback)
  1798. *
  1799. * // is sent as
  1800. * Model.update(query, { $set: { name: 'jason borne' }}, options, callback)
  1801. * // if overwrite option is false. If overwrite is true, sent without the $set wrapper.
  1802. *
  1803. * This helps prevent accidentally overwriting all documents in your collection with `{ name: 'jason borne' }`.
  1804. *
  1805. * ####Note:
  1806. *
  1807. * Be careful to not use an existing model instance for the update clause (this won't work and can cause weird behavior like infinite loops). Also, ensure that the update clause does not have an _id property, which causes Mongo to return a "Mod on _id not allowed" error.
  1808. *
  1809. * ####Note:
  1810. *
  1811. * To update documents without waiting for a response from MongoDB, do not pass a `callback`, then call `exec` on the returned [Query](#query-js):
  1812. *
  1813. * Comment.update({ _id: id }, { $set: { text: 'changed' }}).exec();
  1814. *
  1815. * ####Note:
  1816. *
  1817. * Although values are casted to their appropriate types when using update, the following are *not* applied:
  1818. *
  1819. * - defaults
  1820. * - setters
  1821. * - validators
  1822. * - middleware
  1823. *
  1824. * If you need those features, use the traditional approach of first retrieving the document.
  1825. *
  1826. * Model.findOne({ name: 'borne' }, function (err, doc) {
  1827. * if (err) ..
  1828. * doc.name = 'jason borne';
  1829. * doc.save(callback);
  1830. * })
  1831. *
  1832. * @see strict http://mongoosejs.com/docs/guide.html#strict
  1833. * @see response http://docs.mongodb.org/v2.6/reference/command/update/#output
  1834. * @param {Object} conditions
  1835. * @param {Object} doc
  1836. * @param {Object} [options]
  1837. * @param {Function} [callback]
  1838. * @return {Query}
  1839. * @api public
  1840. */
  1841. Model.update = function update(conditions, doc, options, callback) {
  1842. var mq = new this.Query({}, {}, this, this.collection);
  1843. if (callback) {
  1844. callback = this.$wrapCallback(callback);
  1845. }
  1846. // gh-2406
  1847. // make local deep copy of conditions
  1848. if (conditions instanceof Document) {
  1849. conditions = conditions.toObject();
  1850. } else {
  1851. conditions = utils.clone(conditions, {retainKeyOrder: true});
  1852. }
  1853. options = typeof options === 'function' ? options : utils.clone(options);
  1854. if (this.schema.options.versionKey && options && options.upsert) {
  1855. if (options.overwrite) {
  1856. doc[this.schema.options.versionKey] = 0;
  1857. } else {
  1858. if (!doc.$setOnInsert) {
  1859. doc.$setOnInsert = {};
  1860. }
  1861. doc.$setOnInsert[this.schema.options.versionKey] = 0;
  1862. }
  1863. }
  1864. return mq.update(conditions, doc, options, callback);
  1865. };
  1866. /**
  1867. * Executes a mapReduce command.
  1868. *
  1869. * `o` is an object specifying all mapReduce options as well as the map and reduce functions. All options are delegated to the driver implementation. See [node-mongodb-native mapReduce() documentation](http://mongodb.github.io/node-mongodb-native/api-generated/collection.html#mapreduce) for more detail about options.
  1870. *
  1871. * ####Example:
  1872. *
  1873. * var o = {};
  1874. * o.map = function () { emit(this.name, 1) }
  1875. * o.reduce = function (k, vals) { return vals.length }
  1876. * User.mapReduce(o, function (err, results) {
  1877. * console.log(results)
  1878. * })
  1879. *
  1880. * ####Other options:
  1881. *
  1882. * - `query` {Object} query filter object.
  1883. * - `sort` {Object} sort input objects using this key
  1884. * - `limit` {Number} max number of documents
  1885. * - `keeptemp` {Boolean, default:false} keep temporary data
  1886. * - `finalize` {Function} finalize function
  1887. * - `scope` {Object} scope variables exposed to map/reduce/finalize during execution
  1888. * - `jsMode` {Boolean, default:false} it is possible to make the execution stay in JS. Provided in MongoDB > 2.0.X
  1889. * - `verbose` {Boolean, default:false} provide statistics on job execution time.
  1890. * - `readPreference` {String}
  1891. * - `out*` {Object, default: {inline:1}} sets the output target for the map reduce job.
  1892. *
  1893. * ####* out options:
  1894. *
  1895. * - `{inline:1}` the results are returned in an array
  1896. * - `{replace: 'collectionName'}` add the results to collectionName: the results replace the collection
  1897. * - `{reduce: 'collectionName'}` add the results to collectionName: if dups are detected, uses the reducer / finalize functions
  1898. * - `{merge: 'collectionName'}` add the results to collectionName: if dups exist the new docs overwrite the old
  1899. *
  1900. * If `options.out` is set to `replace`, `merge`, or `reduce`, a Model instance is returned that can be used for further querying. Queries run against this model are all executed with the `lean` option; meaning only the js object is returned and no Mongoose magic is applied (getters, setters, etc).
  1901. *
  1902. * ####Example:
  1903. *
  1904. * var o = {};
  1905. * o.map = function () { emit(this.name, 1) }
  1906. * o.reduce = function (k, vals) { return vals.length }
  1907. * o.out = { replace: 'createdCollectionNameForResults' }
  1908. * o.verbose = true;
  1909. *
  1910. * User.mapReduce(o, function (err, model, stats) {
  1911. * console.log('map reduce took %d ms', stats.processtime)
  1912. * model.find().where('value').gt(10).exec(function (err, docs) {
  1913. * console.log(docs);
  1914. * });
  1915. * })
  1916. *
  1917. * // a promise is returned so you may instead write
  1918. * var promise = User.mapReduce(o);
  1919. * promise.then(function (model, stats) {
  1920. * console.log('map reduce took %d ms', stats.processtime)
  1921. * return model.find().where('value').gt(10).exec();
  1922. * }).then(function (docs) {
  1923. * console.log(docs);
  1924. * }).then(null, handleError).end()
  1925. *
  1926. * @param {Object} o an object specifying map-reduce options
  1927. * @param {Function} [callback] optional callback
  1928. * @see http://www.mongodb.org/display/DOCS/MapReduce
  1929. * @return {Promise}
  1930. * @api public
  1931. */
  1932. Model.mapReduce = function mapReduce(o, callback) {
  1933. var _this = this;
  1934. if (callback) {
  1935. callback = this.$wrapCallback(callback);
  1936. }
  1937. var Promise = PromiseProvider.get();
  1938. return new Promise.ES6(function(resolve, reject) {
  1939. if (!Model.mapReduce.schema) {
  1940. var opts = {noId: true, noVirtualId: true, strict: false};
  1941. Model.mapReduce.schema = new Schema({}, opts);
  1942. }
  1943. if (!o.out) o.out = {inline: 1};
  1944. if (o.verbose !== false) o.verbose = true;
  1945. o.map = String(o.map);
  1946. o.reduce = String(o.reduce);
  1947. if (o.query) {
  1948. var q = new _this.Query(o.query);
  1949. q.cast(_this);
  1950. o.query = q._conditions;
  1951. q = undefined;
  1952. }
  1953. _this.collection.mapReduce(null, null, o, function(err, ret, stats) {
  1954. if (err) {
  1955. callback && callback(err);
  1956. reject(err);
  1957. return;
  1958. }
  1959. if (ret.findOne && ret.mapReduce) {
  1960. // returned a collection, convert to Model
  1961. var model = Model.compile(
  1962. '_mapreduce_' + ret.collectionName
  1963. , Model.mapReduce.schema
  1964. , ret.collectionName
  1965. , _this.db
  1966. , _this.base);
  1967. model._mapreduce = true;
  1968. callback && callback(null, model, stats);
  1969. return resolve(model, stats);
  1970. }
  1971. callback && callback(null, ret, stats);
  1972. resolve(ret, stats);
  1973. });
  1974. });
  1975. };
  1976. /**
  1977. * geoNear support for Mongoose
  1978. *
  1979. * ####Options:
  1980. * - `lean` {Boolean} return the raw object
  1981. * - All options supported by the driver are also supported
  1982. *
  1983. * ####Example:
  1984. *
  1985. * // Legacy point
  1986. * Model.geoNear([1,3], { maxDistance : 5, spherical : true }, function(err, results, stats) {
  1987. * console.log(results);
  1988. * });
  1989. *
  1990. * // geoJson
  1991. * var point = { type : "Point", coordinates : [9,9] };
  1992. * Model.geoNear(point, { maxDistance : 5, spherical : true }, function(err, results, stats) {
  1993. * console.log(results);
  1994. * });
  1995. *
  1996. * @param {Object|Array} GeoJSON point or legacy coordinate pair [x,y] to search near
  1997. * @param {Object} options for the qurery
  1998. * @param {Function} [callback] optional callback for the query
  1999. * @return {Promise}
  2000. * @see http://docs.mongodb.org/manual/core/2dsphere/
  2001. * @see http://mongodb.github.io/node-mongodb-native/api-generated/collection.html?highlight=geonear#geoNear
  2002. * @api public
  2003. */
  2004. Model.geoNear = function(near, options, callback) {
  2005. if (typeof options === 'function') {
  2006. callback = options;
  2007. options = {};
  2008. }
  2009. if (callback) {
  2010. callback = this.$wrapCallback(callback);
  2011. }
  2012. var _this = this;
  2013. var Promise = PromiseProvider.get();
  2014. if (!near) {
  2015. return new Promise.ES6(function(resolve, reject) {
  2016. var error = new Error('Must pass a near option to geoNear');
  2017. reject(error);
  2018. callback && callback(error);
  2019. });
  2020. }
  2021. var x, y;
  2022. return new Promise.ES6(function(resolve, reject) {
  2023. var handler = function(err, res) {
  2024. if (err) {
  2025. reject(err);
  2026. callback && callback(err);
  2027. return;
  2028. }
  2029. if (options.lean) {
  2030. resolve(res.results, res.stats);
  2031. callback && callback(null, res.results, res.stats);
  2032. return;
  2033. }
  2034. var count = res.results.length;
  2035. // if there are no results, fulfill the promise now
  2036. if (count === 0) {
  2037. resolve(res.results, res.stats);
  2038. callback && callback(null, res.results, res.stats);
  2039. return;
  2040. }
  2041. var errSeen = false;
  2042. function init(err) {
  2043. if (err && !errSeen) {
  2044. errSeen = true;
  2045. reject(err);
  2046. callback && callback(err);
  2047. return;
  2048. }
  2049. if (--count <= 0) {
  2050. resolve(res.results, res.stats);
  2051. callback && callback(null, res.results, res.stats);
  2052. }
  2053. }
  2054. for (var i = 0; i < res.results.length; i++) {
  2055. var temp = res.results[i].obj;
  2056. res.results[i].obj = new _this();
  2057. res.results[i].obj.init(temp, init);
  2058. }
  2059. };
  2060. if (Array.isArray(near)) {
  2061. if (near.length !== 2) {
  2062. var error = new Error('If using legacy coordinates, must be an array ' +
  2063. 'of size 2 for geoNear');
  2064. reject(error);
  2065. callback && callback(error);
  2066. return;
  2067. }
  2068. x = near[0];
  2069. y = near[1];
  2070. _this.collection.geoNear(x, y, options, handler);
  2071. } else {
  2072. if (near.type !== 'Point' || !Array.isArray(near.coordinates)) {
  2073. error = new Error('Must pass either a legacy coordinate array or ' +
  2074. 'GeoJSON Point to geoNear');
  2075. reject(error);
  2076. callback && callback(error);
  2077. return;
  2078. }
  2079. _this.collection.geoNear(near, options, handler);
  2080. }
  2081. });
  2082. };
  2083. /**
  2084. * Performs [aggregations](http://docs.mongodb.org/manual/applications/aggregation/) on the models collection.
  2085. *
  2086. * If a `callback` is passed, the `aggregate` is executed and a `Promise` is returned. If a callback is not passed, the `aggregate` itself is returned.
  2087. *
  2088. * ####Example:
  2089. *
  2090. * // Find the max balance of all accounts
  2091. * Users.aggregate(
  2092. * { $group: { _id: null, maxBalance: { $max: '$balance' }}},
  2093. * { $project: { _id: 0, maxBalance: 1 }},
  2094. * function (err, res) {
  2095. * if (err) return handleError(err);
  2096. * console.log(res); // [ { maxBalance: 98000 } ]
  2097. * });
  2098. *
  2099. * // Or use the aggregation pipeline builder.
  2100. * Users.aggregate()
  2101. * .group({ _id: null, maxBalance: { $max: '$balance' } })
  2102. * .select('-id maxBalance')
  2103. * .exec(function (err, res) {
  2104. * if (err) return handleError(err);
  2105. * console.log(res); // [ { maxBalance: 98 } ]
  2106. * });
  2107. *
  2108. * ####NOTE:
  2109. *
  2110. * - Arguments are not cast to the model's schema because `$project` operators allow redefining the "shape" of the documents at any stage of the pipeline, which may leave documents in an incompatible format.
  2111. * - The documents returned are plain javascript objects, not mongoose documents (since any shape of document can be returned).
  2112. * - Requires MongoDB >= 2.1
  2113. *
  2114. * @see Aggregate #aggregate_Aggregate
  2115. * @see MongoDB http://docs.mongodb.org/manual/applications/aggregation/
  2116. * @param {Object|Array} [...] aggregation pipeline operator(s) or operator array
  2117. * @param {Function} [callback]
  2118. * @return {Aggregate|Promise}
  2119. * @api public
  2120. */
  2121. Model.aggregate = function aggregate() {
  2122. var args = [].slice.call(arguments),
  2123. aggregate,
  2124. callback;
  2125. if (typeof args[args.length - 1] === 'function') {
  2126. callback = args.pop();
  2127. }
  2128. if (args.length === 1 && util.isArray(args[0])) {
  2129. aggregate = new Aggregate(args[0]);
  2130. } else {
  2131. aggregate = new Aggregate(args);
  2132. }
  2133. aggregate.model(this);
  2134. if (typeof callback === 'undefined') {
  2135. return aggregate;
  2136. }
  2137. if (callback) {
  2138. callback = this.$wrapCallback(callback);
  2139. }
  2140. aggregate.exec(callback);
  2141. };
  2142. /**
  2143. * Implements `$geoSearch` functionality for Mongoose
  2144. *
  2145. * ####Example:
  2146. *
  2147. * var options = { near: [10, 10], maxDistance: 5 };
  2148. * Locations.geoSearch({ type : "house" }, options, function(err, res) {
  2149. * console.log(res);
  2150. * });
  2151. *
  2152. * ####Options:
  2153. * - `near` {Array} x,y point to search for
  2154. * - `maxDistance` {Number} the maximum distance from the point near that a result can be
  2155. * - `limit` {Number} The maximum number of results to return
  2156. * - `lean` {Boolean} return the raw object instead of the Mongoose Model
  2157. *
  2158. * @param {Object} conditions an object that specifies the match condition (required)
  2159. * @param {Object} options for the geoSearch, some (near, maxDistance) are required
  2160. * @param {Function} [callback] optional callback
  2161. * @return {Promise}
  2162. * @see http://docs.mongodb.org/manual/reference/command/geoSearch/
  2163. * @see http://docs.mongodb.org/manual/core/geohaystack/
  2164. * @api public
  2165. */
  2166. Model.geoSearch = function(conditions, options, callback) {
  2167. if (typeof options === 'function') {
  2168. callback = options;
  2169. options = {};
  2170. }
  2171. if (callback) {
  2172. callback = this.$wrapCallback(callback);
  2173. }
  2174. var _this = this;
  2175. var Promise = PromiseProvider.get();
  2176. return new Promise.ES6(function(resolve, reject) {
  2177. var error;
  2178. if (conditions === undefined || !utils.isObject(conditions)) {
  2179. error = new Error('Must pass conditions to geoSearch');
  2180. } else if (!options.near) {
  2181. error = new Error('Must specify the near option in geoSearch');
  2182. } else if (!Array.isArray(options.near)) {
  2183. error = new Error('near option must be an array [x, y]');
  2184. }
  2185. if (error) {
  2186. callback && callback(error);
  2187. reject(error);
  2188. return;
  2189. }
  2190. // send the conditions in the options object
  2191. options.search = conditions;
  2192. _this.collection.geoHaystackSearch(options.near[0], options.near[1], options, function(err, res) {
  2193. // have to deal with driver problem. Should be fixed in a soon-ish release
  2194. // (7/8/2013)
  2195. if (err) {
  2196. callback && callback(err);
  2197. reject(err);
  2198. return;
  2199. }
  2200. var count = res.results.length;
  2201. if (options.lean || count === 0) {
  2202. callback && callback(null, res.results, res.stats);
  2203. resolve(res.results, res.stats);
  2204. return;
  2205. }
  2206. var errSeen = false;
  2207. function init(err) {
  2208. if (err && !errSeen) {
  2209. callback && callback(err);
  2210. reject(err);
  2211. return;
  2212. }
  2213. if (!--count && !errSeen) {
  2214. callback && callback(null, res.results, res.stats);
  2215. resolve(res.results, res.stats);
  2216. }
  2217. }
  2218. for (var i = 0; i < res.results.length; i++) {
  2219. var temp = res.results[i];
  2220. res.results[i] = new _this();
  2221. res.results[i].init(temp, {}, init);
  2222. }
  2223. });
  2224. });
  2225. };
  2226. /**
  2227. * Populates document references.
  2228. *
  2229. * ####Available options:
  2230. *
  2231. * - path: space delimited path(s) to populate
  2232. * - select: optional fields to select
  2233. * - match: optional query conditions to match
  2234. * - model: optional name of the model to use for population
  2235. * - options: optional query options like sort, limit, etc
  2236. *
  2237. * ####Examples:
  2238. *
  2239. * // populates a single object
  2240. * User.findById(id, function (err, user) {
  2241. * var opts = [
  2242. * { path: 'company', match: { x: 1 }, select: 'name' }
  2243. * , { path: 'notes', options: { limit: 10 }, model: 'override' }
  2244. * ]
  2245. *
  2246. * User.populate(user, opts, function (err, user) {
  2247. * console.log(user);
  2248. * })
  2249. * })
  2250. *
  2251. * // populates an array of objects
  2252. * User.find(match, function (err, users) {
  2253. * var opts = [{ path: 'company', match: { x: 1 }, select: 'name' }]
  2254. *
  2255. * var promise = User.populate(users, opts);
  2256. * promise.then(console.log).end();
  2257. * })
  2258. *
  2259. * // imagine a Weapon model exists with two saved documents:
  2260. * // { _id: 389, name: 'whip' }
  2261. * // { _id: 8921, name: 'boomerang' }
  2262. *
  2263. * var user = { name: 'Indiana Jones', weapon: 389 }
  2264. * Weapon.populate(user, { path: 'weapon', model: 'Weapon' }, function (err, user) {
  2265. * console.log(user.weapon.name) // whip
  2266. * })
  2267. *
  2268. * // populate many plain objects
  2269. * var users = [{ name: 'Indiana Jones', weapon: 389 }]
  2270. * users.push({ name: 'Batman', weapon: 8921 })
  2271. * Weapon.populate(users, { path: 'weapon' }, function (err, users) {
  2272. * users.forEach(function (user) {
  2273. * console.log('%s uses a %s', users.name, user.weapon.name)
  2274. * // Indiana Jones uses a whip
  2275. * // Batman uses a boomerang
  2276. * })
  2277. * })
  2278. * // Note that we didn't need to specify the Weapon model because
  2279. * // we were already using it's populate() method.
  2280. *
  2281. * @param {Document|Array} docs Either a single document or array of documents to populate.
  2282. * @param {Object} options A hash of key/val (path, options) used for population.
  2283. * @param {Function} [callback(err,doc)] Optional callback, executed upon completion. Receives `err` and the `doc(s)`.
  2284. * @return {Promise}
  2285. * @api public
  2286. */
  2287. Model.populate = function(docs, paths, callback) {
  2288. var _this = this;
  2289. if (callback) {
  2290. callback = this.$wrapCallback(callback);
  2291. }
  2292. // normalized paths
  2293. var noPromise = paths && !!paths.__noPromise;
  2294. paths = utils.populate(paths);
  2295. // data that should persist across subPopulate calls
  2296. var cache = {};
  2297. if (noPromise) {
  2298. _populate(this, docs, paths, cache, callback);
  2299. } else {
  2300. var Promise = PromiseProvider.get();
  2301. return new Promise.ES6(function(resolve, reject) {
  2302. _populate(_this, docs, paths, cache, function(error, docs) {
  2303. if (error) {
  2304. callback && callback(error);
  2305. reject(error);
  2306. } else {
  2307. callback && callback(null, docs);
  2308. resolve(docs);
  2309. }
  2310. });
  2311. });
  2312. }
  2313. };
  2314. /*!
  2315. * Populate helper
  2316. *
  2317. * @param {Model} model the model to use
  2318. * @param {Document|Array} docs Either a single document or array of documents to populate.
  2319. * @param {Object} paths
  2320. * @param {Function} [cb(err,doc)] Optional callback, executed upon completion. Receives `err` and the `doc(s)`.
  2321. * @return {Function}
  2322. * @api private
  2323. */
  2324. function _populate(model, docs, paths, cache, callback) {
  2325. var pending = paths.length;
  2326. if (pending === 0) {
  2327. return callback(null, docs);
  2328. }
  2329. // each path has its own query options and must be executed separately
  2330. var i = pending;
  2331. var path;
  2332. while (i--) {
  2333. path = paths[i];
  2334. populate(model, docs, path, next);
  2335. }
  2336. function next(err) {
  2337. if (err) {
  2338. return callback(err);
  2339. }
  2340. if (--pending) {
  2341. return;
  2342. }
  2343. callback(null, docs);
  2344. }
  2345. }
  2346. /*!
  2347. * Populates `docs`
  2348. */
  2349. var excludeIdReg = /\s?-_id\s?/,
  2350. excludeIdRegGlobal = /\s?-_id\s?/g;
  2351. function populate(model, docs, options, callback) {
  2352. var modelsMap;
  2353. // normalize single / multiple docs passed
  2354. if (!Array.isArray(docs)) {
  2355. docs = [docs];
  2356. }
  2357. if (docs.length === 0 || docs.every(utils.isNullOrUndefined)) {
  2358. return callback();
  2359. }
  2360. modelsMap = getModelsMapForPopulate(model, docs, options);
  2361. var i, len = modelsMap.length,
  2362. mod, match, select, vals = [];
  2363. function flatten(item) {
  2364. // no need to include undefined values in our query
  2365. return undefined !== item;
  2366. }
  2367. var _remaining = len;
  2368. var hasOne = false;
  2369. for (i = 0; i < len; i++) {
  2370. mod = modelsMap[i];
  2371. select = mod.options.select;
  2372. if (mod.options.match) {
  2373. match = utils.object.shallowCopy(mod.options.match);
  2374. } else {
  2375. match = {};
  2376. }
  2377. var ids = utils.array.flatten(mod.ids, flatten);
  2378. ids = utils.array.unique(ids);
  2379. if (ids.length === 0 || ids.every(utils.isNullOrUndefined)) {
  2380. --_remaining;
  2381. continue;
  2382. }
  2383. hasOne = true;
  2384. if (mod.foreignField !== '_id' || !match['_id']) {
  2385. match[mod.foreignField] = { $in: ids };
  2386. }
  2387. var assignmentOpts = {};
  2388. assignmentOpts.sort = mod.options.options && mod.options.options.sort || undefined;
  2389. assignmentOpts.excludeId = excludeIdReg.test(select) || (select && select._id === 0);
  2390. if (assignmentOpts.excludeId) {
  2391. // override the exclusion from the query so we can use the _id
  2392. // for document matching during assignment. we'll delete the
  2393. // _id back off before returning the result.
  2394. if (typeof select === 'string') {
  2395. select = select.replace(excludeIdRegGlobal, ' ');
  2396. } else {
  2397. // preserve original select conditions by copying
  2398. select = utils.object.shallowCopy(select);
  2399. delete select._id;
  2400. }
  2401. }
  2402. if (mod.options.options && mod.options.options.limit) {
  2403. assignmentOpts.originalLimit = mod.options.options.limit;
  2404. mod.options.options.limit = mod.options.options.limit * ids.length;
  2405. }
  2406. var subPopulate = mod.options.populate;
  2407. var query = mod.Model.find(match, select, mod.options.options);
  2408. if (subPopulate) {
  2409. query.populate(subPopulate);
  2410. }
  2411. query.exec(next.bind(this, mod, assignmentOpts));
  2412. }
  2413. if (!hasOne) {
  2414. return callback();
  2415. }
  2416. function next(options, assignmentOpts, err, valsFromDb) {
  2417. if (err) return callback(err);
  2418. vals = vals.concat(valsFromDb);
  2419. _assign(null, vals, options, assignmentOpts);
  2420. if (--_remaining === 0) {
  2421. callback();
  2422. }
  2423. }
  2424. function _assign(err, vals, mod, assignmentOpts) {
  2425. if (err) return callback(err);
  2426. var options = mod.options;
  2427. var _val;
  2428. var lean = options.options && options.options.lean,
  2429. len = vals.length,
  2430. rawOrder = {}, rawDocs = {}, key, val;
  2431. // optimization:
  2432. // record the document positions as returned by
  2433. // the query result.
  2434. for (var i = 0; i < len; i++) {
  2435. val = vals[i];
  2436. if (val) {
  2437. _val = utils.getValue(mod.foreignField, val);
  2438. if (Array.isArray(_val)) {
  2439. var _valLength = _val.length;
  2440. for (var j = 0; j < _valLength; ++j) {
  2441. if (_val[j] instanceof Document) {
  2442. _val[j] = _val[j]._id;
  2443. }
  2444. key = String(_val[j]);
  2445. if (rawDocs[key]) {
  2446. if (Array.isArray(rawDocs[key])) {
  2447. rawDocs[key].push(val);
  2448. rawOrder[key].push(i);
  2449. } else {
  2450. rawDocs[key] = [rawDocs[key], val];
  2451. rawOrder[key] = [rawOrder[key], i];
  2452. }
  2453. } else {
  2454. rawDocs[key] = val;
  2455. rawOrder[key] = i;
  2456. }
  2457. }
  2458. } else {
  2459. if (_val instanceof Document) {
  2460. _val = _val._id;
  2461. }
  2462. key = String(_val);
  2463. if (rawDocs[key]) {
  2464. if (Array.isArray(rawDocs[key])) {
  2465. rawDocs[key].push(val);
  2466. rawOrder[key].push(i);
  2467. } else {
  2468. rawDocs[key] = [rawDocs[key], val];
  2469. rawOrder[key] = [rawOrder[key], i];
  2470. }
  2471. } else {
  2472. rawDocs[key] = val;
  2473. rawOrder[key] = i;
  2474. }
  2475. }
  2476. // flag each as result of population
  2477. if (!lean) {
  2478. val.$__.wasPopulated = true;
  2479. }
  2480. }
  2481. }
  2482. assignVals({
  2483. originalModel: model,
  2484. rawIds: mod.ids,
  2485. localField: mod.localField,
  2486. foreignField: mod.foreignField,
  2487. rawDocs: rawDocs,
  2488. rawOrder: rawOrder,
  2489. docs: mod.docs,
  2490. path: options.path,
  2491. options: assignmentOpts,
  2492. justOne: mod.justOne,
  2493. isVirtual: mod.isVirtual
  2494. });
  2495. }
  2496. }
  2497. /*!
  2498. * Assigns documents returned from a population query back
  2499. * to the original document path.
  2500. */
  2501. function assignVals(o) {
  2502. // replace the original ids in our intermediate _ids structure
  2503. // with the documents found by query
  2504. assignRawDocsToIdStructure(o.rawIds, o.rawDocs, o.rawOrder, o.options,
  2505. o.localField, o.foreignField);
  2506. // now update the original documents being populated using the
  2507. // result structure that contains real documents.
  2508. var docs = o.docs;
  2509. var rawIds = o.rawIds;
  2510. var options = o.options;
  2511. function setValue(val) {
  2512. return valueFilter(val, options);
  2513. }
  2514. for (var i = 0; i < docs.length; ++i) {
  2515. if (utils.getValue(o.path, docs[i]) == null &&
  2516. !o.originalModel.schema._getVirtual(o.path)) {
  2517. continue;
  2518. }
  2519. if (o.isVirtual && !o.justOne && !Array.isArray(rawIds[i])) {
  2520. rawIds[i] = [rawIds[i]];
  2521. }
  2522. utils.setValue(o.path, rawIds[i], docs[i], setValue);
  2523. }
  2524. }
  2525. /*!
  2526. * Assign `vals` returned by mongo query to the `rawIds`
  2527. * structure returned from utils.getVals() honoring
  2528. * query sort order if specified by user.
  2529. *
  2530. * This can be optimized.
  2531. *
  2532. * Rules:
  2533. *
  2534. * if the value of the path is not an array, use findOne rules, else find.
  2535. * for findOne the results are assigned directly to doc path (including null results).
  2536. * for find, if user specified sort order, results are assigned directly
  2537. * else documents are put back in original order of array if found in results
  2538. *
  2539. * @param {Array} rawIds
  2540. * @param {Array} vals
  2541. * @param {Boolean} sort
  2542. * @api private
  2543. */
  2544. function assignRawDocsToIdStructure(rawIds, resultDocs, resultOrder, options, localFields, foreignFields, recursed) {
  2545. // honor user specified sort order
  2546. var newOrder = [];
  2547. var sorting = options.sort && rawIds.length > 1;
  2548. var doc;
  2549. var sid;
  2550. var id;
  2551. for (var i = 0; i < rawIds.length; ++i) {
  2552. id = rawIds[i];
  2553. if (Array.isArray(id)) {
  2554. // handle [ [id0, id2], [id3] ]
  2555. assignRawDocsToIdStructure(id, resultDocs, resultOrder, options, localFields, foreignFields, true);
  2556. newOrder.push(id);
  2557. continue;
  2558. }
  2559. if (id === null && !sorting) {
  2560. // keep nulls for findOne unless sorting, which always
  2561. // removes them (backward compat)
  2562. newOrder.push(id);
  2563. continue;
  2564. }
  2565. sid = String(id);
  2566. if (recursed) {
  2567. // apply find behavior
  2568. // assign matching documents in original order unless sorting
  2569. doc = resultDocs[sid];
  2570. if (doc) {
  2571. if (sorting) {
  2572. newOrder[resultOrder[sid]] = doc;
  2573. } else {
  2574. newOrder.push(doc);
  2575. }
  2576. } else {
  2577. newOrder.push(id);
  2578. }
  2579. } else {
  2580. // apply findOne behavior - if document in results, assign, else assign null
  2581. newOrder[i] = doc = resultDocs[sid] || null;
  2582. }
  2583. }
  2584. rawIds.length = 0;
  2585. if (newOrder.length) {
  2586. // reassign the documents based on corrected order
  2587. // forEach skips over sparse entries in arrays so we
  2588. // can safely use this to our advantage dealing with sorted
  2589. // result sets too.
  2590. newOrder.forEach(function(doc, i) {
  2591. if (!doc) {
  2592. return;
  2593. }
  2594. rawIds[i] = doc;
  2595. });
  2596. }
  2597. }
  2598. function getModelsMapForPopulate(model, docs, options) {
  2599. var i, doc, len = docs.length,
  2600. available = {},
  2601. map = [],
  2602. modelNameFromQuery = options.model && options.model.modelName || options.model,
  2603. schema, refPath, Model, currentOptions, modelNames, modelName, discriminatorKey, modelForFindSchema;
  2604. var originalOptions = utils.clone(options);
  2605. var isVirtual = false;
  2606. schema = model._getSchema(options.path);
  2607. if (schema && schema.caster) {
  2608. schema = schema.caster;
  2609. }
  2610. if (!schema && model.discriminators) {
  2611. discriminatorKey = model.schema.discriminatorMapping.key;
  2612. }
  2613. refPath = schema && schema.options && schema.options.refPath;
  2614. for (i = 0; i < len; i++) {
  2615. doc = docs[i];
  2616. if (refPath) {
  2617. modelNames = utils.getValue(refPath, doc);
  2618. if (Array.isArray(modelNames)) {
  2619. modelNames = modelNames.filter(function(v) {
  2620. return v != null;
  2621. });
  2622. }
  2623. } else {
  2624. if (!modelNameFromQuery) {
  2625. var modelForCurrentDoc = model;
  2626. var schemaForCurrentDoc;
  2627. if (!schema && discriminatorKey) {
  2628. modelForFindSchema = utils.getValue(discriminatorKey, doc);
  2629. if (modelForFindSchema) {
  2630. modelForCurrentDoc = model.db.model(modelForFindSchema);
  2631. schemaForCurrentDoc = modelForCurrentDoc._getSchema(options.path);
  2632. if (schemaForCurrentDoc && schemaForCurrentDoc.caster) {
  2633. schemaForCurrentDoc = schemaForCurrentDoc.caster;
  2634. }
  2635. }
  2636. } else {
  2637. schemaForCurrentDoc = schema;
  2638. }
  2639. var virtual = modelForCurrentDoc.schema._getVirtual(options.path);
  2640. if (schemaForCurrentDoc && schemaForCurrentDoc.options && schemaForCurrentDoc.options.ref) {
  2641. modelNames = [schemaForCurrentDoc.options.ref];
  2642. } else if (virtual && virtual.options && virtual.options.ref) {
  2643. modelNames = [virtual && virtual.options && virtual.options.ref];
  2644. isVirtual = true;
  2645. } else {
  2646. modelNames = [model.modelName];
  2647. }
  2648. } else {
  2649. modelNames = [modelNameFromQuery]; // query options
  2650. }
  2651. }
  2652. if (!modelNames) {
  2653. continue;
  2654. }
  2655. if (!Array.isArray(modelNames)) {
  2656. modelNames = [modelNames];
  2657. }
  2658. virtual = model.schema._getVirtual(options.path);
  2659. var localField = virtual && virtual.options ?
  2660. (virtual.$nestedSchemaPath ? virtual.$nestedSchemaPath + '.' : '') + virtual.options.localField :
  2661. options.path;
  2662. var foreignField = virtual && virtual.options ?
  2663. virtual.options.foreignField :
  2664. '_id';
  2665. var justOne = virtual && virtual.options && virtual.options.justOne;
  2666. if (virtual && virtual.options && virtual.options.ref) {
  2667. isVirtual = true;
  2668. }
  2669. if (virtual && (!localField || !foreignField)) {
  2670. throw new Error('If you are populating a virtual, you must set the ' +
  2671. 'localField and foreignField options');
  2672. }
  2673. options.isVirtual = isVirtual;
  2674. var ret = convertTo_id(utils.getValue(localField, doc));
  2675. var id = String(utils.getValue(foreignField, doc));
  2676. options._docs[id] = Array.isArray(ret) ? ret.slice() : ret;
  2677. if (doc.$__) {
  2678. doc.populated(options.path, options._docs[id], options);
  2679. }
  2680. var k = modelNames.length;
  2681. while (k--) {
  2682. modelName = modelNames[k];
  2683. Model = originalOptions.model && originalOptions.model.modelName ?
  2684. originalOptions.model :
  2685. model.db.model(modelName);
  2686. if (!available[modelName]) {
  2687. currentOptions = {
  2688. model: Model
  2689. };
  2690. if (isVirtual && virtual.options && virtual.options.options) {
  2691. currentOptions.options = utils.clone(virtual.options.options, {
  2692. retainKeyOrder: true
  2693. });
  2694. }
  2695. utils.merge(currentOptions, options);
  2696. if (schema && !discriminatorKey) {
  2697. currentOptions.model = Model;
  2698. }
  2699. options.model = Model;
  2700. available[modelName] = {
  2701. Model: Model,
  2702. options: currentOptions,
  2703. docs: [doc],
  2704. ids: [ret],
  2705. // Assume only 1 localField + foreignField
  2706. localField: localField,
  2707. foreignField: foreignField,
  2708. justOne: justOne,
  2709. isVirtual: isVirtual
  2710. };
  2711. map.push(available[modelName]);
  2712. } else {
  2713. available[modelName].docs.push(doc);
  2714. available[modelName].ids.push(ret);
  2715. }
  2716. }
  2717. }
  2718. return map;
  2719. }
  2720. /*!
  2721. * Retrieve the _id of `val` if a Document or Array of Documents.
  2722. *
  2723. * @param {Array|Document|Any} val
  2724. * @return {Array|Document|Any}
  2725. */
  2726. function convertTo_id(val) {
  2727. if (val instanceof Model) return val._id;
  2728. if (Array.isArray(val)) {
  2729. for (var i = 0; i < val.length; ++i) {
  2730. if (val[i] instanceof Model) {
  2731. val[i] = val[i]._id;
  2732. }
  2733. }
  2734. if (val.isMongooseArray) {
  2735. return val._schema.cast(val, val._parent);
  2736. }
  2737. return [].concat(val);
  2738. }
  2739. return val;
  2740. }
  2741. /*!
  2742. * 1) Apply backwards compatible find/findOne behavior to sub documents
  2743. *
  2744. * find logic:
  2745. * a) filter out non-documents
  2746. * b) remove _id from sub docs when user specified
  2747. *
  2748. * findOne
  2749. * a) if no doc found, set to null
  2750. * b) remove _id from sub docs when user specified
  2751. *
  2752. * 2) Remove _ids when specified by users query.
  2753. *
  2754. * background:
  2755. * _ids are left in the query even when user excludes them so
  2756. * that population mapping can occur.
  2757. */
  2758. function valueFilter(val, assignmentOpts) {
  2759. if (Array.isArray(val)) {
  2760. // find logic
  2761. var ret = [];
  2762. var numValues = val.length;
  2763. for (var i = 0; i < numValues; ++i) {
  2764. var subdoc = val[i];
  2765. if (!isDoc(subdoc)) continue;
  2766. maybeRemoveId(subdoc, assignmentOpts);
  2767. ret.push(subdoc);
  2768. if (assignmentOpts.originalLimit &&
  2769. ret.length >= assignmentOpts.originalLimit) {
  2770. break;
  2771. }
  2772. }
  2773. // Since we don't want to have to create a new mongoosearray, make sure to
  2774. // modify the array in place
  2775. while (val.length > ret.length) {
  2776. Array.prototype.pop.apply(val, []);
  2777. }
  2778. for (i = 0; i < ret.length; ++i) {
  2779. val[i] = ret[i];
  2780. }
  2781. return val;
  2782. }
  2783. // findOne
  2784. if (isDoc(val)) {
  2785. maybeRemoveId(val, assignmentOpts);
  2786. return val;
  2787. }
  2788. return null;
  2789. }
  2790. /*!
  2791. * Remove _id from `subdoc` if user specified "lean" query option
  2792. */
  2793. function maybeRemoveId(subdoc, assignmentOpts) {
  2794. if (assignmentOpts.excludeId) {
  2795. if (typeof subdoc.setValue === 'function') {
  2796. delete subdoc._doc._id;
  2797. } else {
  2798. delete subdoc._id;
  2799. }
  2800. }
  2801. }
  2802. /*!
  2803. * Determine if `doc` is a document returned
  2804. * by a populate query.
  2805. */
  2806. function isDoc(doc) {
  2807. if (doc == null) {
  2808. return false;
  2809. }
  2810. var type = typeof doc;
  2811. if (type === 'string') {
  2812. return false;
  2813. }
  2814. if (type === 'number') {
  2815. return false;
  2816. }
  2817. if (Buffer.isBuffer(doc)) {
  2818. return false;
  2819. }
  2820. if (doc.constructor.name === 'ObjectID') {
  2821. return false;
  2822. }
  2823. // only docs
  2824. return true;
  2825. }
  2826. /**
  2827. * Finds the schema for `path`. This is different than
  2828. * calling `schema.path` as it also resolves paths with
  2829. * positional selectors (something.$.another.$.path).
  2830. *
  2831. * @param {String} path
  2832. * @return {Schema}
  2833. * @api private
  2834. */
  2835. Model._getSchema = function _getSchema(path) {
  2836. return this.schema._getSchema(path);
  2837. };
  2838. /*!
  2839. * Compiler utility.
  2840. *
  2841. * @param {String|Function} name model name or class extending Model
  2842. * @param {Schema} schema
  2843. * @param {String} collectionName
  2844. * @param {Connection} connection
  2845. * @param {Mongoose} base mongoose instance
  2846. */
  2847. Model.compile = function compile(name, schema, collectionName, connection, base) {
  2848. var versioningEnabled = schema.options.versionKey !== false;
  2849. if (versioningEnabled && !schema.paths[schema.options.versionKey]) {
  2850. // add versioning to top level documents only
  2851. var o = {};
  2852. o[schema.options.versionKey] = Number;
  2853. schema.add(o);
  2854. }
  2855. var model;
  2856. if (typeof name === 'function' && name.prototype instanceof Model) {
  2857. model = name;
  2858. name = model.name;
  2859. schema.loadClass(model, true);
  2860. } else {
  2861. // generate new class
  2862. model = function model(doc, fields, skipId) {
  2863. if (!(this instanceof model)) {
  2864. return new model(doc, fields, skipId);
  2865. }
  2866. Model.call(this, doc, fields, skipId);
  2867. };
  2868. }
  2869. model.hooks = schema.s.hooks.clone();
  2870. model.base = base;
  2871. model.modelName = name;
  2872. if (!(model.prototype instanceof Model)) {
  2873. model.__proto__ = Model;
  2874. model.prototype.__proto__ = Model.prototype;
  2875. }
  2876. model.model = Model.prototype.model;
  2877. model.db = model.prototype.db = connection;
  2878. model.discriminators = model.prototype.discriminators = undefined;
  2879. model.prototype.$__setSchema(schema);
  2880. var collectionOptions = {
  2881. bufferCommands: schema.options.bufferCommands,
  2882. capped: schema.options.capped
  2883. };
  2884. model.prototype.collection = connection.collection(
  2885. collectionName
  2886. , collectionOptions
  2887. );
  2888. // apply methods and statics
  2889. applyMethods(model, schema);
  2890. applyStatics(model, schema);
  2891. model.schema = model.prototype.schema;
  2892. model.collection = model.prototype.collection;
  2893. // Create custom query constructor
  2894. model.Query = function() {
  2895. Query.apply(this, arguments);
  2896. this.options.retainKeyOrder = model.schema.options.retainKeyOrder;
  2897. };
  2898. model.Query.prototype = Object.create(Query.prototype);
  2899. model.Query.base = Query.base;
  2900. applyQueryMethods(model, schema.query);
  2901. var kareemOptions = { useErrorHandlers: true };
  2902. model.$__insertMany = model.hooks.createWrapper('insertMany',
  2903. model.insertMany, model, kareemOptions);
  2904. model.insertMany = function(arr, callback) {
  2905. var Promise = PromiseProvider.get();
  2906. return new Promise.ES6(function(resolve, reject) {
  2907. model.$__insertMany(arr, function(error, result) {
  2908. if (error) {
  2909. callback && callback(error);
  2910. return reject(error);
  2911. }
  2912. callback && callback(null, result);
  2913. resolve(result);
  2914. });
  2915. });
  2916. };
  2917. return model;
  2918. };
  2919. /*!
  2920. * Register methods for this model
  2921. *
  2922. * @param {Model} model
  2923. * @param {Schema} schema
  2924. */
  2925. var applyMethods = function(model, schema) {
  2926. function apply(method, schema) {
  2927. Object.defineProperty(model.prototype, method, {
  2928. get: function() {
  2929. var h = {};
  2930. for (var k in schema.methods[method]) {
  2931. h[k] = schema.methods[method][k].bind(this);
  2932. }
  2933. return h;
  2934. },
  2935. configurable: true
  2936. });
  2937. }
  2938. for (var method in schema.methods) {
  2939. if (schema.tree.hasOwnProperty(method)) {
  2940. throw new Error('You have a method and a property in your schema both ' +
  2941. 'named "' + method + '"');
  2942. }
  2943. if (typeof schema.methods[method] === 'function') {
  2944. model.prototype[method] = schema.methods[method];
  2945. } else {
  2946. apply(method, schema);
  2947. }
  2948. }
  2949. };
  2950. /*!
  2951. * Register statics for this model
  2952. * @param {Model} model
  2953. * @param {Schema} schema
  2954. */
  2955. var applyStatics = function(model, schema) {
  2956. for (var i in schema.statics) {
  2957. model[i] = schema.statics[i];
  2958. }
  2959. };
  2960. /*!
  2961. * Register custom query methods for this model
  2962. *
  2963. * @param {Model} model
  2964. * @param {Schema} schema
  2965. */
  2966. function applyQueryMethods(model, methods) {
  2967. for (var i in methods) {
  2968. model.Query.prototype[i] = methods[i];
  2969. }
  2970. }
  2971. /*!
  2972. * Subclass this model with `conn`, `schema`, and `collection` settings.
  2973. *
  2974. * @param {Connection} conn
  2975. * @param {Schema} [schema]
  2976. * @param {String} [collection]
  2977. * @return {Model}
  2978. */
  2979. Model.__subclass = function subclass(conn, schema, collection) {
  2980. // subclass model using this connection and collection name
  2981. var _this = this;
  2982. var Model = function Model(doc, fields, skipId) {
  2983. if (!(this instanceof Model)) {
  2984. return new Model(doc, fields, skipId);
  2985. }
  2986. _this.call(this, doc, fields, skipId);
  2987. };
  2988. Model.__proto__ = _this;
  2989. Model.prototype.__proto__ = _this.prototype;
  2990. Model.db = Model.prototype.db = conn;
  2991. var s = schema && typeof schema !== 'string'
  2992. ? schema
  2993. : _this.prototype.schema;
  2994. var options = s.options || {};
  2995. if (!collection) {
  2996. collection = _this.prototype.schema.get('collection')
  2997. || utils.toCollectionName(_this.modelName, options);
  2998. }
  2999. var collectionOptions = {
  3000. bufferCommands: s ? options.bufferCommands : true,
  3001. capped: s && options.capped
  3002. };
  3003. Model.prototype.collection = conn.collection(collection, collectionOptions);
  3004. Model.collection = Model.prototype.collection;
  3005. Model.init();
  3006. return Model;
  3007. };
  3008. Model.$wrapCallback = function(callback) {
  3009. var _this = this;
  3010. return function() {
  3011. try {
  3012. callback.apply(null, arguments);
  3013. } catch (error) {
  3014. _this.emit('error', error);
  3015. }
  3016. };
  3017. };
  3018. /*!
  3019. * Module exports.
  3020. */
  3021. module.exports = exports = Model;