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

model.js 157KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743
  1. 'use strict';
  2. /*!
  3. * Module dependencies.
  4. */
  5. const Aggregate = require('./aggregate');
  6. const ChangeStream = require('./cursor/ChangeStream');
  7. const Document = require('./document');
  8. const DocumentNotFoundError = require('./error').DocumentNotFoundError;
  9. const DivergentArrayError = require('./error').DivergentArrayError;
  10. const Error = require('./error');
  11. const EventEmitter = require('events').EventEmitter;
  12. const MongooseBuffer = require('./types/buffer');
  13. const OverwriteModelError = require('./error').OverwriteModelError;
  14. const PromiseProvider = require('./promise_provider');
  15. const Query = require('./query');
  16. const RemoveOptions = require('./options/removeOptions');
  17. const SaveOptions = require('./options/saveOptions');
  18. const Schema = require('./schema');
  19. const VersionError = require('./error').VersionError;
  20. const ParallelSaveError = require('./error').ParallelSaveError;
  21. const applyQueryMiddleware = require('./helpers/query/applyQueryMiddleware');
  22. const applyHooks = require('./helpers/model/applyHooks');
  23. const applyMethods = require('./helpers/model/applyMethods');
  24. const applyStaticHooks = require('./helpers/model/applyStaticHooks');
  25. const applyStatics = require('./helpers/model/applyStatics');
  26. const applyWriteConcern = require('./helpers/schema/applyWriteConcern');
  27. const assignVals = require('./helpers/populate/assignVals');
  28. const castBulkWrite = require('./helpers/model/castBulkWrite');
  29. const discriminator = require('./helpers/model/discriminator');
  30. const getDiscriminatorByValue = require('./queryhelpers').getDiscriminatorByValue;
  31. const immediate = require('./helpers/immediate');
  32. const internalToObjectOptions = require('./options').internalToObjectOptions;
  33. const isPathExcluded = require('./helpers/projection/isPathExcluded');
  34. const isPathSelectedInclusive = require('./helpers/projection/isPathSelectedInclusive');
  35. const get = require('./helpers/get');
  36. const getSchemaTypes = require('./helpers/populate/getSchemaTypes');
  37. const getVirtual = require('./helpers/populate/getVirtual');
  38. const leanPopulateMap = require('./helpers/populate/leanPopulateMap');
  39. const modifiedPaths = require('./helpers/update/modifiedPaths');
  40. const normalizeRefPath = require('./helpers/populate/normalizeRefPath');
  41. const parallel = require('async/parallel');
  42. const parallelLimit = require('async/parallelLimit');
  43. const util = require('util');
  44. const utils = require('./utils');
  45. const VERSION_WHERE = 1;
  46. const VERSION_INC = 2;
  47. const VERSION_ALL = VERSION_WHERE | VERSION_INC;
  48. const arrayAtomicsSymbol = require('./helpers/symbols').arrayAtomicsSymbol;
  49. const modelCollectionSymbol = Symbol.for('mongoose#Model#collection');
  50. const modelSymbol = require('./helpers/symbols').modelSymbol;
  51. const schemaMixedSymbol = require('./schema/symbols').schemaMixedSymbol;
  52. const subclassedSymbol = Symbol('mongoose#Model#subclassed');
  53. /**
  54. * A Model is a class that's your primary tool for interacting with MongoDB.
  55. * An instance of a Model is called a [Document](./api.html#Document).
  56. *
  57. * In Mongoose, the term "Model" refers to subclasses of the `mongoose.Model`
  58. * class. You should not use the `mongoose.Model` class directly. The
  59. * [`mongoose.model()`](./api.html#mongoose_Mongoose-model) and
  60. * [`connection.model()`](./api.html#connection_Connection-model) functions
  61. * create subclasses of `mongoose.Model` as shown below.
  62. *
  63. * ####Example:
  64. *
  65. * // `UserModel` is a "Model", a subclass of `mongoose.Model`.
  66. * const UserModel = mongoose.model('User', new Schema({ name: String }));
  67. *
  68. * // You can use a Model to create new documents using `new`:
  69. * const userDoc = new UserModel({ name: 'Foo' });
  70. * await userDoc.save();
  71. *
  72. * // You also use a model to create queries:
  73. * const userFromDb = await UserModel.findOne({ name: 'Foo' });
  74. *
  75. * @param {Object} doc values for initial set
  76. * @param [fields] optional object containing the fields that were selected in the query which returned this document. You do **not** need to set this parameter to ensure Mongoose handles your [query projetion](./api.html#query_Query-select).
  77. * @inherits Document http://mongoosejs.com/docs/api.html#document-js
  78. * @event `error`: If listening to this event, 'error' is emitted when a document was saved without passing a callback and an `error` occurred. If not listening, the event bubbles to the connection used to create this Model.
  79. * @event `index`: Emitted after `Model#ensureIndexes` completes. If an error occurred it is passed with the event.
  80. * @event `index-single-start`: Emitted when an individual index starts within `Model#ensureIndexes`. The fields and options being used to build the index are also passed with the event.
  81. * @event `index-single-done`: Emitted when an individual index finishes within `Model#ensureIndexes`. If an error occurred it is passed with the event. The fields, options, and index name are also passed.
  82. * @api public
  83. */
  84. function Model(doc, fields, skipId) {
  85. if (fields instanceof Schema) {
  86. throw new TypeError('2nd argument to `Model` must be a POJO or string, ' +
  87. '**not** a schema. Make sure you\'re calling `mongoose.model()`, not ' +
  88. '`mongoose.Model()`.');
  89. }
  90. Document.call(this, doc, fields, skipId);
  91. }
  92. /*!
  93. * Inherits from Document.
  94. *
  95. * All Model.prototype features are available on
  96. * top level (non-sub) documents.
  97. */
  98. Model.prototype.__proto__ = Document.prototype;
  99. Model.prototype.$isMongooseModelPrototype = true;
  100. /**
  101. * Connection the model uses.
  102. *
  103. * @api public
  104. * @property db
  105. * @memberOf Model
  106. * @instance
  107. */
  108. Model.prototype.db;
  109. /**
  110. * Collection the model uses.
  111. *
  112. * This property is read-only. Modifying this property is a no-op.
  113. *
  114. * @api public
  115. * @property collection
  116. * @memberOf Model
  117. * @instance
  118. */
  119. Model.prototype.collection;
  120. /**
  121. * The name of the model
  122. *
  123. * @api public
  124. * @property modelName
  125. * @memberOf Model
  126. * @instance
  127. */
  128. Model.prototype.modelName;
  129. /**
  130. * Additional properties to attach to the query when calling `save()` and
  131. * `isNew` is false.
  132. *
  133. * @api public
  134. * @property $where
  135. * @memberOf Model
  136. * @instance
  137. */
  138. Model.prototype.$where;
  139. /**
  140. * If this is a discriminator model, `baseModelName` is the name of
  141. * the base model.
  142. *
  143. * @api public
  144. * @property baseModelName
  145. * @memberOf Model
  146. * @instance
  147. */
  148. Model.prototype.baseModelName;
  149. /**
  150. * Event emitter that reports any errors that occurred. Useful for global error
  151. * handling.
  152. *
  153. * ####Example:
  154. *
  155. * MyModel.events.on('error', err => console.log(err.message));
  156. *
  157. * // Prints a 'CastError' because of the above handler
  158. * await MyModel.findOne({ _id: 'notanid' }).catch(noop);
  159. *
  160. * @api public
  161. * @fires error whenever any query or model function errors
  162. * @memberOf Model
  163. * @static events
  164. */
  165. Model.events;
  166. /*!
  167. * Compiled middleware for this model. Set in `applyHooks()`.
  168. *
  169. * @api private
  170. * @property _middleware
  171. * @memberOf Model
  172. * @static
  173. */
  174. Model._middleware;
  175. /*!
  176. * ignore
  177. */
  178. function _applyCustomWhere(doc, where) {
  179. if (doc.$where == null) {
  180. return;
  181. }
  182. const keys = Object.keys(doc.$where);
  183. const len = keys.length;
  184. for (let i = 0; i < len; ++i) {
  185. where[keys[i]] = doc.$where[keys[i]];
  186. }
  187. }
  188. /*!
  189. * ignore
  190. */
  191. Model.prototype.$__handleSave = function(options, callback) {
  192. const _this = this;
  193. let saveOptions = {};
  194. if ('safe' in options) {
  195. _handleSafe(options);
  196. }
  197. applyWriteConcern(this.schema, options);
  198. if ('w' in options) {
  199. saveOptions.w = options.w;
  200. }
  201. if ('j' in options) {
  202. saveOptions.j = options.j;
  203. }
  204. if ('wtimeout' in options) {
  205. saveOptions.wtimeout = options.wtimeout;
  206. }
  207. if ('checkKeys' in options) {
  208. saveOptions.checkKeys = options.checkKeys;
  209. }
  210. const session = this.$session();
  211. if (!saveOptions.hasOwnProperty('session')) {
  212. saveOptions.session = session;
  213. }
  214. if (Object.keys(saveOptions).length === 0) {
  215. saveOptions = null;
  216. }
  217. if (this.isNew) {
  218. // send entire doc
  219. const obj = this.toObject(internalToObjectOptions);
  220. if ((obj || {})._id === void 0) {
  221. // documents must have an _id else mongoose won't know
  222. // what to update later if more changes are made. the user
  223. // wouldn't know what _id was generated by mongodb either
  224. // nor would the ObjectId generated by mongodb necessarily
  225. // match the schema definition.
  226. setTimeout(function() {
  227. callback(new Error('document must have an _id before saving'));
  228. }, 0);
  229. return;
  230. }
  231. this.$__version(true, obj);
  232. this[modelCollectionSymbol].insertOne(obj, saveOptions, function(err, ret) {
  233. if (err) {
  234. _this.isNew = true;
  235. _this.emit('isNew', true);
  236. _this.constructor.emit('isNew', true);
  237. callback(err, null);
  238. return;
  239. }
  240. callback(null, ret);
  241. });
  242. this.$__reset();
  243. this.isNew = false;
  244. this.emit('isNew', false);
  245. this.constructor.emit('isNew', false);
  246. // Make it possible to retry the insert
  247. this.$__.inserting = true;
  248. } else {
  249. // Make sure we don't treat it as a new object on error,
  250. // since it already exists
  251. this.$__.inserting = false;
  252. const delta = this.$__delta();
  253. if (delta) {
  254. if (delta instanceof Error) {
  255. callback(delta);
  256. return;
  257. }
  258. const where = this.$__where(delta[0]);
  259. if (where instanceof Error) {
  260. callback(where);
  261. return;
  262. }
  263. _applyCustomWhere(this, where);
  264. this[modelCollectionSymbol].updateOne(where, delta[1], saveOptions, function(err, ret) {
  265. if (err) {
  266. callback(err);
  267. return;
  268. }
  269. ret.$where = where;
  270. callback(null, ret);
  271. });
  272. } else {
  273. this.$__reset();
  274. callback();
  275. return;
  276. }
  277. this.emit('isNew', false);
  278. this.constructor.emit('isNew', false);
  279. }
  280. };
  281. /*!
  282. * ignore
  283. */
  284. Model.prototype.$__save = function(options, callback) {
  285. this.$__handleSave(options, (error, result) => {
  286. if (error) {
  287. return this.schema.s.hooks.execPost('save:error', this, [this], { error: error }, function(error) {
  288. callback(error);
  289. });
  290. }
  291. // store the modified paths before the document is reset
  292. const modifiedPaths = this.modifiedPaths();
  293. this.$__reset();
  294. let numAffected = 0;
  295. if (get(options, 'safe.w') !== 0 && get(options, 'w') !== 0) {
  296. // Skip checking if write succeeded if writeConcern is set to
  297. // unacknowledged writes, because otherwise `numAffected` will always be 0
  298. if (result) {
  299. if (Array.isArray(result)) {
  300. numAffected = result.length;
  301. } else if (result.result && result.result.n !== undefined) {
  302. numAffected = result.result.n;
  303. } else if (result.result && result.result.nModified !== undefined) {
  304. numAffected = result.result.nModified;
  305. } else {
  306. numAffected = result;
  307. }
  308. }
  309. // was this an update that required a version bump?
  310. if (this.$__.version && !this.$__.inserting) {
  311. const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version);
  312. this.$__.version = undefined;
  313. const key = this.schema.options.versionKey;
  314. const version = this.getValue(key) || 0;
  315. if (numAffected <= 0) {
  316. // the update failed. pass an error back
  317. const err = this.$__.$versionError ||
  318. new VersionError(this, version, modifiedPaths);
  319. return callback(err);
  320. }
  321. // increment version if was successful
  322. if (doIncrement) {
  323. this.setValue(key, version + 1);
  324. }
  325. }
  326. if (result != null && numAffected <= 0) {
  327. error = new DocumentNotFoundError(result.$where);
  328. return this.schema.s.hooks.execPost('save:error', this, [this], { error: error }, function(error) {
  329. callback(error);
  330. });
  331. }
  332. }
  333. this.$__.saving = undefined;
  334. this.emit('save', this, numAffected);
  335. this.constructor.emit('save', this, numAffected);
  336. callback(null, this);
  337. });
  338. };
  339. /*!
  340. * ignore
  341. */
  342. function generateVersionError(doc, modifiedPaths) {
  343. const key = doc.schema.options.versionKey;
  344. if (!key) {
  345. return null;
  346. }
  347. const version = doc.getValue(key) || 0;
  348. return new VersionError(doc, version, modifiedPaths);
  349. }
  350. /**
  351. * Saves this document.
  352. *
  353. * ####Example:
  354. *
  355. * product.sold = Date.now();
  356. * product = await product.save();
  357. *
  358. * If save is successful, the returned promise will fulfill with the document
  359. * saved.
  360. *
  361. * ####Example:
  362. *
  363. * const newProduct = await product.save();
  364. * newProduct === product; // true
  365. *
  366. * @param {Object} [options] options optional options
  367. * @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).
  368. * @param {Object} [options.safe] (DEPRECATED) overrides [schema's safe option](http://mongoosejs.com//docs/guide.html#safe). Use the `w` option instead.
  369. * @param {Boolean} [options.validateBeforeSave] set to false to save without validating.
  370. * @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)
  371. * @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)
  372. * @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).
  373. * @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)
  374. * @param {Boolean} [options.timestamps=true] if `false` and [timestamps](./guide.html#timestamps) are enabled, skip timestamps for this `save()`.
  375. * @param {Function} [fn] optional callback
  376. * @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).
  377. * @return {Promise|undefined} Returns undefined if used with callback or a Promise otherwise.
  378. * @api public
  379. * @see middleware http://mongoosejs.com/docs/middleware.html
  380. */
  381. Model.prototype.save = function(options, fn) {
  382. let parallelSave;
  383. if (this.$__.saving) {
  384. parallelSave = new ParallelSaveError(this);
  385. } else {
  386. this.$__.saving = new ParallelSaveError(this);
  387. }
  388. if (typeof options === 'function') {
  389. fn = options;
  390. options = undefined;
  391. }
  392. if (fn) {
  393. fn = this.constructor.$wrapCallback(fn);
  394. }
  395. options = new SaveOptions(options);
  396. if (options.hasOwnProperty('session')) {
  397. this.$session(options.session);
  398. }
  399. this.$__.$versionError = generateVersionError(this, this.modifiedPaths());
  400. return utils.promiseOrCallback(fn, cb => {
  401. if (parallelSave) {
  402. this.$__handleReject(parallelSave);
  403. return cb(parallelSave);
  404. }
  405. this.$__.saveOptions = options;
  406. this.$__save(options, error => {
  407. this.$__.saving = undefined;
  408. delete this.$__.saveOptions;
  409. if (error) {
  410. this.$__handleReject(error);
  411. return cb(error);
  412. }
  413. cb(null, this);
  414. });
  415. }, this.constructor.events);
  416. };
  417. /*!
  418. * Determines whether versioning should be skipped for the given path
  419. *
  420. * @param {Document} self
  421. * @param {String} path
  422. * @return {Boolean} true if versioning should be skipped for the given path
  423. */
  424. function shouldSkipVersioning(self, path) {
  425. const skipVersioning = self.schema.options.skipVersioning;
  426. if (!skipVersioning) return false;
  427. // Remove any array indexes from the path
  428. path = path.replace(/\.\d+\./, '.');
  429. return skipVersioning[path];
  430. }
  431. /*!
  432. * Apply the operation to the delta (update) clause as
  433. * well as track versioning for our where clause.
  434. *
  435. * @param {Document} self
  436. * @param {Object} where
  437. * @param {Object} delta
  438. * @param {Object} data
  439. * @param {Mixed} val
  440. * @param {String} [operation]
  441. */
  442. function operand(self, where, delta, data, val, op) {
  443. // delta
  444. op || (op = '$set');
  445. if (!delta[op]) delta[op] = {};
  446. delta[op][data.path] = val;
  447. // disabled versioning?
  448. if (self.schema.options.versionKey === false) return;
  449. // path excluded from versioning?
  450. if (shouldSkipVersioning(self, data.path)) return;
  451. // already marked for versioning?
  452. if (VERSION_ALL === (VERSION_ALL & self.$__.version)) return;
  453. switch (op) {
  454. case '$set':
  455. case '$unset':
  456. case '$pop':
  457. case '$pull':
  458. case '$pullAll':
  459. case '$push':
  460. case '$addToSet':
  461. break;
  462. default:
  463. // nothing to do
  464. return;
  465. }
  466. // ensure updates sent with positional notation are
  467. // editing the correct array element.
  468. // only increment the version if an array position changes.
  469. // modifying elements of an array is ok if position does not change.
  470. if (op === '$push' || op === '$addToSet' || op === '$pullAll' || op === '$pull') {
  471. self.$__.version = VERSION_INC;
  472. } else if (/^\$p/.test(op)) {
  473. // potentially changing array positions
  474. self.increment();
  475. } else if (Array.isArray(val)) {
  476. // $set an array
  477. self.increment();
  478. } else if (/\.\d+\.|\.\d+$/.test(data.path)) {
  479. // now handling $set, $unset
  480. // subpath of array
  481. self.$__.version = VERSION_WHERE;
  482. }
  483. }
  484. /*!
  485. * Compiles an update and where clause for a `val` with _atomics.
  486. *
  487. * @param {Document} self
  488. * @param {Object} where
  489. * @param {Object} delta
  490. * @param {Object} data
  491. * @param {Array} value
  492. */
  493. function handleAtomics(self, where, delta, data, value) {
  494. if (delta.$set && delta.$set[data.path]) {
  495. // $set has precedence over other atomics
  496. return;
  497. }
  498. if (typeof value.$__getAtomics === 'function') {
  499. value.$__getAtomics().forEach(function(atomic) {
  500. const op = atomic[0];
  501. const val = atomic[1];
  502. operand(self, where, delta, data, val, op);
  503. });
  504. return;
  505. }
  506. // legacy support for plugins
  507. const atomics = value[arrayAtomicsSymbol];
  508. const ops = Object.keys(atomics);
  509. let i = ops.length;
  510. let val;
  511. let op;
  512. if (i === 0) {
  513. // $set
  514. if (utils.isMongooseObject(value)) {
  515. value = value.toObject({depopulate: 1, _isNested: true});
  516. } else if (value.valueOf) {
  517. value = value.valueOf();
  518. }
  519. return operand(self, where, delta, data, value);
  520. }
  521. function iter(mem) {
  522. return utils.isMongooseObject(mem)
  523. ? mem.toObject({depopulate: 1, _isNested: true})
  524. : mem;
  525. }
  526. while (i--) {
  527. op = ops[i];
  528. val = atomics[op];
  529. if (utils.isMongooseObject(val)) {
  530. val = val.toObject({depopulate: true, transform: false, _isNested: true});
  531. } else if (Array.isArray(val)) {
  532. val = val.map(iter);
  533. } else if (val.valueOf) {
  534. val = val.valueOf();
  535. }
  536. if (op === '$addToSet') {
  537. val = {$each: val};
  538. }
  539. operand(self, where, delta, data, val, op);
  540. }
  541. }
  542. /**
  543. * Produces a special query document of the modified properties used in updates.
  544. *
  545. * @api private
  546. * @method $__delta
  547. * @memberOf Model
  548. * @instance
  549. */
  550. Model.prototype.$__delta = function() {
  551. const dirty = this.$__dirty();
  552. if (!dirty.length && VERSION_ALL !== this.$__.version) {
  553. return;
  554. }
  555. const where = {};
  556. const delta = {};
  557. const len = dirty.length;
  558. const divergent = [];
  559. let d = 0;
  560. where._id = this._doc._id;
  561. // If `_id` is an object, need to depopulate, but also need to be careful
  562. // because `_id` can technically be null (see gh-6406)
  563. if (get(where, '_id.$__', null) != null) {
  564. where._id = where._id.toObject({ transform: false, depopulate: true });
  565. }
  566. for (; d < len; ++d) {
  567. const data = dirty[d];
  568. let value = data.value;
  569. const match = checkDivergentArray(this, data.path, value);
  570. if (match) {
  571. divergent.push(match);
  572. continue;
  573. }
  574. const pop = this.populated(data.path, true);
  575. if (!pop && this.$__.selected) {
  576. // If any array was selected using an $elemMatch projection, we alter the path and where clause
  577. // NOTE: MongoDB only supports projected $elemMatch on top level array.
  578. const pathSplit = data.path.split('.');
  579. const top = pathSplit[0];
  580. if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) {
  581. // If the selected array entry was modified
  582. if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') {
  583. where[top] = this.$__.selected[top];
  584. pathSplit[1] = '$';
  585. data.path = pathSplit.join('.');
  586. }
  587. // if the selected array was modified in any other way throw an error
  588. else {
  589. divergent.push(data.path);
  590. continue;
  591. }
  592. }
  593. }
  594. if (divergent.length) continue;
  595. if (value === undefined) {
  596. operand(this, where, delta, data, 1, '$unset');
  597. } else if (value === null) {
  598. operand(this, where, delta, data, null);
  599. } else if (value.isMongooseArray && value.$path() && value[arrayAtomicsSymbol]) {
  600. // arrays and other custom types (support plugins etc)
  601. handleAtomics(this, where, delta, data, value);
  602. } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) {
  603. // MongooseBuffer
  604. value = value.toObject();
  605. operand(this, where, delta, data, value);
  606. } else {
  607. value = utils.clone(value, {
  608. depopulate: true,
  609. transform: false,
  610. virtuals: false,
  611. getters: false,
  612. _isNested: true
  613. });
  614. operand(this, where, delta, data, value);
  615. }
  616. }
  617. if (divergent.length) {
  618. return new DivergentArrayError(divergent);
  619. }
  620. if (this.$__.version) {
  621. this.$__version(where, delta);
  622. }
  623. return [where, delta];
  624. };
  625. /*!
  626. * Determine if array was populated with some form of filter and is now
  627. * being updated in a manner which could overwrite data unintentionally.
  628. *
  629. * @see https://github.com/Automattic/mongoose/issues/1334
  630. * @param {Document} doc
  631. * @param {String} path
  632. * @return {String|undefined}
  633. */
  634. function checkDivergentArray(doc, path, array) {
  635. // see if we populated this path
  636. const pop = doc.populated(path, true);
  637. if (!pop && doc.$__.selected) {
  638. // If any array was selected using an $elemMatch projection, we deny the update.
  639. // NOTE: MongoDB only supports projected $elemMatch on top level array.
  640. const top = path.split('.')[0];
  641. if (doc.$__.selected[top + '.$']) {
  642. return top;
  643. }
  644. }
  645. if (!(pop && array && array.isMongooseArray)) return;
  646. // If the array was populated using options that prevented all
  647. // documents from being returned (match, skip, limit) or they
  648. // deselected the _id field, $pop and $set of the array are
  649. // not safe operations. If _id was deselected, we do not know
  650. // how to remove elements. $pop will pop off the _id from the end
  651. // of the array in the db which is not guaranteed to be the
  652. // same as the last element we have here. $set of the entire array
  653. // would be similarily destructive as we never received all
  654. // elements of the array and potentially would overwrite data.
  655. const check = pop.options.match ||
  656. pop.options.options && utils.object.hasOwnProperty(pop.options.options, 'limit') || // 0 is not permitted
  657. pop.options.options && pop.options.options.skip || // 0 is permitted
  658. pop.options.select && // deselected _id?
  659. (pop.options.select._id === 0 ||
  660. /\s?-_id\s?/.test(pop.options.select));
  661. if (check) {
  662. const atomics = array[arrayAtomicsSymbol];
  663. if (Object.keys(atomics).length === 0 || atomics.$set || atomics.$pop) {
  664. return path;
  665. }
  666. }
  667. }
  668. /**
  669. * Appends versioning to the where and update clauses.
  670. *
  671. * @api private
  672. * @method $__version
  673. * @memberOf Model
  674. * @instance
  675. */
  676. Model.prototype.$__version = function(where, delta) {
  677. const key = this.schema.options.versionKey;
  678. if (where === true) {
  679. // this is an insert
  680. if (key) this.setValue(key, delta[key] = 0);
  681. return;
  682. }
  683. // updates
  684. // only apply versioning if our versionKey was selected. else
  685. // there is no way to select the correct version. we could fail
  686. // fast here and force them to include the versionKey but
  687. // thats a bit intrusive. can we do this automatically?
  688. if (!this.isSelected(key)) {
  689. return;
  690. }
  691. // $push $addToSet don't need the where clause set
  692. if (VERSION_WHERE === (VERSION_WHERE & this.$__.version)) {
  693. const value = this.getValue(key);
  694. if (value != null) where[key] = value;
  695. }
  696. if (VERSION_INC === (VERSION_INC & this.$__.version)) {
  697. if (get(delta.$set, key, null) != null) {
  698. // Version key is getting set, means we'll increment the doc's version
  699. // after a successful save, so we should set the incremented version so
  700. // future saves don't fail (gh-5779)
  701. ++delta.$set[key];
  702. } else {
  703. delta.$inc = delta.$inc || {};
  704. delta.$inc[key] = 1;
  705. }
  706. }
  707. };
  708. /**
  709. * Signal that we desire an increment of this documents version.
  710. *
  711. * ####Example:
  712. *
  713. * Model.findById(id, function (err, doc) {
  714. * doc.increment();
  715. * doc.save(function (err) { .. })
  716. * })
  717. *
  718. * @see versionKeys http://mongoosejs.com/docs/guide.html#versionKey
  719. * @api public
  720. */
  721. Model.prototype.increment = function increment() {
  722. this.$__.version = VERSION_ALL;
  723. return this;
  724. };
  725. /**
  726. * Returns a query object
  727. *
  728. * @api private
  729. * @method $__where
  730. * @memberOf Model
  731. * @instance
  732. */
  733. Model.prototype.$__where = function _where(where) {
  734. where || (where = {});
  735. if (!where._id) {
  736. where._id = this._doc._id;
  737. }
  738. if (this._doc._id === void 0) {
  739. return new Error('No _id found on document!');
  740. }
  741. return where;
  742. };
  743. /**
  744. * Removes this document from the db.
  745. *
  746. * ####Example:
  747. * product.remove(function (err, product) {
  748. * if (err) return handleError(err);
  749. * Product.findById(product._id, function (err, product) {
  750. * console.log(product) // null
  751. * })
  752. * })
  753. *
  754. *
  755. * As an extra measure of flow control, remove will return a Promise (bound to `fn` if passed) so it could be chained, or hooked to recieve errors
  756. *
  757. * ####Example:
  758. * product.remove().then(function (product) {
  759. * ...
  760. * }).catch(function (err) {
  761. * assert.ok(err)
  762. * })
  763. *
  764. * @param {function(err,product)} [fn] optional callback
  765. * @return {Promise} Promise
  766. * @api public
  767. */
  768. Model.prototype.remove = function remove(options, fn) {
  769. if (typeof options === 'function') {
  770. fn = options;
  771. options = undefined;
  772. }
  773. options = new RemoveOptions(options);
  774. if (options.hasOwnProperty('session')) {
  775. this.$session(options.session);
  776. }
  777. if (fn) {
  778. fn = this.constructor.$wrapCallback(fn);
  779. }
  780. return utils.promiseOrCallback(fn, cb => {
  781. this.$__remove(options, cb);
  782. }, this.constructor.events);
  783. };
  784. /**
  785. * Alias for remove
  786. */
  787. Model.prototype.delete = Model.prototype.remove;
  788. /**
  789. * Removes this document from the db. Equivalent to `.remove()`.
  790. *
  791. * ####Example:
  792. * product = await product.deleteOne();
  793. * await Product.findById(product._id); // null
  794. *
  795. * @param {function(err,product)} [fn] optional callback
  796. * @return {Promise} Promise
  797. * @api public
  798. */
  799. Model.prototype.deleteOne = function deleteOne(options, fn) {
  800. if (typeof options === 'function') {
  801. fn = options;
  802. options = undefined;
  803. }
  804. if (!options) {
  805. options = {};
  806. }
  807. if (fn) {
  808. fn = this.constructor.$wrapCallback(fn);
  809. }
  810. return utils.promiseOrCallback(fn, cb => {
  811. this.$__deleteOne(options, cb);
  812. }, this.constructor.events);
  813. };
  814. /*!
  815. * ignore
  816. */
  817. Model.prototype.$__remove = function $__remove(options, cb) {
  818. if (this.$__.isDeleted) {
  819. return immediate(() => cb(null, this));
  820. }
  821. const where = this.$__where();
  822. if (where instanceof Error) {
  823. return cb(where);
  824. }
  825. _applyCustomWhere(this, where);
  826. const session = this.$session();
  827. if (!options.hasOwnProperty('session')) {
  828. options.session = session;
  829. }
  830. this[modelCollectionSymbol].deleteOne(where, options, err => {
  831. if (!err) {
  832. this.$__.isDeleted = true;
  833. this.emit('remove', this);
  834. this.constructor.emit('remove', this);
  835. return cb(null, this);
  836. }
  837. this.$__.isDeleted = false;
  838. cb(err);
  839. });
  840. };
  841. /*!
  842. * ignore
  843. */
  844. Model.prototype.$__deleteOne = Model.prototype.$__remove;
  845. /**
  846. * Returns another Model instance.
  847. *
  848. * ####Example:
  849. *
  850. * var doc = new Tank;
  851. * doc.model('User').findById(id, callback);
  852. *
  853. * @param {String} name model name
  854. * @api public
  855. */
  856. Model.prototype.model = function model(name) {
  857. return this.db.model(name);
  858. };
  859. /**
  860. * Adds a discriminator type.
  861. *
  862. * ####Example:
  863. *
  864. * function BaseSchema() {
  865. * Schema.apply(this, arguments);
  866. *
  867. * this.add({
  868. * name: String,
  869. * createdAt: Date
  870. * });
  871. * }
  872. * util.inherits(BaseSchema, Schema);
  873. *
  874. * var PersonSchema = new BaseSchema();
  875. * var BossSchema = new BaseSchema({ department: String });
  876. *
  877. * var Person = mongoose.model('Person', PersonSchema);
  878. * var Boss = Person.discriminator('Boss', BossSchema);
  879. * new Boss().__t; // "Boss". `__t` is the default `discriminatorKey`
  880. *
  881. * var employeeSchema = new Schema({ boss: ObjectId });
  882. * var Employee = Person.discriminator('Employee', employeeSchema, 'staff');
  883. * new Employee().__t; // "staff" because of 3rd argument above
  884. *
  885. * @param {String} name discriminator model name
  886. * @param {Schema} schema discriminator model schema
  887. * @param {String} value the string stored in the `discriminatorKey` property
  888. * @api public
  889. */
  890. Model.discriminator = function(name, schema, value) {
  891. let model;
  892. if (typeof name === 'function') {
  893. model = name;
  894. name = utils.getFunctionName(model);
  895. if (!(model.prototype instanceof Model)) {
  896. throw new Error('The provided class ' + name + ' must extend Model');
  897. }
  898. }
  899. schema = discriminator(this, name, schema, value, true);
  900. if (this.db.models[name]) {
  901. throw new OverwriteModelError(name);
  902. }
  903. schema.$isRootDiscriminator = true;
  904. schema.$globalPluginsApplied = true;
  905. model = this.db.model(model || name, schema, this.collection.name);
  906. this.discriminators[name] = model;
  907. const d = this.discriminators[name];
  908. d.prototype.__proto__ = this.prototype;
  909. Object.defineProperty(d, 'baseModelName', {
  910. value: this.modelName,
  911. configurable: true,
  912. writable: false
  913. });
  914. // apply methods and statics
  915. applyMethods(d, schema);
  916. applyStatics(d, schema);
  917. if (this[subclassedSymbol] != null) {
  918. for (const submodel of this[subclassedSymbol]) {
  919. submodel.discriminators = submodel.discriminators || {};
  920. submodel.discriminators[name] =
  921. model.__subclass(model.db, schema, submodel.collection.name);
  922. }
  923. }
  924. return d;
  925. };
  926. // Model (class) features
  927. /*!
  928. * Give the constructor the ability to emit events.
  929. */
  930. for (const i in EventEmitter.prototype) {
  931. Model[i] = EventEmitter.prototype[i];
  932. }
  933. /**
  934. * This function is responsible for building [indexes](https://docs.mongodb.com/manual/indexes/),
  935. * unless [`autoIndex`](http://mongoosejs.com/docs/guide.html#autoIndex) is turned off.
  936. *
  937. * Mongoose calls this function automatically when a model is created using
  938. * [`mongoose.model()`](/docs/api.html#mongoose_Mongoose-model) or
  939. * * [`connection.model()`](/docs/api.html#connection_Connection-model), so you
  940. * don't need to call it. This function is also idempotent, so you may call it
  941. * to get back a promise that will resolve when your indexes are finished
  942. * building as an alternative to [`MyModel.on('index')`](/docs/guide.html#indexes)
  943. *
  944. * ####Example:
  945. *
  946. * var eventSchema = new Schema({ thing: { type: 'string', unique: true }})
  947. * // This calls `Event.init()` implicitly, so you don't need to call
  948. * // `Event.init()` on your own.
  949. * var Event = mongoose.model('Event', eventSchema);
  950. *
  951. * Event.init().then(function(Event) {
  952. * // You can also use `Event.on('index')` if you prefer event emitters
  953. * // over promises.
  954. * console.log('Indexes are done building!');
  955. * });
  956. *
  957. * @api public
  958. * @param {Function} [callback]
  959. * @returns {Promise}
  960. */
  961. Model.init = function init(callback) {
  962. this.schema.emit('init', this);
  963. if (this.$init != null) {
  964. if (callback) {
  965. this.$init.then(() => callback(), err => callback(err));
  966. return null;
  967. }
  968. return this.$init;
  969. }
  970. const Promise = PromiseProvider.get();
  971. const autoIndex = this.schema.options.autoIndex == null ?
  972. this.db.config.autoIndex :
  973. this.schema.options.autoIndex;
  974. const autoCreate = this.schema.options.autoCreate == null ?
  975. this.db.config.autoCreate :
  976. this.schema.options.autoCreate;
  977. const _ensureIndexes = autoIndex ?
  978. cb => this.ensureIndexes({ _automatic: true }, cb) :
  979. cb => cb();
  980. const _createCollection = autoCreate ?
  981. cb => this.createCollection({}, cb) :
  982. cb => cb();
  983. this.$init = new Promise((resolve, reject) => {
  984. _createCollection(error => {
  985. if (error) {
  986. return reject(error);
  987. }
  988. _ensureIndexes(error => {
  989. if (error) {
  990. return reject(error);
  991. }
  992. resolve(this);
  993. });
  994. });
  995. });
  996. if (callback) {
  997. this.$init.then(() => callback(), err => callback(err));
  998. this.$caught = true;
  999. return null;
  1000. } else {
  1001. const _catch = this.$init.catch;
  1002. const _this = this;
  1003. this.$init.catch = function() {
  1004. this.$caught = true;
  1005. return _catch.apply(_this.$init, arguments);
  1006. };
  1007. }
  1008. return this.$init;
  1009. };
  1010. /**
  1011. * Create the collection for this model. By default, if no indexes are specified,
  1012. * mongoose will not create the collection for the model until any documents are
  1013. * created. Use this method to create the collection explicitly.
  1014. *
  1015. * Note 1: You may need to call this before starting a transaction
  1016. * See https://docs.mongodb.com/manual/core/transactions/#transactions-and-operations
  1017. *
  1018. * Note 2: You don't have to call this if your schema contains index or unique field.
  1019. * In that case, just use `Model.init()`
  1020. *
  1021. * ####Example:
  1022. *
  1023. * var userSchema = new Schema({ name: String })
  1024. * var User = mongoose.model('User', userSchema);
  1025. *
  1026. * User.createCollection().then(function(collection) {
  1027. * console.log('Collection is created!');
  1028. * });
  1029. *
  1030. * @api public
  1031. * @param {Object} [options] see [MongoDB driver docs](http://mongodb.github.io/node-mongodb-native/3.1/api/Db.html#createCollection)
  1032. * @param {Function} [callback]
  1033. * @returns {Promise}
  1034. */
  1035. Model.createCollection = function createCollection(options, callback) {
  1036. if (typeof options === 'string') {
  1037. throw new Error('You can\'t specify a new collection name in Model.createCollection.' +
  1038. 'This is not like Connection.createCollection. Only options are accepted here.');
  1039. } else if (typeof options === 'function') {
  1040. callback = options;
  1041. options = null;
  1042. }
  1043. if (callback) {
  1044. callback = this.$wrapCallback(callback);
  1045. }
  1046. const schemaCollation = get(this, 'schema.options.collation', null);
  1047. if (schemaCollation != null) {
  1048. options = Object.assign({ collation: schemaCollation }, options);
  1049. }
  1050. return utils.promiseOrCallback(callback, cb => {
  1051. this.db.createCollection(this.collection.collectionName, options, utils.tick((error) => {
  1052. if (error) {
  1053. return cb(error);
  1054. }
  1055. this.collection = this.db.collection(this.collection.collectionName, options);
  1056. cb(null, this.collection);
  1057. }));
  1058. }, this.events);
  1059. };
  1060. /**
  1061. * Makes the indexes in MongoDB match the indexes defined in this model's
  1062. * schema. This function will drop any indexes that are not defined in
  1063. * the model's schema except the `_id` index, and build any indexes that
  1064. * are in your schema but not in MongoDB.
  1065. *
  1066. * See the [introductory blog post](http://thecodebarbarian.com/whats-new-in-mongoose-5-2-syncindexes)
  1067. * for more information.
  1068. *
  1069. * ####Example:
  1070. *
  1071. * const schema = new Schema({ name: { type: String, unique: true } });
  1072. * const Customer = mongoose.model('Customer', schema);
  1073. * await Customer.createIndex({ age: 1 }); // Index is not in schema
  1074. * // Will drop the 'age' index and create an index on `name`
  1075. * await Customer.syncIndexes();
  1076. *
  1077. * @param {Object} [options] options to pass to `ensureIndexes()`
  1078. * @param {Function} [callback] optional callback
  1079. * @return {Promise|undefined} Returns `undefined` if callback is specified, returns a promise if no callback.
  1080. * @api public
  1081. */
  1082. Model.syncIndexes = function syncIndexes(options, callback) {
  1083. callback = this.$wrapCallback(callback);
  1084. const dropNonSchemaIndexes = (cb) => {
  1085. this.listIndexes((err, indexes) => {
  1086. if (err != null) {
  1087. return cb(err);
  1088. }
  1089. const schemaIndexes = this.schema.indexes();
  1090. const toDrop = [];
  1091. for (const index of indexes) {
  1092. let found = false;
  1093. // Never try to drop `_id` index, MongoDB server doesn't allow it
  1094. if (index.key._id) {
  1095. continue;
  1096. }
  1097. for (const schemaIndex of schemaIndexes) {
  1098. const key = schemaIndex[0];
  1099. const options = _decorateDiscriminatorIndexOptions(this,
  1100. utils.clone(schemaIndex[1]));
  1101. // If these options are different, need to rebuild the index
  1102. const optionKeys = ['unique', 'partialFilterExpression', 'sparse', 'expireAfterSeconds'];
  1103. const indexCopy = Object.assign({}, index);
  1104. for (const key of optionKeys) {
  1105. if (!(key in options) && !(key in indexCopy)) {
  1106. continue;
  1107. }
  1108. indexCopy[key] = options[key];
  1109. }
  1110. if (utils.deepEqual(key, index.key) &&
  1111. utils.deepEqual(index, indexCopy)) {
  1112. found = true;
  1113. break;
  1114. }
  1115. }
  1116. if (!found) {
  1117. toDrop.push(index.name);
  1118. }
  1119. }
  1120. if (toDrop.length === 0) {
  1121. return cb(null, []);
  1122. }
  1123. dropIndexes(toDrop, cb);
  1124. });
  1125. };
  1126. const dropIndexes = (toDrop, cb) => {
  1127. let remaining = toDrop.length;
  1128. let error = false;
  1129. toDrop.forEach(indexName => {
  1130. this.collection.dropIndex(indexName, err => {
  1131. if (err != null) {
  1132. error = true;
  1133. return cb(err);
  1134. }
  1135. if (!error) {
  1136. --remaining || cb(null, toDrop);
  1137. }
  1138. });
  1139. });
  1140. };
  1141. return utils.promiseOrCallback(callback, cb => {
  1142. dropNonSchemaIndexes((err, dropped) => {
  1143. if (err != null) {
  1144. return cb(err);
  1145. }
  1146. this.createIndexes(options, err => {
  1147. if (err != null) {
  1148. return cb(err);
  1149. }
  1150. cb(null, dropped);
  1151. });
  1152. });
  1153. }, this.events);
  1154. };
  1155. /**
  1156. * Lists the indexes currently defined in MongoDB. This may or may not be
  1157. * the same as the indexes defined in your schema depending on whether you
  1158. * use the [`autoIndex` option](/docs/guide.html#autoIndex) and if you
  1159. * build indexes manually.
  1160. *
  1161. * @param {Function} [cb] optional callback
  1162. * @return {Promise|undefined} Returns `undefined` if callback is specified, returns a promise if no callback.
  1163. * @api public
  1164. */
  1165. Model.listIndexes = function init(callback) {
  1166. callback = this.$wrapCallback(callback);
  1167. const _listIndexes = cb => {
  1168. this.collection.listIndexes().toArray(cb);
  1169. };
  1170. return utils.promiseOrCallback(callback, cb => {
  1171. // Buffering
  1172. if (this.collection.buffer) {
  1173. this.collection.addQueue(_listIndexes, [cb]);
  1174. } else {
  1175. _listIndexes(cb);
  1176. }
  1177. }, this.events);
  1178. };
  1179. /**
  1180. * Sends `createIndex` commands to mongo for each index declared in the schema.
  1181. * The `createIndex` commands are sent in series.
  1182. *
  1183. * ####Example:
  1184. *
  1185. * Event.ensureIndexes(function (err) {
  1186. * if (err) return handleError(err);
  1187. * });
  1188. *
  1189. * After completion, an `index` event is emitted on this `Model` passing an error if one occurred.
  1190. *
  1191. * ####Example:
  1192. *
  1193. * var eventSchema = new Schema({ thing: { type: 'string', unique: true }})
  1194. * var Event = mongoose.model('Event', eventSchema);
  1195. *
  1196. * Event.on('index', function (err) {
  1197. * if (err) console.error(err); // error occurred during index creation
  1198. * })
  1199. *
  1200. * _NOTE: It is not recommended that you run this in production. Index creation may impact database performance depending on your load. Use with caution._
  1201. *
  1202. * @param {Object} [options] internal options
  1203. * @param {Function} [cb] optional callback
  1204. * @return {Promise}
  1205. * @api public
  1206. */
  1207. Model.ensureIndexes = function ensureIndexes(options, callback) {
  1208. if (typeof options === 'function') {
  1209. callback = options;
  1210. options = null;
  1211. }
  1212. if (callback) {
  1213. callback = this.$wrapCallback(callback);
  1214. }
  1215. return utils.promiseOrCallback(callback, cb => {
  1216. _ensureIndexes(this, options || {}, error => {
  1217. if (error) {
  1218. return cb(error);
  1219. }
  1220. cb(null);
  1221. });
  1222. }, this.events);
  1223. };
  1224. /**
  1225. * Similar to `ensureIndexes()`, except for it uses the [`createIndex`](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#createIndex)
  1226. * function.
  1227. *
  1228. * @param {Object} [options] internal options
  1229. * @param {Function} [cb] optional callback
  1230. * @return {Promise}
  1231. * @api public
  1232. */
  1233. Model.createIndexes = function createIndexes(options, callback) {
  1234. if (typeof options === 'function') {
  1235. callback = options;
  1236. options = {};
  1237. }
  1238. options = options || {};
  1239. options.createIndex = true;
  1240. return this.ensureIndexes(options, callback);
  1241. };
  1242. /*!
  1243. * ignore
  1244. */
  1245. function _ensureIndexes(model, options, callback) {
  1246. const indexes = model.schema.indexes();
  1247. options = options || {};
  1248. const done = function(err) {
  1249. if (err && !model.$caught) {
  1250. model.emit('error', err);
  1251. }
  1252. model.emit('index', err);
  1253. callback && callback(err);
  1254. };
  1255. for (const index of indexes) {
  1256. const keys = Object.keys(index[0]);
  1257. if (keys.length === 1 && keys[0] === '_id' && index[0]._id !== 'hashed') {
  1258. console.warn('mongoose: Cannot specify a custom index on `_id` for ' +
  1259. 'model name "' + model.modelName + '", ' +
  1260. 'MongoDB does not allow overwriting the default `_id` index. See ' +
  1261. 'http://bit.ly/mongodb-id-index');
  1262. }
  1263. }
  1264. if (!indexes.length) {
  1265. immediate(function() {
  1266. done();
  1267. });
  1268. return;
  1269. }
  1270. // Indexes are created one-by-one to support how MongoDB < 2.4 deals
  1271. // with background indexes.
  1272. const indexSingleDone = function(err, fields, options, name) {
  1273. model.emit('index-single-done', err, fields, options, name);
  1274. };
  1275. const indexSingleStart = function(fields, options) {
  1276. model.emit('index-single-start', fields, options);
  1277. };
  1278. const create = function() {
  1279. if (options._automatic) {
  1280. if (model.schema.options.autoIndex === false ||
  1281. (model.schema.options.autoIndex == null && model.db.config.autoIndex === false)) {
  1282. return done();
  1283. }
  1284. }
  1285. const index = indexes.shift();
  1286. if (!index) {
  1287. return done();
  1288. }
  1289. const indexFields = utils.clone(index[0]);
  1290. const indexOptions = utils.clone(index[1]);
  1291. _decorateDiscriminatorIndexOptions(model, indexOptions);
  1292. if ('safe' in options) {
  1293. _handleSafe(options);
  1294. }
  1295. applyWriteConcern(model.schema, indexOptions);
  1296. indexSingleStart(indexFields, options);
  1297. let useCreateIndex = !!model.base.options.useCreateIndex;
  1298. if ('useCreateIndex' in model.db.config) {
  1299. useCreateIndex = !!model.db.config.useCreateIndex;
  1300. }
  1301. if ('createIndex' in options) {
  1302. useCreateIndex = !!options.createIndex;
  1303. }
  1304. const methodName = useCreateIndex ? 'createIndex' : 'ensureIndex';
  1305. model.collection[methodName](indexFields, indexOptions, utils.tick(function(err, name) {
  1306. indexSingleDone(err, indexFields, indexOptions, name);
  1307. if (err) {
  1308. return done(err);
  1309. }
  1310. create();
  1311. }));
  1312. };
  1313. immediate(function() {
  1314. // If buffering is off, do this manually.
  1315. if (options._automatic && !model.collection.collection) {
  1316. model.collection.addQueue(create, []);
  1317. } else {
  1318. create();
  1319. }
  1320. });
  1321. }
  1322. function _decorateDiscriminatorIndexOptions(model, indexOptions) {
  1323. // If the model is a discriminator and it has a unique index, add a
  1324. // partialFilterExpression by default so the unique index will only apply
  1325. // to that discriminator.
  1326. if (model.baseModelName != null && indexOptions.unique &&
  1327. !('partialFilterExpression' in indexOptions) &&
  1328. !('sparse' in indexOptions)) {
  1329. const value = (
  1330. model.schema.discriminatorMapping &&
  1331. model.schema.discriminatorMapping.value
  1332. ) || model.modelName;
  1333. indexOptions.partialFilterExpression = {
  1334. [model.schema.options.discriminatorKey]: value
  1335. };
  1336. }
  1337. return indexOptions;
  1338. }
  1339. const safeDeprecationWarning = 'Mongoose: the `safe` option for `save()` is ' +
  1340. 'deprecated. Use the `w` option instead: http://bit.ly/mongoose-save';
  1341. const _handleSafe = util.deprecate(function _handleSafe(options) {
  1342. if (options.safe) {
  1343. if (typeof options.safe === 'boolean') {
  1344. options.w = options.safe;
  1345. delete options.safe;
  1346. }
  1347. if (typeof options.safe === 'object') {
  1348. options.w = options.safe.w;
  1349. options.j = options.safe.j;
  1350. options.wtimeout = options.safe.wtimeout;
  1351. delete options.safe;
  1352. }
  1353. }
  1354. }, safeDeprecationWarning);
  1355. /**
  1356. * Schema the model uses.
  1357. *
  1358. * @property schema
  1359. * @receiver Model
  1360. * @api public
  1361. * @memberOf Model
  1362. */
  1363. Model.schema;
  1364. /*!
  1365. * Connection instance the model uses.
  1366. *
  1367. * @property db
  1368. * @api public
  1369. * @memberOf Model
  1370. */
  1371. Model.db;
  1372. /*!
  1373. * Collection the model uses.
  1374. *
  1375. * @property collection
  1376. * @api public
  1377. * @memberOf Model
  1378. */
  1379. Model.collection;
  1380. /**
  1381. * Base Mongoose instance the model uses.
  1382. *
  1383. * @property base
  1384. * @api public
  1385. * @memberOf Model
  1386. */
  1387. Model.base;
  1388. /**
  1389. * Registered discriminators for this model.
  1390. *
  1391. * @property discriminators
  1392. * @api public
  1393. * @memberOf Model
  1394. */
  1395. Model.discriminators;
  1396. /**
  1397. * Translate any aliases fields/conditions so the final query or document object is pure
  1398. *
  1399. * ####Example:
  1400. *
  1401. * Character
  1402. * .find(Character.translateAliases({
  1403. * '名': 'Eddard Stark' // Alias for 'name'
  1404. * })
  1405. * .exec(function(err, characters) {})
  1406. *
  1407. * ####Note:
  1408. * Only translate arguments of object type anything else is returned raw
  1409. *
  1410. * @param {Object} raw fields/conditions that may contain aliased keys
  1411. * @return {Object} the translated 'pure' fields/conditions
  1412. */
  1413. Model.translateAliases = function translateAliases(fields) {
  1414. const translate = (key, value) => {
  1415. let alias;
  1416. const translated = [];
  1417. const fieldKeys = key.split('.');
  1418. let currentSchema = this.schema;
  1419. for (const i in fieldKeys) {
  1420. const name = fieldKeys[i];
  1421. if (currentSchema && currentSchema.aliases[name]) {
  1422. alias = currentSchema.aliases[name];
  1423. // Alias found,
  1424. translated.push(alias);
  1425. } else {
  1426. // Alias not found, so treat as un-aliased key
  1427. translated.push(name);
  1428. }
  1429. // Check if aliased path is a schema
  1430. if (currentSchema && currentSchema.paths[alias]) {
  1431. currentSchema = currentSchema.paths[alias].schema;
  1432. }
  1433. else
  1434. currentSchema = null;
  1435. }
  1436. const translatedKey = translated.join('.');
  1437. if (fields instanceof Map)
  1438. fields.set(translatedKey, value);
  1439. else
  1440. fields[translatedKey] = value;
  1441. if (translatedKey !== key) {
  1442. // We'll be using the translated key instead
  1443. if (fields instanceof Map) {
  1444. // Delete from map
  1445. fields.delete(key);
  1446. } else {
  1447. // Delete from object
  1448. delete fields[key]; // We'll be using the translated key instead
  1449. }
  1450. }
  1451. return fields;
  1452. };
  1453. if (typeof fields === 'object') {
  1454. // Fields is an object (query conditions or document fields)
  1455. if (fields instanceof Map) {
  1456. // A Map was supplied
  1457. for (const field of new Map(fields)) {
  1458. fields = translate(field[0], field[1]);
  1459. }
  1460. } else {
  1461. // Infer a regular object was supplied
  1462. for (const key of Object.keys(fields)) {
  1463. fields = translate(key, fields[key]);
  1464. if (key[0] === '$') {
  1465. if (Array.isArray(fields[key])) {
  1466. for (const i in fields[key]) {
  1467. // Recursively translate nested queries
  1468. fields[key][i] = this.translateAliases(fields[key][i]);
  1469. }
  1470. }
  1471. }
  1472. }
  1473. }
  1474. return fields;
  1475. } else {
  1476. // Don't know typeof fields
  1477. return fields;
  1478. }
  1479. };
  1480. /**
  1481. * Removes all documents that match `conditions` from the collection.
  1482. * To remove just the first document that matches `conditions`, set the `single`
  1483. * option to true.
  1484. *
  1485. * ####Example:
  1486. *
  1487. * const res = await Character.remove({ name: 'Eddard Stark' });
  1488. * res.deletedCount; // Number of documents removed
  1489. *
  1490. * ####Note:
  1491. *
  1492. * This method sends a remove command directly to MongoDB, no Mongoose documents
  1493. * are involved. Because no Mongoose documents are involved, Mongoose does
  1494. * not execute [document middleware](/docs/middleware.html#types-of-middleware).
  1495. *
  1496. * @param {Object} conditions
  1497. * @param {Function} [callback]
  1498. * @return {Query}
  1499. * @api public
  1500. */
  1501. Model.remove = function remove(conditions, callback) {
  1502. if (typeof conditions === 'function') {
  1503. callback = conditions;
  1504. conditions = {};
  1505. }
  1506. // get the mongodb collection object
  1507. const mq = new this.Query({}, {}, this, this.collection);
  1508. callback = this.$wrapCallback(callback);
  1509. return mq.remove(conditions, callback);
  1510. };
  1511. /**
  1512. * Deletes the first document that matches `conditions` from the collection.
  1513. * Behaves like `remove()`, but deletes at most one document regardless of the
  1514. * `single` option.
  1515. *
  1516. * ####Example:
  1517. *
  1518. * Character.deleteOne({ name: 'Eddard Stark' }, function (err) {});
  1519. *
  1520. * ####Note:
  1521. *
  1522. * Like `Model.remove()`, this function does **not** trigger `pre('remove')` or `post('remove')` hooks.
  1523. *
  1524. * @param {Object} conditions
  1525. * @param {Function} [callback]
  1526. * @return {Query}
  1527. * @api public
  1528. */
  1529. Model.deleteOne = function deleteOne(conditions, callback) {
  1530. if (typeof conditions === 'function') {
  1531. callback = conditions;
  1532. conditions = {};
  1533. }
  1534. // get the mongodb collection object
  1535. const mq = new this.Query(conditions, {}, this, this.collection);
  1536. callback = this.$wrapCallback(callback);
  1537. return mq.deleteOne(callback);
  1538. };
  1539. /**
  1540. * Deletes all of the documents that match `conditions` from the collection.
  1541. * Behaves like `remove()`, but deletes all documents that match `conditions`
  1542. * regardless of the `single` option.
  1543. *
  1544. * ####Example:
  1545. *
  1546. * Character.deleteMany({ name: /Stark/, age: { $gte: 18 } }, function (err) {});
  1547. *
  1548. * ####Note:
  1549. *
  1550. * Like `Model.remove()`, this function does **not** trigger `pre('remove')` or `post('remove')` hooks.
  1551. *
  1552. * @param {Object} conditions
  1553. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
  1554. * @param {Function} [callback]
  1555. * @return {Query}
  1556. * @api public
  1557. */
  1558. Model.deleteMany = function deleteMany(conditions, options, callback) {
  1559. if (typeof conditions === 'function') {
  1560. callback = conditions;
  1561. conditions = {};
  1562. options = null;
  1563. }
  1564. else if (typeof options === 'function') {
  1565. callback = options;
  1566. options = null;
  1567. }
  1568. // get the mongodb collection object
  1569. const mq = new this.Query(conditions, {}, this, this.collection);
  1570. mq.setOptions(options);
  1571. if (callback) {
  1572. callback = this.$wrapCallback(callback);
  1573. }
  1574. return mq.deleteMany(callback);
  1575. };
  1576. /**
  1577. * Finds documents
  1578. *
  1579. * The `conditions` are cast to their respective SchemaTypes before the command is sent.
  1580. *
  1581. * ####Examples:
  1582. *
  1583. * // named john and at least 18
  1584. * MyModel.find({ name: 'john', age: { $gte: 18 }});
  1585. *
  1586. * // executes, passing results to callback
  1587. * MyModel.find({ name: 'john', age: { $gte: 18 }}, function (err, docs) {});
  1588. *
  1589. * // executes, name LIKE john and only selecting the "name" and "friends" fields
  1590. * MyModel.find({ name: /john/i }, 'name friends', function (err, docs) { })
  1591. *
  1592. * // passing options
  1593. * MyModel.find({ name: /john/i }, null, { skip: 10 })
  1594. *
  1595. * // passing options and executes
  1596. * MyModel.find({ name: /john/i }, null, { skip: 10 }, function (err, docs) {});
  1597. *
  1598. * // executing a query explicitly
  1599. * var query = MyModel.find({ name: /john/i }, null, { skip: 10 })
  1600. * query.exec(function (err, docs) {});
  1601. *
  1602. * // using the promise returned from executing a query
  1603. * var query = MyModel.find({ name: /john/i }, null, { skip: 10 });
  1604. * var promise = query.exec();
  1605. * promise.addBack(function (err, docs) {});
  1606. *
  1607. * @param {Object} conditions
  1608. * @param {Object|String} [projection] optional fields to return, see [`Query.prototype.select()`](#query_Query-select)
  1609. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
  1610. * @param {Function} [callback]
  1611. * @return {Query}
  1612. * @see field selection #query_Query-select
  1613. * @see promise #promise-js
  1614. * @api public
  1615. */
  1616. Model.find = function find(conditions, projection, options, callback) {
  1617. if (typeof conditions === 'function') {
  1618. callback = conditions;
  1619. conditions = {};
  1620. projection = null;
  1621. options = null;
  1622. } else if (typeof projection === 'function') {
  1623. callback = projection;
  1624. projection = null;
  1625. options = null;
  1626. } else if (typeof options === 'function') {
  1627. callback = options;
  1628. options = null;
  1629. }
  1630. const mq = new this.Query({}, {}, this, this.collection);
  1631. mq.select(projection);
  1632. mq.setOptions(options);
  1633. if (this.schema.discriminatorMapping &&
  1634. this.schema.discriminatorMapping.isRoot &&
  1635. mq.selectedInclusively()) {
  1636. // Need to select discriminator key because original schema doesn't have it
  1637. mq.select(this.schema.options.discriminatorKey);
  1638. }
  1639. if (callback) {
  1640. callback = this.$wrapCallback(callback);
  1641. }
  1642. return mq.find(conditions, callback);
  1643. };
  1644. /**
  1645. * Finds a single document by its _id field. `findById(id)` is almost*
  1646. * equivalent to `findOne({ _id: id })`. If you want to query by a document's
  1647. * `_id`, use `findById()` instead of `findOne()`.
  1648. *
  1649. * The `id` is cast based on the Schema before sending the command.
  1650. *
  1651. * This function triggers the following middleware.
  1652. *
  1653. * - `findOne()`
  1654. *
  1655. * \* Except for how it treats `undefined`. If you use `findOne()`, you'll see
  1656. * that `findOne(undefined)` and `findOne({ _id: undefined })` are equivalent
  1657. * to `findOne({})` and return arbitrary documents. However, mongoose
  1658. * translates `findById(undefined)` into `findOne({ _id: null })`.
  1659. *
  1660. * ####Example:
  1661. *
  1662. * // find adventure by id and execute
  1663. * Adventure.findById(id, function (err, adventure) {});
  1664. *
  1665. * // same as above
  1666. * Adventure.findById(id).exec(callback);
  1667. *
  1668. * // select only the adventures name and length
  1669. * Adventure.findById(id, 'name length', function (err, adventure) {});
  1670. *
  1671. * // same as above
  1672. * Adventure.findById(id, 'name length').exec(callback);
  1673. *
  1674. * // include all properties except for `length`
  1675. * Adventure.findById(id, '-length').exec(function (err, adventure) {});
  1676. *
  1677. * // passing options (in this case return the raw js objects, not mongoose documents by passing `lean`
  1678. * Adventure.findById(id, 'name', { lean: true }, function (err, doc) {});
  1679. *
  1680. * // same as above
  1681. * Adventure.findById(id, 'name').lean().exec(function (err, doc) {});
  1682. *
  1683. * @param {Object|String|Number} id value of `_id` to query by
  1684. * @param {Object|String} [projection] optional fields to return, see [`Query.prototype.select()`](#query_Query-select)
  1685. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
  1686. * @param {Function} [callback]
  1687. * @return {Query}
  1688. * @see field selection #query_Query-select
  1689. * @see lean queries /docs/tutorials/lean.html
  1690. * @api public
  1691. */
  1692. Model.findById = function findById(id, projection, options, callback) {
  1693. if (typeof id === 'undefined') {
  1694. id = null;
  1695. }
  1696. if (callback) {
  1697. callback = this.$wrapCallback(callback);
  1698. }
  1699. return this.findOne({_id: id}, projection, options, callback);
  1700. };
  1701. /**
  1702. * Finds one document.
  1703. *
  1704. * The `conditions` are cast to their respective SchemaTypes before the command is sent.
  1705. *
  1706. * *Note:* `conditions` is optional, and if `conditions` is null or undefined,
  1707. * mongoose will send an empty `findOne` command to MongoDB, which will return
  1708. * an arbitrary document. If you're querying by `_id`, use `findById()` instead.
  1709. *
  1710. * ####Example:
  1711. *
  1712. * // find one iphone adventures - iphone adventures??
  1713. * Adventure.findOne({ type: 'iphone' }, function (err, adventure) {});
  1714. *
  1715. * // same as above
  1716. * Adventure.findOne({ type: 'iphone' }).exec(function (err, adventure) {});
  1717. *
  1718. * // select only the adventures name
  1719. * Adventure.findOne({ type: 'iphone' }, 'name', function (err, adventure) {});
  1720. *
  1721. * // same as above
  1722. * Adventure.findOne({ type: 'iphone' }, 'name').exec(function (err, adventure) {});
  1723. *
  1724. * // specify options, in this case lean
  1725. * Adventure.findOne({ type: 'iphone' }, 'name', { lean: true }, callback);
  1726. *
  1727. * // same as above
  1728. * Adventure.findOne({ type: 'iphone' }, 'name', { lean: true }).exec(callback);
  1729. *
  1730. * // chaining findOne queries (same as above)
  1731. * Adventure.findOne({ type: 'iphone' }).select('name').lean().exec(callback);
  1732. *
  1733. * @param {Object} [conditions]
  1734. * @param {Object|String} [projection] optional fields to return, see [`Query.prototype.select()`](#query_Query-select)
  1735. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
  1736. * @param {Function} [callback]
  1737. * @return {Query}
  1738. * @see field selection #query_Query-select
  1739. * @see lean queries /docs/tutorials/lean.html
  1740. * @api public
  1741. */
  1742. Model.findOne = function findOne(conditions, projection, options, callback) {
  1743. if (typeof options === 'function') {
  1744. callback = options;
  1745. options = null;
  1746. } else if (typeof projection === 'function') {
  1747. callback = projection;
  1748. projection = null;
  1749. options = null;
  1750. } else if (typeof conditions === 'function') {
  1751. callback = conditions;
  1752. conditions = {};
  1753. projection = null;
  1754. options = null;
  1755. }
  1756. // get the mongodb collection object
  1757. const mq = new this.Query({}, {}, this, this.collection);
  1758. mq.select(projection);
  1759. mq.setOptions(options);
  1760. if (this.schema.discriminatorMapping &&
  1761. this.schema.discriminatorMapping.isRoot &&
  1762. mq.selectedInclusively()) {
  1763. mq.select(this.schema.options.discriminatorKey);
  1764. }
  1765. if (callback) {
  1766. callback = this.$wrapCallback(callback);
  1767. }
  1768. return mq.findOne(conditions, callback);
  1769. };
  1770. /**
  1771. * Estimates the number of documents in the MongoDB collection. Faster than
  1772. * using `countDocuments()` for large collections because
  1773. * `estimatedDocumentCount()` uses collection metadata rather than scanning
  1774. * the entire collection.
  1775. *
  1776. * ####Example:
  1777. *
  1778. * const numAdventures = Adventure.estimatedDocumentCount();
  1779. *
  1780. * @param {Object} [options]
  1781. * @param {Function} [callback]
  1782. * @return {Query}
  1783. * @api public
  1784. */
  1785. Model.estimatedDocumentCount = function estimatedDocumentCount(options, callback) {
  1786. // get the mongodb collection object
  1787. const mq = new this.Query({}, {}, this, this.collection);
  1788. callback = this.$wrapCallback(callback);
  1789. return mq.estimatedDocumentCount(options, callback);
  1790. };
  1791. /**
  1792. * Counts number of documents matching `filter` in a database collection.
  1793. *
  1794. * ####Example:
  1795. *
  1796. * Adventure.countDocuments({ type: 'jungle' }, function (err, count) {
  1797. * console.log('there are %d jungle adventures', count);
  1798. * });
  1799. *
  1800. * If you want to count all documents in a large collection,
  1801. * use the [`estimatedDocumentCount()` function](/docs/api.html#model_Model.estimatedDocumentCount)
  1802. * instead. If you call `countDocuments({})`, MongoDB will always execute
  1803. * a full collection scan and **not** use any indexes.
  1804. *
  1805. * The `countDocuments()` function is similar to `count()`, but there are a
  1806. * [few operators that `countDocuments()` does not support](https://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#countDocuments).
  1807. * Below are the operators that `count()` supports but `countDocuments()` does not,
  1808. * and the suggested replacement:
  1809. *
  1810. * - `$where`: [`$expr`](https://docs.mongodb.com/manual/reference/operator/query/expr/)
  1811. * - `$near`: [`$geoWithin`](https://docs.mongodb.com/manual/reference/operator/query/geoWithin/) with [`$center`](https://docs.mongodb.com/manual/reference/operator/query/center/#op._S_center)
  1812. * - `$nearSphere`: [`$geoWithin`](https://docs.mongodb.com/manual/reference/operator/query/geoWithin/) with [`$centerSphere`](https://docs.mongodb.com/manual/reference/operator/query/centerSphere/#op._S_centerSphere)
  1813. *
  1814. * @param {Object} filter
  1815. * @param {Function} [callback]
  1816. * @return {Query}
  1817. * @api public
  1818. */
  1819. Model.countDocuments = function countDocuments(conditions, callback) {
  1820. if (typeof conditions === 'function') {
  1821. callback = conditions;
  1822. conditions = {};
  1823. }
  1824. // get the mongodb collection object
  1825. const mq = new this.Query({}, {}, this, this.collection);
  1826. callback = this.$wrapCallback(callback);
  1827. return mq.countDocuments(conditions, callback);
  1828. };
  1829. /**
  1830. * Counts number of documents that match `filter` in a database collection.
  1831. *
  1832. * This method is deprecated. If you want to count the number of documents in
  1833. * a collection, e.g. `count({})`, use the [`estimatedDocumentCount()` function](/docs/api.html#model_Model.estimatedDocumentCount)
  1834. * instead. Otherwise, use the [`countDocuments()`](/docs/api.html#model_Model.countDocuments) function instead.
  1835. *
  1836. * ####Example:
  1837. *
  1838. * Adventure.count({ type: 'jungle' }, function (err, count) {
  1839. * if (err) ..
  1840. * console.log('there are %d jungle adventures', count);
  1841. * });
  1842. *
  1843. * @deprecated
  1844. * @param {Object} filter
  1845. * @param {Function} [callback]
  1846. * @return {Query}
  1847. * @api public
  1848. */
  1849. Model.count = function count(conditions, callback) {
  1850. if (typeof conditions === 'function') {
  1851. callback = conditions;
  1852. conditions = {};
  1853. }
  1854. // get the mongodb collection object
  1855. const mq = new this.Query({}, {}, this, this.collection);
  1856. if (callback) {
  1857. callback = this.$wrapCallback(callback);
  1858. }
  1859. return mq.count(conditions, callback);
  1860. };
  1861. /**
  1862. * Creates a Query for a `distinct` operation.
  1863. *
  1864. * Passing a `callback` executes the query.
  1865. *
  1866. * ####Example
  1867. *
  1868. * Link.distinct('url', { clicks: {$gt: 100}}, function (err, result) {
  1869. * if (err) return handleError(err);
  1870. *
  1871. * assert(Array.isArray(result));
  1872. * console.log('unique urls with more than 100 clicks', result);
  1873. * })
  1874. *
  1875. * var query = Link.distinct('url');
  1876. * query.exec(callback);
  1877. *
  1878. * @param {String} field
  1879. * @param {Object} [conditions] optional
  1880. * @param {Function} [callback]
  1881. * @return {Query}
  1882. * @api public
  1883. */
  1884. Model.distinct = function distinct(field, conditions, callback) {
  1885. // get the mongodb collection object
  1886. const mq = new this.Query({}, {}, this, this.collection);
  1887. if (typeof conditions === 'function') {
  1888. callback = conditions;
  1889. conditions = {};
  1890. }
  1891. if (callback) {
  1892. callback = this.$wrapCallback(callback);
  1893. }
  1894. return mq.distinct(field, conditions, callback);
  1895. };
  1896. /**
  1897. * Creates a Query, applies the passed conditions, and returns the Query.
  1898. *
  1899. * For example, instead of writing:
  1900. *
  1901. * User.find({age: {$gte: 21, $lte: 65}}, callback);
  1902. *
  1903. * we can instead write:
  1904. *
  1905. * User.where('age').gte(21).lte(65).exec(callback);
  1906. *
  1907. * Since the Query class also supports `where` you can continue chaining
  1908. *
  1909. * User
  1910. * .where('age').gte(21).lte(65)
  1911. * .where('name', /^b/i)
  1912. * ... etc
  1913. *
  1914. * @param {String} path
  1915. * @param {Object} [val] optional value
  1916. * @return {Query}
  1917. * @api public
  1918. */
  1919. Model.where = function where(path, val) {
  1920. void val; // eslint
  1921. // get the mongodb collection object
  1922. const mq = new this.Query({}, {}, this, this.collection).find({});
  1923. return mq.where.apply(mq, arguments);
  1924. };
  1925. /**
  1926. * Creates a `Query` and specifies a `$where` condition.
  1927. *
  1928. * Sometimes you need to query for things in mongodb using a JavaScript expression. You can do so via `find({ $where: javascript })`, or you can use the mongoose shortcut method $where via a Query chain or from your mongoose Model.
  1929. *
  1930. * Blog.$where('this.username.indexOf("val") !== -1').exec(function (err, docs) {});
  1931. *
  1932. * @param {String|Function} argument is a javascript string or anonymous function
  1933. * @method $where
  1934. * @memberOf Model
  1935. * @return {Query}
  1936. * @see Query.$where #query_Query-%24where
  1937. * @api public
  1938. */
  1939. Model.$where = function $where() {
  1940. const mq = new this.Query({}, {}, this, this.collection).find({});
  1941. return mq.$where.apply(mq, arguments);
  1942. };
  1943. /**
  1944. * Issues a mongodb findAndModify update command.
  1945. *
  1946. * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes if `callback` is passed else a Query object is returned.
  1947. *
  1948. * ####Options:
  1949. *
  1950. * - `new`: bool - if true, return the modified document rather than the original. defaults to false (changed in 4.0)
  1951. * - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
  1952. * - `fields`: {Object|String} - Field selection. Equivalent to `.select(fields).findOneAndUpdate()`
  1953. * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
  1954. * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
  1955. * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
  1956. * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
  1957. * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
  1958. * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
  1959. *
  1960. * ####Examples:
  1961. *
  1962. * A.findOneAndUpdate(conditions, update, options, callback) // executes
  1963. * A.findOneAndUpdate(conditions, update, options) // returns Query
  1964. * A.findOneAndUpdate(conditions, update, callback) // executes
  1965. * A.findOneAndUpdate(conditions, update) // returns Query
  1966. * A.findOneAndUpdate() // returns Query
  1967. *
  1968. * ####Note:
  1969. *
  1970. * All top level update keys which are not `atomic` operation names are treated as set operations:
  1971. *
  1972. * ####Example:
  1973. *
  1974. * var query = { name: 'borne' };
  1975. * Model.findOneAndUpdate(query, { name: 'jason bourne' }, options, callback)
  1976. *
  1977. * // is sent as
  1978. * Model.findOneAndUpdate(query, { $set: { name: 'jason bourne' }}, options, callback)
  1979. *
  1980. * This helps prevent accidentally overwriting your document with `{ name: 'jason bourne' }`.
  1981. *
  1982. * ####Note:
  1983. *
  1984. * Values are cast to their appropriate types when using the findAndModify helpers.
  1985. * However, the below are not executed by default.
  1986. *
  1987. * - defaults. Use the `setDefaultsOnInsert` option to override.
  1988. *
  1989. * `findAndModify` helpers support limited validation. You can
  1990. * enable these by setting the `runValidators` options,
  1991. * respectively.
  1992. *
  1993. * If you need full-fledged validation, use the traditional approach of first
  1994. * retrieving the document.
  1995. *
  1996. * Model.findById(id, function (err, doc) {
  1997. * if (err) ..
  1998. * doc.name = 'jason bourne';
  1999. * doc.save(callback);
  2000. * });
  2001. *
  2002. * @param {Object} [conditions]
  2003. * @param {Object} [update]
  2004. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
  2005. * @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).
  2006. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
  2007. * @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.
  2008. * @param {Function} [callback]
  2009. * @return {Query}
  2010. * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
  2011. * @api public
  2012. */
  2013. Model.findOneAndUpdate = function(conditions, update, options, callback) {
  2014. if (typeof options === 'function') {
  2015. callback = options;
  2016. options = null;
  2017. } else if (arguments.length === 1) {
  2018. if (typeof conditions === 'function') {
  2019. const msg = 'Model.findOneAndUpdate(): First argument must not be a function.\n\n'
  2020. + ' ' + this.modelName + '.findOneAndUpdate(conditions, update, options, callback)\n'
  2021. + ' ' + this.modelName + '.findOneAndUpdate(conditions, update, options)\n'
  2022. + ' ' + this.modelName + '.findOneAndUpdate(conditions, update)\n'
  2023. + ' ' + this.modelName + '.findOneAndUpdate(update)\n'
  2024. + ' ' + this.modelName + '.findOneAndUpdate()\n';
  2025. throw new TypeError(msg);
  2026. }
  2027. update = conditions;
  2028. conditions = undefined;
  2029. }
  2030. if (callback) {
  2031. callback = this.$wrapCallback(callback);
  2032. }
  2033. let fields;
  2034. if (options) {
  2035. fields = options.fields || options.projection;
  2036. }
  2037. update = utils.clone(update, {
  2038. depopulate: true,
  2039. _isNested: true
  2040. });
  2041. _decorateUpdateWithVersionKey(update, options, this.schema.options.versionKey);
  2042. const mq = new this.Query({}, {}, this, this.collection);
  2043. mq.select(fields);
  2044. return mq.findOneAndUpdate(conditions, update, options, callback);
  2045. };
  2046. /*!
  2047. * Decorate the update with a version key, if necessary
  2048. */
  2049. function _decorateUpdateWithVersionKey(update, options, versionKey) {
  2050. if (!versionKey || !get(options, 'upsert', false)) {
  2051. return;
  2052. }
  2053. const updatedPaths = modifiedPaths(update);
  2054. if (!updatedPaths[versionKey]) {
  2055. if (options.overwrite) {
  2056. update[versionKey] = 0;
  2057. } else {
  2058. if (!update.$setOnInsert) {
  2059. update.$setOnInsert = {};
  2060. }
  2061. update.$setOnInsert[versionKey] = 0;
  2062. }
  2063. }
  2064. }
  2065. /**
  2066. * Issues a mongodb findAndModify update command by a document's _id field.
  2067. * `findByIdAndUpdate(id, ...)` is equivalent to `findOneAndUpdate({ _id: id }, ...)`.
  2068. *
  2069. * Finds a matching document, updates it according to the `update` arg,
  2070. * passing any `options`, and returns the found document (if any) to the
  2071. * callback. The query executes if `callback` is passed.
  2072. *
  2073. * This function triggers the following middleware.
  2074. *
  2075. * - `findOneAndUpdate()`
  2076. *
  2077. * ####Options:
  2078. *
  2079. * - `new`: bool - true to return the modified document rather than the original. defaults to false
  2080. * - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
  2081. * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
  2082. * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
  2083. * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
  2084. * - `select`: sets the document fields to return
  2085. * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
  2086. * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
  2087. *
  2088. * ####Examples:
  2089. *
  2090. * A.findByIdAndUpdate(id, update, options, callback) // executes
  2091. * A.findByIdAndUpdate(id, update, options) // returns Query
  2092. * A.findByIdAndUpdate(id, update, callback) // executes
  2093. * A.findByIdAndUpdate(id, update) // returns Query
  2094. * A.findByIdAndUpdate() // returns Query
  2095. *
  2096. * ####Note:
  2097. *
  2098. * All top level update keys which are not `atomic` operation names are treated as set operations:
  2099. *
  2100. * ####Example:
  2101. *
  2102. * Model.findByIdAndUpdate(id, { name: 'jason bourne' }, options, callback)
  2103. *
  2104. * // is sent as
  2105. * Model.findByIdAndUpdate(id, { $set: { name: 'jason bourne' }}, options, callback)
  2106. *
  2107. * This helps prevent accidentally overwriting your document with `{ name: 'jason bourne' }`.
  2108. *
  2109. * ####Note:
  2110. *
  2111. * Values are cast to their appropriate types when using the findAndModify helpers.
  2112. * However, the below are not executed by default.
  2113. *
  2114. * - defaults. Use the `setDefaultsOnInsert` option to override.
  2115. *
  2116. * `findAndModify` helpers support limited validation. You can
  2117. * enable these by setting the `runValidators` options,
  2118. * respectively.
  2119. *
  2120. * If you need full-fledged validation, use the traditional approach of first
  2121. * retrieving the document.
  2122. *
  2123. * Model.findById(id, function (err, doc) {
  2124. * if (err) ..
  2125. * doc.name = 'jason bourne';
  2126. * doc.save(callback);
  2127. * });
  2128. *
  2129. * @param {Object|Number|String} id value of `_id` to query by
  2130. * @param {Object} [update]
  2131. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
  2132. * @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).
  2133. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
  2134. * @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.
  2135. * @param {Function} [callback]
  2136. * @return {Query}
  2137. * @see Model.findOneAndUpdate #model_Model.findOneAndUpdate
  2138. * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
  2139. * @api public
  2140. */
  2141. Model.findByIdAndUpdate = function(id, update, options, callback) {
  2142. if (callback) {
  2143. callback = this.$wrapCallback(callback);
  2144. }
  2145. if (arguments.length === 1) {
  2146. if (typeof id === 'function') {
  2147. const msg = 'Model.findByIdAndUpdate(): First argument must not be a function.\n\n'
  2148. + ' ' + this.modelName + '.findByIdAndUpdate(id, callback)\n'
  2149. + ' ' + this.modelName + '.findByIdAndUpdate(id)\n'
  2150. + ' ' + this.modelName + '.findByIdAndUpdate()\n';
  2151. throw new TypeError(msg);
  2152. }
  2153. return this.findOneAndUpdate({_id: id}, undefined);
  2154. }
  2155. // if a model is passed in instead of an id
  2156. if (id instanceof Document) {
  2157. id = id._id;
  2158. }
  2159. return this.findOneAndUpdate.call(this, {_id: id}, update, options, callback);
  2160. };
  2161. /**
  2162. * Issue a MongoDB `findOneAndDelete()` command.
  2163. *
  2164. * Finds a matching document, removes it, and passes the found document
  2165. * (if any) to the callback.
  2166. *
  2167. * Executes the query if `callback` is passed.
  2168. *
  2169. * This function triggers the following middleware.
  2170. *
  2171. * - `findOneAndDelete()`
  2172. *
  2173. * This function differs slightly from `Model.findOneAndRemove()` in that
  2174. * `findOneAndRemove()` becomes a [MongoDB `findAndModify()` command](https://docs.mongodb.com/manual/reference/method/db.collection.findAndModify/),
  2175. * as opposed to a `findOneAndDelete()` command. For most mongoose use cases,
  2176. * this distinction is purely pedantic. You should use `findOneAndDelete()`
  2177. * unless you have a good reason not to.
  2178. *
  2179. * ####Options:
  2180. *
  2181. * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
  2182. * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
  2183. * - `select`: sets the document fields to return
  2184. * - `projection`: like select, it determines which fields to return, ex. `{ projection: { _id: 0 } }`
  2185. * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
  2186. * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
  2187. *
  2188. * ####Examples:
  2189. *
  2190. * A.findOneAndDelete(conditions, options, callback) // executes
  2191. * A.findOneAndDelete(conditions, options) // return Query
  2192. * A.findOneAndDelete(conditions, callback) // executes
  2193. * A.findOneAndDelete(conditions) // returns Query
  2194. * A.findOneAndDelete() // returns Query
  2195. *
  2196. * Values are cast to their appropriate types when using the findAndModify helpers.
  2197. * However, the below are not executed by default.
  2198. *
  2199. * - defaults. Use the `setDefaultsOnInsert` option to override.
  2200. *
  2201. * `findAndModify` helpers support limited validation. You can
  2202. * enable these by setting the `runValidators` options,
  2203. * respectively.
  2204. *
  2205. * If you need full-fledged validation, use the traditional approach of first
  2206. * retrieving the document.
  2207. *
  2208. * Model.findById(id, function (err, doc) {
  2209. * if (err) ..
  2210. * doc.name = 'jason bourne';
  2211. * doc.save(callback);
  2212. * });
  2213. *
  2214. * @param {Object} conditions
  2215. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
  2216. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
  2217. * @param {Function} [callback]
  2218. * @return {Query}
  2219. * @api public
  2220. */
  2221. Model.findOneAndDelete = function(conditions, options, callback) {
  2222. if (arguments.length === 1 && typeof conditions === 'function') {
  2223. const msg = 'Model.findOneAndDelete(): First argument must not be a function.\n\n'
  2224. + ' ' + this.modelName + '.findOneAndDelete(conditions, callback)\n'
  2225. + ' ' + this.modelName + '.findOneAndDelete(conditions)\n'
  2226. + ' ' + this.modelName + '.findOneAndDelete()\n';
  2227. throw new TypeError(msg);
  2228. }
  2229. if (typeof options === 'function') {
  2230. callback = options;
  2231. options = undefined;
  2232. }
  2233. if (callback) {
  2234. callback = this.$wrapCallback(callback);
  2235. }
  2236. let fields;
  2237. if (options) {
  2238. fields = options.select;
  2239. options.select = undefined;
  2240. }
  2241. const mq = new this.Query({}, {}, this, this.collection);
  2242. mq.select(fields);
  2243. return mq.findOneAndDelete(conditions, options, callback);
  2244. };
  2245. /**
  2246. * Issue a MongoDB `findOneAndDelete()` command by a document's _id field.
  2247. * In other words, `findByIdAndDelete(id)` is a shorthand for
  2248. * `findOneAndDelete({ _id: id })`.
  2249. *
  2250. * This function triggers the following middleware.
  2251. *
  2252. * - `findOneAndDelete()`
  2253. *
  2254. * @param {Object|Number|String} id value of `_id` to query by
  2255. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
  2256. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
  2257. * @param {Function} [callback]
  2258. * @return {Query}
  2259. * @see Model.findOneAndRemove #model_Model.findOneAndRemove
  2260. * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
  2261. */
  2262. Model.findByIdAndDelete = function(id, options, callback) {
  2263. if (arguments.length === 1 && typeof id === 'function') {
  2264. const msg = 'Model.findByIdAndDelete(): First argument must not be a function.\n\n'
  2265. + ' ' + this.modelName + '.findByIdAndDelete(id, callback)\n'
  2266. + ' ' + this.modelName + '.findByIdAndDelete(id)\n'
  2267. + ' ' + this.modelName + '.findByIdAndDelete()\n';
  2268. throw new TypeError(msg);
  2269. }
  2270. if (callback) {
  2271. callback = this.$wrapCallback(callback);
  2272. }
  2273. return this.findOneAndDelete({_id: id}, options, callback);
  2274. };
  2275. /**
  2276. * Issue a MongoDB `findOneAndReplace()` command.
  2277. *
  2278. * Finds a matching document, replaces it with the provided doc, and passes the
  2279. * returned doc to the callback.
  2280. *
  2281. * Executes the query if `callback` is passed.
  2282. *
  2283. * This function triggers the following query middleware.
  2284. *
  2285. * - `findOneAndReplace()`
  2286. *
  2287. * ####Options:
  2288. *
  2289. * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
  2290. * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
  2291. * - `select`: sets the document fields to return
  2292. * - `projection`: like select, it determines which fields to return, ex. `{ projection: { _id: 0 } }`
  2293. * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
  2294. * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
  2295. *
  2296. * ####Examples:
  2297. *
  2298. * A.findOneAndReplace(conditions, options, callback) // executes
  2299. * A.findOneAndReplace(conditions, options) // return Query
  2300. * A.findOneAndReplace(conditions, callback) // executes
  2301. * A.findOneAndReplace(conditions) // returns Query
  2302. * A.findOneAndReplace() // returns Query
  2303. *
  2304. * Values are cast to their appropriate types when using the findAndModify helpers.
  2305. * However, the below are not executed by default.
  2306. *
  2307. * - defaults. Use the `setDefaultsOnInsert` option to override.
  2308. *
  2309. * @param {Object} filter Replace the first document that matches this filter
  2310. * @param {Object} [replacement] Replace with this document
  2311. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
  2312. * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](http://mongoosejs.com/docs/api.html#query_Query-lean).
  2313. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
  2314. * @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.
  2315. * @param {Function} [callback]
  2316. * @return {Query}
  2317. * @api public
  2318. */
  2319. Model.findOneAndReplace = function(filter, replacement, options, callback) {
  2320. if (arguments.length === 1 && typeof filter === 'function') {
  2321. const msg = 'Model.findOneAndReplace(): First argument must not be a function.\n\n'
  2322. + ' ' + this.modelName + '.findOneAndReplace(conditions, callback)\n'
  2323. + ' ' + this.modelName + '.findOneAndReplace(conditions)\n'
  2324. + ' ' + this.modelName + '.findOneAndReplace()\n';
  2325. throw new TypeError(msg);
  2326. }
  2327. if (arguments.length === 3 && typeof options === 'function') {
  2328. callback = options;
  2329. options = replacement;
  2330. replacement = void 0;
  2331. }
  2332. if (arguments.length === 2 && typeof replacement === 'function') {
  2333. callback = replacement;
  2334. replacement = void 0;
  2335. options = void 0;
  2336. }
  2337. if (callback) {
  2338. callback = this.$wrapCallback(callback);
  2339. }
  2340. let fields;
  2341. if (options) {
  2342. fields = options.select;
  2343. options.select = undefined;
  2344. }
  2345. const mq = new this.Query({}, {}, this, this.collection);
  2346. mq.select(fields);
  2347. return mq.findOneAndReplace(filter, replacement, options, callback);
  2348. };
  2349. /**
  2350. * Issue a mongodb findAndModify remove command.
  2351. *
  2352. * Finds a matching document, removes it, passing the found document (if any) to the callback.
  2353. *
  2354. * Executes the query if `callback` is passed.
  2355. *
  2356. * This function triggers the following middleware.
  2357. *
  2358. * - `findOneAndRemove()`
  2359. *
  2360. * ####Options:
  2361. *
  2362. * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
  2363. * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
  2364. * - `select`: sets the document fields to return
  2365. * - `projection`: like select, it determines which fields to return, ex. `{ projection: { _id: 0 } }`
  2366. * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
  2367. * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
  2368. *
  2369. * ####Examples:
  2370. *
  2371. * A.findOneAndRemove(conditions, options, callback) // executes
  2372. * A.findOneAndRemove(conditions, options) // return Query
  2373. * A.findOneAndRemove(conditions, callback) // executes
  2374. * A.findOneAndRemove(conditions) // returns Query
  2375. * A.findOneAndRemove() // returns Query
  2376. *
  2377. * Values are cast to their appropriate types when using the findAndModify helpers.
  2378. * However, the below are not executed by default.
  2379. *
  2380. * - defaults. Use the `setDefaultsOnInsert` option to override.
  2381. *
  2382. * `findAndModify` helpers support limited validation. You can
  2383. * enable these by setting the `runValidators` options,
  2384. * respectively.
  2385. *
  2386. * If you need full-fledged validation, use the traditional approach of first
  2387. * retrieving the document.
  2388. *
  2389. * Model.findById(id, function (err, doc) {
  2390. * if (err) ..
  2391. * doc.name = 'jason bourne';
  2392. * doc.save(callback);
  2393. * });
  2394. *
  2395. * @param {Object} conditions
  2396. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
  2397. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
  2398. * @param {Function} [callback]
  2399. * @return {Query}
  2400. * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
  2401. * @api public
  2402. */
  2403. Model.findOneAndRemove = function(conditions, options, callback) {
  2404. if (arguments.length === 1 && typeof conditions === 'function') {
  2405. const msg = 'Model.findOneAndRemove(): First argument must not be a function.\n\n'
  2406. + ' ' + this.modelName + '.findOneAndRemove(conditions, callback)\n'
  2407. + ' ' + this.modelName + '.findOneAndRemove(conditions)\n'
  2408. + ' ' + this.modelName + '.findOneAndRemove()\n';
  2409. throw new TypeError(msg);
  2410. }
  2411. if (typeof options === 'function') {
  2412. callback = options;
  2413. options = undefined;
  2414. }
  2415. if (callback) {
  2416. callback = this.$wrapCallback(callback);
  2417. }
  2418. let fields;
  2419. if (options) {
  2420. fields = options.select;
  2421. options.select = undefined;
  2422. }
  2423. const mq = new this.Query({}, {}, this, this.collection);
  2424. mq.select(fields);
  2425. return mq.findOneAndRemove(conditions, options, callback);
  2426. };
  2427. /**
  2428. * Issue a mongodb findAndModify remove command by a document's _id field. `findByIdAndRemove(id, ...)` is equivalent to `findOneAndRemove({ _id: id }, ...)`.
  2429. *
  2430. * Finds a matching document, removes it, passing the found document (if any) to the callback.
  2431. *
  2432. * Executes the query if `callback` is passed.
  2433. *
  2434. * This function triggers the following middleware.
  2435. *
  2436. * - `findOneAndRemove()`
  2437. *
  2438. * ####Options:
  2439. *
  2440. * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
  2441. * - `select`: sets the document fields to return
  2442. * - `rawResult`: if true, returns the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.0/api/Collection.html#findAndModify)
  2443. * - `strict`: overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict) for this update
  2444. *
  2445. * ####Examples:
  2446. *
  2447. * A.findByIdAndRemove(id, options, callback) // executes
  2448. * A.findByIdAndRemove(id, options) // return Query
  2449. * A.findByIdAndRemove(id, callback) // executes
  2450. * A.findByIdAndRemove(id) // returns Query
  2451. * A.findByIdAndRemove() // returns Query
  2452. *
  2453. * @param {Object|Number|String} id value of `_id` to query by
  2454. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
  2455. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
  2456. * @param {Function} [callback]
  2457. * @return {Query}
  2458. * @see Model.findOneAndRemove #model_Model.findOneAndRemove
  2459. * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
  2460. */
  2461. Model.findByIdAndRemove = function(id, options, callback) {
  2462. if (arguments.length === 1 && typeof id === 'function') {
  2463. const msg = 'Model.findByIdAndRemove(): First argument must not be a function.\n\n'
  2464. + ' ' + this.modelName + '.findByIdAndRemove(id, callback)\n'
  2465. + ' ' + this.modelName + '.findByIdAndRemove(id)\n'
  2466. + ' ' + this.modelName + '.findByIdAndRemove()\n';
  2467. throw new TypeError(msg);
  2468. }
  2469. if (callback) {
  2470. callback = this.$wrapCallback(callback);
  2471. }
  2472. return this.findOneAndRemove({_id: id}, options, callback);
  2473. };
  2474. /**
  2475. * Shortcut for saving one or more documents to the database.
  2476. * `MyModel.create(docs)` does `new MyModel(doc).save()` for every doc in
  2477. * docs.
  2478. *
  2479. * This function triggers the following middleware.
  2480. *
  2481. * - `save()`
  2482. *
  2483. * ####Example:
  2484. *
  2485. * // pass a spread of docs and a callback
  2486. * Candy.create({ type: 'jelly bean' }, { type: 'snickers' }, function (err, jellybean, snickers) {
  2487. * if (err) // ...
  2488. * });
  2489. *
  2490. * // pass an array of docs
  2491. * var array = [{ type: 'jelly bean' }, { type: 'snickers' }];
  2492. * Candy.create(array, function (err, candies) {
  2493. * if (err) // ...
  2494. *
  2495. * var jellybean = candies[0];
  2496. * var snickers = candies[1];
  2497. * // ...
  2498. * });
  2499. *
  2500. * // callback is optional; use the returned promise if you like:
  2501. * var promise = Candy.create({ type: 'jawbreaker' });
  2502. * promise.then(function (jawbreaker) {
  2503. * // ...
  2504. * })
  2505. *
  2506. * @param {Array|Object} docs Documents to insert, as a spread or array
  2507. * @param {Object} [options] Options passed down to `save()`. To specify `options`, `docs` **must** be an array, not a spread.
  2508. * @param {Function} [callback] callback
  2509. * @return {Promise}
  2510. * @api public
  2511. */
  2512. Model.create = function create(doc, options, callback) {
  2513. let args;
  2514. let cb;
  2515. const discriminatorKey = this.schema.options.discriminatorKey;
  2516. if (Array.isArray(doc)) {
  2517. args = doc;
  2518. cb = typeof options === 'function' ? options : callback;
  2519. options = options != null && typeof options === 'object' ? options : {};
  2520. } else {
  2521. const last = arguments[arguments.length - 1];
  2522. options = {};
  2523. // Handle falsy callbacks re: #5061
  2524. if (typeof last === 'function' || !last) {
  2525. cb = last;
  2526. args = utils.args(arguments, 0, arguments.length - 1);
  2527. } else {
  2528. args = utils.args(arguments);
  2529. }
  2530. if (args.length === 2 &&
  2531. args[0] != null &&
  2532. args[1] != null &&
  2533. args[0].session == null &&
  2534. last.session != null &&
  2535. last.session.constructor.name === 'ClientSession' &&
  2536. !this.schema.path('session')) {
  2537. // Probably means the user is running into the common mistake of trying
  2538. // to use a spread to specify options, see gh-7535
  2539. console.warn('WARNING: to pass a `session` to `Model.create()` in ' +
  2540. 'Mongoose, you **must** pass an array as the first argument. See: ' +
  2541. 'https://mongoosejs.com/docs/api.html#model_Model.create');
  2542. }
  2543. }
  2544. if (cb) {
  2545. cb = this.$wrapCallback(cb);
  2546. }
  2547. return utils.promiseOrCallback(cb, cb => {
  2548. if (args.length === 0) {
  2549. return cb(null);
  2550. }
  2551. const toExecute = [];
  2552. let firstError;
  2553. args.forEach(doc => {
  2554. toExecute.push(callback => {
  2555. const Model = this.discriminators && doc[discriminatorKey] != null ?
  2556. this.discriminators[doc[discriminatorKey]] || getDiscriminatorByValue(this, doc[discriminatorKey]) :
  2557. this;
  2558. if (Model == null) {
  2559. throw new Error(`Discriminator "${doc[discriminatorKey]}" not ` +
  2560. `found for model "${this.modelName}"`);
  2561. }
  2562. let toSave = doc;
  2563. const callbackWrapper = (error, doc) => {
  2564. if (error) {
  2565. if (!firstError) {
  2566. firstError = error;
  2567. }
  2568. return callback(null, { error: error });
  2569. }
  2570. callback(null, { doc: doc });
  2571. };
  2572. if (!(toSave instanceof Model)) {
  2573. try {
  2574. toSave = new Model(toSave);
  2575. } catch (error) {
  2576. return callbackWrapper(error);
  2577. }
  2578. }
  2579. toSave.save(options, callbackWrapper);
  2580. });
  2581. });
  2582. parallel(toExecute, (error, res) => {
  2583. const savedDocs = [];
  2584. const len = res.length;
  2585. for (let i = 0; i < len; ++i) {
  2586. if (res[i].doc) {
  2587. savedDocs.push(res[i].doc);
  2588. }
  2589. }
  2590. if (firstError) {
  2591. return cb(firstError, savedDocs);
  2592. }
  2593. if (doc instanceof Array) {
  2594. cb(null, savedDocs);
  2595. } else {
  2596. cb.apply(this, [null].concat(savedDocs));
  2597. }
  2598. });
  2599. }, this.events);
  2600. };
  2601. /**
  2602. * _Requires a replica set running MongoDB >= 3.6.0._ Watches the
  2603. * underlying collection for changes using
  2604. * [MongoDB change streams](https://docs.mongodb.com/manual/changeStreams/).
  2605. *
  2606. * This function does **not** trigger any middleware. In particular, it
  2607. * does **not** trigger aggregate middleware.
  2608. *
  2609. * The ChangeStream object is an event emitter that emits the following events:
  2610. *
  2611. * - 'change': A change occurred, see below example
  2612. * - 'error': An unrecoverable error occurred. In particular, change streams currently error out if they lose connection to the replica set primary. Follow [this GitHub issue](https://github.com/Automattic/mongoose/issues/6799) for updates.
  2613. * - 'end': Emitted if the underlying stream is closed
  2614. * - 'close': Emitted if the underlying stream is closed
  2615. *
  2616. * ####Example:
  2617. *
  2618. * const doc = await Person.create({ name: 'Ned Stark' });
  2619. * const changeStream = Person.watch().on('change', change => console.log(change));
  2620. * // Will print from the above `console.log()`:
  2621. * // { _id: { _data: ... },
  2622. * // operationType: 'delete',
  2623. * // ns: { db: 'mydb', coll: 'Person' },
  2624. * // documentKey: { _id: 5a51b125c5500f5aa094c7bd } }
  2625. * await doc.remove();
  2626. *
  2627. * @param {Array} [pipeline]
  2628. * @param {Object} [options] see the [mongodb driver options](http://mongodb.github.io/node-mongodb-native/3.0/api/Collection.html#watch)
  2629. * @return {ChangeStream} mongoose-specific change stream wrapper, inherits from EventEmitter
  2630. * @api public
  2631. */
  2632. Model.watch = function(pipeline, options) {
  2633. return new ChangeStream(this, pipeline, options);
  2634. };
  2635. /**
  2636. * _Requires MongoDB >= 3.6.0._ Starts a [MongoDB session](https://docs.mongodb.com/manual/release-notes/3.6/#client-sessions)
  2637. * for benefits like causal consistency, [retryable writes](https://docs.mongodb.com/manual/core/retryable-writes/),
  2638. * and [transactions](http://thecodebarbarian.com/a-node-js-perspective-on-mongodb-4-transactions.html).
  2639. *
  2640. * Calling `MyModel.startSession()` is equivalent to calling `MyModel.db.startSession()`.
  2641. *
  2642. * This function does not trigger any middleware.
  2643. *
  2644. * ####Example:
  2645. *
  2646. * const session = await Person.startSession();
  2647. * let doc = await Person.findOne({ name: 'Ned Stark' }, null, { session });
  2648. * await doc.remove();
  2649. * // `doc` will always be null, even if reading from a replica set
  2650. * // secondary. Without causal consistency, it is possible to
  2651. * // get a doc back from the below query if the query reads from a
  2652. * // secondary that is experiencing replication lag.
  2653. * doc = await Person.findOne({ name: 'Ned Stark' }, null, { session, readPreference: 'secondary' });
  2654. *
  2655. * @param {Object} [options] see the [mongodb driver options](http://mongodb.github.io/node-mongodb-native/3.0/api/MongoClient.html#startSession)
  2656. * @param {Boolean} [options.causalConsistency=true] set to false to disable causal consistency
  2657. * @param {Function} [callback]
  2658. * @return {Promise<ClientSession>} promise that resolves to a MongoDB driver `ClientSession`
  2659. * @api public
  2660. */
  2661. Model.startSession = function() {
  2662. return this.db.startSession.apply(this.db, arguments);
  2663. };
  2664. /**
  2665. * Shortcut for validating an array of documents and inserting them into
  2666. * MongoDB if they're all valid. This function is faster than `.create()`
  2667. * because it only sends one operation to the server, rather than one for each
  2668. * document.
  2669. *
  2670. * Mongoose always validates each document **before** sending `insertMany`
  2671. * to MongoDB. So if one document has a validation error, no documents will
  2672. * be saved, unless you set
  2673. * [the `ordered` option to false](https://docs.mongodb.com/manual/reference/method/db.collection.insertMany/#error-handling).
  2674. *
  2675. * This function does **not** trigger save middleware.
  2676. *
  2677. * This function triggers the following middleware.
  2678. *
  2679. * - `insertMany()`
  2680. *
  2681. * ####Example:
  2682. *
  2683. * var arr = [{ name: 'Star Wars' }, { name: 'The Empire Strikes Back' }];
  2684. * Movies.insertMany(arr, function(error, docs) {});
  2685. *
  2686. * @param {Array|Object|*} doc(s)
  2687. * @param {Object} [options] see the [mongodb driver options](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#insertMany)
  2688. * @param {Boolean} [options.ordered = true] if true, will fail fast on the first error encountered. If false, will insert all the documents it can and report errors later. An `insertMany()` with `ordered = false` is called an "unordered" `insertMany()`.
  2689. * @param {Boolean} [options.rawResult = false] if false, the returned promise resolves to the documents that passed mongoose document validation. If `true`, will return the [raw result from the MongoDB driver](http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~insertWriteOpCallback) with a `mongoose` property that contains `validationErrors` if this is an unordered `insertMany`.
  2690. * @param {Function} [callback] callback
  2691. * @return {Promise}
  2692. * @api public
  2693. */
  2694. Model.insertMany = function(arr, options, callback) {
  2695. if (typeof options === 'function') {
  2696. callback = options;
  2697. options = null;
  2698. }
  2699. return utils.promiseOrCallback(callback, cb => {
  2700. this.$__insertMany(arr, options, cb);
  2701. }, this.events);
  2702. };
  2703. /*!
  2704. * ignore
  2705. */
  2706. Model.$__insertMany = function(arr, options, callback) {
  2707. const _this = this;
  2708. if (typeof options === 'function') {
  2709. callback = options;
  2710. options = null;
  2711. }
  2712. if (callback) {
  2713. callback = this.$wrapCallback(callback);
  2714. }
  2715. callback = callback || utils.noop;
  2716. options = options || {};
  2717. const limit = get(options, 'limit', 1000);
  2718. const rawResult = get(options, 'rawResult', false);
  2719. const ordered = get(options, 'ordered', true);
  2720. if (!Array.isArray(arr)) {
  2721. arr = [arr];
  2722. }
  2723. const toExecute = [];
  2724. const validationErrors = [];
  2725. arr.forEach(function(doc) {
  2726. toExecute.push(function(callback) {
  2727. if (!(doc instanceof _this)) {
  2728. doc = new _this(doc);
  2729. }
  2730. if (options.session != null) {
  2731. doc.$session(options.session);
  2732. }
  2733. doc.validate({ __noPromise: true }, function(error) {
  2734. if (error) {
  2735. // Option `ordered` signals that insert should be continued after reaching
  2736. // a failing insert. Therefore we delegate "null", meaning the validation
  2737. // failed. It's up to the next function to filter out all failed models
  2738. if (ordered === false) {
  2739. validationErrors.push(error);
  2740. return callback(null, null);
  2741. }
  2742. return callback(error);
  2743. }
  2744. callback(null, doc);
  2745. });
  2746. });
  2747. });
  2748. parallelLimit(toExecute, limit, function(error, docs) {
  2749. if (error) {
  2750. callback(error, null);
  2751. return;
  2752. }
  2753. // We filter all failed pre-validations by removing nulls
  2754. const docAttributes = docs.filter(function(doc) {
  2755. return doc != null;
  2756. });
  2757. // Quickly escape while there aren't any valid docAttributes
  2758. if (docAttributes.length < 1) {
  2759. callback(null, []);
  2760. return;
  2761. }
  2762. const docObjects = docAttributes.map(function(doc) {
  2763. if (doc.schema.options.versionKey) {
  2764. doc[doc.schema.options.versionKey] = 0;
  2765. }
  2766. if (doc.initializeTimestamps) {
  2767. return doc.initializeTimestamps().toObject(internalToObjectOptions);
  2768. }
  2769. return doc.toObject(internalToObjectOptions);
  2770. });
  2771. _this.collection.insertMany(docObjects, options, function(error, res) {
  2772. if (error) {
  2773. callback(error, null);
  2774. return;
  2775. }
  2776. for (let i = 0; i < docAttributes.length; ++i) {
  2777. docAttributes[i].isNew = false;
  2778. docAttributes[i].emit('isNew', false);
  2779. docAttributes[i].constructor.emit('isNew', false);
  2780. }
  2781. if (rawResult) {
  2782. if (ordered === false) {
  2783. // Decorate with mongoose validation errors in case of unordered,
  2784. // because then still do `insertMany()`
  2785. res.mongoose = {
  2786. validationErrors: validationErrors
  2787. };
  2788. }
  2789. return callback(null, res);
  2790. }
  2791. callback(null, docAttributes);
  2792. });
  2793. });
  2794. };
  2795. /**
  2796. * Sends multiple `insertOne`, `updateOne`, `updateMany`, `replaceOne`,
  2797. * `deleteOne`, and/or `deleteMany` operations to the MongoDB server in one
  2798. * command. This is faster than sending multiple independent operations (like)
  2799. * if you use `create()`) because with `bulkWrite()` there is only one round
  2800. * trip to MongoDB.
  2801. *
  2802. * Mongoose will perform casting on all operations you provide.
  2803. *
  2804. * This function does **not** trigger any middleware, not `save()` nor `update()`.
  2805. * If you need to trigger
  2806. * `save()` middleware for every document use [`create()`](http://mongoosejs.com/docs/api.html#model_Model.create) instead.
  2807. *
  2808. * ####Example:
  2809. *
  2810. * Character.bulkWrite([
  2811. * {
  2812. * insertOne: {
  2813. * document: {
  2814. * name: 'Eddard Stark',
  2815. * title: 'Warden of the North'
  2816. * }
  2817. * }
  2818. * },
  2819. * {
  2820. * updateOne: {
  2821. * filter: { name: 'Eddard Stark' },
  2822. * // If you were using the MongoDB driver directly, you'd need to do
  2823. * // `update: { $set: { title: ... } }` but mongoose adds $set for
  2824. * // you.
  2825. * update: { title: 'Hand of the King' }
  2826. * }
  2827. * },
  2828. * {
  2829. * deleteOne: {
  2830. * {
  2831. * filter: { name: 'Eddard Stark' }
  2832. * }
  2833. * }
  2834. * }
  2835. * ]).then(res => {
  2836. * // Prints "1 1 1"
  2837. * console.log(res.insertedCount, res.modifiedCount, res.deletedCount);
  2838. * });
  2839. *
  2840. * The [supported operations](https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/#db.collection.bulkWrite) are:
  2841. *
  2842. * - `insertOne`
  2843. * - `updateOne`
  2844. * - `updateMany`
  2845. * - `deleteOne`
  2846. * - `deleteMany`
  2847. * - `replaceOne`
  2848. *
  2849. * @param {Array} ops
  2850. * @param {Object} [ops.insertOne.document] The document to insert
  2851. * @param {Object} [opts.updateOne.filter] Update the first document that matches this filter
  2852. * @param {Object} [opts.updateOne.update] An object containing [update operators](https://docs.mongodb.com/manual/reference/operator/update/)
  2853. * @param {Boolean} [opts.updateOne.upsert=false] If true, insert a doc if none match
  2854. * @param {Object} [opts.updateOne.collation] The [MongoDB collation](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-34-collations) to use
  2855. * @param {Array} [opts.updateOne.arrayFilters] The [array filters](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-36-array-filters.html) used in `update`
  2856. * @param {Object} [opts.updateMany.filter] Update all the documents that match this filter
  2857. * @param {Object} [opts.updateMany.update] An object containing [update operators](https://docs.mongodb.com/manual/reference/operator/update/)
  2858. * @param {Boolean} [opts.updateMany.upsert=false] If true, insert a doc if no documents match `filter`
  2859. * @param {Object} [opts.updateMany.collation] The [MongoDB collation](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-34-collations) to use
  2860. * @param {Array} [opts.updateMany.arrayFilters] The [array filters](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-36-array-filters.html) used in `update`
  2861. * @param {Object} [opts.deleteOne.filter] Delete the first document that matches this filter
  2862. * @param {Object} [opts.deleteMany.filter] Delete all documents that match this filter
  2863. * @param {Object} [opts.replaceOne.filter] Replace the first document that matches this filter
  2864. * @param {Object} [opts.replaceOne.replacement] The replacement document
  2865. * @param {Boolean} [opts.replaceOne.upsert=false] If true, insert a doc if no documents match `filter`
  2866. * @param {Object} [options]
  2867. * @param {Boolean} [options.ordered=true] If true, execute writes in order and stop at the first error. If false, execute writes in parallel and continue until all writes have either succeeded or errored.
  2868. * @param {ClientSession} [options.session=null] The session associated with this bulk write. See [transactions docs](/docs/transactions.html).
  2869. * @param {String|number} [options.w=1] The [write concern](https://docs.mongodb.com/manual/reference/write-concern/). See [`Query#w()`](/docs/api.html#query_Query-w) for more information.
  2870. * @param {number} [options.wtimeout=null] The [write concern timeout](https://docs.mongodb.com/manual/reference/write-concern/#wtimeout).
  2871. * @param {Boolean} [options.j=true] If false, disable [journal acknowledgement](https://docs.mongodb.com/manual/reference/write-concern/#j-option)
  2872. * @param {Boolean} [options.bypassDocumentValidation=false] If true, disable [MongoDB server-side schema validation](https://docs.mongodb.com/manual/core/schema-validation/) for all writes in this bulk.
  2873. * @param {Function} [callback] callback `function(error, bulkWriteOpResult) {}`
  2874. * @return {Promise} resolves to a [`BulkWriteOpResult`](http://mongodb.github.io/node-mongodb-native/3.1/api/Collection.html#~BulkWriteOpResult) if the operation succeeds
  2875. * @api public
  2876. */
  2877. Model.bulkWrite = function(ops, options, callback) {
  2878. if (typeof options === 'function') {
  2879. callback = options;
  2880. options = null;
  2881. }
  2882. if (callback) {
  2883. callback = this.$wrapCallback(callback);
  2884. }
  2885. options = options || {};
  2886. const validations = ops.map(op => castBulkWrite(this, op, options));
  2887. return utils.promiseOrCallback(callback, cb => {
  2888. parallel(validations, error => {
  2889. if (error) {
  2890. return cb(error);
  2891. }
  2892. this.collection.bulkWrite(ops, options, (error, res) => {
  2893. if (error) {
  2894. return cb(error);
  2895. }
  2896. cb(null, res);
  2897. });
  2898. });
  2899. }, this.events);
  2900. };
  2901. /**
  2902. * Shortcut for creating a new Document from existing raw data, pre-saved in the DB.
  2903. * The document returned has no paths marked as modified initially.
  2904. *
  2905. * ####Example:
  2906. *
  2907. * // hydrate previous data into a Mongoose document
  2908. * var mongooseCandy = Candy.hydrate({ _id: '54108337212ffb6d459f854c', type: 'jelly bean' });
  2909. *
  2910. * @param {Object} obj
  2911. * @return {Model} document instance
  2912. * @api public
  2913. */
  2914. Model.hydrate = function(obj) {
  2915. const model = require('./queryhelpers').createModel(this, obj);
  2916. model.init(obj);
  2917. return model;
  2918. };
  2919. /**
  2920. * Updates one document in the database without returning it.
  2921. *
  2922. * This function triggers the following middleware.
  2923. *
  2924. * - `update()`
  2925. *
  2926. * ####Examples:
  2927. *
  2928. * MyModel.update({ age: { $gt: 18 } }, { oldEnough: true }, fn);
  2929. *
  2930. * const res = await MyModel.update({ name: 'Tobi' }, { ferret: true });
  2931. * res.n; // Number of documents that matched `{ name: 'Tobi' }`
  2932. * // Number of documents that were changed. If every doc matched already
  2933. * // had `ferret` set to `true`, `nModified` will be 0.
  2934. * res.nModified;
  2935. *
  2936. * ####Valid options:
  2937. *
  2938. * - `safe` (boolean) safe mode (defaults to value set in schema (true))
  2939. * - `upsert` (boolean) whether to create the doc if it doesn't match (false)
  2940. * - `multi` (boolean) whether multiple documents should be updated (false)
  2941. * - `runValidators`: if true, runs [update validators](/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
  2942. * - `setDefaultsOnInsert`: if this and `upsert` are true, mongoose will apply the [defaults](http://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. This option only works on MongoDB >= 2.4 because it relies on [MongoDB's `$setOnInsert` operator](https://docs.mongodb.org/v2.4/reference/operator/update/setOnInsert/).
  2943. * - `strict` (boolean) overrides the `strict` option for this update
  2944. * - `overwrite` (boolean) disables update-only mode, allowing you to overwrite the doc (false)
  2945. *
  2946. * All `update` values are cast to their appropriate SchemaTypes before being sent.
  2947. *
  2948. * The `callback` function receives `(err, rawResponse)`.
  2949. *
  2950. * - `err` is the error if any occurred
  2951. * - `rawResponse` is the full response from Mongo
  2952. *
  2953. * ####Note:
  2954. *
  2955. * All top level keys which are not `atomic` operation names are treated as set operations:
  2956. *
  2957. * ####Example:
  2958. *
  2959. * var query = { name: 'borne' };
  2960. * Model.update(query, { name: 'jason bourne' }, options, callback);
  2961. *
  2962. * // is sent as
  2963. * Model.update(query, { $set: { name: 'jason bourne' }}, options, function(err, res));
  2964. * // if overwrite option is false. If overwrite is true, sent without the $set wrapper.
  2965. *
  2966. * This helps prevent accidentally overwriting all documents in your collection with `{ name: 'jason bourne' }`.
  2967. *
  2968. * ####Note:
  2969. *
  2970. * Be careful to not use an existing model instance for the update clause (this won't work and can cause weird behavior like infinite loops). Also, ensure that the update clause does not have an _id property, which causes Mongo to return a "Mod on _id not allowed" error.
  2971. *
  2972. * ####Note:
  2973. *
  2974. * Although values are casted to their appropriate types when using update, the following are *not* applied:
  2975. *
  2976. * - defaults
  2977. * - setters
  2978. * - validators
  2979. * - middleware
  2980. *
  2981. * If you need those features, use the traditional approach of first retrieving the document.
  2982. *
  2983. * Model.findOne({ name: 'borne' }, function (err, doc) {
  2984. * if (err) ..
  2985. * doc.name = 'jason bourne';
  2986. * doc.save(callback);
  2987. * })
  2988. *
  2989. * @see strict http://mongoosejs.com/docs/guide.html#strict
  2990. * @see response http://docs.mongodb.org/v2.6/reference/command/update/#output
  2991. * @param {Object} conditions
  2992. * @param {Object} doc
  2993. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
  2994. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
  2995. * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
  2996. * @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
  2997. * @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.
  2998. * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
  2999. * @param {Function} [callback] params are (error, writeOpResult)
  3000. * @param {Function} [callback]
  3001. * @return {Query}
  3002. * @see MongoDB docs https://docs.mongodb.com/manual/reference/command/update/#update-command-output
  3003. * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
  3004. * @see Query docs https://mongoosejs.com/docs/queries.html
  3005. * @api public
  3006. */
  3007. Model.update = function update(conditions, doc, options, callback) {
  3008. return _update(this, 'update', conditions, doc, options, callback);
  3009. };
  3010. /**
  3011. * Same as `update()`, except MongoDB will update _all_ documents that match
  3012. * `criteria` (as opposed to just the first one) regardless of the value of
  3013. * the `multi` option.
  3014. *
  3015. * **Note** updateMany will _not_ fire update middleware. Use `pre('updateMany')`
  3016. * and `post('updateMany')` instead.
  3017. *
  3018. * ####Example:
  3019. * const res = await Person.updateMany({ name: /Stark$/ }, { isDeleted: true });
  3020. * res.n; // Number of documents matched
  3021. * res.nModified; // Number of documents modified
  3022. *
  3023. * This function triggers the following middleware.
  3024. *
  3025. * - `updateMany()`
  3026. *
  3027. * @param {Object} conditions
  3028. * @param {Object} doc
  3029. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
  3030. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
  3031. * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
  3032. * @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
  3033. * @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.
  3034. * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
  3035. * @param {Function} [callback] `function(error, res) {}` where `res` has 3 properties: `n`, `nModified`, `ok`.
  3036. * @return {Query}
  3037. * @see Query docs https://mongoosejs.com/docs/queries.html
  3038. * @see MongoDB docs https://docs.mongodb.com/manual/reference/command/update/#update-command-output
  3039. * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
  3040. * @api public
  3041. */
  3042. Model.updateMany = function updateMany(conditions, doc, options, callback) {
  3043. return _update(this, 'updateMany', conditions, doc, options, callback);
  3044. };
  3045. /**
  3046. * Same as `update()`, except it does not support the `multi` or `overwrite`
  3047. * options.
  3048. *
  3049. * - MongoDB will update _only_ the first document that matches `criteria` regardless of the value of the `multi` option.
  3050. * - Use `replaceOne()` if you want to overwrite an entire document rather than using atomic operators like `$set`.
  3051. *
  3052. * ####Example:
  3053. * const res = await Person.updateOne({ name: 'Jean-Luc Picard' }, { ship: 'USS Enterprise' });
  3054. * res.n; // Number of documents matched
  3055. * res.nModified; // Number of documents modified
  3056. *
  3057. * This function triggers the following middleware.
  3058. *
  3059. * - `updateOne()`
  3060. *
  3061. * @param {Object} conditions
  3062. * @param {Object} doc
  3063. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
  3064. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
  3065. * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
  3066. * @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
  3067. * @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.
  3068. * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
  3069. * @param {Function} [callback] params are (error, writeOpResult)
  3070. * @return {Query}
  3071. * @see Query docs https://mongoosejs.com/docs/queries.html
  3072. * @see MongoDB docs https://docs.mongodb.com/manual/reference/command/update/#update-command-output
  3073. * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
  3074. * @api public
  3075. */
  3076. Model.updateOne = function updateOne(conditions, doc, options, callback) {
  3077. return _update(this, 'updateOne', conditions, doc, options, callback);
  3078. };
  3079. /**
  3080. * Same as `update()`, except MongoDB replace the existing document with the
  3081. * given document (no atomic operators like `$set`).
  3082. *
  3083. * ####Example:
  3084. * const res = await Person.replaceOne({ _id: 24601 }, { name: 'Jean Valjean' });
  3085. * res.n; // Number of documents matched
  3086. * res.nModified; // Number of documents modified
  3087. *
  3088. * This function triggers the following middleware.
  3089. *
  3090. * - `replaceOne()`
  3091. *
  3092. * @param {Object} conditions
  3093. * @param {Object} doc
  3094. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](http://mongoosejs.com/docs/api.html#query_Query-setOptions)
  3095. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](http://mongoosejs.com/docs/guide.html#strict)
  3096. * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
  3097. * @param {Object} [options.writeConcern=null] sets the [write concern](https://docs.mongodb.com/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](/docs/guide.html#writeConcern)
  3098. * @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.
  3099. * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
  3100. * @param {Function} [callback] `function(error, res) {}` where `res` has 3 properties: `n`, `nModified`, `ok`.
  3101. * @return {Query}
  3102. * @see Query docs https://mongoosejs.com/docs/queries.html
  3103. * @see writeOpResult http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#~WriteOpResult
  3104. * @return {Query}
  3105. * @api public
  3106. */
  3107. Model.replaceOne = function replaceOne(conditions, doc, options, callback) {
  3108. const versionKey = get(this, 'schema.options.versionKey', null);
  3109. if (versionKey && !doc[versionKey]) {
  3110. doc[versionKey] = 0;
  3111. }
  3112. return _update(this, 'replaceOne', conditions, doc, options, callback);
  3113. };
  3114. /*!
  3115. * Common code for `updateOne()`, `updateMany()`, `replaceOne()`, and `update()`
  3116. * because they need to do the same thing
  3117. */
  3118. function _update(model, op, conditions, doc, options, callback) {
  3119. const mq = new model.Query({}, {}, model, model.collection);
  3120. if (callback) {
  3121. callback = model.$wrapCallback(callback);
  3122. }
  3123. // gh-2406
  3124. // make local deep copy of conditions
  3125. if (conditions instanceof Document) {
  3126. conditions = conditions.toObject();
  3127. } else {
  3128. conditions = utils.clone(conditions);
  3129. }
  3130. options = typeof options === 'function' ? options : utils.clone(options);
  3131. const versionKey = get(model, 'schema.options.versionKey', null);
  3132. _decorateUpdateWithVersionKey(doc, options, versionKey);
  3133. return mq[op](conditions, doc, options, callback);
  3134. }
  3135. /**
  3136. * Executes a mapReduce command.
  3137. *
  3138. * `o` is an object specifying all mapReduce options as well as the map and reduce functions. All options are delegated to the driver implementation. See [node-mongodb-native mapReduce() documentation](http://mongodb.github.io/node-mongodb-native/api-generated/collection.html#mapreduce) for more detail about options.
  3139. *
  3140. * This function does not trigger any middleware.
  3141. *
  3142. * ####Example:
  3143. *
  3144. * var o = {};
  3145. * // `map()` and `reduce()` are run on the MongoDB server, not Node.js,
  3146. * // these functions are converted to strings
  3147. * o.map = function () { emit(this.name, 1) };
  3148. * o.reduce = function (k, vals) { return vals.length };
  3149. * User.mapReduce(o, function (err, results) {
  3150. * console.log(results)
  3151. * })
  3152. *
  3153. * ####Other options:
  3154. *
  3155. * - `query` {Object} query filter object.
  3156. * - `sort` {Object} sort input objects using this key
  3157. * - `limit` {Number} max number of documents
  3158. * - `keeptemp` {Boolean, default:false} keep temporary data
  3159. * - `finalize` {Function} finalize function
  3160. * - `scope` {Object} scope variables exposed to map/reduce/finalize during execution
  3161. * - `jsMode` {Boolean, default:false} it is possible to make the execution stay in JS. Provided in MongoDB > 2.0.X
  3162. * - `verbose` {Boolean, default:false} provide statistics on job execution time.
  3163. * - `readPreference` {String}
  3164. * - `out*` {Object, default: {inline:1}} sets the output target for the map reduce job.
  3165. *
  3166. * ####* out options:
  3167. *
  3168. * - `{inline:1}` the results are returned in an array
  3169. * - `{replace: 'collectionName'}` add the results to collectionName: the results replace the collection
  3170. * - `{reduce: 'collectionName'}` add the results to collectionName: if dups are detected, uses the reducer / finalize functions
  3171. * - `{merge: 'collectionName'}` add the results to collectionName: if dups exist the new docs overwrite the old
  3172. *
  3173. * If `options.out` is set to `replace`, `merge`, or `reduce`, a Model instance is returned that can be used for further querying. Queries run against this model are all executed with the [`lean` option](/docs/tutorials/lean.html); meaning only the js object is returned and no Mongoose magic is applied (getters, setters, etc).
  3174. *
  3175. * ####Example:
  3176. *
  3177. * var o = {};
  3178. * // You can also define `map()` and `reduce()` as strings if your
  3179. * // linter complains about `emit()` not being defined
  3180. * o.map = 'function () { emit(this.name, 1) }';
  3181. * o.reduce = 'function (k, vals) { return vals.length }';
  3182. * o.out = { replace: 'createdCollectionNameForResults' }
  3183. * o.verbose = true;
  3184. *
  3185. * User.mapReduce(o, function (err, model, stats) {
  3186. * console.log('map reduce took %d ms', stats.processtime)
  3187. * model.find().where('value').gt(10).exec(function (err, docs) {
  3188. * console.log(docs);
  3189. * });
  3190. * })
  3191. *
  3192. * // `mapReduce()` returns a promise. However, ES6 promises can only
  3193. * // resolve to exactly one value,
  3194. * o.resolveToObject = true;
  3195. * var promise = User.mapReduce(o);
  3196. * promise.then(function (res) {
  3197. * var model = res.model;
  3198. * var stats = res.stats;
  3199. * console.log('map reduce took %d ms', stats.processtime)
  3200. * return model.find().where('value').gt(10).exec();
  3201. * }).then(function (docs) {
  3202. * console.log(docs);
  3203. * }).then(null, handleError).end()
  3204. *
  3205. * @param {Object} o an object specifying map-reduce options
  3206. * @param {Function} [callback] optional callback
  3207. * @see http://www.mongodb.org/display/DOCS/MapReduce
  3208. * @return {Promise}
  3209. * @api public
  3210. */
  3211. Model.mapReduce = function mapReduce(o, callback) {
  3212. if (callback) {
  3213. callback = this.$wrapCallback(callback);
  3214. }
  3215. return utils.promiseOrCallback(callback, cb => {
  3216. if (!Model.mapReduce.schema) {
  3217. const opts = {noId: true, noVirtualId: true, strict: false};
  3218. Model.mapReduce.schema = new Schema({}, opts);
  3219. }
  3220. if (!o.out) o.out = {inline: 1};
  3221. if (o.verbose !== false) o.verbose = true;
  3222. o.map = String(o.map);
  3223. o.reduce = String(o.reduce);
  3224. if (o.query) {
  3225. let q = new this.Query(o.query);
  3226. q.cast(this);
  3227. o.query = q._conditions;
  3228. q = undefined;
  3229. }
  3230. this.collection.mapReduce(null, null, o, (err, res) => {
  3231. if (err) {
  3232. return cb(err);
  3233. }
  3234. if (res.collection) {
  3235. // returned a collection, convert to Model
  3236. const model = Model.compile('_mapreduce_' + res.collection.collectionName,
  3237. Model.mapReduce.schema, res.collection.collectionName, this.db,
  3238. this.base);
  3239. model._mapreduce = true;
  3240. res.model = model;
  3241. return cb(null, res);
  3242. }
  3243. cb(null, res);
  3244. });
  3245. }, this.events);
  3246. };
  3247. /**
  3248. * Performs [aggregations](http://docs.mongodb.org/manual/applications/aggregation/) on the models collection.
  3249. *
  3250. * If a `callback` is passed, the `aggregate` is executed and a `Promise` is returned. If a callback is not passed, the `aggregate` itself is returned.
  3251. *
  3252. * This function triggers the following middleware.
  3253. *
  3254. * - `aggregate()`
  3255. *
  3256. * ####Example:
  3257. *
  3258. * // Find the max balance of all accounts
  3259. * Users.aggregate([
  3260. * { $group: { _id: null, maxBalance: { $max: '$balance' }}},
  3261. * { $project: { _id: 0, maxBalance: 1 }}
  3262. * ]).
  3263. * then(function (res) {
  3264. * console.log(res); // [ { maxBalance: 98000 } ]
  3265. * });
  3266. *
  3267. * // Or use the aggregation pipeline builder.
  3268. * Users.aggregate().
  3269. * group({ _id: null, maxBalance: { $max: '$balance' } }).
  3270. * project('-id maxBalance').
  3271. * exec(function (err, res) {
  3272. * if (err) return handleError(err);
  3273. * console.log(res); // [ { maxBalance: 98 } ]
  3274. * });
  3275. *
  3276. * ####NOTE:
  3277. *
  3278. * - Arguments are not cast to the model's schema because `$project` operators allow redefining the "shape" of the documents at any stage of the pipeline, which may leave documents in an incompatible format.
  3279. * - The documents returned are plain javascript objects, not mongoose documents (since any shape of document can be returned).
  3280. * - Requires MongoDB >= 2.1
  3281. *
  3282. * @see Aggregate #aggregate_Aggregate
  3283. * @see MongoDB http://docs.mongodb.org/manual/applications/aggregation/
  3284. * @param {Array} [pipeline] aggregation pipeline as an array of objects
  3285. * @param {Function} [callback]
  3286. * @return {Aggregate}
  3287. * @api public
  3288. */
  3289. Model.aggregate = function aggregate(pipeline, callback) {
  3290. if (arguments.length > 2 || get(pipeline, 'constructor.name') === 'Object') {
  3291. throw new Error('Mongoose 5.x disallows passing a spread of operators ' +
  3292. 'to `Model.aggregate()`. Instead of ' +
  3293. '`Model.aggregate({ $match }, { $skip })`, do ' +
  3294. '`Model.aggregate([{ $match }, { $skip }])`');
  3295. }
  3296. if (typeof pipeline === 'function') {
  3297. callback = pipeline;
  3298. pipeline = [];
  3299. }
  3300. const aggregate = new Aggregate(pipeline || []);
  3301. aggregate.model(this);
  3302. if (typeof callback === 'undefined') {
  3303. return aggregate;
  3304. }
  3305. if (callback) {
  3306. callback = this.$wrapCallback(callback);
  3307. }
  3308. aggregate.exec(callback);
  3309. return aggregate;
  3310. };
  3311. /**
  3312. * Implements `$geoSearch` functionality for Mongoose
  3313. *
  3314. * This function does not trigger any middleware
  3315. *
  3316. * ####Example:
  3317. *
  3318. * var options = { near: [10, 10], maxDistance: 5 };
  3319. * Locations.geoSearch({ type : "house" }, options, function(err, res) {
  3320. * console.log(res);
  3321. * });
  3322. *
  3323. * ####Options:
  3324. * - `near` {Array} x,y point to search for
  3325. * - `maxDistance` {Number} the maximum distance from the point near that a result can be
  3326. * - `limit` {Number} The maximum number of results to return
  3327. * - `lean` {Object|Boolean} return the raw object instead of the Mongoose Model
  3328. *
  3329. * @param {Object} conditions an object that specifies the match condition (required)
  3330. * @param {Object} options for the geoSearch, some (near, maxDistance) are required
  3331. * @param {Object|Boolean} [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).
  3332. * @param {Function} [callback] optional callback
  3333. * @return {Promise}
  3334. * @see http://docs.mongodb.org/manual/reference/command/geoSearch/
  3335. * @see http://docs.mongodb.org/manual/core/geohaystack/
  3336. * @api public
  3337. */
  3338. Model.geoSearch = function(conditions, options, callback) {
  3339. if (typeof options === 'function') {
  3340. callback = options;
  3341. options = {};
  3342. }
  3343. if (callback) {
  3344. callback = this.$wrapCallback(callback);
  3345. }
  3346. return utils.promiseOrCallback(callback, cb => {
  3347. let error;
  3348. if (conditions === undefined || !utils.isObject(conditions)) {
  3349. error = new Error('Must pass conditions to geoSearch');
  3350. } else if (!options.near) {
  3351. error = new Error('Must specify the near option in geoSearch');
  3352. } else if (!Array.isArray(options.near)) {
  3353. error = new Error('near option must be an array [x, y]');
  3354. }
  3355. if (error) {
  3356. return cb(error);
  3357. }
  3358. // send the conditions in the options object
  3359. options.search = conditions;
  3360. this.collection.geoHaystackSearch(options.near[0], options.near[1], options, (err, res) => {
  3361. if (err) {
  3362. return cb(err);
  3363. }
  3364. let count = res.results.length;
  3365. if (options.lean || count === 0) {
  3366. return cb(null, res.results);
  3367. }
  3368. const errSeen = false;
  3369. function init(err) {
  3370. if (err && !errSeen) {
  3371. return cb(err);
  3372. }
  3373. if (!--count && !errSeen) {
  3374. cb(null, res.results);
  3375. }
  3376. }
  3377. for (let i = 0; i < res.results.length; ++i) {
  3378. const temp = res.results[i];
  3379. res.results[i] = new this();
  3380. res.results[i].init(temp, {}, init);
  3381. }
  3382. });
  3383. }, this.events);
  3384. };
  3385. /**
  3386. * Populates document references.
  3387. *
  3388. * ####Available top-level options:
  3389. *
  3390. * - path: space delimited path(s) to populate
  3391. * - select: optional fields to select
  3392. * - match: optional query conditions to match
  3393. * - model: optional name of the model to use for population
  3394. * - options: optional query options like sort, limit, etc
  3395. * - justOne: optional boolean, if true Mongoose will always set `path` to an array. Inferred from schema by default.
  3396. *
  3397. * ####Examples:
  3398. *
  3399. * // populates a single object
  3400. * User.findById(id, function (err, user) {
  3401. * var opts = [
  3402. * { path: 'company', match: { x: 1 }, select: 'name' },
  3403. * { path: 'notes', options: { limit: 10 }, model: 'override' }
  3404. * ];
  3405. *
  3406. * User.populate(user, opts, function (err, user) {
  3407. * console.log(user);
  3408. * });
  3409. * });
  3410. *
  3411. * // populates an array of objects
  3412. * User.find(match, function (err, users) {
  3413. * var opts = [{ path: 'company', match: { x: 1 }, select: 'name' }];
  3414. *
  3415. * var promise = User.populate(users, opts);
  3416. * promise.then(console.log).end();
  3417. * })
  3418. *
  3419. * // imagine a Weapon model exists with two saved documents:
  3420. * // { _id: 389, name: 'whip' }
  3421. * // { _id: 8921, name: 'boomerang' }
  3422. * // and this schema:
  3423. * // new Schema({
  3424. * // name: String,
  3425. * // weapon: { type: ObjectId, ref: 'Weapon' }
  3426. * // });
  3427. *
  3428. * var user = { name: 'Indiana Jones', weapon: 389 };
  3429. * Weapon.populate(user, { path: 'weapon', model: 'Weapon' }, function (err, user) {
  3430. * console.log(user.weapon.name); // whip
  3431. * })
  3432. *
  3433. * // populate many plain objects
  3434. * var users = [{ name: 'Indiana Jones', weapon: 389 }]
  3435. * users.push({ name: 'Batman', weapon: 8921 })
  3436. * Weapon.populate(users, { path: 'weapon' }, function (err, users) {
  3437. * users.forEach(function (user) {
  3438. * console.log('%s uses a %s', users.name, user.weapon.name)
  3439. * // Indiana Jones uses a whip
  3440. * // Batman uses a boomerang
  3441. * });
  3442. * });
  3443. * // Note that we didn't need to specify the Weapon model because
  3444. * // it is in the schema's ref
  3445. *
  3446. * @param {Document|Array} docs Either a single document or array of documents to populate.
  3447. * @param {Object} options A hash of key/val (path, options) used for population.
  3448. * @param {boolean} [options.retainNullValues=false] by default, Mongoose removes null and undefined values from populated arrays. Use this option to make `populate()` retain `null` and `undefined` array entries.
  3449. * @param {boolean} [options.getters=false] if true, Mongoose will call any getters defined on the `localField`. By default, Mongoose gets the raw value of `localField`. For example, you would need to set this option to `true` if you wanted to [add a `lowercase` getter to your `localField`](/docs/schematypes.html#schematype-options).
  3450. * @param {boolean} [options.clone=false] When you do `BlogPost.find().populate('author')`, blog posts with the same author will share 1 copy of an `author` doc. Enable this option to make Mongoose clone populated docs before assigning them.
  3451. * @param {Object|Function} [options.match=null] Add an additional filter to the populate query. Can be a filter object containing [MongoDB query syntax](https://docs.mongodb.com/manual/tutorial/query-documents/), or a function that returns a filter object.
  3452. * @param {Function} [callback(err,doc)] Optional callback, executed upon completion. Receives `err` and the `doc(s)`.
  3453. * @return {Promise}
  3454. * @api public
  3455. */
  3456. Model.populate = function(docs, paths, callback) {
  3457. const _this = this;
  3458. if (callback) {
  3459. callback = this.$wrapCallback(callback);
  3460. }
  3461. // normalized paths
  3462. paths = utils.populate(paths);
  3463. // data that should persist across subPopulate calls
  3464. const cache = {};
  3465. return utils.promiseOrCallback(callback, cb => {
  3466. _populate(_this, docs, paths, cache, cb);
  3467. }, this.events);
  3468. };
  3469. /*!
  3470. * Populate helper
  3471. *
  3472. * @param {Model} model the model to use
  3473. * @param {Document|Array} docs Either a single document or array of documents to populate.
  3474. * @param {Object} paths
  3475. * @param {Function} [cb(err,doc)] Optional callback, executed upon completion. Receives `err` and the `doc(s)`.
  3476. * @return {Function}
  3477. * @api private
  3478. */
  3479. function _populate(model, docs, paths, cache, callback) {
  3480. let pending = paths.length;
  3481. if (pending === 0) {
  3482. return callback(null, docs);
  3483. }
  3484. // each path has its own query options and must be executed separately
  3485. let i = pending;
  3486. let path;
  3487. while (i--) {
  3488. path = paths[i];
  3489. populate(model, docs, path, next);
  3490. }
  3491. function next(err) {
  3492. if (err) {
  3493. return callback(err, null);
  3494. }
  3495. if (--pending) {
  3496. return;
  3497. }
  3498. callback(null, docs);
  3499. }
  3500. }
  3501. /*!
  3502. * Populates `docs`
  3503. */
  3504. const excludeIdReg = /\s?-_id\s?/;
  3505. const excludeIdRegGlobal = /\s?-_id\s?/g;
  3506. function populate(model, docs, options, callback) {
  3507. // normalize single / multiple docs passed
  3508. if (!Array.isArray(docs)) {
  3509. docs = [docs];
  3510. }
  3511. if (docs.length === 0 || docs.every(utils.isNullOrUndefined)) {
  3512. return callback();
  3513. }
  3514. const modelsMap = getModelsMapForPopulate(model, docs, options);
  3515. if (modelsMap instanceof Error) {
  3516. return immediate(function() {
  3517. callback(modelsMap);
  3518. });
  3519. }
  3520. const len = modelsMap.length;
  3521. let mod;
  3522. let match;
  3523. let select;
  3524. let vals = [];
  3525. function flatten(item) {
  3526. // no need to include undefined values in our query
  3527. return undefined !== item;
  3528. }
  3529. let _remaining = len;
  3530. let hasOne = false;
  3531. for (let i = 0; i < len; ++i) {
  3532. mod = modelsMap[i];
  3533. select = mod.options.select;
  3534. match = _formatMatch(mod.match);
  3535. let ids = utils.array.flatten(mod.ids, flatten);
  3536. ids = utils.array.unique(ids);
  3537. const assignmentOpts = {};
  3538. assignmentOpts.sort = get(mod, 'options.options.sort', void 0);
  3539. assignmentOpts.excludeId = excludeIdReg.test(select) || (select && select._id === 0);
  3540. if (ids.length === 0 || ids.every(utils.isNullOrUndefined)) {
  3541. --_remaining;
  3542. // Ensure that we set populate virtuals with count option to 0 even
  3543. // if we don't actually execute a query.
  3544. if (mod.count) {
  3545. next(mod, assignmentOpts, null, []);
  3546. }
  3547. continue;
  3548. }
  3549. hasOne = true;
  3550. if (mod.foreignField.size === 1) {
  3551. const foreignField = Array.from(mod.foreignField)[0];
  3552. if (foreignField !== '_id' || !match['_id']) {
  3553. match[foreignField] = { $in: ids };
  3554. }
  3555. } else {
  3556. const $or = [];
  3557. if (Array.isArray(match.$or)) {
  3558. match.$and = [{ $or: match.$or }, { $or: $or }];
  3559. delete match.$or;
  3560. } else {
  3561. match.$or = $or;
  3562. }
  3563. for (const foreignField of mod.foreignField) {
  3564. if (foreignField !== '_id' || !match['_id']) {
  3565. $or.push({ [foreignField]: { $in: ids } });
  3566. }
  3567. }
  3568. }
  3569. if (assignmentOpts.excludeId) {
  3570. // override the exclusion from the query so we can use the _id
  3571. // for document matching during assignment. we'll delete the
  3572. // _id back off before returning the result.
  3573. if (typeof select === 'string') {
  3574. select = select.replace(excludeIdRegGlobal, ' ');
  3575. } else {
  3576. // preserve original select conditions by copying
  3577. select = utils.object.shallowCopy(select);
  3578. delete select._id;
  3579. }
  3580. }
  3581. if (mod.options.options && mod.options.options.limit) {
  3582. assignmentOpts.originalLimit = mod.options.options.limit;
  3583. mod.options.options.limit = mod.options.options.limit * ids.length;
  3584. }
  3585. const subPopulate = utils.clone(mod.options.populate);
  3586. const query = mod.model.find(match, select, mod.options.options);
  3587. // If using count, still need the `foreignField` so we can match counts
  3588. // to documents, otherwise we would need a separate `count()` for every doc.
  3589. if (mod.count) {
  3590. for (const foreignField of mod.foreignField) {
  3591. query.select(foreignField);
  3592. }
  3593. }
  3594. // If we're doing virtual populate and projection is inclusive and foreign
  3595. // field is not selected, automatically select it because mongoose needs it.
  3596. // If projection is exclusive and client explicitly unselected the foreign
  3597. // field, that's the client's fault.
  3598. for (const foreignField of mod.foreignField) {
  3599. if (foreignField !== '_id' && query.selectedInclusively() &&
  3600. !isPathSelectedInclusive(query._fields, foreignField)) {
  3601. query.select(foreignField);
  3602. }
  3603. }
  3604. // If we need to sub-populate, call populate recursively
  3605. if (subPopulate) {
  3606. query.populate(subPopulate);
  3607. }
  3608. query.exec(next.bind(this, mod, assignmentOpts));
  3609. }
  3610. if (!hasOne) {
  3611. return callback();
  3612. }
  3613. function next(options, assignmentOpts, err, valsFromDb) {
  3614. if (mod.options.options && mod.options.options.limit) {
  3615. mod.options.options.limit = assignmentOpts.originalLimit;
  3616. }
  3617. if (err) return callback(err, null);
  3618. vals = vals.concat(valsFromDb);
  3619. _assign(null, vals, options, assignmentOpts);
  3620. if (--_remaining === 0) {
  3621. callback();
  3622. }
  3623. }
  3624. function _assign(err, vals, mod, assignmentOpts) {
  3625. if (err) {
  3626. return callback(err, null);
  3627. }
  3628. const options = mod.options;
  3629. const isVirtual = mod.isVirtual;
  3630. const justOne = mod.justOne;
  3631. let _val;
  3632. const lean = options.options && options.options.lean;
  3633. const len = vals.length;
  3634. const rawOrder = {};
  3635. const rawDocs = {};
  3636. let key;
  3637. let val;
  3638. // Clone because `assignRawDocsToIdStructure` will mutate the array
  3639. const allIds = utils.clone(mod.allIds);
  3640. // optimization:
  3641. // record the document positions as returned by
  3642. // the query result.
  3643. for (let i = 0; i < len; i++) {
  3644. val = vals[i];
  3645. if (val == null) {
  3646. continue;
  3647. }
  3648. for (const foreignField of mod.foreignField) {
  3649. _val = utils.getValue(foreignField, val);
  3650. if (Array.isArray(_val)) {
  3651. _val = utils.array.flatten(_val);
  3652. const _valLength = _val.length;
  3653. for (let j = 0; j < _valLength; ++j) {
  3654. let __val = _val[j];
  3655. if (__val instanceof Document) {
  3656. __val = __val._id;
  3657. }
  3658. key = String(__val);
  3659. if (rawDocs[key]) {
  3660. if (Array.isArray(rawDocs[key])) {
  3661. rawDocs[key].push(val);
  3662. rawOrder[key].push(i);
  3663. } else {
  3664. rawDocs[key] = [rawDocs[key], val];
  3665. rawOrder[key] = [rawOrder[key], i];
  3666. }
  3667. } else {
  3668. if (isVirtual && !justOne) {
  3669. rawDocs[key] = [val];
  3670. rawOrder[key] = [i];
  3671. } else {
  3672. rawDocs[key] = val;
  3673. rawOrder[key] = i;
  3674. }
  3675. }
  3676. }
  3677. } else {
  3678. if (_val instanceof Document) {
  3679. _val = _val._id;
  3680. }
  3681. key = String(_val);
  3682. if (rawDocs[key]) {
  3683. if (Array.isArray(rawDocs[key])) {
  3684. rawDocs[key].push(val);
  3685. rawOrder[key].push(i);
  3686. } else {
  3687. rawDocs[key] = [rawDocs[key], val];
  3688. rawOrder[key] = [rawOrder[key], i];
  3689. }
  3690. } else {
  3691. rawDocs[key] = val;
  3692. rawOrder[key] = i;
  3693. }
  3694. }
  3695. // flag each as result of population
  3696. if (lean) {
  3697. leanPopulateMap.set(val, mod.model);
  3698. } else {
  3699. val.$__.wasPopulated = true;
  3700. }
  3701. }
  3702. }
  3703. assignVals({
  3704. originalModel: model,
  3705. // If virtual, make sure to not mutate original field
  3706. rawIds: mod.isVirtual ? allIds : mod.allIds,
  3707. allIds: allIds,
  3708. foreignField: mod.foreignField,
  3709. rawDocs: rawDocs,
  3710. rawOrder: rawOrder,
  3711. docs: mod.docs,
  3712. path: options.path,
  3713. options: assignmentOpts,
  3714. justOne: mod.justOne,
  3715. isVirtual: mod.isVirtual,
  3716. allOptions: mod,
  3717. lean: lean,
  3718. virtual: mod.virtual,
  3719. count: mod.count,
  3720. match: mod.match
  3721. });
  3722. }
  3723. }
  3724. /*!
  3725. * Format `mod.match` given that it may be an array that we need to $or if
  3726. * the client has multiple docs with match functions
  3727. */
  3728. function _formatMatch(match) {
  3729. if (Array.isArray(match)) {
  3730. if (match.length > 1) {
  3731. return { $or: [].concat(match.map(m => Object.assign({}, m))) };
  3732. }
  3733. return Object.assign({}, match[0]);
  3734. }
  3735. return Object.assign({}, match);
  3736. }
  3737. function getModelsMapForPopulate(model, docs, options) {
  3738. let i;
  3739. let doc;
  3740. const len = docs.length;
  3741. const available = {};
  3742. const map = [];
  3743. const modelNameFromQuery = options.model && options.model.modelName || options.model;
  3744. let schema;
  3745. let refPath;
  3746. let Model;
  3747. let currentOptions;
  3748. let modelNames;
  3749. let modelName;
  3750. let discriminatorKey;
  3751. let modelForFindSchema;
  3752. const originalModel = options.model;
  3753. let isVirtual = false;
  3754. const modelSchema = model.schema;
  3755. for (i = 0; i < len; i++) {
  3756. doc = docs[i];
  3757. schema = getSchemaTypes(modelSchema, doc, options.path);
  3758. const isUnderneathDocArray = schema && schema.$isUnderneathDocArray;
  3759. if (isUnderneathDocArray && get(options, 'options.sort') != null) {
  3760. return new Error('Cannot populate with `sort` on path ' + options.path +
  3761. ' because it is a subproperty of a document array');
  3762. }
  3763. modelNames = null;
  3764. let isRefPath = false;
  3765. if (Array.isArray(schema)) {
  3766. for (let j = 0; j < schema.length; ++j) {
  3767. let _modelNames;
  3768. try {
  3769. const res = _getModelNames(doc, schema[j]);
  3770. _modelNames = res.modelNames;
  3771. isRefPath = res.isRefPath;
  3772. } catch (error) {
  3773. return error;
  3774. }
  3775. if (!_modelNames) {
  3776. continue;
  3777. }
  3778. modelNames = modelNames || [];
  3779. for (let x = 0; x < _modelNames.length; ++x) {
  3780. if (modelNames.indexOf(_modelNames[x]) === -1) {
  3781. modelNames.push(_modelNames[x]);
  3782. }
  3783. }
  3784. }
  3785. } else {
  3786. try {
  3787. const res = _getModelNames(doc, schema);
  3788. modelNames = res.modelNames;
  3789. isRefPath = res.isRefPath;
  3790. } catch (error) {
  3791. return error;
  3792. }
  3793. if (!modelNames) {
  3794. continue;
  3795. }
  3796. }
  3797. const virtual = getVirtual(model.schema, options.path);
  3798. let localField;
  3799. let count = false;
  3800. if (virtual && virtual.options) {
  3801. const virtualPrefix = virtual.$nestedSchemaPath ?
  3802. virtual.$nestedSchemaPath + '.' : '';
  3803. if (typeof virtual.options.localField === 'function') {
  3804. localField = virtualPrefix + virtual.options.localField.call(doc, doc);
  3805. } else {
  3806. localField = virtualPrefix + virtual.options.localField;
  3807. }
  3808. count = virtual.options.count;
  3809. } else {
  3810. localField = options.path;
  3811. }
  3812. let foreignField = virtual && virtual.options ?
  3813. virtual.options.foreignField :
  3814. '_id';
  3815. // `justOne = null` means we don't know from the schema whether the end
  3816. // result should be an array or a single doc. This can result from
  3817. // populating a POJO using `Model.populate()`
  3818. let justOne = null;
  3819. if ('justOne' in options) {
  3820. justOne = options.justOne;
  3821. } else if (virtual && virtual.options && virtual.options.ref) {
  3822. let normalizedRef;
  3823. if (typeof virtual.options.ref === 'function') {
  3824. normalizedRef = virtual.options.ref.call(doc, doc);
  3825. } else {
  3826. normalizedRef = virtual.options.ref;
  3827. }
  3828. justOne = !!virtual.options.justOne;
  3829. isVirtual = true;
  3830. if (!modelNames) {
  3831. modelNames = [].concat(normalizedRef);
  3832. }
  3833. } else if (schema && !schema[schemaMixedSymbol]) {
  3834. // Skip Mixed types because we explicitly don't do casting on those.
  3835. justOne = !schema.$isMongooseArray;
  3836. }
  3837. if (!modelNames) {
  3838. continue;
  3839. }
  3840. if (virtual && (!localField || !foreignField)) {
  3841. return new Error('If you are populating a virtual, you must set the ' +
  3842. 'localField and foreignField options');
  3843. }
  3844. options.isVirtual = isVirtual;
  3845. options.virtual = virtual;
  3846. if (typeof localField === 'function') {
  3847. localField = localField.call(doc, doc);
  3848. }
  3849. if (typeof foreignField === 'function') {
  3850. foreignField = foreignField.call(doc);
  3851. }
  3852. const localFieldPathType = modelSchema._getPathType(localField);
  3853. const localFieldPath = localFieldPathType === 'real' ? modelSchema.paths[localField] : localFieldPathType.schema;
  3854. const localFieldGetters = localFieldPath && localFieldPath.getters ? localFieldPath.getters : [];
  3855. let ret;
  3856. const _populateOptions = get(options, 'options', {});
  3857. const getters = 'getters' in _populateOptions ?
  3858. _populateOptions.getters :
  3859. options.isVirtual && get(virtual, 'options.getters', false);
  3860. if (localFieldGetters.length > 0 && getters) {
  3861. const hydratedDoc = (doc.$__ != null) ? doc : model.hydrate(doc);
  3862. const localFieldValue = utils.getValue(localField, doc);
  3863. if (Array.isArray(localFieldValue)) {
  3864. const localFieldHydratedValue = utils.getValue(localField.split('.').slice(0, -1), hydratedDoc);
  3865. ret = localFieldValue.map((localFieldArrVal, localFieldArrIndex) =>
  3866. localFieldPath.applyGetters(localFieldArrVal, localFieldHydratedValue[localFieldArrIndex]));
  3867. } else {
  3868. ret = localFieldPath.applyGetters(localFieldValue, hydratedDoc);
  3869. }
  3870. } else {
  3871. ret = convertTo_id(utils.getValue(localField, doc));
  3872. }
  3873. const id = String(utils.getValue(foreignField, doc));
  3874. options._docs[id] = Array.isArray(ret) ? ret.slice() : ret;
  3875. let match = get(options, 'match', null) ||
  3876. get(currentOptions, 'match', null) ||
  3877. get(options, 'virtual.options.options.match', null);
  3878. const hasMatchFunction = typeof match === 'function';
  3879. if (hasMatchFunction) {
  3880. match = match.call(doc, doc);
  3881. }
  3882. let k = modelNames.length;
  3883. while (k--) {
  3884. modelName = modelNames[k];
  3885. if (modelName == null) {
  3886. continue;
  3887. }
  3888. try {
  3889. Model = originalModel && originalModel[modelSymbol] ?
  3890. originalModel :
  3891. modelName[modelSymbol] ? modelName : model.db.model(modelName);
  3892. } catch (error) {
  3893. return error;
  3894. }
  3895. let ids = ret;
  3896. const flat = Array.isArray(ret) ? utils.array.flatten(ret) : [];
  3897. if (isRefPath && Array.isArray(ret) && flat.length === modelNames.length) {
  3898. ids = flat.filter((val, i) => modelNames[i] === modelName);
  3899. }
  3900. if (!available[modelName]) {
  3901. currentOptions = {
  3902. model: Model
  3903. };
  3904. if (isVirtual && virtual.options && virtual.options.options) {
  3905. currentOptions.options = utils.clone(virtual.options.options);
  3906. }
  3907. utils.merge(currentOptions, options);
  3908. if (schema && !discriminatorKey) {
  3909. currentOptions.model = Model;
  3910. }
  3911. options.model = Model;
  3912. available[modelName] = {
  3913. model: Model,
  3914. options: currentOptions,
  3915. match: hasMatchFunction ? [match] : match,
  3916. docs: [doc],
  3917. ids: [ids],
  3918. allIds: [ret],
  3919. localField: new Set([localField]),
  3920. foreignField: new Set([foreignField]),
  3921. justOne: justOne,
  3922. isVirtual: isVirtual,
  3923. virtual: virtual,
  3924. count: count
  3925. };
  3926. map.push(available[modelName]);
  3927. } else {
  3928. available[modelName].localField.add(localField);
  3929. available[modelName].foreignField.add(foreignField);
  3930. available[modelName].docs.push(doc);
  3931. available[modelName].ids.push(ids);
  3932. available[modelName].allIds.push(ret);
  3933. if (hasMatchFunction) {
  3934. available[modelName].match.push(match);
  3935. }
  3936. }
  3937. }
  3938. }
  3939. function _getModelNames(doc, schema) {
  3940. let modelNames;
  3941. let discriminatorKey;
  3942. let isRefPath = false;
  3943. if (schema && schema.caster) {
  3944. schema = schema.caster;
  3945. }
  3946. if (schema && schema.$isSchemaMap) {
  3947. schema = schema.$__schemaType;
  3948. }
  3949. if (!schema && model.discriminators) {
  3950. discriminatorKey = model.schema.discriminatorMapping.key;
  3951. }
  3952. refPath = schema && schema.options && schema.options.refPath;
  3953. const normalizedRefPath = normalizeRefPath(refPath, doc, options.path);
  3954. if (modelNameFromQuery) {
  3955. modelNames = [modelNameFromQuery]; // query options
  3956. } else if (normalizedRefPath) {
  3957. if (options._queryProjection != null && isPathExcluded(options._queryProjection, normalizedRefPath)) {
  3958. throw new Error('refPath `' + normalizedRefPath +
  3959. '` must not be excluded in projection, got ' +
  3960. util.inspect(options._queryProjection));
  3961. }
  3962. modelNames = utils.getValue(normalizedRefPath, doc);
  3963. if (Array.isArray(modelNames)) {
  3964. modelNames = utils.array.flatten(modelNames);
  3965. }
  3966. isRefPath = true;
  3967. } else {
  3968. let modelForCurrentDoc = model;
  3969. let schemaForCurrentDoc;
  3970. if (!schema && discriminatorKey) {
  3971. modelForFindSchema = utils.getValue(discriminatorKey, doc);
  3972. if (modelForFindSchema) {
  3973. try {
  3974. modelForCurrentDoc = model.db.model(modelForFindSchema);
  3975. } catch (error) {
  3976. return error;
  3977. }
  3978. schemaForCurrentDoc = modelForCurrentDoc.schema._getSchema(options.path);
  3979. if (schemaForCurrentDoc && schemaForCurrentDoc.caster) {
  3980. schemaForCurrentDoc = schemaForCurrentDoc.caster;
  3981. }
  3982. }
  3983. } else {
  3984. schemaForCurrentDoc = schema;
  3985. }
  3986. const virtual = getVirtual(modelForCurrentDoc.schema, options.path);
  3987. let ref;
  3988. if ((ref = get(schemaForCurrentDoc, 'options.ref')) != null) {
  3989. modelNames = [ref];
  3990. } else if ((ref = get(virtual, 'options.ref')) != null) {
  3991. if (typeof ref === 'function') {
  3992. ref = ref.call(doc, doc);
  3993. }
  3994. // When referencing nested arrays, the ref should be an Array
  3995. // of modelNames.
  3996. if (Array.isArray(ref)) {
  3997. modelNames = ref;
  3998. } else {
  3999. modelNames = [ref];
  4000. }
  4001. isVirtual = true;
  4002. } else {
  4003. // We may have a discriminator, in which case we don't want to
  4004. // populate using the base model by default
  4005. modelNames = discriminatorKey ? null : [model.modelName];
  4006. }
  4007. }
  4008. if (!modelNames) {
  4009. return { modelNames: modelNames, isRefPath: isRefPath };
  4010. }
  4011. if (!Array.isArray(modelNames)) {
  4012. modelNames = [modelNames];
  4013. }
  4014. return { modelNames: modelNames, isRefPath: isRefPath };
  4015. }
  4016. return map;
  4017. }
  4018. /*!
  4019. * Retrieve the _id of `val` if a Document or Array of Documents.
  4020. *
  4021. * @param {Array|Document|Any} val
  4022. * @return {Array|Document|Any}
  4023. */
  4024. function convertTo_id(val) {
  4025. if (val instanceof Model) return val._id;
  4026. if (Array.isArray(val)) {
  4027. for (let i = 0; i < val.length; ++i) {
  4028. if (val[i] instanceof Model) {
  4029. val[i] = val[i]._id;
  4030. }
  4031. }
  4032. if (val.isMongooseArray && val.$schema()) {
  4033. return val.$schema().cast(val, val.$parent());
  4034. }
  4035. return [].concat(val);
  4036. }
  4037. // `populate('map')` may be an object if populating on a doc that hasn't
  4038. // been hydrated yet
  4039. if (val != null && val.constructor.name === 'Object') {
  4040. const ret = [];
  4041. for (const key of Object.keys(val)) {
  4042. ret.push(val[key]);
  4043. }
  4044. return ret;
  4045. }
  4046. // If doc has already been hydrated, e.g. `doc.populate('map').execPopulate()`
  4047. // then `val` will already be a map
  4048. if (val instanceof Map) {
  4049. return Array.from(val.values());
  4050. }
  4051. return val;
  4052. }
  4053. /*!
  4054. * Compiler utility.
  4055. *
  4056. * @param {String|Function} name model name or class extending Model
  4057. * @param {Schema} schema
  4058. * @param {String} collectionName
  4059. * @param {Connection} connection
  4060. * @param {Mongoose} base mongoose instance
  4061. */
  4062. Model.compile = function compile(name, schema, collectionName, connection, base) {
  4063. const versioningEnabled = schema.options.versionKey !== false;
  4064. if (versioningEnabled && !schema.paths[schema.options.versionKey]) {
  4065. // add versioning to top level documents only
  4066. const o = {};
  4067. o[schema.options.versionKey] = Number;
  4068. schema.add(o);
  4069. }
  4070. let model;
  4071. if (typeof name === 'function' && name.prototype instanceof Model) {
  4072. model = name;
  4073. name = model.name;
  4074. schema.loadClass(model, false);
  4075. model.prototype.$isMongooseModelPrototype = true;
  4076. } else {
  4077. // generate new class
  4078. model = function model(doc, fields, skipId) {
  4079. model.hooks.execPreSync('createModel', doc);
  4080. if (!(this instanceof model)) {
  4081. return new model(doc, fields, skipId);
  4082. }
  4083. const discriminatorKey = model.schema.options.discriminatorKey;
  4084. // If discriminator key is set, use the discriminator instead (gh-7586)
  4085. if (model.discriminators != null &&
  4086. doc != null &&
  4087. doc[discriminatorKey] != null &&
  4088. model.discriminators[doc[discriminatorKey]] != null) {
  4089. return new model.discriminators[doc[discriminatorKey]](doc, fields, skipId);
  4090. }
  4091. // Otherwise, just use the top-level model
  4092. Model.call(this, doc, fields, skipId);
  4093. };
  4094. }
  4095. model.hooks = schema.s.hooks.clone();
  4096. model.base = base;
  4097. model.modelName = name;
  4098. if (!(model.prototype instanceof Model)) {
  4099. model.__proto__ = Model;
  4100. model.prototype.__proto__ = Model.prototype;
  4101. }
  4102. model.model = Model.prototype.model;
  4103. model.db = model.prototype.db = connection;
  4104. model.discriminators = model.prototype.discriminators = undefined;
  4105. model[modelSymbol] = true;
  4106. model.events = new EventEmitter();
  4107. model.prototype.$__setSchema(schema);
  4108. const _userProvidedOptions = schema._userProvidedOptions || {};
  4109. // `bufferCommands` is true by default...
  4110. let bufferCommands = true;
  4111. // First, take the global option
  4112. if (connection.base.get('bufferCommands') != null) {
  4113. bufferCommands = connection.base.get('bufferCommands');
  4114. }
  4115. // Connection-specific overrides the global option
  4116. if (connection.config.bufferCommands != null) {
  4117. bufferCommands = connection.config.bufferCommands;
  4118. }
  4119. // And schema options override global and connection
  4120. if (_userProvidedOptions.bufferCommands != null) {
  4121. bufferCommands = _userProvidedOptions.bufferCommands;
  4122. }
  4123. const collectionOptions = {
  4124. bufferCommands: bufferCommands,
  4125. capped: schema.options.capped,
  4126. Promise: model.base.Promise
  4127. };
  4128. model.prototype.collection = connection.collection(
  4129. collectionName,
  4130. collectionOptions
  4131. );
  4132. model.prototype[modelCollectionSymbol] = model.prototype.collection;
  4133. // apply methods and statics
  4134. applyMethods(model, schema);
  4135. applyStatics(model, schema);
  4136. applyHooks(model, schema);
  4137. applyStaticHooks(model, schema.s.hooks, schema.statics);
  4138. model.schema = model.prototype.schema;
  4139. model.collection = model.prototype.collection;
  4140. // Create custom query constructor
  4141. model.Query = function() {
  4142. Query.apply(this, arguments);
  4143. };
  4144. model.Query.prototype = Object.create(Query.prototype);
  4145. model.Query.base = Query.base;
  4146. applyQueryMiddleware(model.Query, model);
  4147. applyQueryMethods(model, schema.query);
  4148. return model;
  4149. };
  4150. /*!
  4151. * Register custom query methods for this model
  4152. *
  4153. * @param {Model} model
  4154. * @param {Schema} schema
  4155. */
  4156. function applyQueryMethods(model, methods) {
  4157. for (const i in methods) {
  4158. model.Query.prototype[i] = methods[i];
  4159. }
  4160. }
  4161. /*!
  4162. * Subclass this model with `conn`, `schema`, and `collection` settings.
  4163. *
  4164. * @param {Connection} conn
  4165. * @param {Schema} [schema]
  4166. * @param {String} [collection]
  4167. * @return {Model}
  4168. */
  4169. Model.__subclass = function subclass(conn, schema, collection) {
  4170. // subclass model using this connection and collection name
  4171. const _this = this;
  4172. const Model = function Model(doc, fields, skipId) {
  4173. if (!(this instanceof Model)) {
  4174. return new Model(doc, fields, skipId);
  4175. }
  4176. _this.call(this, doc, fields, skipId);
  4177. };
  4178. Model.__proto__ = _this;
  4179. Model.prototype.__proto__ = _this.prototype;
  4180. Model.db = Model.prototype.db = conn;
  4181. _this[subclassedSymbol] = _this[subclassedSymbol] || [];
  4182. _this[subclassedSymbol].push(Model);
  4183. if (_this.discriminators != null) {
  4184. Model.discriminators = {};
  4185. for (const key of Object.keys(_this.discriminators)) {
  4186. Model.discriminators[key] = _this.discriminators[key].
  4187. __subclass(_this.db, _this.discriminators[key].schema, collection);
  4188. }
  4189. }
  4190. const s = schema && typeof schema !== 'string'
  4191. ? schema
  4192. : _this.prototype.schema;
  4193. const options = s.options || {};
  4194. const _userProvidedOptions = s._userProvidedOptions || {};
  4195. if (!collection) {
  4196. collection = _this.prototype.schema.get('collection') ||
  4197. utils.toCollectionName(_this.modelName, this.base.pluralize());
  4198. }
  4199. let bufferCommands = true;
  4200. if (s) {
  4201. if (conn.config.bufferCommands != null) {
  4202. bufferCommands = conn.config.bufferCommands;
  4203. }
  4204. if (_userProvidedOptions.bufferCommands != null) {
  4205. bufferCommands = _userProvidedOptions.bufferCommands;
  4206. }
  4207. }
  4208. const collectionOptions = {
  4209. bufferCommands: bufferCommands,
  4210. capped: s && options.capped
  4211. };
  4212. Model.prototype.collection = conn.collection(collection, collectionOptions);
  4213. Model.prototype[modelCollectionSymbol] = Model.prototype.collection;
  4214. Model.collection = Model.prototype.collection;
  4215. // Errors handled internally, so ignore
  4216. Model.init(() => {});
  4217. return Model;
  4218. };
  4219. Model.$wrapCallback = function(callback) {
  4220. if (callback == null) {
  4221. return callback;
  4222. }
  4223. if (typeof callback !== 'function') {
  4224. throw new Error('Callback must be a function, got ' + callback);
  4225. }
  4226. const _this = this;
  4227. return function() {
  4228. try {
  4229. callback.apply(null, arguments);
  4230. } catch (error) {
  4231. _this.emit('error', error);
  4232. }
  4233. };
  4234. };
  4235. /*!
  4236. * Module exports.
  4237. */
  4238. module.exports = exports = Model;