document.js 104 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802
  1. 'use strict';
  2. /*!
  3. * Module dependencies.
  4. */
  5. const EventEmitter = require('events').EventEmitter;
  6. const InternalCache = require('./internal');
  7. const MongooseError = require('./error/index');
  8. const MixedSchema = require('./schema/mixed');
  9. const ObjectExpectedError = require('./error/objectExpected');
  10. const ObjectParameterError = require('./error/objectParameter');
  11. const ParallelValidateError = require('./error/parallelValidate');
  12. const Schema = require('./schema');
  13. const StrictModeError = require('./error/strict');
  14. const ValidationError = require('./error/validation');
  15. const ValidatorError = require('./error/validator');
  16. const VirtualType = require('./virtualtype');
  17. const promiseOrCallback = require('./helpers/promiseOrCallback');
  18. const cleanModifiedSubpaths = require('./helpers/document/cleanModifiedSubpaths');
  19. const compile = require('./helpers/document/compile').compile;
  20. const defineKey = require('./helpers/document/compile').defineKey;
  21. const flatten = require('./helpers/common').flatten;
  22. const get = require('./helpers/get');
  23. const getEmbeddedDiscriminatorPath = require('./helpers/document/getEmbeddedDiscriminatorPath');
  24. const handleSpreadDoc = require('./helpers/document/handleSpreadDoc');
  25. const idGetter = require('./plugins/idGetter');
  26. const isDefiningProjection = require('./helpers/projection/isDefiningProjection');
  27. const isExclusive = require('./helpers/projection/isExclusive');
  28. const inspect = require('util').inspect;
  29. const internalToObjectOptions = require('./options').internalToObjectOptions;
  30. const mpath = require('mpath');
  31. const utils = require('./utils');
  32. const clone = utils.clone;
  33. const deepEqual = utils.deepEqual;
  34. const isMongooseObject = utils.isMongooseObject;
  35. const arrayAtomicsSymbol = require('./helpers/symbols').arrayAtomicsSymbol;
  36. const documentArrayParent = require('./helpers/symbols').documentArrayParent;
  37. const documentSchemaSymbol = require('./helpers/symbols').documentSchemaSymbol;
  38. const getSymbol = require('./helpers/symbols').getSymbol;
  39. const populateModelSymbol = require('./helpers/symbols').populateModelSymbol;
  40. const scopeSymbol = require('./helpers/symbols').scopeSymbol;
  41. let DocumentArray;
  42. let MongooseArray;
  43. let Embedded;
  44. const specialProperties = utils.specialProperties;
  45. /**
  46. * The core Mongoose document constructor. You should not call this directly,
  47. * the Mongoose [Model constructor](./api.html#Model) calls this for you.
  48. *
  49. * @param {Object} obj the values to set
  50. * @param {Object} [fields] optional object containing the fields which were selected in the query returning this document and any populated paths data
  51. * @param {Boolean} [skipId] bool, should we auto create an ObjectId _id
  52. * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
  53. * @event `init`: Emitted on a document after it has been retrieved from the db and fully hydrated by Mongoose.
  54. * @event `save`: Emitted when the document is successfully saved
  55. * @api private
  56. */
  57. function Document(obj, fields, skipId, options) {
  58. if (typeof skipId === 'object' && skipId != null) {
  59. options = skipId;
  60. skipId = options.skipId;
  61. }
  62. options = options || {};
  63. // Support `browserDocument.js` syntax
  64. if (this.schema == null) {
  65. const _schema = utils.isObject(fields) && !fields.instanceOfSchema ?
  66. new Schema(fields) :
  67. fields;
  68. this.$__setSchema(_schema);
  69. fields = skipId;
  70. skipId = options;
  71. options = arguments[4] || {};
  72. }
  73. this.$__ = new InternalCache;
  74. this.$__.emitter = new EventEmitter();
  75. this.isNew = 'isNew' in options ? options.isNew : true;
  76. this.errors = undefined;
  77. this.$__.$options = options || {};
  78. if (obj != null && typeof obj !== 'object') {
  79. throw new ObjectParameterError(obj, 'obj', 'Document');
  80. }
  81. const schema = this.schema;
  82. if (typeof fields === 'boolean' || fields === 'throw') {
  83. this.$__.strictMode = fields;
  84. fields = undefined;
  85. } else {
  86. this.$__.strictMode = schema.options.strict;
  87. this.$__.selected = fields;
  88. }
  89. const requiredPaths = schema.requiredPaths(true);
  90. for (const path of requiredPaths) {
  91. this.$__.activePaths.require(path);
  92. }
  93. this.$__.emitter.setMaxListeners(0);
  94. let exclude = null;
  95. // determine if this doc is a result of a query with
  96. // excluded fields
  97. if (utils.isPOJO(fields)) {
  98. exclude = isExclusive(fields);
  99. }
  100. const hasIncludedChildren = exclude === false && fields ?
  101. $__hasIncludedChildren(fields) :
  102. {};
  103. if (this._doc == null) {
  104. this.$__buildDoc(obj, fields, skipId, exclude, hasIncludedChildren, false);
  105. // By default, defaults get applied **before** setting initial values
  106. // Re: gh-6155
  107. $__applyDefaults(this, fields, skipId, exclude, hasIncludedChildren, true, {
  108. isNew: this.isNew
  109. });
  110. }
  111. if (obj) {
  112. // Skip set hooks
  113. if (this.$__original_set) {
  114. this.$__original_set(obj, undefined, true);
  115. } else {
  116. this.$set(obj, undefined, true);
  117. }
  118. if (obj instanceof Document) {
  119. this.isNew = obj.isNew;
  120. }
  121. }
  122. // Function defaults get applied **after** setting initial values so they
  123. // see the full doc rather than an empty one, unless they opt out.
  124. // Re: gh-3781, gh-6155
  125. if (options.willInit) {
  126. EventEmitter.prototype.once.call(this, 'init', () => {
  127. $__applyDefaults(this, fields, skipId, exclude, hasIncludedChildren, false, options.skipDefaults, {
  128. isNew: this.isNew
  129. });
  130. });
  131. } else {
  132. $__applyDefaults(this, fields, skipId, exclude, hasIncludedChildren, false, options.skipDefaults, {
  133. isNew: this.isNew
  134. });
  135. }
  136. this.$__._id = this._id;
  137. this.$locals = {};
  138. this.$op = null;
  139. if (!this.$__.strictMode && obj) {
  140. const _this = this;
  141. const keys = Object.keys(this._doc);
  142. keys.forEach(function(key) {
  143. if (!(key in schema.tree)) {
  144. defineKey(key, null, _this);
  145. }
  146. });
  147. }
  148. applyQueue(this);
  149. }
  150. /*!
  151. * Document exposes the NodeJS event emitter API, so you can use
  152. * `on`, `once`, etc.
  153. */
  154. utils.each(
  155. ['on', 'once', 'emit', 'listeners', 'removeListener', 'setMaxListeners',
  156. 'removeAllListeners', 'addListener'],
  157. function(emitterFn) {
  158. Document.prototype[emitterFn] = function() {
  159. return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments);
  160. };
  161. });
  162. Document.prototype.constructor = Document;
  163. for (const i in EventEmitter.prototype) {
  164. Document[i] = EventEmitter.prototype[i];
  165. }
  166. /**
  167. * The documents schema.
  168. *
  169. * @api public
  170. * @property schema
  171. * @memberOf Document
  172. * @instance
  173. */
  174. Document.prototype.schema;
  175. /**
  176. * Empty object that you can use for storing properties on the document. This
  177. * is handy for passing data to middleware without conflicting with Mongoose
  178. * internals.
  179. *
  180. * ####Example:
  181. *
  182. * schema.pre('save', function() {
  183. * // Mongoose will set `isNew` to `false` if `save()` succeeds
  184. * this.$locals.wasNew = this.isNew;
  185. * });
  186. *
  187. * schema.post('save', function() {
  188. * // Prints true if `isNew` was set before `save()`
  189. * console.log(this.$locals.wasNew);
  190. * });
  191. *
  192. * @api public
  193. * @property $locals
  194. * @memberOf Document
  195. * @instance
  196. */
  197. Object.defineProperty(Document.prototype, '$locals', {
  198. configurable: false,
  199. enumerable: false,
  200. writable: true
  201. });
  202. /**
  203. * Boolean flag specifying if the document is new.
  204. *
  205. * @api public
  206. * @property isNew
  207. * @memberOf Document
  208. * @instance
  209. */
  210. Document.prototype.isNew;
  211. /**
  212. * The string version of this documents _id.
  213. *
  214. * ####Note:
  215. *
  216. * This getter exists on all documents by default. The getter can be disabled by setting the `id` [option](/docs/guide.html#id) of its `Schema` to false at construction time.
  217. *
  218. * new Schema({ name: String }, { id: false });
  219. *
  220. * @api public
  221. * @see Schema options /docs/guide.html#options
  222. * @property id
  223. * @memberOf Document
  224. * @instance
  225. */
  226. Document.prototype.id;
  227. /**
  228. * Hash containing current validation errors.
  229. *
  230. * @api public
  231. * @property errors
  232. * @memberOf Document
  233. * @instance
  234. */
  235. Document.prototype.errors;
  236. /**
  237. * A string containing the current operation that Mongoose is executing
  238. * on this document. May be `null`, `'save'`, `'validate'`, or `'remove'`.
  239. *
  240. * ####Example:
  241. *
  242. * const doc = new Model({ name: 'test' });
  243. * doc.$op; // null
  244. *
  245. * const promise = doc.save();
  246. * doc.$op; // 'save'
  247. *
  248. * await promise;
  249. * doc.$op; // null
  250. *
  251. * @api public
  252. * @property $op
  253. * @memberOf Document
  254. * @instance
  255. */
  256. Document.prototype.$op;
  257. /*!
  258. * ignore
  259. */
  260. function $__hasIncludedChildren(fields) {
  261. const hasIncludedChildren = {};
  262. const keys = Object.keys(fields);
  263. for (const key of keys) {
  264. const parts = key.split('.');
  265. const c = [];
  266. for (const part of parts) {
  267. c.push(part);
  268. hasIncludedChildren[c.join('.')] = 1;
  269. }
  270. }
  271. return hasIncludedChildren;
  272. }
  273. /*!
  274. * ignore
  275. */
  276. function $__applyDefaults(doc, fields, skipId, exclude, hasIncludedChildren, isBeforeSetters, pathsToSkip) {
  277. const paths = Object.keys(doc.schema.paths);
  278. const plen = paths.length;
  279. for (let i = 0; i < plen; ++i) {
  280. let def;
  281. let curPath = '';
  282. const p = paths[i];
  283. if (p === '_id' && skipId) {
  284. continue;
  285. }
  286. const type = doc.schema.paths[p];
  287. const path = p.indexOf('.') === -1 ? [p] : p.split('.');
  288. const len = path.length;
  289. let included = false;
  290. let doc_ = doc._doc;
  291. for (let j = 0; j < len; ++j) {
  292. if (doc_ == null) {
  293. break;
  294. }
  295. const piece = path[j];
  296. curPath += (!curPath.length ? '' : '.') + piece;
  297. if (exclude === true) {
  298. if (curPath in fields) {
  299. break;
  300. }
  301. } else if (exclude === false && fields && !included) {
  302. if (curPath in fields) {
  303. included = true;
  304. } else if (!hasIncludedChildren[curPath]) {
  305. break;
  306. }
  307. }
  308. if (j === len - 1) {
  309. if (doc_[piece] !== void 0) {
  310. break;
  311. }
  312. if (typeof type.defaultValue === 'function') {
  313. if (!type.defaultValue.$runBeforeSetters && isBeforeSetters) {
  314. break;
  315. }
  316. if (type.defaultValue.$runBeforeSetters && !isBeforeSetters) {
  317. break;
  318. }
  319. } else if (!isBeforeSetters) {
  320. // Non-function defaults should always run **before** setters
  321. continue;
  322. }
  323. if (pathsToSkip && pathsToSkip[curPath]) {
  324. break;
  325. }
  326. if (fields && exclude !== null) {
  327. if (exclude === true) {
  328. // apply defaults to all non-excluded fields
  329. if (p in fields) {
  330. continue;
  331. }
  332. def = type.getDefault(doc, false);
  333. if (typeof def !== 'undefined') {
  334. doc_[piece] = def;
  335. doc.$__.activePaths.default(p);
  336. }
  337. } else if (included) {
  338. // selected field
  339. def = type.getDefault(doc, false);
  340. if (typeof def !== 'undefined') {
  341. doc_[piece] = def;
  342. doc.$__.activePaths.default(p);
  343. }
  344. }
  345. } else {
  346. def = type.getDefault(doc, false);
  347. if (typeof def !== 'undefined') {
  348. doc_[piece] = def;
  349. doc.$__.activePaths.default(p);
  350. }
  351. }
  352. } else {
  353. doc_ = doc_[piece];
  354. }
  355. }
  356. }
  357. }
  358. /**
  359. * Builds the default doc structure
  360. *
  361. * @param {Object} obj
  362. * @param {Object} [fields]
  363. * @param {Boolean} [skipId]
  364. * @api private
  365. * @method $__buildDoc
  366. * @memberOf Document
  367. * @instance
  368. */
  369. Document.prototype.$__buildDoc = function(obj, fields, skipId, exclude, hasIncludedChildren) {
  370. const doc = {};
  371. const paths = Object.keys(this.schema.paths).
  372. // Don't build up any paths that are underneath a map, we don't know
  373. // what the keys will be
  374. filter(p => !p.includes('$*'));
  375. const plen = paths.length;
  376. let ii = 0;
  377. for (; ii < plen; ++ii) {
  378. const p = paths[ii];
  379. if (p === '_id') {
  380. if (skipId) {
  381. continue;
  382. }
  383. if (obj && '_id' in obj) {
  384. continue;
  385. }
  386. }
  387. const path = p.split('.');
  388. const len = path.length;
  389. const last = len - 1;
  390. let curPath = '';
  391. let doc_ = doc;
  392. let included = false;
  393. for (let i = 0; i < len; ++i) {
  394. const piece = path[i];
  395. curPath += (!curPath.length ? '' : '.') + piece;
  396. // support excluding intermediary levels
  397. if (exclude === true) {
  398. if (curPath in fields) {
  399. break;
  400. }
  401. } else if (exclude === false && fields && !included) {
  402. if (curPath in fields) {
  403. included = true;
  404. } else if (!hasIncludedChildren[curPath]) {
  405. break;
  406. }
  407. }
  408. if (i < last) {
  409. doc_ = doc_[piece] || (doc_[piece] = {});
  410. }
  411. }
  412. }
  413. this._doc = doc;
  414. };
  415. /*!
  416. * Converts to POJO when you use the document for querying
  417. */
  418. Document.prototype.toBSON = function() {
  419. return this.toObject(internalToObjectOptions);
  420. };
  421. /**
  422. * Initializes the document without setters or marking anything modified.
  423. *
  424. * Called internally after a document is returned from mongodb. Normally,
  425. * you do **not** need to call this function on your own.
  426. *
  427. * This function triggers `init` [middleware](/docs/middleware.html).
  428. * Note that `init` hooks are [synchronous](/docs/middleware.html#synchronous).
  429. *
  430. * @param {Object} doc document returned by mongo
  431. * @api public
  432. * @memberOf Document
  433. * @instance
  434. */
  435. Document.prototype.init = function(doc, opts, fn) {
  436. if (typeof opts === 'function') {
  437. fn = opts;
  438. opts = null;
  439. }
  440. this.$__init(doc, opts);
  441. if (fn) {
  442. fn(null, this);
  443. }
  444. return this;
  445. };
  446. /*!
  447. * ignore
  448. */
  449. Document.prototype.$__init = function(doc, opts) {
  450. this.isNew = false;
  451. this.$init = true;
  452. opts = opts || {};
  453. // handle docs with populated paths
  454. // If doc._id is not null or undefined
  455. if (doc._id != null && opts.populated && opts.populated.length) {
  456. const id = String(doc._id);
  457. for (const item of opts.populated) {
  458. if (item.isVirtual) {
  459. this.populated(item.path, utils.getValue(item.path, doc), item);
  460. } else {
  461. this.populated(item.path, item._docs[id], item);
  462. }
  463. }
  464. }
  465. init(this, doc, this._doc, opts);
  466. markArraySubdocsPopulated(this, opts.populated);
  467. this.emit('init', this);
  468. this.constructor.emit('init', this);
  469. this.$__._id = this._id;
  470. return this;
  471. };
  472. /*!
  473. * If populating a path within a document array, make sure each
  474. * subdoc within the array knows its subpaths are populated.
  475. *
  476. * ####Example:
  477. * const doc = await Article.findOne().populate('comments.author');
  478. * doc.comments[0].populated('author'); // Should be set
  479. */
  480. function markArraySubdocsPopulated(doc, populated) {
  481. if (doc._id == null || populated == null || populated.length === 0) {
  482. return;
  483. }
  484. const id = String(doc._id);
  485. for (const item of populated) {
  486. if (item.isVirtual) {
  487. continue;
  488. }
  489. const path = item.path;
  490. const pieces = path.split('.');
  491. for (let i = 0; i < pieces.length - 1; ++i) {
  492. const subpath = pieces.slice(0, i + 1).join('.');
  493. const rest = pieces.slice(i + 1).join('.');
  494. const val = doc.get(subpath);
  495. if (val == null) {
  496. continue;
  497. }
  498. if (val.isMongooseDocumentArray) {
  499. for (let j = 0; j < val.length; ++j) {
  500. val[j].populated(rest, item._docs[id] == null ? [] : item._docs[id][j], item);
  501. }
  502. break;
  503. }
  504. }
  505. }
  506. }
  507. /*!
  508. * Init helper.
  509. *
  510. * @param {Object} self document instance
  511. * @param {Object} obj raw mongodb doc
  512. * @param {Object} doc object we are initializing
  513. * @api private
  514. */
  515. function init(self, obj, doc, opts, prefix) {
  516. prefix = prefix || '';
  517. const keys = Object.keys(obj);
  518. const len = keys.length;
  519. let schema;
  520. let path;
  521. let i;
  522. let index = 0;
  523. while (index < len) {
  524. _init(index++);
  525. }
  526. function _init(index) {
  527. i = keys[index];
  528. path = prefix + i;
  529. schema = self.schema.path(path);
  530. // Should still work if not a model-level discriminator, but should not be
  531. // necessary. This is *only* to catch the case where we queried using the
  532. // base model and the discriminated model has a projection
  533. if (self.schema.$isRootDiscriminator && !self.isSelected(path)) {
  534. return;
  535. }
  536. if (!schema && utils.isPOJO(obj[i])) {
  537. // assume nested object
  538. if (!doc[i]) {
  539. doc[i] = {};
  540. }
  541. init(self, obj[i], doc[i], opts, path + '.');
  542. } else if (!schema) {
  543. doc[i] = obj[i];
  544. } else {
  545. if (obj[i] === null) {
  546. doc[i] = null;
  547. } else if (obj[i] !== undefined) {
  548. const intCache = obj[i].$__ || {};
  549. const wasPopulated = intCache.wasPopulated || null;
  550. if (schema && !wasPopulated) {
  551. try {
  552. doc[i] = schema.cast(obj[i], self, true);
  553. } catch (e) {
  554. self.invalidate(e.path, new ValidatorError({
  555. path: e.path,
  556. message: e.message,
  557. type: 'cast',
  558. value: e.value
  559. }));
  560. }
  561. } else {
  562. doc[i] = obj[i];
  563. }
  564. }
  565. // mark as hydrated
  566. if (!self.isModified(path)) {
  567. self.$__.activePaths.init(path);
  568. }
  569. }
  570. }
  571. }
  572. /**
  573. * Sends an update command with this document `_id` as the query selector.
  574. *
  575. * ####Example:
  576. *
  577. * weirdCar.update({$inc: {wheels:1}}, { w: 1 }, callback);
  578. *
  579. * ####Valid options:
  580. *
  581. * - same as in [Model.update](#model_Model.update)
  582. *
  583. * @see Model.update #model_Model.update
  584. * @param {Object} doc
  585. * @param {Object} options
  586. * @param {Function} callback
  587. * @return {Query}
  588. * @api public
  589. * @memberOf Document
  590. * @instance
  591. */
  592. Document.prototype.update = function update() {
  593. const args = utils.args(arguments);
  594. args.unshift({ _id: this._id });
  595. const query = this.constructor.update.apply(this.constructor, args);
  596. if (this.$session() != null) {
  597. if (!('session' in query.options)) {
  598. query.options.session = this.$session();
  599. }
  600. }
  601. return query;
  602. };
  603. /**
  604. * Sends an updateOne command with this document `_id` as the query selector.
  605. *
  606. * ####Example:
  607. *
  608. * weirdCar.updateOne({$inc: {wheels:1}}, { w: 1 }, callback);
  609. *
  610. * ####Valid options:
  611. *
  612. * - same as in [Model.updateOne](#model_Model.updateOne)
  613. *
  614. * @see Model.updateOne #model_Model.updateOne
  615. * @param {Object} doc
  616. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
  617. * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](/docs/api.html#query_Query-lean) and the [Mongoose lean tutorial](/docs/tutorials/lean.html).
  618. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
  619. * @param {Boolean} [options.omitUndefined=false] If true, delete any properties whose value is `undefined` when casting an update. In other words, if this is set, Mongoose will delete `baz` from the update in `Model.updateOne({}, { foo: 'bar', baz: undefined })` before sending the update to the server.
  620. * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set.
  621. * @param {Function} callback
  622. * @return {Query}
  623. * @api public
  624. * @memberOf Document
  625. * @instance
  626. */
  627. Document.prototype.updateOne = function updateOne(doc, options, callback) {
  628. const query = this.constructor.updateOne({ _id: this._id }, doc, options);
  629. query._pre(cb => {
  630. this.constructor._middleware.execPre('updateOne', this, [this], cb);
  631. });
  632. query._post(cb => {
  633. this.constructor._middleware.execPost('updateOne', this, [this], {}, cb);
  634. });
  635. if (this.$session() != null) {
  636. if (!('session' in query.options)) {
  637. query.options.session = this.$session();
  638. }
  639. }
  640. if (callback != null) {
  641. return query.exec(callback);
  642. }
  643. return query;
  644. };
  645. /**
  646. * Sends a replaceOne command with this document `_id` as the query selector.
  647. *
  648. * ####Valid options:
  649. *
  650. * - same as in [Model.replaceOne](#model_Model.replaceOne)
  651. *
  652. * @see Model.replaceOne #model_Model.replaceOne
  653. * @param {Object} doc
  654. * @param {Object} options
  655. * @param {Function} callback
  656. * @return {Query}
  657. * @api public
  658. * @memberOf Document
  659. * @instance
  660. */
  661. Document.prototype.replaceOne = function replaceOne() {
  662. const args = utils.args(arguments);
  663. args.unshift({ _id: this._id });
  664. return this.constructor.replaceOne.apply(this.constructor, args);
  665. };
  666. /**
  667. * Getter/setter around the session associated with this document. Used to
  668. * automatically set `session` if you `save()` a doc that you got from a
  669. * query with an associated session.
  670. *
  671. * ####Example:
  672. *
  673. * const session = MyModel.startSession();
  674. * const doc = await MyModel.findOne().session(session);
  675. * doc.$session() === session; // true
  676. * doc.$session(null);
  677. * doc.$session() === null; // true
  678. *
  679. * If this is a top-level document, setting the session propagates to all child
  680. * docs.
  681. *
  682. * @param {ClientSession} [session] overwrite the current session
  683. * @return {ClientSession}
  684. * @method $session
  685. * @api public
  686. * @memberOf Document
  687. */
  688. Document.prototype.$session = function $session(session) {
  689. if (arguments.length === 0) {
  690. return this.$__.session;
  691. }
  692. this.$__.session = session;
  693. if (!this.ownerDocument) {
  694. const subdocs = this.$__getAllSubdocs();
  695. for (const child of subdocs) {
  696. child.$session(session);
  697. }
  698. }
  699. return session;
  700. };
  701. /**
  702. * Overwrite all values in this document with the values of `obj`, except
  703. * for immutable properties. Behaves similarly to `set()`, except for it
  704. * unsets all properties that aren't in `obj`.
  705. *
  706. * @param {Object} obj the object to overwrite this document with
  707. * @method overwrite
  708. * @name overwrite
  709. * @memberOf Document
  710. * @instance
  711. * @api public
  712. */
  713. Document.prototype.overwrite = function overwrite(obj) {
  714. const keys = Array.from(new Set(Object.keys(this._doc).concat(Object.keys(obj))));
  715. for (const key of keys) {
  716. if (key === '_id') {
  717. continue;
  718. }
  719. // Explicitly skip version key
  720. if (this.schema.options.versionKey && key === this.schema.options.versionKey) {
  721. continue;
  722. }
  723. if (this.schema.options.discriminatorKey && key === this.schema.options.discriminatorKey) {
  724. continue;
  725. }
  726. this.$set(key, obj[key]);
  727. }
  728. return this;
  729. };
  730. /**
  731. * Alias for `set()`, used internally to avoid conflicts
  732. *
  733. * @param {String|Object} path path or object of key/vals to set
  734. * @param {Any} val the value to set
  735. * @param {Schema|String|Number|Buffer|*} [type] optionally specify a type for "on-the-fly" attributes
  736. * @param {Object} [options] optionally specify options that modify the behavior of the set
  737. * @method $set
  738. * @name $set
  739. * @memberOf Document
  740. * @instance
  741. * @api public
  742. */
  743. Document.prototype.$set = function $set(path, val, type, options) {
  744. if (utils.isPOJO(type)) {
  745. options = type;
  746. type = undefined;
  747. }
  748. options = options || {};
  749. const merge = options.merge;
  750. const adhoc = type && type !== true;
  751. const constructing = type === true;
  752. let adhocs;
  753. let keys;
  754. let i = 0;
  755. let pathtype;
  756. let key;
  757. let prefix;
  758. const strict = 'strict' in options
  759. ? options.strict
  760. : this.$__.strictMode;
  761. if (adhoc) {
  762. adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {});
  763. adhocs[path] = this.schema.interpretAsType(path, type, this.schema.options);
  764. }
  765. if (typeof path !== 'string') {
  766. // new Document({ key: val })
  767. if (path instanceof Document) {
  768. if (path.$__isNested) {
  769. path = path.toObject();
  770. } else {
  771. path = path._doc;
  772. }
  773. }
  774. if (path == null) {
  775. const _ = path;
  776. path = val;
  777. val = _;
  778. } else {
  779. prefix = val ? val + '.' : '';
  780. keys = Object.keys(path);
  781. const len = keys.length;
  782. // `_skipMinimizeTopLevel` is because we may have deleted the top-level
  783. // nested key to ensure key order.
  784. const _skipMinimizeTopLevel = get(options, '_skipMinimizeTopLevel', false);
  785. if (len === 0 && _skipMinimizeTopLevel) {
  786. delete options._skipMinimizeTopLevel;
  787. if (val) {
  788. this.$set(val, {});
  789. }
  790. return this;
  791. }
  792. while (i < len) {
  793. _handleIndex.call(this, i++);
  794. }
  795. return this;
  796. }
  797. } else {
  798. this.$__.$setCalled.add(path);
  799. }
  800. function _handleIndex(i) {
  801. key = keys[i];
  802. const pathName = prefix + key;
  803. pathtype = this.schema.pathType(pathName);
  804. // On initial set, delete any nested keys if we're going to overwrite
  805. // them to ensure we keep the user's key order.
  806. if (type === true &&
  807. !prefix &&
  808. path[key] != null &&
  809. pathtype === 'nested' &&
  810. this._doc[key] != null &&
  811. Object.keys(this._doc[key]).length === 0) {
  812. delete this._doc[key];
  813. // Make sure we set `{}` back even if we minimize re: gh-8565
  814. options = Object.assign({}, options, { _skipMinimizeTopLevel: true });
  815. }
  816. if (typeof path[key] === 'object' &&
  817. !utils.isNativeObject(path[key]) &&
  818. !utils.isMongooseType(path[key]) &&
  819. path[key] != null &&
  820. pathtype !== 'virtual' &&
  821. pathtype !== 'real' &&
  822. pathtype !== 'adhocOrUndefined' &&
  823. !(this.$__path(pathName) instanceof MixedSchema) &&
  824. !(this.schema.paths[pathName] &&
  825. this.schema.paths[pathName].options &&
  826. this.schema.paths[pathName].options.ref)) {
  827. this.$__.$setCalled.add(prefix + key);
  828. this.$set(path[key], prefix + key, constructing, options);
  829. } else if (strict) {
  830. // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039)
  831. if (constructing && path[key] === void 0 &&
  832. this.get(pathName) !== void 0) {
  833. return;
  834. }
  835. if (pathtype === 'adhocOrUndefined') {
  836. pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true });
  837. }
  838. if (pathtype === 'real' || pathtype === 'virtual') {
  839. // Check for setting single embedded schema to document (gh-3535)
  840. let p = path[key];
  841. if (this.schema.paths[pathName] &&
  842. this.schema.paths[pathName].$isSingleNested &&
  843. path[key] instanceof Document) {
  844. p = p.toObject({ virtuals: false, transform: false });
  845. }
  846. this.$set(prefix + key, p, constructing, options);
  847. } else if (pathtype === 'nested' && path[key] instanceof Document) {
  848. this.$set(prefix + key,
  849. path[key].toObject({ transform: false }), constructing, options);
  850. } else if (strict === 'throw') {
  851. if (pathtype === 'nested') {
  852. throw new ObjectExpectedError(key, path[key]);
  853. } else {
  854. throw new StrictModeError(key);
  855. }
  856. }
  857. } else if (path[key] !== void 0) {
  858. this.$set(prefix + key, path[key], constructing, options);
  859. }
  860. }
  861. let pathType = this.schema.pathType(path);
  862. if (pathType === 'adhocOrUndefined') {
  863. pathType = getEmbeddedDiscriminatorPath(this, path, { typeOnly: true });
  864. }
  865. // Assume this is a Mongoose document that was copied into a POJO using
  866. // `Object.assign()` or `{...doc}`
  867. val = handleSpreadDoc(val);
  868. if (pathType === 'nested' && val) {
  869. if (typeof val === 'object' && val != null) {
  870. if (!merge) {
  871. this.$__setValue(path, null);
  872. cleanModifiedSubpaths(this, path);
  873. } else {
  874. return this.$set(val, path, constructing);
  875. }
  876. const keys = Object.keys(val);
  877. this.$__setValue(path, {});
  878. for (const key of keys) {
  879. this.$set(path + '.' + key, val[key], constructing);
  880. }
  881. this.markModified(path);
  882. cleanModifiedSubpaths(this, path, { skipDocArrays: true });
  883. return this;
  884. }
  885. this.invalidate(path, new MongooseError.CastError('Object', val, path));
  886. return this;
  887. }
  888. let schema;
  889. const parts = path.indexOf('.') === -1 ? [path] : path.split('.');
  890. // Might need to change path for top-level alias
  891. if (typeof this.schema.aliases[parts[0]] == 'string') {
  892. parts[0] = this.schema.aliases[parts[0]];
  893. }
  894. if (pathType === 'adhocOrUndefined' && strict) {
  895. // check for roots that are Mixed types
  896. let mixed;
  897. for (i = 0; i < parts.length; ++i) {
  898. const subpath = parts.slice(0, i + 1).join('.');
  899. // If path is underneath a virtual, bypass everything and just set it.
  900. if (i + 1 < parts.length && this.schema.pathType(subpath) === 'virtual') {
  901. mpath.set(path, val, this);
  902. return this;
  903. }
  904. schema = this.schema.path(subpath);
  905. if (schema == null) {
  906. continue;
  907. }
  908. if (schema instanceof MixedSchema) {
  909. // allow changes to sub paths of mixed types
  910. mixed = true;
  911. break;
  912. }
  913. }
  914. if (schema == null) {
  915. // Check for embedded discriminators
  916. schema = getEmbeddedDiscriminatorPath(this, path);
  917. }
  918. if (!mixed && !schema) {
  919. if (strict === 'throw') {
  920. throw new StrictModeError(path);
  921. }
  922. return this;
  923. }
  924. } else if (pathType === 'virtual') {
  925. schema = this.schema.virtualpath(path);
  926. schema.applySetters(val, this);
  927. return this;
  928. } else {
  929. schema = this.$__path(path);
  930. }
  931. // gh-4578, if setting a deeply nested path that doesn't exist yet, create it
  932. let cur = this._doc;
  933. let curPath = '';
  934. for (i = 0; i < parts.length - 1; ++i) {
  935. cur = cur[parts[i]];
  936. curPath += (curPath.length > 0 ? '.' : '') + parts[i];
  937. if (!cur) {
  938. this.$set(curPath, {});
  939. // Hack re: gh-5800. If nested field is not selected, it probably exists
  940. // so `MongoError: cannot use the part (nested of nested.num) to
  941. // traverse the element ({nested: null})` is not likely. If user gets
  942. // that error, its their fault for now. We should reconsider disallowing
  943. // modifying not selected paths for 6.x
  944. if (!this.isSelected(curPath)) {
  945. this.unmarkModified(curPath);
  946. }
  947. cur = this.$__getValue(curPath);
  948. }
  949. }
  950. let pathToMark;
  951. // When using the $set operator the path to the field must already exist.
  952. // Else mongodb throws: "LEFT_SUBFIELD only supports Object"
  953. if (parts.length <= 1) {
  954. pathToMark = path;
  955. } else {
  956. for (i = 0; i < parts.length; ++i) {
  957. const subpath = parts.slice(0, i + 1).join('.');
  958. if (this.get(subpath, null, { getters: false }) === null) {
  959. pathToMark = subpath;
  960. break;
  961. }
  962. }
  963. if (!pathToMark) {
  964. pathToMark = path;
  965. }
  966. }
  967. // if this doc is being constructed we should not trigger getters
  968. const priorVal = (() => {
  969. if (this.$__.$options.priorDoc != null) {
  970. return this.$__.$options.priorDoc.$__getValue(path);
  971. }
  972. if (constructing) {
  973. return void 0;
  974. }
  975. return this.$__getValue(path);
  976. })();
  977. if (!schema) {
  978. this.$__set(pathToMark, path, constructing, parts, schema, val, priorVal);
  979. return this;
  980. }
  981. if (schema.$isSingleNested && val != null && merge) {
  982. if (val instanceof Document) {
  983. val = val.toObject({ virtuals: false, transform: false });
  984. }
  985. const keys = Object.keys(val);
  986. for (const key of keys) {
  987. this.$set(path + '.' + key, val[key], constructing, options);
  988. }
  989. return this;
  990. }
  991. let shouldSet = true;
  992. try {
  993. // If the user is trying to set a ref path to a document with
  994. // the correct model name, treat it as populated
  995. const refMatches = (() => {
  996. if (schema.options == null) {
  997. return false;
  998. }
  999. if (!(val instanceof Document)) {
  1000. return false;
  1001. }
  1002. const model = val.constructor;
  1003. // Check ref
  1004. const ref = schema.options.ref;
  1005. if (ref != null && (ref === model.modelName || ref === model.baseModelName)) {
  1006. return true;
  1007. }
  1008. // Check refPath
  1009. const refPath = schema.options.refPath;
  1010. if (refPath == null) {
  1011. return false;
  1012. }
  1013. const modelName = val.get(refPath);
  1014. return modelName === model.modelName || modelName === model.baseModelName;
  1015. })();
  1016. let didPopulate = false;
  1017. if (refMatches && val instanceof Document) {
  1018. this.populated(path, val._id, { [populateModelSymbol]: val.constructor });
  1019. didPopulate = true;
  1020. }
  1021. let popOpts;
  1022. if (schema.options &&
  1023. Array.isArray(schema.options[this.schema.options.typeKey]) &&
  1024. schema.options[this.schema.options.typeKey].length &&
  1025. schema.options[this.schema.options.typeKey][0].ref &&
  1026. _isManuallyPopulatedArray(val, schema.options[this.schema.options.typeKey][0].ref)) {
  1027. if (this.ownerDocument) {
  1028. popOpts = { [populateModelSymbol]: val[0].constructor };
  1029. this.ownerDocument().populated(this.$__fullPath(path),
  1030. val.map(function(v) { return v._id; }), popOpts);
  1031. } else {
  1032. popOpts = { [populateModelSymbol]: val[0].constructor };
  1033. this.populated(path, val.map(function(v) { return v._id; }), popOpts);
  1034. }
  1035. didPopulate = true;
  1036. }
  1037. if (this.schema.singleNestedPaths[path] == null) {
  1038. // If this path is underneath a single nested schema, we'll call the setter
  1039. // later in `$__set()` because we don't take `_doc` when we iterate through
  1040. // a single nested doc. That's to make sure we get the correct context.
  1041. // Otherwise we would double-call the setter, see gh-7196.
  1042. val = schema.applySetters(val, this, false, priorVal);
  1043. }
  1044. if (schema.$isMongooseDocumentArray &&
  1045. Array.isArray(val) &&
  1046. val.length > 0 &&
  1047. val[0] != null &&
  1048. val[0].$__ != null &&
  1049. val[0].$__.populated != null) {
  1050. const populatedPaths = Object.keys(val[0].$__.populated);
  1051. for (const populatedPath of populatedPaths) {
  1052. this.populated(path + '.' + populatedPath,
  1053. val.map(v => v.populated(populatedPath)),
  1054. val[0].$__.populated[populatedPath].options);
  1055. }
  1056. didPopulate = true;
  1057. }
  1058. if (!didPopulate && this.$__.populated) {
  1059. // If this array partially contains populated documents, convert them
  1060. // all to ObjectIds re: #8443
  1061. if (Array.isArray(val) && this.$__.populated[path]) {
  1062. for (let i = 0; i < val.length; ++i) {
  1063. if (val[i] instanceof Document) {
  1064. val[i] = val[i]._id;
  1065. }
  1066. }
  1067. }
  1068. delete this.$__.populated[path];
  1069. }
  1070. this.$markValid(path);
  1071. } catch (e) {
  1072. if (e instanceof MongooseError.StrictModeError && e.isImmutableError) {
  1073. this.invalidate(path, e);
  1074. } else if (e instanceof MongooseError.CastError) {
  1075. this.invalidate(e.path, e);
  1076. if (e.$originalErrorPath) {
  1077. this.invalidate(path,
  1078. new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath));
  1079. }
  1080. } else {
  1081. this.invalidate(path,
  1082. new MongooseError.CastError(schema.instance, val, path, e));
  1083. }
  1084. shouldSet = false;
  1085. }
  1086. if (shouldSet) {
  1087. this.$__set(pathToMark, path, constructing, parts, schema, val, priorVal);
  1088. }
  1089. if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) {
  1090. cleanModifiedSubpaths(this, path);
  1091. }
  1092. return this;
  1093. };
  1094. /*!
  1095. * ignore
  1096. */
  1097. function _isManuallyPopulatedArray(val, ref) {
  1098. if (!Array.isArray(val)) {
  1099. return false;
  1100. }
  1101. if (val.length === 0) {
  1102. return false;
  1103. }
  1104. for (const el of val) {
  1105. if (!(el instanceof Document)) {
  1106. return false;
  1107. }
  1108. const modelName = el.constructor.modelName;
  1109. if (modelName == null) {
  1110. return false;
  1111. }
  1112. if (el.constructor.modelName != ref && el.constructor.baseModelName != ref) {
  1113. return false;
  1114. }
  1115. }
  1116. return true;
  1117. }
  1118. /**
  1119. * Sets the value of a path, or many paths.
  1120. *
  1121. * ####Example:
  1122. *
  1123. * // path, value
  1124. * doc.set(path, value)
  1125. *
  1126. * // object
  1127. * doc.set({
  1128. * path : value
  1129. * , path2 : {
  1130. * path : value
  1131. * }
  1132. * })
  1133. *
  1134. * // on-the-fly cast to number
  1135. * doc.set(path, value, Number)
  1136. *
  1137. * // on-the-fly cast to string
  1138. * doc.set(path, value, String)
  1139. *
  1140. * // changing strict mode behavior
  1141. * doc.set(path, value, { strict: false });
  1142. *
  1143. * @param {String|Object} path path or object of key/vals to set
  1144. * @param {Any} val the value to set
  1145. * @param {Schema|String|Number|Buffer|*} [type] optionally specify a type for "on-the-fly" attributes
  1146. * @param {Object} [options] optionally specify options that modify the behavior of the set
  1147. * @api public
  1148. * @method set
  1149. * @memberOf Document
  1150. * @instance
  1151. */
  1152. Document.prototype.set = Document.prototype.$set;
  1153. /**
  1154. * Determine if we should mark this change as modified.
  1155. *
  1156. * @return {Boolean}
  1157. * @api private
  1158. * @method $__shouldModify
  1159. * @memberOf Document
  1160. * @instance
  1161. */
  1162. Document.prototype.$__shouldModify = function(pathToMark, path, constructing, parts, schema, val, priorVal) {
  1163. if (this.isNew) {
  1164. return true;
  1165. }
  1166. // Re: the note about gh-7196, `val` is the raw value without casting or
  1167. // setters if the full path is under a single nested subdoc because we don't
  1168. // want to double run setters. So don't set it as modified. See gh-7264.
  1169. if (this.schema.singleNestedPaths[path] != null) {
  1170. return false;
  1171. }
  1172. if (val === void 0 && !this.isSelected(path)) {
  1173. // when a path is not selected in a query, its initial
  1174. // value will be undefined.
  1175. return true;
  1176. }
  1177. if (val === void 0 && path in this.$__.activePaths.states.default) {
  1178. // we're just unsetting the default value which was never saved
  1179. return false;
  1180. }
  1181. // gh-3992: if setting a populated field to a doc, don't mark modified
  1182. // if they have the same _id
  1183. if (this.populated(path) &&
  1184. val instanceof Document &&
  1185. deepEqual(val._id, priorVal)) {
  1186. return false;
  1187. }
  1188. if (!deepEqual(val, priorVal || this.get(path))) {
  1189. return true;
  1190. }
  1191. if (!constructing &&
  1192. val !== null &&
  1193. val !== undefined &&
  1194. path in this.$__.activePaths.states.default &&
  1195. deepEqual(val, schema.getDefault(this, constructing))) {
  1196. // a path with a default was $unset on the server
  1197. // and the user is setting it to the same value again
  1198. return true;
  1199. }
  1200. return false;
  1201. };
  1202. /**
  1203. * Handles the actual setting of the value and marking the path modified if appropriate.
  1204. *
  1205. * @api private
  1206. * @method $__set
  1207. * @memberOf Document
  1208. * @instance
  1209. */
  1210. Document.prototype.$__set = function(pathToMark, path, constructing, parts, schema, val, priorVal) {
  1211. Embedded = Embedded || require('./types/embedded');
  1212. const shouldModify = this.$__shouldModify(pathToMark, path, constructing, parts,
  1213. schema, val, priorVal);
  1214. const _this = this;
  1215. if (shouldModify) {
  1216. this.markModified(pathToMark);
  1217. // handle directly setting arrays (gh-1126)
  1218. MongooseArray || (MongooseArray = require('./types/array'));
  1219. if (val && val.isMongooseArray) {
  1220. val._registerAtomic('$set', val);
  1221. // Update embedded document parent references (gh-5189)
  1222. if (val.isMongooseDocumentArray) {
  1223. val.forEach(function(item) {
  1224. item && item.__parentArray && (item.__parentArray = val);
  1225. });
  1226. }
  1227. // Small hack for gh-1638: if we're overwriting the entire array, ignore
  1228. // paths that were modified before the array overwrite
  1229. this.$__.activePaths.forEach(function(modifiedPath) {
  1230. if (modifiedPath.startsWith(path + '.')) {
  1231. _this.$__.activePaths.ignore(modifiedPath);
  1232. }
  1233. });
  1234. }
  1235. }
  1236. let obj = this._doc;
  1237. let i = 0;
  1238. const l = parts.length;
  1239. let cur = '';
  1240. for (; i < l; i++) {
  1241. const next = i + 1;
  1242. const last = next === l;
  1243. cur += (cur ? '.' + parts[i] : parts[i]);
  1244. if (specialProperties.has(parts[i])) {
  1245. return;
  1246. }
  1247. if (last) {
  1248. if (obj instanceof Map) {
  1249. obj.set(parts[i], val);
  1250. } else {
  1251. obj[parts[i]] = val;
  1252. }
  1253. } else {
  1254. if (utils.isPOJO(obj[parts[i]])) {
  1255. obj = obj[parts[i]];
  1256. } else if (obj[parts[i]] && obj[parts[i]] instanceof Embedded) {
  1257. obj = obj[parts[i]];
  1258. } else if (obj[parts[i]] && obj[parts[i]].$isSingleNested) {
  1259. obj = obj[parts[i]];
  1260. } else if (obj[parts[i]] && Array.isArray(obj[parts[i]])) {
  1261. obj = obj[parts[i]];
  1262. } else {
  1263. obj[parts[i]] = obj[parts[i]] || {};
  1264. obj = obj[parts[i]];
  1265. }
  1266. }
  1267. }
  1268. };
  1269. /**
  1270. * Gets a raw value from a path (no getters)
  1271. *
  1272. * @param {String} path
  1273. * @api private
  1274. */
  1275. Document.prototype.$__getValue = function(path) {
  1276. return utils.getValue(path, this._doc);
  1277. };
  1278. /**
  1279. * Sets a raw value for a path (no casting, setters, transformations)
  1280. *
  1281. * @param {String} path
  1282. * @param {Object} value
  1283. * @api private
  1284. */
  1285. Document.prototype.$__setValue = function(path, val) {
  1286. utils.setValue(path, val, this._doc);
  1287. return this;
  1288. };
  1289. /**
  1290. * Returns the value of a path.
  1291. *
  1292. * ####Example
  1293. *
  1294. * // path
  1295. * doc.get('age') // 47
  1296. *
  1297. * // dynamic casting to a string
  1298. * doc.get('age', String) // "47"
  1299. *
  1300. * @param {String} path
  1301. * @param {Schema|String|Number|Buffer|*} [type] optionally specify a type for on-the-fly attributes
  1302. * @param {Object} [options]
  1303. * @param {Boolean} [options.virtuals=false] Apply virtuals before getting this path
  1304. * @param {Boolean} [options.getters=true] If false, skip applying getters and just get the raw value
  1305. * @api public
  1306. */
  1307. Document.prototype.get = function(path, type, options) {
  1308. let adhoc;
  1309. options = options || {};
  1310. if (type) {
  1311. adhoc = this.schema.interpretAsType(path, type, this.schema.options);
  1312. }
  1313. let schema = this.$__path(path);
  1314. if (schema == null) {
  1315. schema = this.schema.virtualpath(path);
  1316. }
  1317. if (schema instanceof MixedSchema) {
  1318. const virtual = this.schema.virtualpath(path);
  1319. if (virtual != null) {
  1320. schema = virtual;
  1321. }
  1322. }
  1323. const pieces = path.split('.');
  1324. let obj = this._doc;
  1325. if (schema instanceof VirtualType) {
  1326. if (schema.getters.length === 0) {
  1327. return void 0;
  1328. }
  1329. return schema.applyGetters(null, this);
  1330. }
  1331. // Might need to change path for top-level alias
  1332. if (typeof this.schema.aliases[pieces[0]] == 'string') {
  1333. pieces[0] = this.schema.aliases[pieces[0]];
  1334. }
  1335. for (let i = 0, l = pieces.length; i < l; i++) {
  1336. if (obj && obj._doc) {
  1337. obj = obj._doc;
  1338. }
  1339. if (obj == null) {
  1340. obj = void 0;
  1341. } else if (obj instanceof Map) {
  1342. obj = obj.get(pieces[i], { getters: false });
  1343. } else if (i === l - 1) {
  1344. obj = utils.getValue(pieces[i], obj);
  1345. } else {
  1346. obj = obj[pieces[i]];
  1347. }
  1348. }
  1349. if (adhoc) {
  1350. obj = adhoc.cast(obj);
  1351. }
  1352. if (schema != null && options.getters !== false) {
  1353. obj = schema.applyGetters(obj, this);
  1354. } else if (this.schema.nested[path] && options.virtuals) {
  1355. // Might need to apply virtuals if this is a nested path
  1356. return applyVirtuals(this, utils.clone(obj) || {}, { path: path });
  1357. }
  1358. return obj;
  1359. };
  1360. /*!
  1361. * ignore
  1362. */
  1363. Document.prototype[getSymbol] = Document.prototype.get;
  1364. /**
  1365. * Returns the schematype for the given `path`.
  1366. *
  1367. * @param {String} path
  1368. * @api private
  1369. * @method $__path
  1370. * @memberOf Document
  1371. * @instance
  1372. */
  1373. Document.prototype.$__path = function(path) {
  1374. const adhocs = this.$__.adhocPaths;
  1375. const adhocType = adhocs && adhocs.hasOwnProperty(path) ? adhocs[path] : null;
  1376. if (adhocType) {
  1377. return adhocType;
  1378. }
  1379. return this.schema.path(path);
  1380. };
  1381. /**
  1382. * Marks the path as having pending changes to write to the db.
  1383. *
  1384. * _Very helpful when using [Mixed](./schematypes.html#mixed) types._
  1385. *
  1386. * ####Example:
  1387. *
  1388. * doc.mixed.type = 'changed';
  1389. * doc.markModified('mixed.type');
  1390. * doc.save() // changes to mixed.type are now persisted
  1391. *
  1392. * @param {String} path the path to mark modified
  1393. * @param {Document} [scope] the scope to run validators with
  1394. * @api public
  1395. */
  1396. Document.prototype.markModified = function(path, scope) {
  1397. this.$__.activePaths.modify(path);
  1398. if (scope != null && !this.ownerDocument) {
  1399. this.$__.pathsToScopes[path] = scope;
  1400. }
  1401. };
  1402. /**
  1403. * Clears the modified state on the specified path.
  1404. *
  1405. * ####Example:
  1406. *
  1407. * doc.foo = 'bar';
  1408. * doc.unmarkModified('foo');
  1409. * doc.save(); // changes to foo will not be persisted
  1410. *
  1411. * @param {String} path the path to unmark modified
  1412. * @api public
  1413. */
  1414. Document.prototype.unmarkModified = function(path) {
  1415. this.$__.activePaths.init(path);
  1416. delete this.$__.pathsToScopes[path];
  1417. };
  1418. /**
  1419. * Don't run validation on this path or persist changes to this path.
  1420. *
  1421. * ####Example:
  1422. *
  1423. * doc.foo = null;
  1424. * doc.$ignore('foo');
  1425. * doc.save(); // changes to foo will not be persisted and validators won't be run
  1426. *
  1427. * @memberOf Document
  1428. * @instance
  1429. * @method $ignore
  1430. * @param {String} path the path to ignore
  1431. * @api public
  1432. */
  1433. Document.prototype.$ignore = function(path) {
  1434. this.$__.activePaths.ignore(path);
  1435. };
  1436. /**
  1437. * Returns the list of paths that have been directly modified. A direct
  1438. * modified path is a path that you explicitly set, whether via `doc.foo = 'bar'`,
  1439. * `Object.assign(doc, { foo: 'bar' })`, or `doc.set('foo', 'bar')`.
  1440. *
  1441. * A path `a` may be in `modifiedPaths()` but not in `directModifiedPaths()`
  1442. * because a child of `a` was directly modified.
  1443. *
  1444. * ####Example
  1445. * const schema = new Schema({ foo: String, nested: { bar: String } });
  1446. * const Model = mongoose.model('Test', schema);
  1447. * await Model.create({ foo: 'original', nested: { bar: 'original' } });
  1448. *
  1449. * const doc = await Model.findOne();
  1450. * doc.nested.bar = 'modified';
  1451. * doc.directModifiedPaths(); // ['nested.bar']
  1452. * doc.modifiedPaths(); // ['nested', 'nested.bar']
  1453. *
  1454. * @return {Array}
  1455. * @api public
  1456. */
  1457. Document.prototype.directModifiedPaths = function() {
  1458. return Object.keys(this.$__.activePaths.states.modify);
  1459. };
  1460. /**
  1461. * Returns true if the given path is nullish or only contains empty objects.
  1462. * Useful for determining whether this subdoc will get stripped out by the
  1463. * [minimize option](/docs/guide.html#minimize).
  1464. *
  1465. * ####Example:
  1466. * const schema = new Schema({ nested: { foo: String } });
  1467. * const Model = mongoose.model('Test', schema);
  1468. * const doc = new Model({});
  1469. * doc.$isEmpty('nested'); // true
  1470. * doc.nested.$isEmpty(); // true
  1471. *
  1472. * doc.nested.foo = 'bar';
  1473. * doc.$isEmpty('nested'); // false
  1474. * doc.nested.$isEmpty(); // false
  1475. *
  1476. * @memberOf Document
  1477. * @instance
  1478. * @api public
  1479. * @method $isEmpty
  1480. * @return {Boolean}
  1481. */
  1482. Document.prototype.$isEmpty = function(path) {
  1483. const isEmptyOptions = {
  1484. minimize: true,
  1485. virtuals: false,
  1486. getters: false,
  1487. transform: false
  1488. };
  1489. if (arguments.length > 0) {
  1490. const v = this.get(path);
  1491. if (v == null) {
  1492. return true;
  1493. }
  1494. if (typeof v !== 'object') {
  1495. return false;
  1496. }
  1497. if (utils.isPOJO(v)) {
  1498. return _isEmpty(v);
  1499. }
  1500. return Object.keys(v.toObject(isEmptyOptions)).length === 0;
  1501. }
  1502. return Object.keys(this.toObject(isEmptyOptions)).length === 0;
  1503. };
  1504. function _isEmpty(v) {
  1505. if (v == null) {
  1506. return true;
  1507. }
  1508. if (typeof v !== 'object' || Array.isArray(v)) {
  1509. return false;
  1510. }
  1511. for (const key of Object.keys(v)) {
  1512. if (!_isEmpty(v[key])) {
  1513. return false;
  1514. }
  1515. }
  1516. return true;
  1517. }
  1518. /**
  1519. * Returns the list of paths that have been modified.
  1520. *
  1521. * @param {Object} [options]
  1522. * @param {Boolean} [options.includeChildren=false] if true, returns children of modified paths as well. For example, if false, the list of modified paths for `doc.colors = { primary: 'blue' };` will **not** contain `colors.primary`. If true, `modifiedPaths()` will return an array that contains `colors.primary`.
  1523. * @return {Array}
  1524. * @api public
  1525. */
  1526. Document.prototype.modifiedPaths = function(options) {
  1527. options = options || {};
  1528. const directModifiedPaths = Object.keys(this.$__.activePaths.states.modify);
  1529. const _this = this;
  1530. return directModifiedPaths.reduce(function(list, path) {
  1531. const parts = path.split('.');
  1532. list = list.concat(parts.reduce(function(chains, part, i) {
  1533. return chains.concat(parts.slice(0, i).concat(part).join('.'));
  1534. }, []).filter(function(chain) {
  1535. return (list.indexOf(chain) === -1);
  1536. }));
  1537. if (!options.includeChildren) {
  1538. return list;
  1539. }
  1540. let cur = _this.get(path);
  1541. if (cur != null && typeof cur === 'object') {
  1542. if (cur._doc) {
  1543. cur = cur._doc;
  1544. }
  1545. if (Array.isArray(cur)) {
  1546. const len = cur.length;
  1547. for (let i = 0; i < len; ++i) {
  1548. if (list.indexOf(path + '.' + i) === -1) {
  1549. list.push(path + '.' + i);
  1550. if (cur[i] != null && cur[i].$__) {
  1551. const modified = cur[i].modifiedPaths();
  1552. for (const childPath of modified) {
  1553. list.push(path + '.' + i + '.' + childPath);
  1554. }
  1555. }
  1556. }
  1557. }
  1558. } else {
  1559. Object.keys(cur).
  1560. filter(function(key) {
  1561. return list.indexOf(path + '.' + key) === -1;
  1562. }).
  1563. forEach(function(key) {
  1564. list.push(path + '.' + key);
  1565. });
  1566. }
  1567. }
  1568. return list;
  1569. }, []);
  1570. };
  1571. /**
  1572. * Returns true if this document was modified, else false.
  1573. *
  1574. * If `path` is given, checks if a path or any full path containing `path` as part of its path chain has been modified.
  1575. *
  1576. * ####Example
  1577. *
  1578. * doc.set('documents.0.title', 'changed');
  1579. * doc.isModified() // true
  1580. * doc.isModified('documents') // true
  1581. * doc.isModified('documents.0.title') // true
  1582. * doc.isModified('documents otherProp') // true
  1583. * doc.isDirectModified('documents') // false
  1584. *
  1585. * @param {String} [path] optional
  1586. * @return {Boolean}
  1587. * @api public
  1588. */
  1589. Document.prototype.isModified = function(paths, modifiedPaths) {
  1590. if (paths) {
  1591. if (!Array.isArray(paths)) {
  1592. paths = paths.split(' ');
  1593. }
  1594. const modified = modifiedPaths || this.modifiedPaths();
  1595. const directModifiedPaths = Object.keys(this.$__.activePaths.states.modify);
  1596. const isModifiedChild = paths.some(function(path) {
  1597. return !!~modified.indexOf(path);
  1598. });
  1599. return isModifiedChild || paths.some(function(path) {
  1600. return directModifiedPaths.some(function(mod) {
  1601. return mod === path || path.startsWith(mod + '.');
  1602. });
  1603. });
  1604. }
  1605. return this.$__.activePaths.some('modify');
  1606. };
  1607. /**
  1608. * Checks if a path is set to its default.
  1609. *
  1610. * ####Example
  1611. *
  1612. * MyModel = mongoose.model('test', { name: { type: String, default: 'Val '} });
  1613. * const m = new MyModel();
  1614. * m.$isDefault('name'); // true
  1615. *
  1616. * @memberOf Document
  1617. * @instance
  1618. * @method $isDefault
  1619. * @param {String} [path]
  1620. * @return {Boolean}
  1621. * @api public
  1622. */
  1623. Document.prototype.$isDefault = function(path) {
  1624. return (path in this.$__.activePaths.states.default);
  1625. };
  1626. /**
  1627. * Getter/setter, determines whether the document was removed or not.
  1628. *
  1629. * ####Example:
  1630. * product.remove(function (err, product) {
  1631. * product.$isDeleted(); // true
  1632. * product.remove(); // no-op, doesn't send anything to the db
  1633. *
  1634. * product.$isDeleted(false);
  1635. * product.$isDeleted(); // false
  1636. * product.remove(); // will execute a remove against the db
  1637. * })
  1638. *
  1639. * @param {Boolean} [val] optional, overrides whether mongoose thinks the doc is deleted
  1640. * @return {Boolean} whether mongoose thinks this doc is deleted.
  1641. * @method $isDeleted
  1642. * @memberOf Document
  1643. * @instance
  1644. * @api public
  1645. */
  1646. Document.prototype.$isDeleted = function(val) {
  1647. if (arguments.length === 0) {
  1648. return !!this.$__.isDeleted;
  1649. }
  1650. this.$__.isDeleted = !!val;
  1651. return this;
  1652. };
  1653. /**
  1654. * Returns true if `path` was directly set and modified, else false.
  1655. *
  1656. * ####Example
  1657. *
  1658. * doc.set('documents.0.title', 'changed');
  1659. * doc.isDirectModified('documents.0.title') // true
  1660. * doc.isDirectModified('documents') // false
  1661. *
  1662. * @param {String} path
  1663. * @return {Boolean}
  1664. * @api public
  1665. */
  1666. Document.prototype.isDirectModified = function(path) {
  1667. return (path in this.$__.activePaths.states.modify);
  1668. };
  1669. /**
  1670. * Checks if `path` was initialized.
  1671. *
  1672. * @param {String} path
  1673. * @return {Boolean}
  1674. * @api public
  1675. */
  1676. Document.prototype.isInit = function(path) {
  1677. return (path in this.$__.activePaths.states.init);
  1678. };
  1679. /**
  1680. * Checks if `path` was selected in the source query which initialized this document.
  1681. *
  1682. * ####Example
  1683. *
  1684. * Thing.findOne().select('name').exec(function (err, doc) {
  1685. * doc.isSelected('name') // true
  1686. * doc.isSelected('age') // false
  1687. * })
  1688. *
  1689. * @param {String} path
  1690. * @return {Boolean}
  1691. * @api public
  1692. */
  1693. Document.prototype.isSelected = function isSelected(path) {
  1694. if (this.$__.selected) {
  1695. if (path === '_id') {
  1696. return this.$__.selected._id !== 0;
  1697. }
  1698. const paths = Object.keys(this.$__.selected);
  1699. let i = paths.length;
  1700. let inclusive = null;
  1701. let cur;
  1702. if (i === 1 && paths[0] === '_id') {
  1703. // only _id was selected.
  1704. return this.$__.selected._id === 0;
  1705. }
  1706. while (i--) {
  1707. cur = paths[i];
  1708. if (cur === '_id') {
  1709. continue;
  1710. }
  1711. if (!isDefiningProjection(this.$__.selected[cur])) {
  1712. continue;
  1713. }
  1714. inclusive = !!this.$__.selected[cur];
  1715. break;
  1716. }
  1717. if (inclusive === null) {
  1718. return true;
  1719. }
  1720. if (path in this.$__.selected) {
  1721. return inclusive;
  1722. }
  1723. i = paths.length;
  1724. const pathDot = path + '.';
  1725. while (i--) {
  1726. cur = paths[i];
  1727. if (cur === '_id') {
  1728. continue;
  1729. }
  1730. if (cur.startsWith(pathDot)) {
  1731. return inclusive || cur !== pathDot;
  1732. }
  1733. if (pathDot.startsWith(cur + '.')) {
  1734. return inclusive;
  1735. }
  1736. }
  1737. return !inclusive;
  1738. }
  1739. return true;
  1740. };
  1741. /**
  1742. * Checks if `path` was explicitly selected. If no projection, always returns
  1743. * true.
  1744. *
  1745. * ####Example
  1746. *
  1747. * Thing.findOne().select('nested.name').exec(function (err, doc) {
  1748. * doc.isDirectSelected('nested.name') // true
  1749. * doc.isDirectSelected('nested.otherName') // false
  1750. * doc.isDirectSelected('nested') // false
  1751. * })
  1752. *
  1753. * @param {String} path
  1754. * @return {Boolean}
  1755. * @api public
  1756. */
  1757. Document.prototype.isDirectSelected = function isDirectSelected(path) {
  1758. if (this.$__.selected) {
  1759. if (path === '_id') {
  1760. return this.$__.selected._id !== 0;
  1761. }
  1762. const paths = Object.keys(this.$__.selected);
  1763. let i = paths.length;
  1764. let inclusive = null;
  1765. let cur;
  1766. if (i === 1 && paths[0] === '_id') {
  1767. // only _id was selected.
  1768. return this.$__.selected._id === 0;
  1769. }
  1770. while (i--) {
  1771. cur = paths[i];
  1772. if (cur === '_id') {
  1773. continue;
  1774. }
  1775. if (!isDefiningProjection(this.$__.selected[cur])) {
  1776. continue;
  1777. }
  1778. inclusive = !!this.$__.selected[cur];
  1779. break;
  1780. }
  1781. if (inclusive === null) {
  1782. return true;
  1783. }
  1784. if (path in this.$__.selected) {
  1785. return inclusive;
  1786. }
  1787. return !inclusive;
  1788. }
  1789. return true;
  1790. };
  1791. /**
  1792. * Executes registered validation rules for this document.
  1793. *
  1794. * ####Note:
  1795. *
  1796. * This method is called `pre` save and if a validation rule is violated, [save](#model_Model-save) is aborted and the error is returned to your `callback`.
  1797. *
  1798. * ####Example:
  1799. *
  1800. * doc.validate(function (err) {
  1801. * if (err) handleError(err);
  1802. * else // validation passed
  1803. * });
  1804. *
  1805. * @param {Array|String} [pathsToValidate] list of paths to validate. If set, Mongoose will validate only the modified paths that are in the given list.
  1806. * @param {Object} [options] internal options
  1807. * @param {Function} [callback] optional callback called after validation completes, passing an error if one occurred
  1808. * @return {Promise} Promise
  1809. * @api public
  1810. */
  1811. Document.prototype.validate = function(pathsToValidate, options, callback) {
  1812. let parallelValidate;
  1813. this.$op = 'validate';
  1814. if (this.ownerDocument != null) {
  1815. // Skip parallel validate check for subdocuments
  1816. } else if (this.$__.validating) {
  1817. parallelValidate = new ParallelValidateError(this, {
  1818. parentStack: options && options.parentStack,
  1819. conflictStack: this.$__.validating.stack
  1820. });
  1821. } else {
  1822. this.$__.validating = new ParallelValidateError(this, { parentStack: options && options.parentStack });
  1823. }
  1824. if (typeof pathsToValidate === 'function') {
  1825. callback = pathsToValidate;
  1826. options = null;
  1827. pathsToValidate = null;
  1828. } else if (typeof options === 'function') {
  1829. callback = options;
  1830. options = pathsToValidate;
  1831. pathsToValidate = null;
  1832. }
  1833. return promiseOrCallback(callback, cb => {
  1834. if (parallelValidate != null) {
  1835. return cb(parallelValidate);
  1836. }
  1837. this.$__validate(pathsToValidate, options, (error) => {
  1838. this.$op = null;
  1839. cb(error);
  1840. });
  1841. }, this.constructor.events);
  1842. };
  1843. /*!
  1844. * ignore
  1845. */
  1846. function _evaluateRequiredFunctions(doc) {
  1847. Object.keys(doc.$__.activePaths.states.require).forEach(path => {
  1848. const p = doc.schema.path(path);
  1849. if (p != null && typeof p.originalRequiredValue === 'function') {
  1850. doc.$__.cachedRequired[path] = p.originalRequiredValue.call(doc);
  1851. }
  1852. });
  1853. }
  1854. /*!
  1855. * ignore
  1856. */
  1857. function _getPathsToValidate(doc) {
  1858. const skipSchemaValidators = {};
  1859. _evaluateRequiredFunctions(doc);
  1860. // only validate required fields when necessary
  1861. let paths = new Set(Object.keys(doc.$__.activePaths.states.require).filter(function(path) {
  1862. if (!doc.isSelected(path) && !doc.isModified(path)) {
  1863. return false;
  1864. }
  1865. if (path in doc.$__.cachedRequired) {
  1866. return doc.$__.cachedRequired[path];
  1867. }
  1868. return true;
  1869. }));
  1870. Object.keys(doc.$__.activePaths.states.init).forEach(addToPaths);
  1871. Object.keys(doc.$__.activePaths.states.modify).forEach(addToPaths);
  1872. Object.keys(doc.$__.activePaths.states.default).forEach(addToPaths);
  1873. function addToPaths(p) { paths.add(p); }
  1874. const subdocs = doc.$__getAllSubdocs();
  1875. const modifiedPaths = doc.modifiedPaths();
  1876. for (const subdoc of subdocs) {
  1877. if (subdoc.$basePath) {
  1878. // Remove child paths for now, because we'll be validating the whole
  1879. // subdoc
  1880. for (const p of paths) {
  1881. if (p === null || p.startsWith(subdoc.$basePath + '.')) {
  1882. paths.delete(p);
  1883. }
  1884. }
  1885. if (doc.isModified(subdoc.$basePath, modifiedPaths) &&
  1886. !doc.isDirectModified(subdoc.$basePath) &&
  1887. !doc.$isDefault(subdoc.$basePath)) {
  1888. paths.add(subdoc.$basePath);
  1889. skipSchemaValidators[subdoc.$basePath] = true;
  1890. }
  1891. }
  1892. }
  1893. // from here on we're not removing items from paths
  1894. // gh-661: if a whole array is modified, make sure to run validation on all
  1895. // the children as well
  1896. for (const path of paths) {
  1897. const _pathType = doc.schema.path(path);
  1898. if (!_pathType ||
  1899. !_pathType.$isMongooseArray ||
  1900. // To avoid potential performance issues, skip doc arrays whose children
  1901. // are not required. `getPositionalPathType()` may be slow, so avoid
  1902. // it unless we have a case of #6364
  1903. (_pathType.$isMongooseDocumentArray && !get(_pathType, 'schemaOptions.required'))) {
  1904. continue;
  1905. }
  1906. const val = doc.$__getValue(path);
  1907. _pushNestedArrayPaths(val, paths, path);
  1908. }
  1909. function _pushNestedArrayPaths(val, paths, path) {
  1910. if (val != null) {
  1911. const numElements = val.length;
  1912. for (let j = 0; j < numElements; ++j) {
  1913. if (Array.isArray(val[j])) {
  1914. _pushNestedArrayPaths(val[j], paths, path + '.' + j);
  1915. } else {
  1916. paths.add(path + '.' + j);
  1917. }
  1918. }
  1919. }
  1920. }
  1921. const flattenOptions = { skipArrays: true };
  1922. for (const pathToCheck of paths) {
  1923. if (doc.schema.nested[pathToCheck]) {
  1924. let _v = doc.$__getValue(pathToCheck);
  1925. if (isMongooseObject(_v)) {
  1926. _v = _v.toObject({ transform: false });
  1927. }
  1928. const flat = flatten(_v, pathToCheck, flattenOptions, doc.schema);
  1929. Object.keys(flat).forEach(addToPaths);
  1930. }
  1931. }
  1932. for (const path of paths) {
  1933. // Single nested paths (paths embedded under single nested subdocs) will
  1934. // be validated on their own when we call `validate()` on the subdoc itself.
  1935. // Re: gh-8468
  1936. if (doc.schema.singleNestedPaths.hasOwnProperty(path)) {
  1937. paths.delete(path);
  1938. continue;
  1939. }
  1940. const _pathType = doc.schema.path(path);
  1941. if (!_pathType || !_pathType.$isSchemaMap) {
  1942. continue;
  1943. }
  1944. const val = doc.$__getValue(path);
  1945. if (val == null) {
  1946. continue;
  1947. }
  1948. for (const key of val.keys()) {
  1949. paths.add(path + '.' + key);
  1950. }
  1951. }
  1952. paths = Array.from(paths);
  1953. return [paths, skipSchemaValidators];
  1954. }
  1955. /*!
  1956. * ignore
  1957. */
  1958. Document.prototype.$__validate = function(pathsToValidate, options, callback) {
  1959. if (typeof pathsToValidate === 'function') {
  1960. callback = pathsToValidate;
  1961. options = null;
  1962. pathsToValidate = null;
  1963. } else if (typeof options === 'function') {
  1964. callback = options;
  1965. options = null;
  1966. }
  1967. const hasValidateModifiedOnlyOption = options &&
  1968. (typeof options === 'object') &&
  1969. ('validateModifiedOnly' in options);
  1970. let shouldValidateModifiedOnly;
  1971. if (hasValidateModifiedOnlyOption) {
  1972. shouldValidateModifiedOnly = !!options.validateModifiedOnly;
  1973. } else {
  1974. shouldValidateModifiedOnly = this.schema.options.validateModifiedOnly;
  1975. }
  1976. const _this = this;
  1977. const _complete = () => {
  1978. let validationError = this.$__.validationError;
  1979. this.$__.validationError = undefined;
  1980. if (shouldValidateModifiedOnly && validationError != null) {
  1981. // Remove any validation errors that aren't from modified paths
  1982. const errors = Object.keys(validationError.errors);
  1983. for (const errPath of errors) {
  1984. if (!this.isModified(errPath)) {
  1985. delete validationError.errors[errPath];
  1986. }
  1987. }
  1988. if (Object.keys(validationError.errors).length === 0) {
  1989. validationError = void 0;
  1990. }
  1991. }
  1992. this.$__.cachedRequired = {};
  1993. this.emit('validate', _this);
  1994. this.constructor.emit('validate', _this);
  1995. this.$__.validating = null;
  1996. if (validationError) {
  1997. for (const key in validationError.errors) {
  1998. // Make sure cast errors persist
  1999. if (!this[documentArrayParent] &&
  2000. validationError.errors[key] instanceof MongooseError.CastError) {
  2001. this.invalidate(key, validationError.errors[key]);
  2002. }
  2003. }
  2004. return validationError;
  2005. }
  2006. };
  2007. // only validate required fields when necessary
  2008. const pathDetails = _getPathsToValidate(this);
  2009. let paths = shouldValidateModifiedOnly ?
  2010. pathDetails[0].filter((path) => this.isModified(path)) :
  2011. pathDetails[0];
  2012. const skipSchemaValidators = pathDetails[1];
  2013. if (Array.isArray(pathsToValidate)) {
  2014. paths = _handlePathsToValidate(paths, pathsToValidate);
  2015. }
  2016. if (paths.length === 0) {
  2017. return process.nextTick(function() {
  2018. const error = _complete();
  2019. if (error) {
  2020. return _this.schema.s.hooks.execPost('validate:error', _this, [_this], { error: error }, function(error) {
  2021. callback(error);
  2022. });
  2023. }
  2024. callback(null, _this);
  2025. });
  2026. }
  2027. const validated = {};
  2028. let total = 0;
  2029. const complete = function() {
  2030. const error = _complete();
  2031. if (error) {
  2032. return _this.schema.s.hooks.execPost('validate:error', _this, [_this], { error: error }, function(error) {
  2033. callback(error);
  2034. });
  2035. }
  2036. callback(null, _this);
  2037. };
  2038. const validatePath = function(path) {
  2039. if (path == null || validated[path]) {
  2040. return;
  2041. }
  2042. validated[path] = true;
  2043. total++;
  2044. process.nextTick(function() {
  2045. const schemaType = _this.schema.path(path);
  2046. if (!schemaType) {
  2047. return --total || complete();
  2048. }
  2049. // If user marked as invalid or there was a cast error, don't validate
  2050. if (!_this.$isValid(path)) {
  2051. --total || complete();
  2052. return;
  2053. }
  2054. let val = _this.$__getValue(path);
  2055. // If you `populate()` and get back a null value, required validators
  2056. // shouldn't fail (gh-8018). We should always fall back to the populated
  2057. // value.
  2058. let pop;
  2059. if (val == null && (pop = _this.populated(path))) {
  2060. val = pop;
  2061. }
  2062. const scope = path in _this.$__.pathsToScopes ?
  2063. _this.$__.pathsToScopes[path] :
  2064. _this;
  2065. const doValidateOptions = {
  2066. skipSchemaValidators: skipSchemaValidators[path],
  2067. path: path
  2068. };
  2069. schemaType.doValidate(val, function(err) {
  2070. if (err && (!schemaType.$isMongooseDocumentArray || err.$isArrayValidatorError)) {
  2071. if (schemaType.$isSingleNested &&
  2072. err instanceof ValidationError &&
  2073. schemaType.schema.options.storeSubdocValidationError === false) {
  2074. return --total || complete();
  2075. }
  2076. _this.invalidate(path, err, undefined, true);
  2077. }
  2078. --total || complete();
  2079. }, scope, doValidateOptions);
  2080. });
  2081. };
  2082. const numPaths = paths.length;
  2083. for (let i = 0; i < numPaths; ++i) {
  2084. validatePath(paths[i]);
  2085. }
  2086. };
  2087. /*!
  2088. * ignore
  2089. */
  2090. function _handlePathsToValidate(paths, pathsToValidate) {
  2091. const _pathsToValidate = new Set(pathsToValidate);
  2092. const parentPaths = new Map([]);
  2093. for (const path of pathsToValidate) {
  2094. if (path.indexOf('.') === -1) {
  2095. continue;
  2096. }
  2097. const pieces = path.split('.');
  2098. let cur = pieces[0];
  2099. for (let i = 1; i < pieces.length; ++i) {
  2100. // Since we skip subpaths under single nested subdocs to
  2101. // avoid double validation, we need to add back the
  2102. // single nested subpath if the user asked for it (gh-8626)
  2103. parentPaths.set(cur, path);
  2104. cur = cur + '.' + pieces[i];
  2105. }
  2106. }
  2107. const ret = [];
  2108. for (const path of paths) {
  2109. if (_pathsToValidate.has(path)) {
  2110. ret.push(path);
  2111. } else if (parentPaths.has(path)) {
  2112. ret.push(parentPaths.get(path));
  2113. }
  2114. }
  2115. return ret;
  2116. }
  2117. /**
  2118. * Executes registered validation rules (skipping asynchronous validators) for this document.
  2119. *
  2120. * ####Note:
  2121. *
  2122. * This method is useful if you need synchronous validation.
  2123. *
  2124. * ####Example:
  2125. *
  2126. * const err = doc.validateSync();
  2127. * if (err) {
  2128. * handleError(err);
  2129. * } else {
  2130. * // validation passed
  2131. * }
  2132. *
  2133. * @param {Array|string} pathsToValidate only validate the given paths
  2134. * @return {ValidationError|undefined} ValidationError if there are errors during validation, or undefined if there is no error.
  2135. * @api public
  2136. */
  2137. Document.prototype.validateSync = function(pathsToValidate, options) {
  2138. const _this = this;
  2139. const hasValidateModifiedOnlyOption = options &&
  2140. (typeof options === 'object') &&
  2141. ('validateModifiedOnly' in options);
  2142. let shouldValidateModifiedOnly;
  2143. if (hasValidateModifiedOnlyOption) {
  2144. shouldValidateModifiedOnly = !!options.validateModifiedOnly;
  2145. } else {
  2146. shouldValidateModifiedOnly = this.schema.options.validateModifiedOnly;
  2147. }
  2148. if (typeof pathsToValidate === 'string') {
  2149. pathsToValidate = pathsToValidate.split(' ');
  2150. }
  2151. // only validate required fields when necessary
  2152. const pathDetails = _getPathsToValidate(this);
  2153. let paths = shouldValidateModifiedOnly ?
  2154. pathDetails[0].filter((path) => this.isModified(path)) :
  2155. pathDetails[0];
  2156. const skipSchemaValidators = pathDetails[1];
  2157. if (Array.isArray(pathsToValidate)) {
  2158. paths = _handlePathsToValidate(paths, pathsToValidate);
  2159. }
  2160. const validating = {};
  2161. paths.forEach(function(path) {
  2162. if (validating[path]) {
  2163. return;
  2164. }
  2165. validating[path] = true;
  2166. const p = _this.schema.path(path);
  2167. if (!p) {
  2168. return;
  2169. }
  2170. if (!_this.$isValid(path)) {
  2171. return;
  2172. }
  2173. const val = _this.$__getValue(path);
  2174. const err = p.doValidateSync(val, _this, {
  2175. skipSchemaValidators: skipSchemaValidators[path],
  2176. path: path
  2177. });
  2178. if (err && (!p.$isMongooseDocumentArray || err.$isArrayValidatorError)) {
  2179. if (p.$isSingleNested &&
  2180. err instanceof ValidationError &&
  2181. p.schema.options.storeSubdocValidationError === false) {
  2182. return;
  2183. }
  2184. _this.invalidate(path, err, undefined, true);
  2185. }
  2186. });
  2187. const err = _this.$__.validationError;
  2188. _this.$__.validationError = undefined;
  2189. _this.emit('validate', _this);
  2190. _this.constructor.emit('validate', _this);
  2191. if (err) {
  2192. for (const key in err.errors) {
  2193. // Make sure cast errors persist
  2194. if (err.errors[key] instanceof MongooseError.CastError) {
  2195. _this.invalidate(key, err.errors[key]);
  2196. }
  2197. }
  2198. }
  2199. return err;
  2200. };
  2201. /**
  2202. * Marks a path as invalid, causing validation to fail.
  2203. *
  2204. * The `errorMsg` argument will become the message of the `ValidationError`.
  2205. *
  2206. * The `value` argument (if passed) will be available through the `ValidationError.value` property.
  2207. *
  2208. * doc.invalidate('size', 'must be less than 20', 14);
  2209. * doc.validate(function (err) {
  2210. * console.log(err)
  2211. * // prints
  2212. * { message: 'Validation failed',
  2213. * name: 'ValidationError',
  2214. * errors:
  2215. * { size:
  2216. * { message: 'must be less than 20',
  2217. * name: 'ValidatorError',
  2218. * path: 'size',
  2219. * type: 'user defined',
  2220. * value: 14 } } }
  2221. * })
  2222. *
  2223. * @param {String} path the field to invalidate
  2224. * @param {String|Error} errorMsg the error which states the reason `path` was invalid
  2225. * @param {Object|String|Number|any} value optional invalid value
  2226. * @param {String} [kind] optional `kind` property for the error
  2227. * @return {ValidationError} the current ValidationError, with all currently invalidated paths
  2228. * @api public
  2229. */
  2230. Document.prototype.invalidate = function(path, err, val, kind) {
  2231. if (!this.$__.validationError) {
  2232. this.$__.validationError = new ValidationError(this);
  2233. }
  2234. if (this.$__.validationError.errors[path]) {
  2235. return;
  2236. }
  2237. if (!err || typeof err === 'string') {
  2238. err = new ValidatorError({
  2239. path: path,
  2240. message: err,
  2241. type: kind || 'user defined',
  2242. value: val
  2243. });
  2244. }
  2245. if (this.$__.validationError === err) {
  2246. return this.$__.validationError;
  2247. }
  2248. this.$__.validationError.addError(path, err);
  2249. return this.$__.validationError;
  2250. };
  2251. /**
  2252. * Marks a path as valid, removing existing validation errors.
  2253. *
  2254. * @param {String} path the field to mark as valid
  2255. * @api public
  2256. * @memberOf Document
  2257. * @instance
  2258. * @method $markValid
  2259. */
  2260. Document.prototype.$markValid = function(path) {
  2261. if (!this.$__.validationError || !this.$__.validationError.errors[path]) {
  2262. return;
  2263. }
  2264. delete this.$__.validationError.errors[path];
  2265. if (Object.keys(this.$__.validationError.errors).length === 0) {
  2266. this.$__.validationError = null;
  2267. }
  2268. };
  2269. /**
  2270. * Saves this document by inserting a new document into the database if [document.isNew](/docs/api.html#document_Document-isNew) is `true`,
  2271. * or sends an [updateOne](/docs/api.html#document_Document-updateOne) operation **only** with the modifications to the database, it does not replace the whole document in the latter case.
  2272. *
  2273. * ####Example:
  2274. *
  2275. * product.sold = Date.now();
  2276. * product = await product.save();
  2277. *
  2278. * If save is successful, the returned promise will fulfill with the document
  2279. * saved.
  2280. *
  2281. * ####Example:
  2282. *
  2283. * const newProduct = await product.save();
  2284. * newProduct === product; // true
  2285. *
  2286. * @param {Object} [options] options optional options
  2287. * @param {Session} [options.session=null] the [session](https://docs.mongodb.com/manual/reference/server-sessions/) associated with this save operation. If not specified, defaults to the [document's associated session](api.html#document_Document-$session).
  2288. * @param {Object} [options.safe] (DEPRECATED) overrides [schema's safe option](http://mongoosejs.com//docs/guide.html#safe). Use the `w` option instead.
  2289. * @param {Boolean} [options.validateBeforeSave] set to false to save without validating.
  2290. * @param {Number|String} [options.w] set the [write concern](https://docs.mongodb.com/manual/reference/write-concern/#w-option). Overrides the [schema-level `writeConcern` option](/docs/guide.html#writeConcern)
  2291. * @param {Boolean} [options.j] set to true for MongoDB to wait until this `save()` has been [journaled before resolving the returned promise](https://docs.mongodb.com/manual/reference/write-concern/#j-option). Overrides the [schema-level `writeConcern` option](/docs/guide.html#writeConcern)
  2292. * @param {Number} [options.wtimeout] sets a [timeout for the write concern](https://docs.mongodb.com/manual/reference/write-concern/#wtimeout). Overrides the [schema-level `writeConcern` option](/docs/guide.html#writeConcern).
  2293. * @param {Boolean} [options.checkKeys=true] the MongoDB driver prevents you from saving keys that start with '$' or contain '.' by default. Set this option to `false` to skip that check. See [restrictions on field names](https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names)
  2294. * @param {Boolean} [options.timestamps=true] if `false` and [timestamps](./guide.html#timestamps) are enabled, skip timestamps for this `save()`.
  2295. * @param {Function} [fn] optional callback
  2296. * @method save
  2297. * @memberOf Document
  2298. * @instance
  2299. * @throws {DocumentNotFoundError} if this [save updates an existing document](api.html#document_Document-isNew) but the document doesn't exist in the database. For example, you will get this error if the document is [deleted between when you retrieved the document and when you saved it](documents.html#updating).
  2300. * @return {Promise|undefined} Returns undefined if used with callback or a Promise otherwise.
  2301. * @api public
  2302. * @see middleware http://mongoosejs.com/docs/middleware.html
  2303. */
  2304. /**
  2305. * Checks if a path is invalid
  2306. *
  2307. * @param {String} path the field to check
  2308. * @method $isValid
  2309. * @memberOf Document
  2310. * @instance
  2311. * @api private
  2312. */
  2313. Document.prototype.$isValid = function(path) {
  2314. return !this.$__.validationError || !this.$__.validationError.errors[path];
  2315. };
  2316. /**
  2317. * Resets the internal modified state of this document.
  2318. *
  2319. * @api private
  2320. * @return {Document}
  2321. * @method $__reset
  2322. * @memberOf Document
  2323. * @instance
  2324. */
  2325. Document.prototype.$__reset = function reset() {
  2326. let _this = this;
  2327. DocumentArray || (DocumentArray = require('./types/documentarray'));
  2328. this.$__.activePaths
  2329. .map('init', 'modify', function(i) {
  2330. return _this.$__getValue(i);
  2331. })
  2332. .filter(function(val) {
  2333. return val && val instanceof Array && val.isMongooseDocumentArray && val.length;
  2334. })
  2335. .forEach(function(array) {
  2336. let i = array.length;
  2337. while (i--) {
  2338. const doc = array[i];
  2339. if (!doc) {
  2340. continue;
  2341. }
  2342. doc.$__reset();
  2343. }
  2344. _this.$__.activePaths.init(array.$path());
  2345. array[arrayAtomicsSymbol] = {};
  2346. });
  2347. this.$__.activePaths.
  2348. map('init', 'modify', function(i) {
  2349. return _this.$__getValue(i);
  2350. }).
  2351. filter(function(val) {
  2352. return val && val.$isSingleNested;
  2353. }).
  2354. forEach(function(doc) {
  2355. doc.$__reset();
  2356. _this.$__.activePaths.init(doc.$basePath);
  2357. });
  2358. // clear atomics
  2359. this.$__dirty().forEach(function(dirt) {
  2360. const type = dirt.value;
  2361. if (type && type[arrayAtomicsSymbol]) {
  2362. type[arrayAtomicsSymbol] = {};
  2363. }
  2364. });
  2365. // Clear 'dirty' cache
  2366. this.$__.activePaths.clear('modify');
  2367. this.$__.activePaths.clear('default');
  2368. this.$__.validationError = undefined;
  2369. this.errors = undefined;
  2370. _this = this;
  2371. this.schema.requiredPaths().forEach(function(path) {
  2372. _this.$__.activePaths.require(path);
  2373. });
  2374. return this;
  2375. };
  2376. /**
  2377. * Returns this documents dirty paths / vals.
  2378. *
  2379. * @api private
  2380. * @method $__dirty
  2381. * @memberOf Document
  2382. * @instance
  2383. */
  2384. Document.prototype.$__dirty = function() {
  2385. const _this = this;
  2386. let all = this.$__.activePaths.map('modify', function(path) {
  2387. return {
  2388. path: path,
  2389. value: _this.$__getValue(path),
  2390. schema: _this.$__path(path)
  2391. };
  2392. });
  2393. // gh-2558: if we had to set a default and the value is not undefined,
  2394. // we have to save as well
  2395. all = all.concat(this.$__.activePaths.map('default', function(path) {
  2396. if (path === '_id' || _this.$__getValue(path) == null) {
  2397. return;
  2398. }
  2399. return {
  2400. path: path,
  2401. value: _this.$__getValue(path),
  2402. schema: _this.$__path(path)
  2403. };
  2404. }));
  2405. // Sort dirty paths in a flat hierarchy.
  2406. all.sort(function(a, b) {
  2407. return (a.path < b.path ? -1 : (a.path > b.path ? 1 : 0));
  2408. });
  2409. // Ignore "foo.a" if "foo" is dirty already.
  2410. const minimal = [];
  2411. let lastPath;
  2412. let top;
  2413. all.forEach(function(item) {
  2414. if (!item) {
  2415. return;
  2416. }
  2417. if (lastPath == null || item.path.indexOf(lastPath) !== 0) {
  2418. lastPath = item.path + '.';
  2419. minimal.push(item);
  2420. top = item;
  2421. } else if (top != null &&
  2422. top.value != null &&
  2423. top.value[arrayAtomicsSymbol] != null &&
  2424. top.value.hasAtomics()) {
  2425. // special case for top level MongooseArrays
  2426. // the `top` array itself and a sub path of `top` are being modified.
  2427. // the only way to honor all of both modifications is through a $set
  2428. // of entire array.
  2429. top.value[arrayAtomicsSymbol] = {};
  2430. top.value[arrayAtomicsSymbol].$set = top.value;
  2431. }
  2432. });
  2433. top = lastPath = null;
  2434. return minimal;
  2435. };
  2436. /**
  2437. * Assigns/compiles `schema` into this documents prototype.
  2438. *
  2439. * @param {Schema} schema
  2440. * @api private
  2441. * @method $__setSchema
  2442. * @memberOf Document
  2443. * @instance
  2444. */
  2445. Document.prototype.$__setSchema = function(schema) {
  2446. schema.plugin(idGetter, { deduplicate: true });
  2447. compile(schema.tree, this, undefined, schema.options);
  2448. // Apply default getters if virtual doesn't have any (gh-6262)
  2449. for (const key of Object.keys(schema.virtuals)) {
  2450. schema.virtuals[key]._applyDefaultGetters();
  2451. }
  2452. this.schema = schema;
  2453. this[documentSchemaSymbol] = schema;
  2454. };
  2455. /**
  2456. * Get active path that were changed and are arrays
  2457. *
  2458. * @api private
  2459. * @method $__getArrayPathsToValidate
  2460. * @memberOf Document
  2461. * @instance
  2462. */
  2463. Document.prototype.$__getArrayPathsToValidate = function() {
  2464. DocumentArray || (DocumentArray = require('./types/documentarray'));
  2465. // validate all document arrays.
  2466. return this.$__.activePaths
  2467. .map('init', 'modify', function(i) {
  2468. return this.$__getValue(i);
  2469. }.bind(this))
  2470. .filter(function(val) {
  2471. return val && val instanceof Array && val.isMongooseDocumentArray && val.length;
  2472. }).reduce(function(seed, array) {
  2473. return seed.concat(array);
  2474. }, [])
  2475. .filter(function(doc) {
  2476. return doc;
  2477. });
  2478. };
  2479. /**
  2480. * Get all subdocs (by bfs)
  2481. *
  2482. * @api private
  2483. * @method $__getAllSubdocs
  2484. * @memberOf Document
  2485. * @instance
  2486. */
  2487. Document.prototype.$__getAllSubdocs = function() {
  2488. DocumentArray || (DocumentArray = require('./types/documentarray'));
  2489. Embedded = Embedded || require('./types/embedded');
  2490. function docReducer(doc, seed, path) {
  2491. let val = doc;
  2492. if (path) {
  2493. if (doc instanceof Document && doc[documentSchemaSymbol].paths[path]) {
  2494. val = doc._doc[path];
  2495. } else {
  2496. val = doc[path];
  2497. }
  2498. }
  2499. if (val instanceof Embedded) {
  2500. seed.push(val);
  2501. } else if (val instanceof Map) {
  2502. seed = Array.from(val.keys()).reduce(function(seed, path) {
  2503. return docReducer(val.get(path), seed, null);
  2504. }, seed);
  2505. } else if (val && val.$isSingleNested) {
  2506. seed = Object.keys(val._doc).reduce(function(seed, path) {
  2507. return docReducer(val._doc, seed, path);
  2508. }, seed);
  2509. seed.push(val);
  2510. } else if (val && val.isMongooseDocumentArray) {
  2511. val.forEach(function _docReduce(doc) {
  2512. if (!doc || !doc._doc) {
  2513. return;
  2514. }
  2515. seed = Object.keys(doc._doc).reduce(function(seed, path) {
  2516. return docReducer(doc._doc, seed, path);
  2517. }, seed);
  2518. if (doc instanceof Embedded) {
  2519. seed.push(doc);
  2520. }
  2521. });
  2522. } else if (val instanceof Document && val.$__isNested) {
  2523. seed = Object.keys(val).reduce(function(seed, path) {
  2524. return docReducer(val, seed, path);
  2525. }, seed);
  2526. }
  2527. return seed;
  2528. }
  2529. const _this = this;
  2530. const subDocs = Object.keys(this._doc).reduce(function(seed, path) {
  2531. return docReducer(_this, seed, path);
  2532. }, []);
  2533. return subDocs;
  2534. };
  2535. /*!
  2536. * Runs queued functions
  2537. */
  2538. function applyQueue(doc) {
  2539. const q = doc.schema && doc.schema.callQueue;
  2540. if (!q.length) {
  2541. return;
  2542. }
  2543. for (const pair of q) {
  2544. if (pair[0] !== 'pre' && pair[0] !== 'post' && pair[0] !== 'on') {
  2545. doc[pair[0]].apply(doc, pair[1]);
  2546. }
  2547. }
  2548. }
  2549. /*!
  2550. * ignore
  2551. */
  2552. Document.prototype.$__handleReject = function handleReject(err) {
  2553. // emit on the Model if listening
  2554. if (this.listeners('error').length) {
  2555. this.emit('error', err);
  2556. } else if (this.constructor.listeners && this.constructor.listeners('error').length) {
  2557. this.constructor.emit('error', err);
  2558. }
  2559. };
  2560. /**
  2561. * Internal helper for toObject() and toJSON() that doesn't manipulate options
  2562. *
  2563. * @api private
  2564. * @method $toObject
  2565. * @memberOf Document
  2566. * @instance
  2567. */
  2568. Document.prototype.$toObject = function(options, json) {
  2569. let defaultOptions = {
  2570. transform: true,
  2571. flattenDecimals: true
  2572. };
  2573. const path = json ? 'toJSON' : 'toObject';
  2574. const baseOptions = get(this, 'constructor.base.options.' + path, {});
  2575. const schemaOptions = get(this, 'schema.options', {});
  2576. // merge base default options with Schema's set default options if available.
  2577. // `clone` is necessary here because `utils.options` directly modifies the second input.
  2578. defaultOptions = utils.options(defaultOptions, clone(baseOptions));
  2579. defaultOptions = utils.options(defaultOptions, clone(schemaOptions[path] || {}));
  2580. // If options do not exist or is not an object, set it to empty object
  2581. options = utils.isPOJO(options) ? clone(options) : {};
  2582. if (!('flattenMaps' in options)) {
  2583. options.flattenMaps = defaultOptions.flattenMaps;
  2584. }
  2585. let _minimize;
  2586. if (options.minimize != null) {
  2587. _minimize = options.minimize;
  2588. } else if (defaultOptions.minimize != null) {
  2589. _minimize = defaultOptions.minimize;
  2590. } else {
  2591. _minimize = schemaOptions.minimize;
  2592. }
  2593. // The original options that will be passed to `clone()`. Important because
  2594. // `clone()` will recursively call `$toObject()` on embedded docs, so we
  2595. // need the original options the user passed in, plus `_isNested` and
  2596. // `_parentOptions` for checking whether we need to depopulate.
  2597. const cloneOptions = Object.assign(utils.clone(options), {
  2598. _isNested: true,
  2599. json: json,
  2600. minimize: _minimize
  2601. });
  2602. if (utils.hasUserDefinedProperty(options, 'getters')) {
  2603. cloneOptions.getters = options.getters;
  2604. }
  2605. if (utils.hasUserDefinedProperty(options, 'virtuals')) {
  2606. cloneOptions.virtuals = options.virtuals;
  2607. }
  2608. const depopulate = options.depopulate ||
  2609. get(options, '_parentOptions.depopulate', false);
  2610. // _isNested will only be true if this is not the top level document, we
  2611. // should never depopulate
  2612. if (depopulate && options._isNested && this.$__.wasPopulated) {
  2613. // populated paths that we set to a document
  2614. return clone(this._id, cloneOptions);
  2615. }
  2616. // merge default options with input options.
  2617. options = utils.options(defaultOptions, options);
  2618. options._isNested = true;
  2619. options.json = json;
  2620. options.minimize = _minimize;
  2621. cloneOptions._parentOptions = options;
  2622. cloneOptions._skipSingleNestedGetters = true;
  2623. const gettersOptions = Object.assign({}, cloneOptions);
  2624. gettersOptions._skipSingleNestedGetters = false;
  2625. // remember the root transform function
  2626. // to save it from being overwritten by sub-transform functions
  2627. const originalTransform = options.transform;
  2628. let ret = clone(this._doc, cloneOptions) || {};
  2629. if (options.getters) {
  2630. applyGetters(this, ret, gettersOptions);
  2631. if (options.minimize) {
  2632. ret = minimize(ret) || {};
  2633. }
  2634. }
  2635. if (options.virtuals || (options.getters && options.virtuals !== false)) {
  2636. applyVirtuals(this, ret, gettersOptions, options);
  2637. }
  2638. if (options.versionKey === false && this.schema.options.versionKey) {
  2639. delete ret[this.schema.options.versionKey];
  2640. }
  2641. let transform = options.transform;
  2642. // In the case where a subdocument has its own transform function, we need to
  2643. // check and see if the parent has a transform (options.transform) and if the
  2644. // child schema has a transform (this.schema.options.toObject) In this case,
  2645. // we need to adjust options.transform to be the child schema's transform and
  2646. // not the parent schema's
  2647. if (transform) {
  2648. applySchemaTypeTransforms(this, ret, gettersOptions, options);
  2649. }
  2650. if (transform === true || (schemaOptions.toObject && transform)) {
  2651. const opts = options.json ? schemaOptions.toJSON : schemaOptions.toObject;
  2652. if (opts) {
  2653. transform = (typeof options.transform === 'function' ? options.transform : opts.transform);
  2654. }
  2655. } else {
  2656. options.transform = originalTransform;
  2657. }
  2658. if (typeof transform === 'function') {
  2659. const xformed = transform(this, ret, options);
  2660. if (typeof xformed !== 'undefined') {
  2661. ret = xformed;
  2662. }
  2663. }
  2664. return ret;
  2665. };
  2666. /**
  2667. * Converts this document into a plain javascript object, ready for storage in MongoDB.
  2668. *
  2669. * Buffers are converted to instances of [mongodb.Binary](http://mongodb.github.com/node-mongodb-native/api-bson-generated/binary.html) for proper storage.
  2670. *
  2671. * ####Options:
  2672. *
  2673. * - `getters` apply all getters (path and virtual getters), defaults to false
  2674. * - `aliases` apply all aliases if `virtuals=true`, defaults to true
  2675. * - `virtuals` apply virtual getters (can override `getters` option), defaults to false
  2676. * - `minimize` remove empty objects, defaults to true
  2677. * - `transform` a transform function to apply to the resulting document before returning
  2678. * - `depopulate` depopulate any populated paths, replacing them with their original refs, defaults to false
  2679. * - `versionKey` whether to include the version key, defaults to true
  2680. * - `flattenMaps` convert Maps to POJOs. Useful if you want to JSON.stringify() the result of toObject(), defaults to false
  2681. *
  2682. * ####Getters/Virtuals
  2683. *
  2684. * Example of only applying path getters
  2685. *
  2686. * doc.toObject({ getters: true, virtuals: false })
  2687. *
  2688. * Example of only applying virtual getters
  2689. *
  2690. * doc.toObject({ virtuals: true })
  2691. *
  2692. * Example of applying both path and virtual getters
  2693. *
  2694. * doc.toObject({ getters: true })
  2695. *
  2696. * To apply these options to every document of your schema by default, set your [schemas](#schema_Schema) `toObject` option to the same argument.
  2697. *
  2698. * schema.set('toObject', { virtuals: true })
  2699. *
  2700. * ####Transform
  2701. *
  2702. * We may need to perform a transformation of the resulting object based on some criteria, say to remove some sensitive information or return a custom object. In this case we set the optional `transform` function.
  2703. *
  2704. * Transform functions receive three arguments
  2705. *
  2706. * function (doc, ret, options) {}
  2707. *
  2708. * - `doc` The mongoose document which is being converted
  2709. * - `ret` The plain object representation which has been converted
  2710. * - `options` The options in use (either schema options or the options passed inline)
  2711. *
  2712. * ####Example
  2713. *
  2714. * // specify the transform schema option
  2715. * if (!schema.options.toObject) schema.options.toObject = {};
  2716. * schema.options.toObject.transform = function (doc, ret, options) {
  2717. * // remove the _id of every document before returning the result
  2718. * delete ret._id;
  2719. * return ret;
  2720. * }
  2721. *
  2722. * // without the transformation in the schema
  2723. * doc.toObject(); // { _id: 'anId', name: 'Wreck-it Ralph' }
  2724. *
  2725. * // with the transformation
  2726. * doc.toObject(); // { name: 'Wreck-it Ralph' }
  2727. *
  2728. * With transformations we can do a lot more than remove properties. We can even return completely new customized objects:
  2729. *
  2730. * if (!schema.options.toObject) schema.options.toObject = {};
  2731. * schema.options.toObject.transform = function (doc, ret, options) {
  2732. * return { movie: ret.name }
  2733. * }
  2734. *
  2735. * // without the transformation in the schema
  2736. * doc.toObject(); // { _id: 'anId', name: 'Wreck-it Ralph' }
  2737. *
  2738. * // with the transformation
  2739. * doc.toObject(); // { movie: 'Wreck-it Ralph' }
  2740. *
  2741. * _Note: if a transform function returns `undefined`, the return value will be ignored._
  2742. *
  2743. * Transformations may also be applied inline, overridding any transform set in the options:
  2744. *
  2745. * function xform (doc, ret, options) {
  2746. * return { inline: ret.name, custom: true }
  2747. * }
  2748. *
  2749. * // pass the transform as an inline option
  2750. * doc.toObject({ transform: xform }); // { inline: 'Wreck-it Ralph', custom: true }
  2751. *
  2752. * If you want to skip transformations, use `transform: false`:
  2753. *
  2754. * schema.options.toObject.hide = '_id';
  2755. * schema.options.toObject.transform = function (doc, ret, options) {
  2756. * if (options.hide) {
  2757. * options.hide.split(' ').forEach(function (prop) {
  2758. * delete ret[prop];
  2759. * });
  2760. * }
  2761. * return ret;
  2762. * }
  2763. *
  2764. * const doc = new Doc({ _id: 'anId', secret: 47, name: 'Wreck-it Ralph' });
  2765. * doc.toObject(); // { secret: 47, name: 'Wreck-it Ralph' }
  2766. * doc.toObject({ hide: 'secret _id', transform: false });// { _id: 'anId', secret: 47, name: 'Wreck-it Ralph' }
  2767. * doc.toObject({ hide: 'secret _id', transform: true }); // { name: 'Wreck-it Ralph' }
  2768. *
  2769. * If you pass a transform in `toObject()` options, Mongoose will apply the transform
  2770. * to [subdocuments](/docs/subdocs.html) in addition to the top-level document.
  2771. * Similarly, `transform: false` skips transforms for all subdocuments.
  2772. * Note that this is behavior is different for transforms defined in the schema:
  2773. * if you define a transform in `schema.options.toObject.transform`, that transform
  2774. * will **not** apply to subdocuments.
  2775. *
  2776. * const memberSchema = new Schema({ name: String, email: String });
  2777. * const groupSchema = new Schema({ members: [memberSchema], name: String, email });
  2778. * const Group = mongoose.model('Group', groupSchema);
  2779. *
  2780. * const doc = new Group({
  2781. * name: 'Engineering',
  2782. * email: 'dev@mongoosejs.io',
  2783. * members: [{ name: 'Val', email: 'val@mongoosejs.io' }]
  2784. * });
  2785. *
  2786. * // Removes `email` from both top-level document **and** array elements
  2787. * // { name: 'Engineering', members: [{ name: 'Val' }] }
  2788. * doc.toObject({ transform: (doc, ret) => { delete ret.email; return ret; } });
  2789. *
  2790. * Transforms, like all of these options, are also available for `toJSON`. See [this guide to `JSON.stringify()`](https://thecodebarbarian.com/the-80-20-guide-to-json-stringify-in-javascript.html) to learn why `toJSON()` and `toObject()` are separate functions.
  2791. *
  2792. * See [schema options](/docs/guide.html#toObject) for some more details.
  2793. *
  2794. * _During save, no custom options are applied to the document before being sent to the database._
  2795. *
  2796. * @param {Object} [options]
  2797. * @param {Boolean} [options.getters=false] if true, apply all getters, including virtuals
  2798. * @param {Boolean} [options.virtuals=false] if true, apply virtuals, including aliases. Use `{ getters: true, virtuals: false }` to just apply getters, not virtuals
  2799. * @param {Boolean} [options.aliases=true] if `options.virtuals = true`, you can set `options.aliases = false` to skip applying aliases. This option is a no-op if `options.virtuals = false`.
  2800. * @param {Boolean} [options.minimize=true] if true, omit any empty objects from the output
  2801. * @param {Function|null} [options.transform=null] if set, mongoose will call this function to allow you to transform the returned object
  2802. * @param {Boolean} [options.depopulate=false] if true, replace any conventionally populated paths with the original id in the output. Has no affect on virtual populated paths.
  2803. * @param {Boolean} [options.versionKey=true] if false, exclude the version key (`__v` by default) from the output
  2804. * @param {Boolean} [options.flattenMaps=false] if true, convert Maps to POJOs. Useful if you want to `JSON.stringify()` the result of `toObject()`.
  2805. * @return {Object} js object
  2806. * @see mongodb.Binary http://mongodb.github.com/node-mongodb-native/api-bson-generated/binary.html
  2807. * @api public
  2808. * @memberOf Document
  2809. * @instance
  2810. */
  2811. Document.prototype.toObject = function(options) {
  2812. return this.$toObject(options);
  2813. };
  2814. /*!
  2815. * Minimizes an object, removing undefined values and empty objects
  2816. *
  2817. * @param {Object} object to minimize
  2818. * @return {Object}
  2819. */
  2820. function minimize(obj) {
  2821. const keys = Object.keys(obj);
  2822. let i = keys.length;
  2823. let hasKeys;
  2824. let key;
  2825. let val;
  2826. while (i--) {
  2827. key = keys[i];
  2828. val = obj[key];
  2829. if (utils.isObject(val) && !Buffer.isBuffer(val)) {
  2830. obj[key] = minimize(val);
  2831. }
  2832. if (undefined === obj[key]) {
  2833. delete obj[key];
  2834. continue;
  2835. }
  2836. hasKeys = true;
  2837. }
  2838. return hasKeys
  2839. ? obj
  2840. : undefined;
  2841. }
  2842. /*!
  2843. * Applies virtuals properties to `json`.
  2844. */
  2845. function applyVirtuals(self, json, options, toObjectOptions) {
  2846. const schema = self.schema;
  2847. const paths = Object.keys(schema.virtuals);
  2848. let i = paths.length;
  2849. const numPaths = i;
  2850. let path;
  2851. let assignPath;
  2852. let cur = self._doc;
  2853. let v;
  2854. const aliases = get(toObjectOptions, 'aliases', true);
  2855. if (!cur) {
  2856. return json;
  2857. }
  2858. options = options || {};
  2859. for (i = 0; i < numPaths; ++i) {
  2860. path = paths[i];
  2861. // Allow skipping aliases with `toObject({ virtuals: true, aliases: false })`
  2862. if (!aliases && schema.aliases.hasOwnProperty(path)) {
  2863. continue;
  2864. }
  2865. // We may be applying virtuals to a nested object, for example if calling
  2866. // `doc.nestedProp.toJSON()`. If so, the path we assign to, `assignPath`,
  2867. // will be a trailing substring of the `path`.
  2868. assignPath = path;
  2869. if (options.path != null) {
  2870. if (!path.startsWith(options.path + '.')) {
  2871. continue;
  2872. }
  2873. assignPath = path.substr(options.path.length + 1);
  2874. }
  2875. const parts = assignPath.split('.');
  2876. v = clone(self.get(path), options);
  2877. if (v === void 0) {
  2878. continue;
  2879. }
  2880. const plen = parts.length;
  2881. cur = json;
  2882. for (let j = 0; j < plen - 1; ++j) {
  2883. cur[parts[j]] = cur[parts[j]] || {};
  2884. cur = cur[parts[j]];
  2885. }
  2886. cur[parts[plen - 1]] = v;
  2887. }
  2888. return json;
  2889. }
  2890. /*!
  2891. * Applies virtuals properties to `json`.
  2892. *
  2893. * @param {Document} self
  2894. * @param {Object} json
  2895. * @return {Object} `json`
  2896. */
  2897. function applyGetters(self, json, options) {
  2898. const schema = self.schema;
  2899. const paths = Object.keys(schema.paths);
  2900. let i = paths.length;
  2901. let path;
  2902. let cur = self._doc;
  2903. let v;
  2904. if (!cur) {
  2905. return json;
  2906. }
  2907. while (i--) {
  2908. path = paths[i];
  2909. const parts = path.split('.');
  2910. const plen = parts.length;
  2911. const last = plen - 1;
  2912. let branch = json;
  2913. let part;
  2914. cur = self._doc;
  2915. if (!self.isSelected(path)) {
  2916. continue;
  2917. }
  2918. for (let ii = 0; ii < plen; ++ii) {
  2919. part = parts[ii];
  2920. v = cur[part];
  2921. if (ii === last) {
  2922. const val = self.get(path);
  2923. branch[part] = clone(val, options);
  2924. } else if (v == null) {
  2925. if (part in cur) {
  2926. branch[part] = v;
  2927. }
  2928. break;
  2929. } else {
  2930. branch = branch[part] || (branch[part] = {});
  2931. }
  2932. cur = v;
  2933. }
  2934. }
  2935. return json;
  2936. }
  2937. /*!
  2938. * Applies schema type transforms to `json`.
  2939. *
  2940. * @param {Document} self
  2941. * @param {Object} json
  2942. * @return {Object} `json`
  2943. */
  2944. function applySchemaTypeTransforms(self, json) {
  2945. const schema = self.schema;
  2946. const paths = Object.keys(schema.paths || {});
  2947. const cur = self._doc;
  2948. if (!cur) {
  2949. return json;
  2950. }
  2951. for (const path of paths) {
  2952. const schematype = schema.paths[path];
  2953. if (typeof schematype.options.transform === 'function') {
  2954. const val = self.get(path);
  2955. json[path] = schematype.options.transform.call(self, val);
  2956. } else if (schematype.$embeddedSchemaType != null &&
  2957. typeof schematype.$embeddedSchemaType.options.transform === 'function') {
  2958. const vals = [].concat(self.get(path));
  2959. const transform = schematype.$embeddedSchemaType.options.transform;
  2960. for (let i = 0; i < vals.length; ++i) {
  2961. vals[i] = transform.call(self, vals[i]);
  2962. }
  2963. json[path] = vals;
  2964. }
  2965. }
  2966. return json;
  2967. }
  2968. /**
  2969. * The return value of this method is used in calls to JSON.stringify(doc).
  2970. *
  2971. * This method accepts the same options as [Document#toObject](#document_Document-toObject). To apply the options to every document of your schema by default, set your [schemas](#schema_Schema) `toJSON` option to the same argument.
  2972. *
  2973. * schema.set('toJSON', { virtuals: true })
  2974. *
  2975. * See [schema options](/docs/guide.html#toJSON) for details.
  2976. *
  2977. * @param {Object} options
  2978. * @return {Object}
  2979. * @see Document#toObject #document_Document-toObject
  2980. * @see JSON.stringify() in JavaScript https://thecodebarbarian.com/the-80-20-guide-to-json-stringify-in-javascript.html
  2981. * @api public
  2982. * @memberOf Document
  2983. * @instance
  2984. */
  2985. Document.prototype.toJSON = function(options) {
  2986. return this.$toObject(options, true);
  2987. };
  2988. /**
  2989. * Helper for console.log
  2990. *
  2991. * @api public
  2992. * @method inspect
  2993. * @memberOf Document
  2994. * @instance
  2995. */
  2996. Document.prototype.inspect = function(options) {
  2997. const isPOJO = utils.isPOJO(options);
  2998. let opts;
  2999. if (isPOJO) {
  3000. opts = options;
  3001. opts.minimize = false;
  3002. }
  3003. const ret = this.toObject(opts);
  3004. if (ret == null) {
  3005. // If `toObject()` returns null, `this` is still an object, so if `inspect()`
  3006. // prints out null this can cause some serious confusion. See gh-7942.
  3007. return 'MongooseDocument { ' + ret + ' }';
  3008. }
  3009. return ret;
  3010. };
  3011. if (inspect.custom) {
  3012. /*!
  3013. * Avoid Node deprecation warning DEP0079
  3014. */
  3015. Document.prototype[inspect.custom] = Document.prototype.inspect;
  3016. }
  3017. /**
  3018. * Helper for console.log
  3019. *
  3020. * @api public
  3021. * @method toString
  3022. * @memberOf Document
  3023. * @instance
  3024. */
  3025. Document.prototype.toString = function() {
  3026. const ret = this.inspect();
  3027. if (typeof ret === 'string') {
  3028. return ret;
  3029. }
  3030. return inspect(ret);
  3031. };
  3032. /**
  3033. * Returns true if the Document stores the same data as doc.
  3034. *
  3035. * Documents are considered equal when they have matching `_id`s, unless neither
  3036. * document has an `_id`, in which case this function falls back to using
  3037. * `deepEqual()`.
  3038. *
  3039. * @param {Document} doc a document to compare
  3040. * @return {Boolean}
  3041. * @api public
  3042. * @memberOf Document
  3043. * @instance
  3044. */
  3045. Document.prototype.equals = function(doc) {
  3046. if (!doc) {
  3047. return false;
  3048. }
  3049. const tid = this.get('_id');
  3050. const docid = doc.get ? doc.get('_id') : doc;
  3051. if (!tid && !docid) {
  3052. return deepEqual(this, doc);
  3053. }
  3054. return tid && tid.equals
  3055. ? tid.equals(docid)
  3056. : tid === docid;
  3057. };
  3058. /**
  3059. * Populates document references, executing the `callback` when complete.
  3060. * If you want to use promises instead, use this function with
  3061. * [`execPopulate()`](#document_Document-execPopulate)
  3062. *
  3063. * ####Example:
  3064. *
  3065. * doc
  3066. * .populate('company')
  3067. * .populate({
  3068. * path: 'notes',
  3069. * match: /airline/,
  3070. * select: 'text',
  3071. * model: 'modelName'
  3072. * options: opts
  3073. * }, function (err, user) {
  3074. * assert(doc._id === user._id) // the document itself is passed
  3075. * })
  3076. *
  3077. * // summary
  3078. * doc.populate(path) // not executed
  3079. * doc.populate(options); // not executed
  3080. * doc.populate(path, callback) // executed
  3081. * doc.populate(options, callback); // executed
  3082. * doc.populate(callback); // executed
  3083. * doc.populate(options).execPopulate() // executed, returns promise
  3084. *
  3085. *
  3086. * ####NOTE:
  3087. *
  3088. * Population does not occur unless a `callback` is passed *or* you explicitly
  3089. * call `execPopulate()`.
  3090. * Passing the same path a second time will overwrite the previous path options.
  3091. * See [Model.populate()](#model_Model.populate) for explaination of options.
  3092. *
  3093. * @see Model.populate #model_Model.populate
  3094. * @see Document.execPopulate #document_Document-execPopulate
  3095. * @param {String|Object} [path] The path to populate or an options object
  3096. * @param {Function} [callback] When passed, population is invoked
  3097. * @api public
  3098. * @return {Document} this
  3099. * @memberOf Document
  3100. * @instance
  3101. */
  3102. Document.prototype.populate = function populate() {
  3103. if (arguments.length === 0) {
  3104. return this;
  3105. }
  3106. const pop = this.$__.populate || (this.$__.populate = {});
  3107. const args = utils.args(arguments);
  3108. let fn;
  3109. if (typeof args[args.length - 1] === 'function') {
  3110. fn = args.pop();
  3111. }
  3112. // allow `doc.populate(callback)`
  3113. if (args.length) {
  3114. // use hash to remove duplicate paths
  3115. const res = utils.populate.apply(null, args);
  3116. for (const populateOptions of res) {
  3117. pop[populateOptions.path] = populateOptions;
  3118. }
  3119. }
  3120. if (fn) {
  3121. const paths = utils.object.vals(pop);
  3122. this.$__.populate = undefined;
  3123. let topLevelModel = this.constructor;
  3124. if (this.$__isNested) {
  3125. topLevelModel = this.$__[scopeSymbol].constructor;
  3126. const nestedPath = this.$__.nestedPath;
  3127. paths.forEach(function(populateOptions) {
  3128. populateOptions.path = nestedPath + '.' + populateOptions.path;
  3129. });
  3130. }
  3131. // Use `$session()` by default if the document has an associated session
  3132. // See gh-6754
  3133. if (this.$session() != null) {
  3134. const session = this.$session();
  3135. paths.forEach(path => {
  3136. if (path.options == null) {
  3137. path.options = { session: session };
  3138. return;
  3139. }
  3140. if (!('session' in path.options)) {
  3141. path.options.session = session;
  3142. }
  3143. });
  3144. }
  3145. topLevelModel.populate(this, paths, fn);
  3146. }
  3147. return this;
  3148. };
  3149. /**
  3150. * Explicitly executes population and returns a promise. Useful for ES2015
  3151. * integration.
  3152. *
  3153. * ####Example:
  3154. *
  3155. * const promise = doc.
  3156. * populate('company').
  3157. * populate({
  3158. * path: 'notes',
  3159. * match: /airline/,
  3160. * select: 'text',
  3161. * model: 'modelName'
  3162. * options: opts
  3163. * }).
  3164. * execPopulate();
  3165. *
  3166. * // summary
  3167. * doc.execPopulate().then(resolve, reject);
  3168. *
  3169. * // you can also use doc.execPopulate(options) as a shorthand for
  3170. * // doc.populate(options).execPopulate()
  3171. *
  3172. *
  3173. * ####Example:
  3174. * const promise = doc.execPopulate({ path: 'company', select: 'employees' });
  3175. *
  3176. * // summary
  3177. * promise.then(resolve,reject);
  3178. *
  3179. * @see Document.populate #document_Document-populate
  3180. * @api public
  3181. * @param {Function} [callback] optional callback. If specified, a promise will **not** be returned
  3182. * @return {Promise} promise that resolves to the document when population is done
  3183. * @memberOf Document
  3184. * @instance
  3185. */
  3186. Document.prototype.execPopulate = function(callback) {
  3187. const isUsingShorthand = callback != null && typeof callback !== 'function';
  3188. if (isUsingShorthand) {
  3189. return this.populate.apply(this, arguments).execPopulate();
  3190. }
  3191. return promiseOrCallback(callback, cb => {
  3192. this.populate(cb);
  3193. }, this.constructor.events);
  3194. };
  3195. /**
  3196. * Gets _id(s) used during population of the given `path`.
  3197. *
  3198. * ####Example:
  3199. *
  3200. * Model.findOne().populate('author').exec(function (err, doc) {
  3201. * console.log(doc.author.name) // Dr.Seuss
  3202. * console.log(doc.populated('author')) // '5144cf8050f071d979c118a7'
  3203. * })
  3204. *
  3205. * If the path was not populated, undefined is returned.
  3206. *
  3207. * @param {String} path
  3208. * @return {Array|ObjectId|Number|Buffer|String|undefined}
  3209. * @memberOf Document
  3210. * @instance
  3211. * @api public
  3212. */
  3213. Document.prototype.populated = function(path, val, options) {
  3214. // val and options are internal
  3215. if (val === null || val === void 0) {
  3216. if (!this.$__.populated) {
  3217. return undefined;
  3218. }
  3219. const v = this.$__.populated[path];
  3220. if (v) {
  3221. return v.value;
  3222. }
  3223. return undefined;
  3224. }
  3225. // internal
  3226. if (val === true) {
  3227. if (!this.$__.populated) {
  3228. return undefined;
  3229. }
  3230. return this.$__.populated[path];
  3231. }
  3232. this.$__.populated || (this.$__.populated = {});
  3233. this.$__.populated[path] = { value: val, options: options };
  3234. // If this was a nested populate, make sure each populated doc knows
  3235. // about its populated children (gh-7685)
  3236. const pieces = path.split('.');
  3237. for (let i = 0; i < pieces.length - 1; ++i) {
  3238. const subpath = pieces.slice(0, i + 1).join('.');
  3239. const subdoc = this.get(subpath);
  3240. if (subdoc != null && subdoc.$__ != null && this.populated(subpath)) {
  3241. const rest = pieces.slice(i + 1).join('.');
  3242. subdoc.populated(rest, val, options);
  3243. // No need to continue because the above recursion should take care of
  3244. // marking the rest of the docs as populated
  3245. break;
  3246. }
  3247. }
  3248. return val;
  3249. };
  3250. /**
  3251. * Takes a populated field and returns it to its unpopulated state.
  3252. *
  3253. * ####Example:
  3254. *
  3255. * Model.findOne().populate('author').exec(function (err, doc) {
  3256. * console.log(doc.author.name); // Dr.Seuss
  3257. * console.log(doc.depopulate('author'));
  3258. * console.log(doc.author); // '5144cf8050f071d979c118a7'
  3259. * })
  3260. *
  3261. * If the path was not populated, this is a no-op.
  3262. *
  3263. * @param {String} path
  3264. * @return {Document} this
  3265. * @see Document.populate #document_Document-populate
  3266. * @api public
  3267. * @memberOf Document
  3268. * @instance
  3269. */
  3270. Document.prototype.depopulate = function(path) {
  3271. if (typeof path === 'string') {
  3272. path = path.split(' ');
  3273. }
  3274. let populatedIds;
  3275. const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : [];
  3276. const populated = get(this, '$__.populated', {});
  3277. if (arguments.length === 0) {
  3278. // Depopulate all
  3279. for (const virtualKey of virtualKeys) {
  3280. delete this.$$populatedVirtuals[virtualKey];
  3281. delete this._doc[virtualKey];
  3282. delete populated[virtualKey];
  3283. }
  3284. const keys = Object.keys(populated);
  3285. for (const key of keys) {
  3286. populatedIds = this.populated(key);
  3287. if (!populatedIds) {
  3288. continue;
  3289. }
  3290. delete populated[key];
  3291. this.$set(key, populatedIds);
  3292. }
  3293. return this;
  3294. }
  3295. for (const singlePath of path) {
  3296. populatedIds = this.populated(singlePath);
  3297. delete populated[singlePath];
  3298. if (virtualKeys.indexOf(singlePath) !== -1) {
  3299. delete this.$$populatedVirtuals[singlePath];
  3300. delete this._doc[singlePath];
  3301. } else if (populatedIds) {
  3302. this.$set(singlePath, populatedIds);
  3303. }
  3304. }
  3305. return this;
  3306. };
  3307. /**
  3308. * Returns the full path to this document.
  3309. *
  3310. * @param {String} [path]
  3311. * @return {String}
  3312. * @api private
  3313. * @method $__fullPath
  3314. * @memberOf Document
  3315. * @instance
  3316. */
  3317. Document.prototype.$__fullPath = function(path) {
  3318. // overridden in SubDocuments
  3319. return path || '';
  3320. };
  3321. /*!
  3322. * Module exports.
  3323. */
  3324. Document.ValidationError = ValidationError;
  3325. module.exports = exports = Document;