model.js 174 KB

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