model.js 171 KB

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