query.js 178 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658
  1. 'use strict';
  2. /*!
  3. * Module dependencies.
  4. */
  5. const CastError = require('./error/cast');
  6. const DocumentNotFoundError = require('./error/notFound');
  7. const Kareem = require('kareem');
  8. const MongooseError = require('./error/mongooseError');
  9. const ObjectParameterError = require('./error/objectParameter');
  10. const QueryCursor = require('./cursor/queryCursor');
  11. const ValidationError = require('./error/validation');
  12. const { applyGlobalMaxTimeMS, applyGlobalDiskUse } = require('./helpers/query/applyGlobalOption');
  13. const handleReadPreferenceAliases = require('./helpers/query/handleReadPreferenceAliases');
  14. const applyReadConcern = require('./helpers/schema/applyReadConcern');
  15. const applyWriteConcern = require('./helpers/schema/applyWriteConcern');
  16. const cast = require('./cast');
  17. const castArrayFilters = require('./helpers/update/castArrayFilters');
  18. const castNumber = require('./cast/number');
  19. const castUpdate = require('./helpers/query/castUpdate');
  20. const clone = require('./helpers/clone');
  21. const getDiscriminatorByValue = require('./helpers/discriminator/getDiscriminatorByValue');
  22. const helpers = require('./queryHelpers');
  23. const internalToObjectOptions = require('./options').internalToObjectOptions;
  24. const isExclusive = require('./helpers/projection/isExclusive');
  25. const isInclusive = require('./helpers/projection/isInclusive');
  26. const isPathSelectedInclusive = require('./helpers/projection/isPathSelectedInclusive');
  27. const isSubpath = require('./helpers/projection/isSubpath');
  28. const mpath = require('mpath');
  29. const mquery = require('mquery');
  30. const parseProjection = require('./helpers/projection/parseProjection');
  31. const removeUnusedArrayFilters = require('./helpers/update/removeUnusedArrayFilters');
  32. const sanitizeFilter = require('./helpers/query/sanitizeFilter');
  33. const sanitizeProjection = require('./helpers/query/sanitizeProjection');
  34. const selectPopulatedFields = require('./helpers/query/selectPopulatedFields');
  35. const setDefaultsOnInsert = require('./helpers/setDefaultsOnInsert');
  36. const specialProperties = require('./helpers/specialProperties');
  37. const updateValidators = require('./helpers/updateValidators');
  38. const util = require('util');
  39. const utils = require('./utils');
  40. const queryMiddlewareFunctions = require('./constants').queryMiddlewareFunctions;
  41. const queryOptionMethods = new Set([
  42. 'allowDiskUse',
  43. 'batchSize',
  44. 'collation',
  45. 'comment',
  46. 'explain',
  47. 'hint',
  48. 'j',
  49. 'lean',
  50. 'limit',
  51. 'maxTimeMS',
  52. 'populate',
  53. 'projection',
  54. 'read',
  55. 'select',
  56. 'skip',
  57. 'slice',
  58. 'sort',
  59. 'tailable',
  60. 'w',
  61. 'writeConcern',
  62. 'wtimeout'
  63. ]);
  64. // Map from operation name to the name of the function that executes the actual operation against MongoDB.
  65. // Called a thunk for legacy reasons, "thunk" means function that takes exactly 1 param, a callback.
  66. // Currently `_countDocuments()`, etc. are async functions that take no params.
  67. const opToThunk = new Map([
  68. ['countDocuments', '_countDocuments'],
  69. ['distinct', '__distinct'],
  70. ['estimatedDocumentCount', '_estimatedDocumentCount'],
  71. ['find', '_find'],
  72. ['findOne', '_findOne'],
  73. ['findOneAndReplace', '_findOneAndReplace'],
  74. ['findOneAndUpdate', '_findOneAndUpdate'],
  75. ['replaceOne', '_replaceOne'],
  76. ['updateMany', '_updateMany'],
  77. ['updateOne', '_updateOne'],
  78. ['deleteMany', '_deleteMany'],
  79. ['deleteOne', '_deleteOne'],
  80. ['findOneAndDelete', '_findOneAndDelete']
  81. ]);
  82. /**
  83. * Query constructor used for building queries. You do not need
  84. * to instantiate a `Query` directly. Instead use Model functions like
  85. * [`Model.find()`](https://mongoosejs.com/docs/api/model.html#Model.find()).
  86. *
  87. * #### Example:
  88. *
  89. * const query = MyModel.find(); // `query` is an instance of `Query`
  90. * query.setOptions({ lean : true });
  91. * query.collection(MyModel.collection);
  92. * query.where('age').gte(21).exec(callback);
  93. *
  94. * // You can instantiate a query directly. There is no need to do
  95. * // this unless you're an advanced user with a very good reason to.
  96. * const query = new mongoose.Query();
  97. *
  98. * @param {Object} [options]
  99. * @param {Object} [model]
  100. * @param {Object} [conditions]
  101. * @param {Object} [collection] Mongoose collection
  102. * @api public
  103. */
  104. function Query(conditions, options, model, collection) {
  105. // this stuff is for dealing with custom queries created by #toConstructor
  106. if (!this._mongooseOptions) {
  107. this._mongooseOptions = {};
  108. }
  109. options = options || {};
  110. this._transforms = [];
  111. this._hooks = new Kareem();
  112. this._execCount = 0;
  113. // this is the case where we have a CustomQuery, we need to check if we got
  114. // options passed in, and if we did, merge them in
  115. const keys = Object.keys(options);
  116. for (const key of keys) {
  117. this._mongooseOptions[key] = options[key];
  118. }
  119. if (collection) {
  120. this.mongooseCollection = collection;
  121. }
  122. if (model) {
  123. this.model = model;
  124. this.schema = model.schema;
  125. }
  126. // this is needed because map reduce returns a model that can be queried, but
  127. // all of the queries on said model should be lean
  128. if (this.model?._mapreduce) {
  129. this.lean();
  130. }
  131. // inherit mquery
  132. mquery.call(this, null, options);
  133. if (collection) {
  134. this.collection(collection);
  135. }
  136. if (conditions) {
  137. this.find(conditions);
  138. }
  139. this.options = this.options || {};
  140. // For gh-6880. mquery still needs to support `fields` by default for old
  141. // versions of MongoDB
  142. this.$useProjection = true;
  143. const collation = this?.schema?.options?.collation || null;
  144. if (collation != null) {
  145. this.options.collation = collation;
  146. }
  147. }
  148. // Helper function to check if an object is empty or contains only empty objects/arrays
  149. function isEmptyFilter(obj) {
  150. if (obj == null) return true;
  151. if (typeof obj !== 'object') return true;
  152. if (utils.hasOwnKeys(obj) === false) return true;
  153. // Check $and, $or, $nor arrays
  154. for (const key of ['$and', '$or', '$nor']) {
  155. if (Array.isArray(obj[key])) {
  156. // If array is empty or all elements are empty objects, consider it empty
  157. if (obj[key].length === 0 || obj[key].every(item => isEmptyFilter(item))) {
  158. return true;
  159. }
  160. }
  161. }
  162. return false;
  163. }
  164. // Helper function to check for empty/invalid filter
  165. function checkRequireFilter(filter, options) {
  166. if (options?.requireFilter && isEmptyFilter(filter)) {
  167. throw new Error('Empty or invalid filter not allowed with requireFilter enabled');
  168. }
  169. }
  170. /*!
  171. * inherit mquery
  172. */
  173. Query.prototype = new mquery();
  174. Query.prototype.constructor = Query;
  175. // Remove some legacy methods that we removed in Mongoose 8, but
  176. // are still in mquery 5.
  177. Query.prototype.count = undefined;
  178. Query.prototype.findOneAndRemove = undefined;
  179. Query.base = mquery.prototype;
  180. /*!
  181. * Overwrite mquery's `_distinct`, because Mongoose uses that name
  182. * to store the field to apply distinct on.
  183. */
  184. Object.defineProperty(Query.prototype, '_distinct', {
  185. configurable: true,
  186. writable: true,
  187. enumerable: true,
  188. value: undefined
  189. });
  190. /**
  191. * Flag to opt out of using `$geoWithin`.
  192. *
  193. * ```javascript
  194. * mongoose.Query.use$geoWithin = false;
  195. * ```
  196. *
  197. * MongoDB 2.4 deprecated the use of `$within`, replacing it with `$geoWithin`. Mongoose uses `$geoWithin` by default (which is 100% backward compatible with `$within`). If you are running an older version of MongoDB, set this flag to `false` so your `within()` queries continue to work.
  198. *
  199. * @see geoWithin https://www.mongodb.com/docs/manual/reference/operator/geoWithin/
  200. * @default true
  201. * @property use$geoWithin
  202. * @memberOf Query
  203. * @static
  204. * @api public
  205. */
  206. Query.use$geoWithin = mquery.use$geoWithin;
  207. /**
  208. * Converts this query to a customized, reusable query constructor with all arguments and options retained.
  209. *
  210. * #### Example:
  211. *
  212. * // Create a query for adventure movies and read from the primary
  213. * // node in the replica-set unless it is down, in which case we'll
  214. * // read from a secondary node.
  215. * const query = Movie.find({ tags: 'adventure' }).read('primaryPreferred');
  216. *
  217. * // create a custom Query constructor based off these settings
  218. * const Adventure = query.toConstructor();
  219. *
  220. * // further narrow down our query results while still using the previous settings
  221. * await Adventure().where({ name: /^Life/ }).exec();
  222. *
  223. * // since Adventure is a stand-alone constructor we can also add our own
  224. * // helper methods and getters without impacting global queries
  225. * Adventure.prototype.startsWith = function (prefix) {
  226. * this.where({ name: new RegExp('^' + prefix) })
  227. * return this;
  228. * }
  229. * Object.defineProperty(Adventure.prototype, 'highlyRated', {
  230. * get: function () {
  231. * this.where({ rating: { $gt: 4.5 }});
  232. * return this;
  233. * }
  234. * })
  235. * await Adventure().highlyRated.startsWith('Life').exec();
  236. *
  237. * @return {Query} subclass-of-Query
  238. * @api public
  239. */
  240. Query.prototype.toConstructor = function toConstructor() {
  241. const model = this.model;
  242. const coll = this.mongooseCollection;
  243. const CustomQuery = function(criteria, options) {
  244. if (!(this instanceof CustomQuery)) {
  245. return new CustomQuery(criteria, options);
  246. }
  247. this._mongooseOptions = clone(p._mongooseOptions);
  248. Query.call(this, criteria, options || null, model, coll);
  249. };
  250. util.inherits(CustomQuery, model.Query);
  251. // set inherited defaults
  252. const p = CustomQuery.prototype;
  253. p.options = {};
  254. // Need to handle `sort()` separately because entries-style `sort()` syntax
  255. // `sort([['prop1', 1]])` confuses mquery into losing the outer nested array.
  256. // See gh-8159
  257. const options = Object.assign({}, this.options);
  258. if (options.sort != null) {
  259. p.sort(options.sort);
  260. delete options.sort;
  261. }
  262. p.setOptions(options);
  263. p.op = this.op;
  264. p._conditions = clone(this._conditions);
  265. p._fields = clone(this._fields);
  266. p._update = clone(this._update, {
  267. flattenDecimals: false
  268. });
  269. p._path = this._path;
  270. p._distinct = this._distinct;
  271. p._collection = this._collection;
  272. p._mongooseOptions = this._mongooseOptions;
  273. return CustomQuery;
  274. };
  275. /**
  276. * Make a copy of this query so you can re-execute it.
  277. *
  278. * #### Example:
  279. *
  280. * const q = Book.findOne({ title: 'Casino Royale' });
  281. * await q.exec();
  282. * await q.exec(); // Throws an error because you can't execute a query twice
  283. *
  284. * await q.clone().exec(); // Works
  285. *
  286. * @method clone
  287. * @return {Query} copy
  288. * @memberOf Query
  289. * @instance
  290. * @api public
  291. */
  292. Query.prototype.clone = function() {
  293. const model = this.model;
  294. const collection = this.mongooseCollection;
  295. const q = new this.model.Query({}, {}, model, collection);
  296. // Need to handle `sort()` separately because entries-style `sort()` syntax
  297. // `sort([['prop1', 1]])` confuses mquery into losing the outer nested array.
  298. // See gh-8159
  299. const options = Object.assign({}, this.options);
  300. if (options.sort != null) {
  301. q.sort(options.sort);
  302. delete options.sort;
  303. }
  304. q.setOptions(options);
  305. q.op = this.op;
  306. q._conditions = clone(this._conditions);
  307. q._fields = clone(this._fields);
  308. q._update = clone(this._update, {
  309. flattenDecimals: false
  310. });
  311. q._path = this._path;
  312. q._distinct = this._distinct;
  313. q._collection = this._collection;
  314. q._mongooseOptions = this._mongooseOptions;
  315. return q;
  316. };
  317. /**
  318. * Specifies a javascript function or expression to pass to MongoDBs query system.
  319. *
  320. * #### Example:
  321. *
  322. * query.$where('this.comments.length === 10 || this.name.length === 5')
  323. *
  324. * // or
  325. *
  326. * query.$where(function () {
  327. * return this.comments.length === 10 || this.name.length === 5;
  328. * })
  329. *
  330. * #### Note:
  331. *
  332. * Only use `$where` when you have a condition that cannot be met using other MongoDB operators like `$lt`.
  333. * **Be sure to read about all of [its caveats](https://www.mongodb.com/docs/manual/reference/operator/where/) before using.**
  334. *
  335. * @see $where https://www.mongodb.com/docs/manual/reference/operator/where/
  336. * @method $where
  337. * @param {String|Function} js javascript string or function
  338. * @return {Query} this
  339. * @memberOf Query
  340. * @instance
  341. * @method $where
  342. * @api public
  343. */
  344. /**
  345. * Specifies a `path` for use with chaining.
  346. *
  347. * #### Example:
  348. *
  349. * // instead of writing:
  350. * User.find({age: {$gte: 21, $lte: 65}});
  351. *
  352. * // we can instead write:
  353. * User.where('age').gte(21).lte(65);
  354. *
  355. * // passing query conditions is permitted
  356. * User.find().where({ name: 'vonderful' })
  357. *
  358. * // chaining
  359. * User
  360. * .where('age').gte(21).lte(65)
  361. * .where('name', /^vonderful/i)
  362. * .where('friends').slice(10)
  363. * .exec()
  364. *
  365. * @method where
  366. * @memberOf Query
  367. * @instance
  368. * @param {String|Object} [path]
  369. * @param {any} [val]
  370. * @return {Query} this
  371. * @api public
  372. */
  373. /**
  374. * Specifies a `$slice` projection for an array.
  375. *
  376. * #### Example:
  377. *
  378. * query.slice('comments', 5); // Returns the first 5 comments
  379. * query.slice('comments', -5); // Returns the last 5 comments
  380. * query.slice('comments', [10, 5]); // Returns the first 5 comments after the 10-th
  381. * query.where('comments').slice(5); // Returns the first 5 comments
  382. * query.where('comments').slice([-10, 5]); // Returns the first 5 comments after the 10-th to last
  383. *
  384. * **Note:** If the absolute value of the number of elements to be sliced is greater than the number of elements in the array, all array elements will be returned.
  385. *
  386. * // Given `arr`: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  387. * query.slice('arr', 20); // Returns [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  388. * query.slice('arr', -20); // Returns [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  389. *
  390. * **Note:** If the number of elements to skip is positive and greater than the number of elements in the array, an empty array will be returned.
  391. *
  392. * // Given `arr`: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  393. * query.slice('arr', [20, 5]); // Returns []
  394. *
  395. * **Note:** If the number of elements to skip is negative and its absolute value is greater than the number of elements in the array, the starting position is the start of the array.
  396. *
  397. * // Given `arr`: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  398. * query.slice('arr', [-20, 5]); // Returns [1, 2, 3, 4, 5]
  399. *
  400. * @method slice
  401. * @memberOf Query
  402. * @instance
  403. * @param {String} [path]
  404. * @param {Number|Array} val number of elements to slice or array with number of elements to skip and number of elements to slice
  405. * @return {Query} this
  406. * @see mongodb https://www.mongodb.com/docs/manual/tutorial/query-documents/#projection
  407. * @see $slice https://www.mongodb.com/docs/manual/reference/projection/slice/#prj._S_slice
  408. * @api public
  409. */
  410. Query.prototype.slice = function(...args) {
  411. if (args.length === 0) {
  412. return this;
  413. }
  414. this._validate('slice');
  415. let path;
  416. let val;
  417. if (args.length === 1) {
  418. const arg = args[0];
  419. if (typeof arg === 'object' && !Array.isArray(arg)) {
  420. const keys = Object.keys(arg);
  421. const numKeys = keys.length;
  422. for (let i = 0; i < numKeys; ++i) {
  423. this.slice(keys[i], arg[keys[i]]);
  424. }
  425. return this;
  426. }
  427. this._ensurePath('slice');
  428. path = this._path;
  429. val = args[0];
  430. } else if (args.length === 2) {
  431. if ('number' === typeof args[0]) {
  432. this._ensurePath('slice');
  433. path = this._path;
  434. val = [args[0], args[1]];
  435. } else {
  436. path = args[0];
  437. val = args[1];
  438. }
  439. } else if (args.length === 3) {
  440. path = args[0];
  441. val = [args[1], args[2]];
  442. }
  443. const p = {};
  444. p[path] = { $slice: val };
  445. this.select(p);
  446. return this;
  447. };
  448. /*!
  449. * ignore
  450. */
  451. const validOpsSet = new Set(queryMiddlewareFunctions);
  452. Query.prototype._validateOp = function() {
  453. if (this.op != null && !validOpsSet.has(this.op)) {
  454. this.error(new Error('Query has invalid `op`: "' + this.op + '"'));
  455. }
  456. if (this.op !== 'estimatedDocumentCount' && this._conditions == null) {
  457. throw new ObjectParameterError(this._conditions, 'filter', this.op);
  458. }
  459. };
  460. /**
  461. * Specifies the complementary comparison value for paths specified with `where()`
  462. *
  463. * #### Example:
  464. *
  465. * User.where('age').equals(49);
  466. *
  467. * // is the same as
  468. *
  469. * User.where('age', 49);
  470. *
  471. * @method equals
  472. * @memberOf Query
  473. * @instance
  474. * @param {Object} val
  475. * @return {Query} this
  476. * @api public
  477. */
  478. /**
  479. * Specifies arguments for an `$or` condition.
  480. *
  481. * #### Example:
  482. *
  483. * query.or([{ color: 'red' }, { status: 'emergency' }]);
  484. *
  485. * @see $or https://www.mongodb.com/docs/manual/reference/operator/or/
  486. * @method or
  487. * @memberOf Query
  488. * @instance
  489. * @param {Array} array array of conditions
  490. * @return {Query} this
  491. * @api public
  492. */
  493. /**
  494. * Specifies arguments for a `$nor` condition.
  495. *
  496. * #### Example:
  497. *
  498. * query.nor([{ color: 'green' }, { status: 'ok' }]);
  499. *
  500. * @see $nor https://www.mongodb.com/docs/manual/reference/operator/nor/
  501. * @method nor
  502. * @memberOf Query
  503. * @instance
  504. * @param {Array} array array of conditions
  505. * @return {Query} this
  506. * @api public
  507. */
  508. /**
  509. * Specifies arguments for a `$and` condition.
  510. *
  511. * #### Example:
  512. *
  513. * query.and([{ color: 'green' }, { status: 'ok' }])
  514. *
  515. * @method and
  516. * @memberOf Query
  517. * @instance
  518. * @see $and https://www.mongodb.com/docs/manual/reference/operator/and/
  519. * @param {Array} array array of conditions
  520. * @return {Query} this
  521. * @api public
  522. */
  523. /**
  524. * Specifies a `$gt` query condition.
  525. *
  526. * When called with one argument, the most recent path passed to `where()` is used.
  527. *
  528. * #### Example:
  529. *
  530. * Thing.find().where('age').gt(21);
  531. *
  532. * // or
  533. * Thing.find().gt('age', 21);
  534. *
  535. * @method gt
  536. * @memberOf Query
  537. * @instance
  538. * @param {String} [path]
  539. * @param {Number} val
  540. * @see $gt https://www.mongodb.com/docs/manual/reference/operator/gt/
  541. * @api public
  542. */
  543. /**
  544. * Specifies a `$gte` query condition.
  545. *
  546. * When called with one argument, the most recent path passed to `where()` is used.
  547. *
  548. * @method gte
  549. * @memberOf Query
  550. * @instance
  551. * @param {String} [path]
  552. * @param {Number} val
  553. * @see $gte https://www.mongodb.com/docs/manual/reference/operator/gte/
  554. * @api public
  555. */
  556. /**
  557. * Specifies a `$lt` query condition.
  558. *
  559. * When called with one argument, the most recent path passed to `where()` is used.
  560. *
  561. * @method lt
  562. * @memberOf Query
  563. * @instance
  564. * @param {String} [path]
  565. * @param {Number} val
  566. * @see $lt https://www.mongodb.com/docs/manual/reference/operator/lt/
  567. * @api public
  568. */
  569. /**
  570. * Specifies a `$lte` query condition.
  571. *
  572. * When called with one argument, the most recent path passed to `where()` is used.
  573. *
  574. * @method lte
  575. * @see $lte https://www.mongodb.com/docs/manual/reference/operator/lte/
  576. * @memberOf Query
  577. * @instance
  578. * @param {String} [path]
  579. * @param {Number} val
  580. * @api public
  581. */
  582. /**
  583. * Specifies a `$ne` query condition.
  584. *
  585. * When called with one argument, the most recent path passed to `where()` is used.
  586. *
  587. * @see $ne https://www.mongodb.com/docs/manual/reference/operator/ne/
  588. * @method ne
  589. * @memberOf Query
  590. * @instance
  591. * @param {String} [path]
  592. * @param {any} val
  593. * @api public
  594. */
  595. /**
  596. * Specifies an `$in` query condition.
  597. *
  598. * When called with one argument, the most recent path passed to `where()` is used.
  599. *
  600. * @see $in https://www.mongodb.com/docs/manual/reference/operator/in/
  601. * @method in
  602. * @memberOf Query
  603. * @instance
  604. * @param {String} [path]
  605. * @param {Array} val
  606. * @api public
  607. */
  608. /**
  609. * Specifies an `$nin` query condition.
  610. *
  611. * When called with one argument, the most recent path passed to `where()` is used.
  612. *
  613. * @see $nin https://www.mongodb.com/docs/manual/reference/operator/nin/
  614. * @method nin
  615. * @memberOf Query
  616. * @instance
  617. * @param {String} [path]
  618. * @param {Array} val
  619. * @api public
  620. */
  621. /**
  622. * Specifies an `$all` query condition.
  623. *
  624. * When called with one argument, the most recent path passed to `where()` is used.
  625. *
  626. * #### Example:
  627. *
  628. * MyModel.find().where('pets').all(['dog', 'cat', 'ferret']);
  629. * // Equivalent:
  630. * MyModel.find().all('pets', ['dog', 'cat', 'ferret']);
  631. *
  632. * @see $all https://www.mongodb.com/docs/manual/reference/operator/all/
  633. * @method all
  634. * @memberOf Query
  635. * @instance
  636. * @param {String} [path]
  637. * @param {Array} val
  638. * @api public
  639. */
  640. /**
  641. * Specifies a `$size` query condition.
  642. *
  643. * When called with one argument, the most recent path passed to `where()` is used.
  644. *
  645. * #### Example:
  646. *
  647. * const docs = await MyModel.where('tags').size(0).exec();
  648. * assert(Array.isArray(docs));
  649. * console.log('documents with 0 tags', docs);
  650. *
  651. * @see $size https://www.mongodb.com/docs/manual/reference/operator/size/
  652. * @method size
  653. * @memberOf Query
  654. * @instance
  655. * @param {String} [path]
  656. * @param {Number} val
  657. * @api public
  658. */
  659. /**
  660. * Specifies a `$regex` query condition.
  661. *
  662. * When called with one argument, the most recent path passed to `where()` is used.
  663. *
  664. * @see $regex https://www.mongodb.com/docs/manual/reference/operator/regex/
  665. * @method regex
  666. * @memberOf Query
  667. * @instance
  668. * @param {String} [path]
  669. * @param {String|RegExp} val
  670. * @api public
  671. */
  672. /**
  673. * Specifies a `maxDistance` query condition.
  674. *
  675. * When called with one argument, the most recent path passed to `where()` is used.
  676. *
  677. * @see $maxDistance https://www.mongodb.com/docs/manual/reference/operator/maxDistance/
  678. * @method maxDistance
  679. * @memberOf Query
  680. * @instance
  681. * @param {String} [path]
  682. * @param {Number} val
  683. * @api public
  684. */
  685. /**
  686. * Specifies a `$mod` condition, filters documents for documents whose
  687. * `path` property is a number that is equal to `remainder` modulo `divisor`.
  688. *
  689. * #### Example:
  690. *
  691. * // All find products whose inventory is odd
  692. * Product.find().mod('inventory', [2, 1]);
  693. * Product.find().where('inventory').mod([2, 1]);
  694. * // This syntax is a little strange, but supported.
  695. * Product.find().where('inventory').mod(2, 1);
  696. *
  697. * @method mod
  698. * @memberOf Query
  699. * @instance
  700. * @param {String} [path]
  701. * @param {Array} val must be of length 2, first element is `divisor`, 2nd element is `remainder`.
  702. * @return {Query} this
  703. * @see $mod https://www.mongodb.com/docs/manual/reference/operator/mod/
  704. * @api public
  705. */
  706. Query.prototype.mod = function() {
  707. let val;
  708. let path;
  709. if (arguments.length === 1) {
  710. this._ensurePath('mod');
  711. val = arguments[0];
  712. path = this._path;
  713. } else if (arguments.length === 2 && !Array.isArray(arguments[1])) {
  714. this._ensurePath('mod');
  715. val = [arguments[0], arguments[1]];
  716. path = this._path;
  717. } else if (arguments.length === 3) {
  718. val = [arguments[1], arguments[2]];
  719. path = arguments[0];
  720. } else {
  721. val = arguments[1];
  722. path = arguments[0];
  723. }
  724. const conds = this._conditions[path] || (this._conditions[path] = {});
  725. conds.$mod = val;
  726. return this;
  727. };
  728. /**
  729. * Specifies an `$exists` condition
  730. *
  731. * #### Example:
  732. *
  733. * // { name: { $exists: true }}
  734. * Thing.where('name').exists()
  735. * Thing.where('name').exists(true)
  736. * Thing.find().exists('name')
  737. *
  738. * // { name: { $exists: false }}
  739. * Thing.where('name').exists(false);
  740. * Thing.find().exists('name', false);
  741. *
  742. * @method exists
  743. * @memberOf Query
  744. * @instance
  745. * @param {String} [path]
  746. * @param {Boolean} val
  747. * @return {Query} this
  748. * @see $exists https://www.mongodb.com/docs/manual/reference/operator/exists/
  749. * @api public
  750. */
  751. /**
  752. * Specifies an `$elemMatch` condition
  753. *
  754. * #### Example:
  755. *
  756. * query.elemMatch('comment', { author: 'autobot', votes: {$gte: 5}})
  757. *
  758. * query.where('comment').elemMatch({ author: 'autobot', votes: {$gte: 5}})
  759. *
  760. * query.elemMatch('comment', function (elem) {
  761. * elem.where('author').equals('autobot');
  762. * elem.where('votes').gte(5);
  763. * })
  764. *
  765. * query.where('comment').elemMatch(function (elem) {
  766. * elem.where({ author: 'autobot' });
  767. * elem.where('votes').gte(5);
  768. * })
  769. *
  770. * @method elemMatch
  771. * @memberOf Query
  772. * @instance
  773. * @param {String|Object|Function} path
  774. * @param {Object|Function} filter
  775. * @return {Query} this
  776. * @see $elemMatch https://www.mongodb.com/docs/manual/reference/operator/elemMatch/
  777. * @api public
  778. */
  779. /**
  780. * Defines a `$within` or `$geoWithin` argument for geo-spatial queries.
  781. *
  782. * #### Example:
  783. *
  784. * query.where(path).within().box()
  785. * query.where(path).within().circle()
  786. * query.where(path).within().geometry()
  787. *
  788. * query.where('loc').within({ center: [50,50], radius: 10, unique: true, spherical: true });
  789. * query.where('loc').within({ box: [[40.73, -73.9], [40.7, -73.988]] });
  790. * query.where('loc').within({ polygon: [[],[],[],[]] });
  791. *
  792. * query.where('loc').within([], [], []) // polygon
  793. * query.where('loc').within([], []) // box
  794. * query.where('loc').within({ type: 'LineString', coordinates: [...] }); // geometry
  795. *
  796. * **MUST** be used after `where()`.
  797. *
  798. * #### Note:
  799. *
  800. * As of Mongoose 3.7, `$geoWithin` is always used for queries. To change this behavior, see [Query.use$geoWithin](https://mongoosejs.com/docs/api/query.html#Query.prototype.use$geoWithin).
  801. *
  802. * #### Note:
  803. *
  804. * In Mongoose 3.7, `within` changed from a getter to a function. If you need the old syntax, use [this](https://github.com/ebensing/mongoose-within).
  805. *
  806. * @method within
  807. * @see $polygon https://www.mongodb.com/docs/manual/reference/operator/polygon/
  808. * @see $box https://www.mongodb.com/docs/manual/reference/operator/box/
  809. * @see $geometry https://www.mongodb.com/docs/manual/reference/operator/geometry/
  810. * @see $center https://www.mongodb.com/docs/manual/reference/operator/center/
  811. * @see $centerSphere https://www.mongodb.com/docs/manual/reference/operator/centerSphere/
  812. * @memberOf Query
  813. * @instance
  814. * @return {Query} this
  815. * @api public
  816. */
  817. /**
  818. * Specifies the maximum number of documents the query will return.
  819. *
  820. * #### Example:
  821. *
  822. * query.limit(20);
  823. *
  824. * #### Note:
  825. *
  826. * Cannot be used with `distinct()`
  827. *
  828. * @method limit
  829. * @memberOf Query
  830. * @instance
  831. * @param {Number} val
  832. * @api public
  833. */
  834. Query.prototype.limit = function limit(v) {
  835. this._validate('limit');
  836. if (typeof v === 'string') {
  837. try {
  838. v = castNumber(v);
  839. } catch {
  840. throw new CastError('Number', v, 'limit');
  841. }
  842. }
  843. this.options.limit = v;
  844. return this;
  845. };
  846. /**
  847. * Specifies the number of documents to skip.
  848. *
  849. * #### Example:
  850. *
  851. * query.skip(100).limit(20);
  852. *
  853. * #### Note:
  854. *
  855. * Cannot be used with `distinct()`
  856. *
  857. * @method skip
  858. * @memberOf Query
  859. * @instance
  860. * @param {Number} val
  861. * @see cursor.skip https://www.mongodb.com/docs/manual/reference/method/cursor.skip/
  862. * @api public
  863. */
  864. Query.prototype.skip = function skip(v) {
  865. this._validate('skip');
  866. if (typeof v === 'string') {
  867. try {
  868. v = castNumber(v);
  869. } catch {
  870. throw new CastError('Number', v, 'skip');
  871. }
  872. }
  873. this.options.skip = v;
  874. return this;
  875. };
  876. /**
  877. * Specifies the batchSize option.
  878. *
  879. * #### Example:
  880. *
  881. * query.batchSize(100)
  882. *
  883. * #### Note:
  884. *
  885. * Cannot be used with `distinct()`
  886. *
  887. * @method batchSize
  888. * @memberOf Query
  889. * @instance
  890. * @param {Number} val
  891. * @see batchSize https://www.mongodb.com/docs/manual/reference/method/cursor.batchSize/
  892. * @api public
  893. */
  894. /**
  895. * Specifies the `comment` option.
  896. *
  897. * #### Example:
  898. *
  899. * query.comment('login query')
  900. *
  901. * #### Note:
  902. *
  903. * Cannot be used with `distinct()`
  904. *
  905. * @method comment
  906. * @memberOf Query
  907. * @instance
  908. * @param {String} val
  909. * @see comment https://www.mongodb.com/docs/manual/reference/operator/comment/
  910. * @api public
  911. */
  912. /**
  913. * Sets query hints.
  914. *
  915. * #### Example:
  916. *
  917. * query.hint({ indexA: 1, indexB: -1 });
  918. *
  919. * #### Note:
  920. *
  921. * Cannot be used with `distinct()`
  922. *
  923. * @method hint
  924. * @memberOf Query
  925. * @instance
  926. * @param {Object} val a hint object
  927. * @return {Query} this
  928. * @see $hint https://www.mongodb.com/docs/manual/reference/operator/hint/
  929. * @api public
  930. */
  931. /**
  932. * Get/set the current projection (AKA fields). Pass `null` to remove the
  933. * current projection.
  934. *
  935. * Unlike `projection()`, the `select()` function modifies the current
  936. * projection in place. This function overwrites the existing projection.
  937. *
  938. * #### Example:
  939. *
  940. * const q = Model.find();
  941. * q.projection(); // null
  942. *
  943. * q.select('a b');
  944. * q.projection(); // { a: 1, b: 1 }
  945. *
  946. * q.projection({ c: 1 });
  947. * q.projection(); // { c: 1 }
  948. *
  949. * q.projection(null);
  950. * q.projection(); // null
  951. *
  952. *
  953. * @method projection
  954. * @memberOf Query
  955. * @instance
  956. * @param {Object|null} arg
  957. * @return {Object} the current projection
  958. * @api public
  959. */
  960. Query.prototype.projection = function(arg) {
  961. if (arguments.length === 0) {
  962. return this._fields;
  963. }
  964. this._fields = {};
  965. this._userProvidedFields = {};
  966. this.select(arg);
  967. return this._fields;
  968. };
  969. /**
  970. * Specifies which document fields to include or exclude (also known as the query "projection")
  971. *
  972. * When using string syntax, prefixing a path with `-` will flag that path as excluded. When a path does not have the `-` prefix, it is included. Lastly, if a path is prefixed with `+`, it forces inclusion of the path, which is useful for paths excluded at the [schema level](https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.select()).
  973. *
  974. * A projection _must_ be either inclusive or exclusive. In other words, you must
  975. * either list the fields to include (which excludes all others), or list the fields
  976. * to exclude (which implies all other fields are included). The [`_id` field is the only exception because MongoDB includes it by default](https://www.mongodb.com/docs/manual/tutorial/project-fields-from-query-results/#suppress-id-field).
  977. *
  978. * #### Example:
  979. *
  980. * // include a and b, exclude other fields
  981. * query.select('a b');
  982. * // Equivalent syntaxes:
  983. * query.select(['a', 'b']);
  984. * query.select({ a: 1, b: 1 });
  985. *
  986. * // exclude c and d, include other fields
  987. * query.select('-c -d');
  988. *
  989. * // Use `+` to override schema-level `select: false` without making the
  990. * // projection inclusive.
  991. * const schema = new Schema({
  992. * foo: { type: String, select: false },
  993. * bar: String
  994. * });
  995. * // ...
  996. * query.select('+foo'); // Override foo's `select: false` without excluding `bar`
  997. *
  998. * // or you may use object notation, useful when
  999. * // you have keys already prefixed with a "-"
  1000. * query.select({ a: 1, b: 1 });
  1001. * query.select({ c: 0, d: 0 });
  1002. *
  1003. * Additional calls to select can override the previous selection:
  1004. * query.select({ a: 1, b: 1 }).select({ b: 0 }); // selection is now { a: 1 }
  1005. * query.select({ a: 0, b: 0 }).select({ b: 1 }); // selection is now { a: 0 }
  1006. *
  1007. *
  1008. * @method select
  1009. * @memberOf Query
  1010. * @instance
  1011. * @param {Object|String|String[]} arg
  1012. * @return {Query} this
  1013. * @see SchemaType https://mongoosejs.com/docs/api/schematype.html
  1014. * @api public
  1015. */
  1016. Query.prototype.select = function select() {
  1017. let arg = arguments[0];
  1018. if (!arg) return this;
  1019. if (arguments.length !== 1) {
  1020. throw new Error('Invalid select: select only takes 1 argument');
  1021. }
  1022. this._validate('select');
  1023. const fields = this._fields || (this._fields = {});
  1024. const userProvidedFields = this._userProvidedFields || (this._userProvidedFields = {});
  1025. let sanitizeProjection = undefined;
  1026. if (this.model != null && utils.hasUserDefinedProperty(this.model.db.options, 'sanitizeProjection')) {
  1027. sanitizeProjection = this.model.db.options.sanitizeProjection;
  1028. } else if (this.model != null && utils.hasUserDefinedProperty(this.model.base.options, 'sanitizeProjection')) {
  1029. sanitizeProjection = this.model.base.options.sanitizeProjection;
  1030. } else {
  1031. sanitizeProjection = this._mongooseOptions.sanitizeProjection;
  1032. }
  1033. function sanitizeValue(value) {
  1034. return typeof value === 'string' && sanitizeProjection ? value = 1 : value;
  1035. }
  1036. arg = parseProjection(arg, true); // we want to keep the minus and pluses, so add boolean arg.
  1037. if (utils.isObject(arg)) {
  1038. if (this.selectedInclusively()) {
  1039. Object.entries(arg).forEach(([key, value]) => {
  1040. if (value) {
  1041. // Add the field to the projection
  1042. if (fields['-' + key] != null) {
  1043. delete fields['-' + key];
  1044. }
  1045. fields[key] = userProvidedFields[key] = sanitizeValue(value);
  1046. } else {
  1047. // Remove the field from the projection
  1048. Object.keys(userProvidedFields).forEach(field => {
  1049. if (isSubpath(key, field)) {
  1050. delete fields[field];
  1051. delete userProvidedFields[field];
  1052. }
  1053. });
  1054. }
  1055. });
  1056. } else if (this.selectedExclusively()) {
  1057. Object.entries(arg).forEach(([key, value]) => {
  1058. if (!value) {
  1059. // Add the field to the projection
  1060. if (fields['+' + key] != null) {
  1061. delete fields['+' + key];
  1062. }
  1063. fields[key] = userProvidedFields[key] = sanitizeValue(value);
  1064. } else {
  1065. // Remove the field from the projection
  1066. Object.keys(userProvidedFields).forEach(field => {
  1067. if (isSubpath(key, field)) {
  1068. delete fields[field];
  1069. delete userProvidedFields[field];
  1070. }
  1071. });
  1072. }
  1073. });
  1074. } else {
  1075. const keys = Object.keys(arg);
  1076. for (let i = 0; i < keys.length; ++i) {
  1077. const value = arg[keys[i]];
  1078. const key = keys[i];
  1079. fields[key] = sanitizeValue(value);
  1080. userProvidedFields[key] = sanitizeValue(value);
  1081. }
  1082. }
  1083. return this;
  1084. }
  1085. throw new TypeError('Invalid select() argument. Must be string or object.');
  1086. };
  1087. /**
  1088. * Enable or disable schema level projections for this query. Enabled by default.
  1089. * Set to `false` to include fields with `select: false` in the query result by default.
  1090. *
  1091. * #### Example:
  1092. *
  1093. * const userSchema = new Schema({
  1094. * email: { type: String, required: true },
  1095. * passwordHash: { type: String, select: false, required: true }
  1096. * });
  1097. * const UserModel = mongoose.model('User', userSchema);
  1098. *
  1099. * const doc = await UserModel.findOne().orFail().schemaLevelProjections(false);
  1100. *
  1101. * // Contains password hash, because `schemaLevelProjections()` overrides `select: false`
  1102. * doc.passwordHash;
  1103. *
  1104. * @method schemaLevelProjections
  1105. * @memberOf Query
  1106. * @instance
  1107. * @param {Boolean} value
  1108. * @return {Query} this
  1109. * @see SchemaTypeOptions https://mongoosejs.com/docs/schematypes.html#all-schema-types
  1110. * @api public
  1111. */
  1112. Query.prototype.schemaLevelProjections = function schemaLevelProjections(value) {
  1113. this._mongooseOptions.schemaLevelProjections = value;
  1114. return this;
  1115. };
  1116. /**
  1117. * Sets this query's `sanitizeProjection` option. If set, `sanitizeProjection` does
  1118. * two things:
  1119. *
  1120. * 1. Enforces that projection values are numbers, not strings.
  1121. * 2. Prevents using `+` syntax to override properties that are deselected by default.
  1122. *
  1123. * With `sanitizeProjection()`, you can pass potentially untrusted user data to `.select()`.
  1124. *
  1125. * #### Example
  1126. *
  1127. * const userSchema = new Schema({
  1128. * name: String,
  1129. * password: { type: String, select: false }
  1130. * });
  1131. * const UserModel = mongoose.model('User', userSchema);
  1132. * const { _id } = await UserModel.create({ name: 'John', password: 'secret' })
  1133. *
  1134. * // The MongoDB server has special handling for string values that start with '$'
  1135. * // in projections, which can lead to unexpected leaking of sensitive data.
  1136. * let doc = await UserModel.findOne().select({ name: '$password' });
  1137. * doc.name; // 'secret'
  1138. * doc.password; // undefined
  1139. *
  1140. * // With `sanitizeProjection`, Mongoose forces all projection values to be numbers
  1141. * doc = await UserModel.findOne().sanitizeProjection(true).select({ name: '$password' });
  1142. * doc.name; // 'John'
  1143. * doc.password; // undefined
  1144. *
  1145. * // By default, Mongoose supports projecting in `password` using `+password`
  1146. * doc = await UserModel.findOne().select('+password');
  1147. * doc.password; // 'secret'
  1148. *
  1149. * // With `sanitizeProjection`, Mongoose prevents projecting in `password` and other
  1150. * // fields that have `select: false` in the schema.
  1151. * doc = await UserModel.findOne().sanitizeProjection(true).select('+password');
  1152. * doc.password; // undefined
  1153. *
  1154. * @method sanitizeProjection
  1155. * @memberOf Query
  1156. * @instance
  1157. * @param {Boolean} value
  1158. * @return {Query} this
  1159. * @see sanitizeProjection https://thecodebarbarian.com/whats-new-in-mongoose-5-13-sanitizeprojection.html
  1160. * @api public
  1161. */
  1162. Query.prototype.sanitizeProjection = function sanitizeProjection(value) {
  1163. this._mongooseOptions.sanitizeProjection = value;
  1164. return this;
  1165. };
  1166. /**
  1167. * Determines the MongoDB nodes from which to read.
  1168. *
  1169. * #### Preferences:
  1170. *
  1171. * ```
  1172. * primary - (default) Read from primary only. Operations will produce an error if primary is unavailable. Cannot be combined with tags.
  1173. * secondary Read from secondary if available, otherwise error.
  1174. * primaryPreferred Read from primary if available, otherwise a secondary.
  1175. * secondaryPreferred Read from a secondary if available, otherwise read from the primary.
  1176. * nearest All operations read from among the nearest candidates, but unlike other modes, this option will include both the primary and all secondaries in the random selection.
  1177. * ```
  1178. *
  1179. * Aliases
  1180. *
  1181. * ```
  1182. * p primary
  1183. * pp primaryPreferred
  1184. * s secondary
  1185. * sp secondaryPreferred
  1186. * n nearest
  1187. * ```
  1188. *
  1189. * #### Example:
  1190. *
  1191. * new Query().read('primary')
  1192. * new Query().read('p') // same as primary
  1193. *
  1194. * new Query().read('primaryPreferred')
  1195. * new Query().read('pp') // same as primaryPreferred
  1196. *
  1197. * new Query().read('secondary')
  1198. * new Query().read('s') // same as secondary
  1199. *
  1200. * new Query().read('secondaryPreferred')
  1201. * new Query().read('sp') // same as secondaryPreferred
  1202. *
  1203. * new Query().read('nearest')
  1204. * new Query().read('n') // same as nearest
  1205. *
  1206. * // read from secondaries with matching tags
  1207. * new Query().read('s', [{ dc:'sf', s: 1 },{ dc:'ma', s: 2 }])
  1208. *
  1209. * Read more about how to use read preferences [here](https://www.mongodb.com/docs/manual/applications/replication/#read-preference).
  1210. *
  1211. * @method read
  1212. * @memberOf Query
  1213. * @instance
  1214. * @param {String} mode one of the listed preference options or aliases
  1215. * @param {Array} [tags] optional tags for this query
  1216. * @see mongodb https://www.mongodb.com/docs/manual/applications/replication/#read-preference
  1217. * @return {Query} this
  1218. * @api public
  1219. */
  1220. Query.prototype.read = function read(mode, tags) {
  1221. if (typeof mode === 'string') {
  1222. mode = handleReadPreferenceAliases(mode);
  1223. this.options.readPreference = { mode, tags };
  1224. } else {
  1225. this.options.readPreference = mode;
  1226. }
  1227. return this;
  1228. };
  1229. /**
  1230. * Overwrite default `.toString` to make logging more useful
  1231. *
  1232. * @memberOf Query
  1233. * @instance
  1234. * @method toString
  1235. * @api private
  1236. */
  1237. Query.prototype.toString = function toString() {
  1238. if (this.op === 'count' ||
  1239. this.op === 'countDocuments' ||
  1240. this.op === 'find' ||
  1241. this.op === 'findOne' ||
  1242. this.op === 'deleteMany' ||
  1243. this.op === 'deleteOne' ||
  1244. this.op === 'findOneAndDelete' ||
  1245. this.op === 'remove') {
  1246. return `${this.model.modelName}.${this.op}(${util.inspect(this._conditions)})`;
  1247. }
  1248. if (this.op === 'distinct') {
  1249. return `${this.model.modelName}.distinct('${this._distinct}', ${util.inspect(this._conditions)})`;
  1250. }
  1251. if (this.op === 'findOneAndReplace' ||
  1252. this.op === 'findOneAndUpdate' ||
  1253. this.op === 'replaceOne' ||
  1254. this.op === 'update' ||
  1255. this.op === 'updateMany' ||
  1256. this.op === 'updateOne') {
  1257. return `${this.model.modelName}.${this.op}(${util.inspect(this._conditions)}, ${util.inspect(this._update)})`;
  1258. }
  1259. // 'estimatedDocumentCount' or any others
  1260. return `${this.model.modelName}.${this.op}()`;
  1261. };
  1262. /**
  1263. * Sets the [MongoDB session](https://www.mongodb.com/docs/manual/reference/server-sessions/)
  1264. * associated with this query. Sessions are how you mark a query as part of a
  1265. * [transaction](https://mongoosejs.com/docs/transactions.html).
  1266. *
  1267. * Calling `session(null)` removes the session from this query.
  1268. *
  1269. * #### Example:
  1270. *
  1271. * const s = await mongoose.startSession();
  1272. * await mongoose.model('Person').findOne({ name: 'Axl Rose' }).session(s);
  1273. *
  1274. * @method session
  1275. * @memberOf Query
  1276. * @instance
  1277. * @param {ClientSession} [session] from `await conn.startSession()`
  1278. * @see Connection.prototype.startSession() https://mongoosejs.com/docs/api/connection.html#Connection.prototype.startSession()
  1279. * @see mongoose.startSession() https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.startSession()
  1280. * @return {Query} this
  1281. * @api public
  1282. */
  1283. Query.prototype.session = function session(v) {
  1284. if (v == null) {
  1285. delete this.options.session;
  1286. }
  1287. this.options.session = v;
  1288. return this;
  1289. };
  1290. /**
  1291. * Sets the 3 write concern parameters for this query:
  1292. *
  1293. * - `w`: Sets the specified number of `mongod` servers, or tag set of `mongod` servers, that must acknowledge this write before this write is considered successful.
  1294. * - `j`: Boolean, set to `true` to request acknowledgement that this operation has been persisted to MongoDB's on-disk journal.
  1295. * - `wtimeout`: If [`w > 1`](https://mongoosejs.com/docs/api/query.html#Query.prototype.w()), the maximum amount of time to wait for this write to propagate through the replica set before this operation fails. The default is `0`, which means no timeout.
  1296. *
  1297. * This option is only valid for operations that write to the database:
  1298. *
  1299. * - `deleteOne()`
  1300. * - `deleteMany()`
  1301. * - `findOneAndDelete()`
  1302. * - `findOneAndReplace()`
  1303. * - `findOneAndUpdate()`
  1304. * - `updateOne()`
  1305. * - `updateMany()`
  1306. *
  1307. * Defaults to the schema's [`writeConcern` option](https://mongoosejs.com/docs/guide.html#writeConcern)
  1308. *
  1309. * #### Example:
  1310. *
  1311. * // The 'majority' option means the `deleteOne()` promise won't resolve
  1312. * // until the `deleteOne()` has propagated to the majority of the replica set
  1313. * await mongoose.model('Person').
  1314. * deleteOne({ name: 'Ned Stark' }).
  1315. * writeConcern({ w: 'majority' });
  1316. *
  1317. * @method writeConcern
  1318. * @memberOf Query
  1319. * @instance
  1320. * @param {Object} writeConcern the write concern value to set
  1321. * @see WriteConcernSettings https://mongodb.github.io/node-mongodb-native/4.9/interfaces/WriteConcernSettings.html
  1322. * @return {Query} this
  1323. * @api public
  1324. */
  1325. Query.prototype.writeConcern = function writeConcern(val) {
  1326. if (val == null) {
  1327. delete this.options.writeConcern;
  1328. return this;
  1329. }
  1330. this.options.writeConcern = val;
  1331. return this;
  1332. };
  1333. /**
  1334. * Sets the specified number of `mongod` servers, or tag set of `mongod` servers,
  1335. * that must acknowledge this write before this write is considered successful.
  1336. * This option is only valid for operations that write to the database:
  1337. *
  1338. * - `deleteOne()`
  1339. * - `deleteMany()`
  1340. * - `findOneAndDelete()`
  1341. * - `findOneAndReplace()`
  1342. * - `findOneAndUpdate()`
  1343. * - `updateOne()`
  1344. * - `updateMany()`
  1345. *
  1346. * Defaults to the schema's [`writeConcern.w` option](https://mongoosejs.com/docs/guide.html#writeConcern)
  1347. *
  1348. * #### Example:
  1349. *
  1350. * // The 'majority' option means the `deleteOne()` promise won't resolve
  1351. * // until the `deleteOne()` has propagated to the majority of the replica set
  1352. * await mongoose.model('Person').
  1353. * deleteOne({ name: 'Ned Stark' }).
  1354. * w('majority');
  1355. *
  1356. * @method w
  1357. * @memberOf Query
  1358. * @instance
  1359. * @param {String|number} val 0 for fire-and-forget, 1 for acknowledged by one server, 'majority' for majority of the replica set, or [any of the more advanced options](https://www.mongodb.com/docs/manual/reference/write-concern/#w-option).
  1360. * @see mongodb https://www.mongodb.com/docs/manual/reference/write-concern/#w-option
  1361. * @return {Query} this
  1362. * @api public
  1363. */
  1364. Query.prototype.w = function w(val) {
  1365. if (val == null) {
  1366. delete this.options.w;
  1367. }
  1368. if (this.options.writeConcern != null) {
  1369. this.options.writeConcern.w = val;
  1370. } else {
  1371. this.options.w = val;
  1372. }
  1373. return this;
  1374. };
  1375. /**
  1376. * Requests acknowledgement that this operation has been persisted to MongoDB's
  1377. * on-disk journal.
  1378. * This option is only valid for operations that write to the database:
  1379. *
  1380. * - `deleteOne()`
  1381. * - `deleteMany()`
  1382. * - `findOneAndDelete()`
  1383. * - `findOneAndReplace()`
  1384. * - `findOneAndUpdate()`
  1385. * - `updateOne()`
  1386. * - `updateMany()`
  1387. *
  1388. * Defaults to the schema's [`writeConcern.j` option](https://mongoosejs.com/docs/guide.html#writeConcern)
  1389. *
  1390. * #### Example:
  1391. *
  1392. * await mongoose.model('Person').deleteOne({ name: 'Ned Stark' }).j(true);
  1393. *
  1394. * @method j
  1395. * @memberOf Query
  1396. * @instance
  1397. * @param {boolean} val
  1398. * @see mongodb https://www.mongodb.com/docs/manual/reference/write-concern/#j-option
  1399. * @return {Query} this
  1400. * @api public
  1401. */
  1402. Query.prototype.j = function j(val) {
  1403. if (val == null) {
  1404. delete this.options.j;
  1405. }
  1406. if (this.options.writeConcern != null) {
  1407. this.options.writeConcern.j = val;
  1408. } else {
  1409. this.options.j = val;
  1410. }
  1411. return this;
  1412. };
  1413. /**
  1414. * If [`w > 1`](https://mongoosejs.com/docs/api/query.html#Query.prototype.w()), the maximum amount of time to
  1415. * wait for this write to propagate through the replica set before this
  1416. * operation fails. The default is `0`, which means no timeout.
  1417. *
  1418. * This option is only valid for operations that write to the database:
  1419. *
  1420. * - `deleteOne()`
  1421. * - `deleteMany()`
  1422. * - `findOneAndDelete()`
  1423. * - `findOneAndReplace()`
  1424. * - `findOneAndUpdate()`
  1425. * - `updateOne()`
  1426. * - `updateMany()`
  1427. *
  1428. * Defaults to the schema's [`writeConcern.wtimeout` option](https://mongoosejs.com/docs/guide.html#writeConcern)
  1429. *
  1430. * #### Example:
  1431. *
  1432. * // The `deleteOne()` promise won't resolve until this `deleteOne()` has
  1433. * // propagated to at least `w = 2` members of the replica set. If it takes
  1434. * // longer than 1 second, this `deleteOne()` will fail.
  1435. * await mongoose.model('Person').
  1436. * deleteOne({ name: 'Ned Stark' }).
  1437. * w(2).
  1438. * wtimeout(1000);
  1439. *
  1440. * @method wtimeout
  1441. * @memberOf Query
  1442. * @instance
  1443. * @param {number} ms number of milliseconds to wait
  1444. * @see mongodb https://www.mongodb.com/docs/manual/reference/write-concern/#wtimeout
  1445. * @return {Query} this
  1446. * @api public
  1447. */
  1448. Query.prototype.wtimeout = function wtimeout(ms) {
  1449. if (ms == null) {
  1450. delete this.options.wtimeout;
  1451. }
  1452. if (this.options.writeConcern != null) {
  1453. this.options.writeConcern.wtimeout = ms;
  1454. } else {
  1455. this.options.wtimeout = ms;
  1456. }
  1457. return this;
  1458. };
  1459. /**
  1460. * Sets the readConcern option for the query.
  1461. *
  1462. * #### Example:
  1463. *
  1464. * new Query().readConcern('local')
  1465. * new Query().readConcern('l') // same as local
  1466. *
  1467. * new Query().readConcern('available')
  1468. * new Query().readConcern('a') // same as available
  1469. *
  1470. * new Query().readConcern('majority')
  1471. * new Query().readConcern('m') // same as majority
  1472. *
  1473. * new Query().readConcern('linearizable')
  1474. * new Query().readConcern('lz') // same as linearizable
  1475. *
  1476. * new Query().readConcern('snapshot')
  1477. * new Query().readConcern('s') // same as snapshot
  1478. *
  1479. *
  1480. * #### Read Concern Level:
  1481. *
  1482. * ```
  1483. * local MongoDB 3.2+ The query returns from the instance with no guarantee guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back).
  1484. * available MongoDB 3.6+ The query returns from the instance with no guarantee guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back).
  1485. * majority MongoDB 3.2+ The query returns the data that has been acknowledged by a majority of the replica set members. The documents returned by the read operation are durable, even in the event of failure.
  1486. * linearizable MongoDB 3.4+ The query returns data that reflects all successful majority-acknowledged writes that completed prior to the start of the read operation. The query may wait for concurrently executing writes to propagate to a majority of replica set members before returning results.
  1487. * snapshot MongoDB 4.0+ Only available for operations within multi-document transactions. Upon transaction commit with write concern "majority", the transaction operations are guaranteed to have read from a snapshot of majority-committed data.
  1488. * ```
  1489. *
  1490. * Aliases
  1491. *
  1492. * ```
  1493. * l local
  1494. * a available
  1495. * m majority
  1496. * lz linearizable
  1497. * s snapshot
  1498. * ```
  1499. *
  1500. * Read more about how to use read concern [here](https://www.mongodb.com/docs/manual/reference/read-concern/).
  1501. *
  1502. * @memberOf Query
  1503. * @method readConcern
  1504. * @param {String} level one of the listed read concern level or their aliases
  1505. * @see mongodb https://www.mongodb.com/docs/manual/reference/read-concern/
  1506. * @return {Query} this
  1507. * @api public
  1508. */
  1509. /**
  1510. * Gets query options.
  1511. *
  1512. * #### Example:
  1513. *
  1514. * const query = new Query();
  1515. * query.limit(10);
  1516. * query.setOptions({ maxTimeMS: 1000 });
  1517. * query.getOptions(); // { limit: 10, maxTimeMS: 1000 }
  1518. *
  1519. * @return {Object} the options
  1520. * @api public
  1521. */
  1522. Query.prototype.getOptions = function() {
  1523. return this.options;
  1524. };
  1525. /**
  1526. * Sets query options. Some options only make sense for certain operations.
  1527. *
  1528. * #### Options:
  1529. *
  1530. * The following options are only for `find()`:
  1531. *
  1532. * - [tailable](https://www.mongodb.com/docs/manual/core/tailable-cursors/)
  1533. * - [limit](https://www.mongodb.com/docs/manual/reference/method/cursor.limit/)
  1534. * - [skip](https://www.mongodb.com/docs/manual/reference/method/cursor.skip/)
  1535. * - [allowDiskUse](https://www.mongodb.com/docs/manual/reference/method/cursor.allowDiskUse/)
  1536. * - [batchSize](https://www.mongodb.com/docs/manual/reference/method/cursor.batchSize/)
  1537. * - [readPreference](https://www.mongodb.com/docs/manual/applications/replication/#read-preference)
  1538. * - [hint](https://www.mongodb.com/docs/manual/reference/method/cursor.hint/)
  1539. * - [comment](https://www.mongodb.com/docs/manual/reference/method/cursor.comment/)
  1540. *
  1541. * The following options are only for write operations: `updateOne()`, `updateMany()`, `replaceOne()`, `findOneAndUpdate()`, and `findByIdAndUpdate()`:
  1542. *
  1543. * - [upsert](https://www.mongodb.com/docs/manual/reference/method/db.collection.update/)
  1544. * - [writeConcern](https://www.mongodb.com/docs/manual/reference/method/db.collection.update/)
  1545. * - [timestamps](https://mongoosejs.com/docs/guide.html#timestamps): If `timestamps` is set in the schema, set this option to `false` to skip timestamps for that particular update. Has no effect if `timestamps` is not enabled in the schema options.
  1546. * - overwriteDiscriminatorKey: allow setting the discriminator key in the update. Will use the correct discriminator schema if the update changes the discriminator key.
  1547. * - overwriteImmutable: allow overwriting properties that are set to `immutable` in the schema. Defaults to false.
  1548. *
  1549. * The following options are only for `find()`, `findOne()`, `findById()`, `findOneAndUpdate()`, `findOneAndReplace()`, `findOneAndDelete()`, and `findByIdAndUpdate()`:
  1550. *
  1551. * - [lean](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean())
  1552. * - [populate](https://mongoosejs.com/docs/populate.html)
  1553. * - [projection](https://mongoosejs.com/docs/api/query.html#Query.prototype.projection())
  1554. * - sanitizeProjection
  1555. * - useBigInt64
  1556. *
  1557. * The following options are only for all operations **except** `updateOne()`, `updateMany()`, `deleteOne()`, and `deleteMany()`:
  1558. *
  1559. * - [maxTimeMS](https://www.mongodb.com/docs/manual/reference/operator/meta/maxTimeMS/)
  1560. *
  1561. * The following options are for `find()`, `findOne()`, `findOneAndUpdate()`, `findOneAndDelete()`, `updateOne()`, and `deleteOne()`:
  1562. *
  1563. * - [sort](https://www.mongodb.com/docs/manual/reference/method/cursor.sort/)
  1564. *
  1565. * The following options are for `findOneAndUpdate()` and `findOneAndDelete()`
  1566. *
  1567. * - includeResultMetadata
  1568. *
  1569. * The following options are for all operations:
  1570. *
  1571. * - [strict](https://mongoosejs.com/docs/guide.html#strict)
  1572. * - [collation](https://www.mongodb.com/docs/manual/reference/collation/)
  1573. * - [session](https://www.mongodb.com/docs/manual/reference/server-sessions/)
  1574. * - [explain](https://www.mongodb.com/docs/manual/reference/method/cursor.explain/)
  1575. *
  1576. * @param {Object} options
  1577. * @return {Query} this
  1578. * @api public
  1579. */
  1580. Query.prototype.setOptions = function(options, overwrite) {
  1581. // overwrite is only for internal use
  1582. if (overwrite) {
  1583. // ensure that _mongooseOptions & options are two different objects
  1584. this._mongooseOptions = (options && clone(options)) || {};
  1585. this.options = options || {};
  1586. if ('populate' in options) {
  1587. this.populate(this._mongooseOptions);
  1588. }
  1589. return this;
  1590. }
  1591. if (options == null) {
  1592. return this;
  1593. }
  1594. if (typeof options !== 'object') {
  1595. throw new Error('Options must be an object, got "' + options + '"');
  1596. }
  1597. options = Object.assign({}, options);
  1598. if (Array.isArray(options.populate)) {
  1599. const populate = options.populate;
  1600. delete options.populate;
  1601. const _numPopulate = populate.length;
  1602. for (let i = 0; i < _numPopulate; ++i) {
  1603. this.populate(populate[i]);
  1604. }
  1605. }
  1606. if ('setDefaultsOnInsert' in options) {
  1607. this._mongooseOptions.setDefaultsOnInsert = options.setDefaultsOnInsert;
  1608. delete options.setDefaultsOnInsert;
  1609. }
  1610. if ('overwriteDiscriminatorKey' in options) {
  1611. this._mongooseOptions.overwriteDiscriminatorKey = options.overwriteDiscriminatorKey;
  1612. delete options.overwriteDiscriminatorKey;
  1613. }
  1614. if ('overwriteImmutable' in options) {
  1615. this._mongooseOptions.overwriteImmutable = options.overwriteImmutable;
  1616. delete options.overwriteImmutable;
  1617. }
  1618. if ('updatePipeline' in options) {
  1619. this._mongooseOptions.updatePipeline = options.updatePipeline;
  1620. delete options.updatePipeline;
  1621. }
  1622. if ('sanitizeProjection' in options) {
  1623. if (options.sanitizeProjection && !this._mongooseOptions.sanitizeProjection) {
  1624. sanitizeProjection(this._fields);
  1625. }
  1626. this._mongooseOptions.sanitizeProjection = options.sanitizeProjection;
  1627. delete options.sanitizeProjection;
  1628. }
  1629. if ('sanitizeFilter' in options) {
  1630. this._mongooseOptions.sanitizeFilter = options.sanitizeFilter;
  1631. delete options.sanitizeFilter;
  1632. }
  1633. if ('timestamps' in options) {
  1634. this._mongooseOptions.timestamps = options.timestamps;
  1635. delete options.timestamps;
  1636. }
  1637. if ('defaults' in options) {
  1638. this._mongooseOptions.defaults = options.defaults;
  1639. // deleting options.defaults will cause 7287 to fail
  1640. }
  1641. if ('translateAliases' in options) {
  1642. this._mongooseOptions.translateAliases = options.translateAliases;
  1643. delete options.translateAliases;
  1644. }
  1645. if ('schemaLevelProjections' in options) {
  1646. this._mongooseOptions.schemaLevelProjections = options.schemaLevelProjections;
  1647. delete options.schemaLevelProjections;
  1648. }
  1649. if (options.lean == null && this.schema && 'lean' in this.schema.options) {
  1650. this._mongooseOptions.lean = this.schema.options.lean;
  1651. }
  1652. if (typeof options.limit === 'string') {
  1653. try {
  1654. options.limit = castNumber(options.limit);
  1655. } catch {
  1656. throw new CastError('Number', options.limit, 'limit');
  1657. }
  1658. }
  1659. if (typeof options.skip === 'string') {
  1660. try {
  1661. options.skip = castNumber(options.skip);
  1662. } catch {
  1663. throw new CastError('Number', options.skip, 'skip');
  1664. }
  1665. }
  1666. // set arbitrary options
  1667. for (const key of Object.keys(options)) {
  1668. if (queryOptionMethods.has(key)) {
  1669. const args = Array.isArray(options[key]) ?
  1670. options[key] :
  1671. [options[key]];
  1672. this[key].apply(this, args);
  1673. } else {
  1674. this.options[key] = options[key];
  1675. }
  1676. }
  1677. return this;
  1678. };
  1679. /**
  1680. * Sets the [`explain` option](https://www.mongodb.com/docs/manual/reference/method/cursor.explain/),
  1681. * which makes this query return detailed execution stats instead of the actual
  1682. * query result. This method is useful for determining what index your queries
  1683. * use.
  1684. *
  1685. * Calling `query.explain(v)` is equivalent to `query.setOptions({ explain: v })`
  1686. *
  1687. * #### Example:
  1688. *
  1689. * const query = new Query();
  1690. * const res = await query.find({ a: 1 }).explain('queryPlanner');
  1691. * console.log(res);
  1692. *
  1693. * @param {String} [verbose] The verbosity mode. Either 'queryPlanner', 'executionStats', or 'allPlansExecution'. The default is 'queryPlanner'
  1694. * @return {Query} this
  1695. * @api public
  1696. */
  1697. Query.prototype.explain = function explain(verbose) {
  1698. if (arguments.length === 0) {
  1699. this.options.explain = true;
  1700. } else if (verbose === false) {
  1701. delete this.options.explain;
  1702. } else {
  1703. this.options.explain = verbose;
  1704. }
  1705. return this;
  1706. };
  1707. /**
  1708. * Sets the [`allowDiskUse` option](https://www.mongodb.com/docs/manual/reference/method/cursor.allowDiskUse/),
  1709. * which allows the MongoDB server to use more than 100 MB for this query's `sort()`. This option can
  1710. * let you work around `QueryExceededMemoryLimitNoDiskUseAllowed` errors from the MongoDB server.
  1711. *
  1712. * Note that this option requires MongoDB server >= 4.4. Setting this option is a no-op for MongoDB 4.2
  1713. * and earlier.
  1714. *
  1715. * Calling `query.allowDiskUse(v)` is equivalent to `query.setOptions({ allowDiskUse: v })`
  1716. *
  1717. * #### Example:
  1718. *
  1719. * await query.find().sort({ name: 1 }).allowDiskUse(true);
  1720. * // Equivalent:
  1721. * await query.find().sort({ name: 1 }).allowDiskUse();
  1722. *
  1723. * @param {Boolean} [v] Enable/disable `allowDiskUse`. If called with 0 arguments, sets `allowDiskUse: true`
  1724. * @return {Query} this
  1725. * @api public
  1726. */
  1727. Query.prototype.allowDiskUse = function(v) {
  1728. if (arguments.length === 0) {
  1729. this.options.allowDiskUse = true;
  1730. } else if (v === false) {
  1731. delete this.options.allowDiskUse;
  1732. } else {
  1733. this.options.allowDiskUse = v;
  1734. }
  1735. return this;
  1736. };
  1737. /**
  1738. * Sets the [maxTimeMS](https://www.mongodb.com/docs/manual/reference/method/cursor.maxTimeMS/)
  1739. * option. This will tell the MongoDB server to abort if the query or write op
  1740. * has been running for more than `ms` milliseconds.
  1741. *
  1742. * Calling `query.maxTimeMS(v)` is equivalent to `query.setOptions({ maxTimeMS: v })`
  1743. *
  1744. * #### Example:
  1745. *
  1746. * const query = new Query();
  1747. * // Throws an error 'operation exceeded time limit' as long as there's
  1748. * // >= 1 doc in the queried collection
  1749. * const res = await query.find({ $where: 'sleep(1000) || true' }).maxTimeMS(100);
  1750. *
  1751. * @param {Number} [ms] The number of milliseconds
  1752. * @return {Query} this
  1753. * @api public
  1754. */
  1755. Query.prototype.maxTimeMS = function(ms) {
  1756. this.options.maxTimeMS = ms;
  1757. return this;
  1758. };
  1759. /**
  1760. * Returns the current query filter (also known as conditions) as a [POJO](https://masteringjs.io/tutorials/fundamentals/pojo).
  1761. *
  1762. * #### Example:
  1763. *
  1764. * const query = new Query();
  1765. * query.find({ a: 1 }).where('b').gt(2);
  1766. * query.getFilter(); // { a: 1, b: { $gt: 2 } }
  1767. *
  1768. * @return {Object} current query filter
  1769. * @api public
  1770. */
  1771. Query.prototype.getFilter = function() {
  1772. return this._conditions;
  1773. };
  1774. /**
  1775. * Returns the current query filter. Equivalent to `getFilter()`.
  1776. *
  1777. * You should use `getFilter()` instead of `getQuery()` where possible. `getQuery()`
  1778. * will likely be deprecated in a future release.
  1779. *
  1780. * #### Example:
  1781. *
  1782. * const query = new Query();
  1783. * query.find({ a: 1 }).where('b').gt(2);
  1784. * query.getQuery(); // { a: 1, b: { $gt: 2 } }
  1785. *
  1786. * @return {Object} current query filter
  1787. * @api public
  1788. */
  1789. Query.prototype.getQuery = function() {
  1790. return this._conditions;
  1791. };
  1792. /**
  1793. * Sets the query conditions to the provided JSON object.
  1794. *
  1795. * #### Example:
  1796. *
  1797. * const query = new Query();
  1798. * query.find({ a: 1 })
  1799. * query.setQuery({ a: 2 });
  1800. * query.getQuery(); // { a: 2 }
  1801. *
  1802. * @param {Object} new query conditions
  1803. * @return {undefined}
  1804. * @api public
  1805. */
  1806. Query.prototype.setQuery = function(val) {
  1807. this._conditions = val;
  1808. };
  1809. /**
  1810. * Returns the current update operations as a JSON object.
  1811. *
  1812. * #### Example:
  1813. *
  1814. * const query = new Query();
  1815. * query.updateOne({}, { $set: { a: 5 } });
  1816. * query.getUpdate(); // { $set: { a: 5 } }
  1817. *
  1818. * @return {Object} current update operations
  1819. * @api public
  1820. */
  1821. Query.prototype.getUpdate = function() {
  1822. return this._update;
  1823. };
  1824. /**
  1825. * Sets the current update operation to new value.
  1826. *
  1827. * #### Example:
  1828. *
  1829. * const query = new Query();
  1830. * query.updateOne({}, { $set: { a: 5 } });
  1831. * query.setUpdate({ $set: { b: 6 } });
  1832. * query.getUpdate(); // { $set: { b: 6 } }
  1833. *
  1834. * @param {Object} new update operation
  1835. * @return {undefined}
  1836. * @api public
  1837. */
  1838. Query.prototype.setUpdate = function(val) {
  1839. this._update = val;
  1840. };
  1841. /**
  1842. * Returns fields selection for this query.
  1843. *
  1844. * @method _fieldsForExec
  1845. * @return {Object}
  1846. * @api private
  1847. * @memberOf Query
  1848. */
  1849. Query.prototype._fieldsForExec = function() {
  1850. if (this._fields == null) {
  1851. return null;
  1852. }
  1853. if (utils.hasOwnKeys(this._fields) === false) {
  1854. return null;
  1855. }
  1856. return clone(this._fields);
  1857. };
  1858. /**
  1859. * Return an update document with corrected `$set` operations.
  1860. *
  1861. * @method _updateForExec
  1862. * @return {Object}
  1863. * @api private
  1864. * @memberOf Query
  1865. */
  1866. Query.prototype._updateForExec = function() {
  1867. const update = clone(this._update, {
  1868. transform: false,
  1869. depopulate: true
  1870. });
  1871. const ops = Object.keys(update);
  1872. let i = ops.length;
  1873. const ret = {};
  1874. while (i--) {
  1875. const op = ops[i];
  1876. if ('$' !== op[0]) {
  1877. // fix up $set sugar
  1878. if (!ret.$set) {
  1879. if (update.$set) {
  1880. ret.$set = update.$set;
  1881. } else {
  1882. ret.$set = {};
  1883. }
  1884. }
  1885. ret.$set[op] = update[op];
  1886. ops.splice(i, 1);
  1887. if (!~ops.indexOf('$set')) ops.push('$set');
  1888. } else if ('$set' === op) {
  1889. if (!ret.$set) {
  1890. ret[op] = update[op];
  1891. }
  1892. } else {
  1893. ret[op] = update[op];
  1894. }
  1895. }
  1896. return ret;
  1897. };
  1898. /**
  1899. * Makes sure _path is set.
  1900. *
  1901. * This method is inherited by `mquery`
  1902. *
  1903. * @method _ensurePath
  1904. * @param {String} method
  1905. * @api private
  1906. * @memberOf Query
  1907. */
  1908. /**
  1909. * Determines if `conds` can be merged using `mquery().merge()`
  1910. *
  1911. * @method canMerge
  1912. * @memberOf Query
  1913. * @instance
  1914. * @param {Object} conds
  1915. * @return {Boolean}
  1916. * @api private
  1917. */
  1918. /**
  1919. * Returns default options for this query.
  1920. *
  1921. * @param {Model} model
  1922. * @api private
  1923. */
  1924. Query.prototype._optionsForExec = function(model) {
  1925. const options = clone(this.options);
  1926. delete options.populate;
  1927. model = model || this.model;
  1928. if (!model) {
  1929. return options;
  1930. }
  1931. applyReadConcern(model.schema, options);
  1932. // Apply schema-level `writeConcern` option
  1933. applyWriteConcern(model.schema, options);
  1934. const asyncLocalStorage = this.model?.db?.base.transactionAsyncLocalStorage?.getStore();
  1935. if (!Object.hasOwn(this.options, 'session') && asyncLocalStorage?.session != null) {
  1936. options.session = asyncLocalStorage.session;
  1937. }
  1938. const readPreference = model?.schema?.options?.read;
  1939. if (!('readPreference' in options) && readPreference) {
  1940. options.readPreference = readPreference;
  1941. }
  1942. if (options.upsert !== void 0) {
  1943. options.upsert = !!options.upsert;
  1944. }
  1945. if (options.writeConcern) {
  1946. if (options.j) {
  1947. options.writeConcern.j = options.j;
  1948. delete options.j;
  1949. }
  1950. if (options.w) {
  1951. options.writeConcern.w = options.w;
  1952. delete options.w;
  1953. }
  1954. if (options.wtimeout) {
  1955. options.writeConcern.wtimeout = options.wtimeout;
  1956. delete options.wtimeout;
  1957. }
  1958. }
  1959. this._applyPaths();
  1960. if (this._fields != null) {
  1961. this._fields = this._castFields(this._fields);
  1962. const projection = this._fieldsForExec();
  1963. if (projection != null) {
  1964. options.projection = projection;
  1965. }
  1966. }
  1967. if (this._mongooseOptions.populate) {
  1968. if (options.readPreference) {
  1969. for (const pop of Object.values(this._mongooseOptions.populate)) {
  1970. if (pop.options?.readPreference === undefined) {
  1971. if (!pop.options) {
  1972. pop.options = {};
  1973. }
  1974. pop.options.readPreference = options.readPreference;
  1975. }
  1976. }
  1977. }
  1978. if (options.readConcern) {
  1979. for (const pop of Object.values(this._mongooseOptions.populate)) {
  1980. if (pop.options?.readConcern === undefined) {
  1981. if (!pop.options) {
  1982. pop.options = {};
  1983. }
  1984. pop.options.readConcern = options.readConcern;
  1985. }
  1986. }
  1987. }
  1988. }
  1989. return options;
  1990. };
  1991. /**
  1992. * Sets the lean option.
  1993. *
  1994. * Documents returned from queries with the `lean` option enabled are plain
  1995. * javascript objects, not [Mongoose Documents](https://mongoosejs.com/docs/api/document.html). They have no
  1996. * `save` method, getters/setters, virtuals, or other Mongoose features.
  1997. *
  1998. * #### Example:
  1999. *
  2000. * new Query().lean() // true
  2001. * new Query().lean(true)
  2002. * new Query().lean(false)
  2003. *
  2004. * const docs = await Model.find().lean();
  2005. * docs[0] instanceof mongoose.Document; // false
  2006. *
  2007. * [Lean is great for high-performance, read-only cases](https://mongoosejs.com/docs/tutorials/lean.html),
  2008. * especially when combined
  2009. * with [cursors](https://mongoosejs.com/docs/queries.html#streaming).
  2010. *
  2011. * If you need virtuals, getters/setters, or defaults with `lean()`, you need
  2012. * to use a plugin. See:
  2013. *
  2014. * - [mongoose-lean-virtuals](https://plugins.mongoosejs.io/plugins/lean-virtuals)
  2015. * - [mongoose-lean-getters](https://plugins.mongoosejs.io/plugins/lean-getters)
  2016. * - [mongoose-lean-defaults](https://www.npmjs.com/package/mongoose-lean-defaults)
  2017. *
  2018. * @param {Boolean|Object} bool defaults to true
  2019. * @return {Query} this
  2020. * @api public
  2021. */
  2022. Query.prototype.lean = function(v) {
  2023. this._mongooseOptions.lean = arguments.length ? v : true;
  2024. return this;
  2025. };
  2026. /**
  2027. * Adds a `$set` to this query's update without changing the operation.
  2028. * This is useful for query middleware so you can add an update regardless
  2029. * of whether you use `updateOne()`, `updateMany()`, `findOneAndUpdate()`, etc.
  2030. *
  2031. * #### Example:
  2032. *
  2033. * // Updates `{ $set: { updatedAt: new Date() } }`
  2034. * new Query().updateOne({}, {}).set('updatedAt', new Date());
  2035. * new Query().updateMany({}, {}).set({ updatedAt: new Date() });
  2036. *
  2037. * @param {String|Object} path path or object of key/value pairs to set
  2038. * @param {Any} [val] the value to set
  2039. * @return {Query} this
  2040. * @api public
  2041. */
  2042. Query.prototype.set = function(path, val) {
  2043. if (typeof path === 'object') {
  2044. const keys = Object.keys(path);
  2045. for (const key of keys) {
  2046. this.set(key, path[key]);
  2047. }
  2048. return this;
  2049. }
  2050. this._update = this._update || {};
  2051. if (path in this._update) {
  2052. delete this._update[path];
  2053. }
  2054. this._update.$set = this._update.$set || {};
  2055. this._update.$set[path] = val;
  2056. return this;
  2057. };
  2058. /**
  2059. * For update operations, returns the value of a path in the update's `$set`.
  2060. * Useful for writing getters/setters that can work with both update operations
  2061. * and `save()`.
  2062. *
  2063. * #### Example:
  2064. *
  2065. * const query = Model.updateOne({}, { $set: { name: 'Jean-Luc Picard' } });
  2066. * query.get('name'); // 'Jean-Luc Picard'
  2067. *
  2068. * @param {String|Object} path path or object of key/value pairs to get
  2069. * @return {Query} this
  2070. * @api public
  2071. */
  2072. Query.prototype.get = function get(path) {
  2073. const update = this._update;
  2074. if (update == null) {
  2075. return void 0;
  2076. }
  2077. const $set = update.$set;
  2078. if ($set == null) {
  2079. return update[path];
  2080. }
  2081. if (utils.hasUserDefinedProperty(update, path)) {
  2082. return update[path];
  2083. }
  2084. if (utils.hasUserDefinedProperty($set, path)) {
  2085. return $set[path];
  2086. }
  2087. return void 0;
  2088. };
  2089. /**
  2090. * Gets/sets the error flag on this query. If this flag is not null or
  2091. * undefined, the `exec()` promise will reject without executing.
  2092. *
  2093. * #### Example:
  2094. *
  2095. * Query().error(); // Get current error value
  2096. * Query().error(null); // Unset the current error
  2097. * Query().error(new Error('test')); // `exec()` will resolve with test
  2098. * Schema.pre('find', function() {
  2099. * if (!this.getQuery().userId) {
  2100. * this.error(new Error('Not allowed to query without setting userId'));
  2101. * }
  2102. * });
  2103. *
  2104. * Note that query casting runs **after** hooks, so cast errors will override
  2105. * custom errors.
  2106. *
  2107. * #### Example:
  2108. *
  2109. * const TestSchema = new Schema({ num: Number });
  2110. * const TestModel = db.model('Test', TestSchema);
  2111. * TestModel.find({ num: 'not a number' }).error(new Error('woops')).exec(function(error) {
  2112. * // `error` will be a cast error because `num` failed to cast
  2113. * });
  2114. *
  2115. * @param {Error|null} err if set, `exec()` will fail fast before sending the query to MongoDB
  2116. * @return {Query} this
  2117. * @api public
  2118. */
  2119. Query.prototype.error = function error(err) {
  2120. if (arguments.length === 0) {
  2121. return this._error;
  2122. }
  2123. this._error = err;
  2124. return this;
  2125. };
  2126. /**
  2127. * ignore
  2128. * @method _unsetCastError
  2129. * @instance
  2130. * @memberOf Query
  2131. * @api private
  2132. */
  2133. Query.prototype._unsetCastError = function _unsetCastError() {
  2134. if (this._error == null || !(this._error instanceof CastError)) {
  2135. return;
  2136. }
  2137. return this.error(null);
  2138. };
  2139. /**
  2140. * Getter/setter around the current mongoose-specific options for this query
  2141. * Below are the current Mongoose-specific options.
  2142. *
  2143. * - `populate`: an array representing what paths will be populated. Should have one entry for each call to [`Query.prototype.populate()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.populate())
  2144. * - `lean`: if truthy, Mongoose will not [hydrate](https://mongoosejs.com/docs/api/model.html#Model.hydrate()) any documents that are returned from this query. See [`Query.prototype.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) for more information.
  2145. * - `strict`: controls how Mongoose handles keys that aren't in the schema for updates. This option is `true` by default, which means Mongoose will silently strip any paths in the update that aren't in the schema. See the [`strict` mode docs](https://mongoosejs.com/docs/guide.html#strict) for more information.
  2146. * - `strictQuery`: controls how Mongoose handles keys that aren't in the schema for the query `filter`. This option is `false` by default, which means Mongoose will allow `Model.find({ foo: 'bar' })` even if `foo` is not in the schema. See the [`strictQuery` docs](https://mongoosejs.com/docs/guide.html#strictQuery) for more information.
  2147. * - `nearSphere`: use `$nearSphere` instead of `near()`. See the [`Query.prototype.nearSphere()` docs](https://mongoosejs.com/docs/api/query.html#Query.prototype.nearSphere())
  2148. * - `schemaLevelProjections`: if `false`, Mongoose will not apply schema-level `select: false` or `select: true` for this query
  2149. *
  2150. * Mongoose maintains a separate object for internal options because
  2151. * Mongoose sends `Query.prototype.options` to the MongoDB server, and the
  2152. * above options are not relevant for the MongoDB server.
  2153. *
  2154. * @param {Object} options if specified, overwrites the current options
  2155. * @return {Object} the options
  2156. * @api public
  2157. */
  2158. Query.prototype.mongooseOptions = function(v) {
  2159. if (arguments.length > 0) {
  2160. this._mongooseOptions = v;
  2161. }
  2162. return this._mongooseOptions;
  2163. };
  2164. /**
  2165. * ignore
  2166. * @method _castConditions
  2167. * @memberOf Query
  2168. * @api private
  2169. * @instance
  2170. */
  2171. Query.prototype._castConditions = function() {
  2172. let sanitizeFilterOpt = undefined;
  2173. if (this.model?.db.options?.sanitizeFilter != null) {
  2174. sanitizeFilterOpt = this.model.db.options.sanitizeFilter;
  2175. } else if (this.model?.base.options?.sanitizeFilter != null) {
  2176. sanitizeFilterOpt = this.model.base.options.sanitizeFilter;
  2177. } else {
  2178. sanitizeFilterOpt = this._mongooseOptions.sanitizeFilter;
  2179. }
  2180. if (sanitizeFilterOpt) {
  2181. sanitizeFilter(this._conditions);
  2182. }
  2183. try {
  2184. this.cast(this.model);
  2185. this._unsetCastError();
  2186. } catch (err) {
  2187. this.error(err);
  2188. }
  2189. };
  2190. /*!
  2191. * ignore
  2192. */
  2193. function _castArrayFilters(query) {
  2194. try {
  2195. castArrayFilters(query);
  2196. } catch (err) {
  2197. query.error(err);
  2198. }
  2199. }
  2200. /**
  2201. * Execute a `find()`
  2202. *
  2203. * @return {Query} this
  2204. * @api private
  2205. */
  2206. Query.prototype._find = async function _find() {
  2207. this._applyTranslateAliases();
  2208. this._castConditions();
  2209. if (this.error() != null) {
  2210. throw this.error();
  2211. }
  2212. const mongooseOptions = this._mongooseOptions;
  2213. const userProvidedFields = this._userProvidedFields || {};
  2214. const dbOptions = this.model.db.options;
  2215. const baseOptions = this.model.base.options;
  2216. applyGlobalMaxTimeMS(this.options, dbOptions, baseOptions);
  2217. applyGlobalDiskUse(this.options, dbOptions, baseOptions);
  2218. // Separate options to pass down to `completeMany()` in case we need to
  2219. // set a session on the document
  2220. const completeManyOptions = {
  2221. session: this?.options?.session || null,
  2222. lean: mongooseOptions.lean || null
  2223. };
  2224. const options = this._optionsForExec();
  2225. const filter = this._conditions;
  2226. const fields = options.projection;
  2227. const cursor = await this.mongooseCollection.find(filter, options);
  2228. if (options.explain) {
  2229. return cursor.explain();
  2230. }
  2231. let docs = await cursor.toArray();
  2232. if (docs.length === 0) {
  2233. return docs;
  2234. }
  2235. if (!mongooseOptions.populate) {
  2236. const versionKey = this.schema.options.versionKey;
  2237. if (mongooseOptions.lean?.versionKey === false && versionKey) {
  2238. docs.forEach((doc) => {
  2239. if (versionKey in doc) {
  2240. delete doc[versionKey];
  2241. }
  2242. });
  2243. }
  2244. return mongooseOptions.lean ?
  2245. _completeManyLean(this.model.schema, docs, null, completeManyOptions) :
  2246. this._completeMany(docs, fields, userProvidedFields, completeManyOptions);
  2247. }
  2248. const pop = helpers.preparePopulationOptionsMQ(this, mongooseOptions);
  2249. if (mongooseOptions.lean) {
  2250. return this.model.populate(docs, pop);
  2251. }
  2252. docs = await this._completeMany(docs, fields, userProvidedFields, completeManyOptions);
  2253. await this.model.populate(docs, pop);
  2254. return docs;
  2255. };
  2256. /**
  2257. * Find all documents that match `selector`. The result will be an array of documents.
  2258. *
  2259. * If there are too many documents in the result to fit in memory, use
  2260. * [`Query.prototype.cursor()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.cursor())
  2261. *
  2262. * #### Example:
  2263. *
  2264. * const arr = await Movie.find({ year: { $gte: 1980, $lte: 1989 } });
  2265. *
  2266. * @param {Object|ObjectId} [filter] mongodb filter. If not specified, returns all documents.
  2267. * @return {Query} this
  2268. * @api public
  2269. */
  2270. Query.prototype.find = function(conditions) {
  2271. if (typeof conditions === 'function' ||
  2272. typeof arguments[1] === 'function') {
  2273. throw new MongooseError('Query.prototype.find() no longer accepts a callback');
  2274. }
  2275. this.op = 'find';
  2276. if (canMerge(conditions)) {
  2277. this.merge(conditions);
  2278. prepareDiscriminatorCriteria(this);
  2279. } else if (conditions != null) {
  2280. this.error(new ObjectParameterError(conditions, 'filter', 'find'));
  2281. }
  2282. return this;
  2283. };
  2284. /**
  2285. * Merges another Query or conditions object into this one.
  2286. *
  2287. * When a Query is passed, conditions, field selection and options are merged.
  2288. *
  2289. * @param {Query|Object} source
  2290. * @return {Query} this
  2291. */
  2292. Query.prototype.merge = function(source) {
  2293. if (!source) {
  2294. if (source === null) {
  2295. this._conditions = null;
  2296. }
  2297. return this;
  2298. }
  2299. this._conditions = this._conditions ?? {};
  2300. const opts = { overwrite: true };
  2301. if (source instanceof Query) {
  2302. // if source has a feature, apply it to ourselves
  2303. if (source._conditions) {
  2304. opts.omit = {};
  2305. if (this._conditions && this._conditions.$and && source._conditions.$and) {
  2306. opts.omit['$and'] = true;
  2307. this._conditions.$and = this._conditions.$and.concat(source._conditions.$and);
  2308. }
  2309. if (this._conditions && this._conditions.$or && source._conditions.$or) {
  2310. opts.omit['$or'] = true;
  2311. this._conditions.$or = this._conditions.$or.concat(source._conditions.$or);
  2312. }
  2313. utils.merge(this._conditions, source._conditions, opts);
  2314. }
  2315. if (source._fields) {
  2316. this._fields || (this._fields = {});
  2317. utils.merge(this._fields, source._fields, opts);
  2318. }
  2319. if (source.options) {
  2320. this.options || (this.options = {});
  2321. utils.merge(this.options, source.options, opts);
  2322. }
  2323. if (source._update) {
  2324. this._update || (this._update = {});
  2325. utils.mergeClone(this._update, source._update);
  2326. }
  2327. if (source._distinct) {
  2328. this._distinct = source._distinct;
  2329. }
  2330. utils.merge(this._mongooseOptions, source._mongooseOptions);
  2331. return this;
  2332. } else if (this.model != null && source instanceof this.model.base.Types.ObjectId) {
  2333. utils.merge(this._conditions, { _id: source }, opts);
  2334. return this;
  2335. } else if (source?.$__) {
  2336. source = source.toObject(internalToObjectOptions);
  2337. }
  2338. opts.omit = {};
  2339. if (Array.isArray(source.$and)) {
  2340. opts.omit['$and'] = true;
  2341. if (!this._conditions) {
  2342. this._conditions = {};
  2343. }
  2344. this._conditions.$and = (this._conditions.$and || []).concat(
  2345. source.$and.map(el => utils.isPOJO(el) ? utils.merge({}, el) : el)
  2346. );
  2347. }
  2348. if (Array.isArray(source.$or)) {
  2349. opts.omit['$or'] = true;
  2350. if (!this._conditions) {
  2351. this._conditions = {};
  2352. }
  2353. this._conditions.$or = (this._conditions.$or || []).concat(
  2354. source.$or.map(el => utils.isPOJO(el) ? utils.merge({}, el) : el)
  2355. );
  2356. }
  2357. // plain object
  2358. utils.merge(this._conditions, source, opts);
  2359. return this;
  2360. };
  2361. /**
  2362. * Adds a collation to this op (MongoDB 3.4 and up)
  2363. *
  2364. * @param {Object} value
  2365. * @return {Query} this
  2366. * @see MongoDB docs https://www.mongodb.com/docs/manual/reference/method/cursor.collation/#cursor.collation
  2367. * @api public
  2368. */
  2369. Query.prototype.collation = function(value) {
  2370. if (this.options == null) {
  2371. this.options = {};
  2372. }
  2373. this.options.collation = value;
  2374. return this;
  2375. };
  2376. /**
  2377. * Hydrate a single doc from `findOne()`, `findOneAndUpdate()`, etc.
  2378. *
  2379. * @api private
  2380. */
  2381. Query.prototype._completeOne = function(doc, res, projection, callback) {
  2382. if (!doc && !this.options.includeResultMetadata) {
  2383. return callback(null, null);
  2384. }
  2385. const model = this.model;
  2386. const userProvidedFields = this._userProvidedFields || {};
  2387. // `populate`, `lean`
  2388. const mongooseOptions = this._mongooseOptions;
  2389. const options = this.options;
  2390. if (!options.lean && mongooseOptions.lean) {
  2391. options.lean = mongooseOptions.lean;
  2392. }
  2393. if (options.explain) {
  2394. return callback(null, doc);
  2395. }
  2396. if (!mongooseOptions.populate) {
  2397. const versionKey = this.schema.options.versionKey;
  2398. if (mongooseOptions.lean?.versionKey === false && versionKey) {
  2399. if (versionKey in doc) {
  2400. delete doc[versionKey];
  2401. }
  2402. }
  2403. return mongooseOptions.lean ?
  2404. _completeOneLean(model.schema, doc, null, res, options, callback) :
  2405. completeOne(model, doc, res, options, projection, userProvidedFields,
  2406. null, callback);
  2407. }
  2408. const pop = helpers.preparePopulationOptionsMQ(this, this._mongooseOptions);
  2409. if (mongooseOptions.lean) {
  2410. return model.populate(doc, pop).then(
  2411. doc => {
  2412. _completeOneLean(model.schema, doc, null, res, options, callback);
  2413. },
  2414. error => {
  2415. callback(error);
  2416. }
  2417. );
  2418. }
  2419. completeOne(model, doc, res, options, projection, userProvidedFields, [], (err, doc) => {
  2420. if (err != null) {
  2421. return callback(err);
  2422. }
  2423. model.populate(doc, pop).then(res => { callback(null, res); }, err => { callback(err); });
  2424. });
  2425. };
  2426. /**
  2427. * Given a model and an array of docs, hydrates all the docs to be instances
  2428. * of the model. Used to initialize docs returned from the db from `find()`
  2429. *
  2430. * @param {Array} docs
  2431. * @param {Object} fields the projection used, including `select` from schemas
  2432. * @param {Object} userProvidedFields the user-specified projection
  2433. * @param {Object} [opts]
  2434. * @param {Array} [opts.populated]
  2435. * @param {ClientSession} [opts.session]
  2436. * @api private
  2437. */
  2438. Query.prototype._completeMany = async function _completeMany(docs, fields, userProvidedFields, opts) {
  2439. const model = this.model;
  2440. return Promise.all(docs.map(doc => new Promise((resolve, reject) => {
  2441. const rawDoc = doc;
  2442. doc = helpers.createModel(model, doc, fields, userProvidedFields);
  2443. if (opts.session != null) {
  2444. doc.$session(opts.session);
  2445. }
  2446. doc.$init(rawDoc, opts, (err) => {
  2447. if (err != null) {
  2448. return reject(err);
  2449. }
  2450. resolve(doc);
  2451. });
  2452. })));
  2453. };
  2454. /**
  2455. * Internal helper to execute a findOne() operation
  2456. *
  2457. * @see findOne https://www.mongodb.com/docs/manual/reference/method/db.collection.findOne/
  2458. * @api private
  2459. */
  2460. Query.prototype._findOne = async function _findOne() {
  2461. this._applyTranslateAliases();
  2462. this._castConditions();
  2463. if (this.error()) {
  2464. const err = this.error();
  2465. throw err;
  2466. }
  2467. const dbOptions = this.model.db.options;
  2468. const baseOptions = this.model.base.options;
  2469. applyGlobalMaxTimeMS(this.options, dbOptions, baseOptions);
  2470. applyGlobalDiskUse(this.options, dbOptions, baseOptions);
  2471. const options = this._optionsForExec();
  2472. // don't pass in the conditions because we already merged them in
  2473. const doc = await this.mongooseCollection.findOne(this._conditions, options);
  2474. return new Promise((resolve, reject) => {
  2475. this._completeOne(doc, null, options.projection, (err, res) => {
  2476. if (err) {
  2477. return reject(err);
  2478. }
  2479. resolve(res);
  2480. });
  2481. });
  2482. };
  2483. /**
  2484. * Declares the query a findOne operation. When executed, the first found document is passed to the callback.
  2485. *
  2486. * The result of the query is a single document, or `null` if no document was found.
  2487. *
  2488. * * *Note:* `conditions` is optional, and if `conditions` is null or undefined,
  2489. * mongoose will send an empty `findOne` command to MongoDB, which will return
  2490. * an arbitrary document. If you're querying by `_id`, use `Model.findById()`
  2491. * instead.
  2492. *
  2493. * This function triggers the following middleware.
  2494. *
  2495. * - `findOne()`
  2496. *
  2497. * #### Example:
  2498. *
  2499. * const query = Kitten.where({ color: 'white' });
  2500. * const kitten = await query.findOne();
  2501. *
  2502. * @param {Object} [filter] mongodb selector
  2503. * @param {Object} [projection] optional fields to return
  2504. * @param {Object} [options] see [`setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
  2505. * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
  2506. * @return {Query} this
  2507. * @see findOne https://www.mongodb.com/docs/manual/reference/method/db.collection.findOne/
  2508. * @see Query.select https://mongoosejs.com/docs/api/query.html#Query.prototype.select()
  2509. * @api public
  2510. */
  2511. Query.prototype.findOne = function(conditions, projection, options) {
  2512. if (typeof conditions === 'function' ||
  2513. typeof projection === 'function' ||
  2514. typeof options === 'function' ||
  2515. typeof arguments[3] === 'function') {
  2516. throw new MongooseError('Query.prototype.findOne() no longer accepts a callback');
  2517. }
  2518. this.op = 'findOne';
  2519. if (options) {
  2520. this.setOptions(options);
  2521. }
  2522. if (projection) {
  2523. this.select(projection);
  2524. }
  2525. if (canMerge(conditions)) {
  2526. this.merge(conditions);
  2527. prepareDiscriminatorCriteria(this);
  2528. } else if (conditions != null) {
  2529. this.error(new ObjectParameterError(conditions, 'filter', 'findOne'));
  2530. }
  2531. return this;
  2532. };
  2533. /**
  2534. * Execute a countDocuments query
  2535. *
  2536. * @see countDocuments https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#countDocuments
  2537. * @api private
  2538. */
  2539. Query.prototype._countDocuments = async function _countDocuments() {
  2540. this._applyTranslateAliases();
  2541. try {
  2542. this.cast(this.model);
  2543. } catch (err) {
  2544. this.error(err);
  2545. }
  2546. if (this.error()) {
  2547. throw this.error();
  2548. }
  2549. const dbOptions = this.model.db.options;
  2550. const baseOptions = this.model.base.options;
  2551. applyGlobalMaxTimeMS(this.options, dbOptions, baseOptions);
  2552. applyGlobalDiskUse(this.options, dbOptions, baseOptions);
  2553. const options = this._optionsForExec();
  2554. const conds = this._conditions;
  2555. return this.mongooseCollection.countDocuments(conds, options);
  2556. };
  2557. /*!
  2558. * If `translateAliases` option is set, call `Model.translateAliases()`
  2559. * on the following query properties: filter, projection, update, distinct.
  2560. */
  2561. Query.prototype._applyTranslateAliases = function _applyTranslateAliases() {
  2562. let applyTranslateAliases = false;
  2563. if ('translateAliases' in this._mongooseOptions) {
  2564. applyTranslateAliases = this._mongooseOptions.translateAliases;
  2565. } else if (this.model?.schema?._userProvidedOptions?.translateAliases != null) {
  2566. applyTranslateAliases = this.model.schema._userProvidedOptions.translateAliases;
  2567. } else if (this.model?.base?.options?.translateAliases != null) {
  2568. applyTranslateAliases = this.model.base.options.translateAliases;
  2569. }
  2570. if (!applyTranslateAliases) {
  2571. return;
  2572. }
  2573. if (this.model?.schema?.aliases && utils.hasOwnKeys(this.model.schema.aliases)) {
  2574. this.model.translateAliases(this._conditions, true);
  2575. this.model.translateAliases(this._fields, true);
  2576. this.model.translateAliases(this._update, true);
  2577. if (this._distinct != null && this.model.schema.aliases[this._distinct] != null) {
  2578. this._distinct = this.model.schema.aliases[this._distinct];
  2579. }
  2580. }
  2581. };
  2582. /**
  2583. * Execute a estimatedDocumentCount() query
  2584. *
  2585. * @see estimatedDocumentCount https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#estimatedDocumentCount
  2586. * @api private
  2587. */
  2588. Query.prototype._estimatedDocumentCount = async function _estimatedDocumentCount() {
  2589. if (this.error()) {
  2590. throw this.error();
  2591. }
  2592. const options = this._optionsForExec();
  2593. return this.mongooseCollection.estimatedDocumentCount(options);
  2594. };
  2595. /**
  2596. * Specifies this query as a `estimatedDocumentCount()` query. Faster than
  2597. * using `countDocuments()` for large collections because
  2598. * `estimatedDocumentCount()` uses collection metadata rather than scanning
  2599. * the entire collection.
  2600. *
  2601. * `estimatedDocumentCount()` does **not** accept a filter. `Model.find({ foo: bar }).estimatedDocumentCount()`
  2602. * is equivalent to `Model.find().estimatedDocumentCount()`
  2603. *
  2604. * This function triggers the following middleware.
  2605. *
  2606. * - `estimatedDocumentCount()`
  2607. *
  2608. * #### Example:
  2609. *
  2610. * await Model.find().estimatedDocumentCount();
  2611. *
  2612. * @param {Object} [options] passed transparently to the [MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/EstimatedDocumentCountOptions.html)
  2613. * @return {Query} this
  2614. * @see estimatedDocumentCount https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#estimatedDocumentCount
  2615. * @api public
  2616. */
  2617. Query.prototype.estimatedDocumentCount = function(options) {
  2618. if (typeof options === 'function' ||
  2619. typeof arguments[1] === 'function') {
  2620. throw new MongooseError('Query.prototype.estimatedDocumentCount() no longer accepts a callback');
  2621. }
  2622. this.op = 'estimatedDocumentCount';
  2623. if (options != null) {
  2624. this.setOptions(options);
  2625. }
  2626. return this;
  2627. };
  2628. /**
  2629. * Specifies this query as a `countDocuments()` query. Behaves like `count()`,
  2630. * except it always does a full collection scan when passed an empty filter `{}`.
  2631. *
  2632. * There are also minor differences in how `countDocuments()` handles
  2633. * [`$where` and a couple geospatial operators](https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#countDocuments).
  2634. * versus `count()`.
  2635. *
  2636. * This function triggers the following middleware.
  2637. *
  2638. * - `countDocuments()`
  2639. *
  2640. * #### Example:
  2641. *
  2642. * const countQuery = model.where({ 'color': 'black' }).countDocuments();
  2643. *
  2644. * query.countDocuments({ color: 'black' }).count().exec();
  2645. *
  2646. * await query.countDocuments({ color: 'black' });
  2647. *
  2648. * query.where('color', 'black').countDocuments().exec();
  2649. *
  2650. * The `countDocuments()` function is similar to `count()`, but there are a
  2651. * [few operators that `countDocuments()` does not support](https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#countDocuments).
  2652. * Below are the operators that `count()` supports but `countDocuments()` does not,
  2653. * and the suggested replacement:
  2654. *
  2655. * - `$where`: [`$expr`](https://www.mongodb.com/docs/manual/reference/operator/query/expr/)
  2656. * - `$near`: [`$geoWithin`](https://www.mongodb.com/docs/manual/reference/operator/query/geoWithin/) with [`$center`](https://www.mongodb.com/docs/manual/reference/operator/query/center/#op._S_center)
  2657. * - `$nearSphere`: [`$geoWithin`](https://www.mongodb.com/docs/manual/reference/operator/query/geoWithin/) with [`$centerSphere`](https://www.mongodb.com/docs/manual/reference/operator/query/centerSphere/#op._S_centerSphere)
  2658. *
  2659. * @param {Object} [filter] mongodb selector
  2660. * @param {Object} [options]
  2661. * @return {Query} this
  2662. * @see countDocuments https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#countDocuments
  2663. * @api public
  2664. */
  2665. Query.prototype.countDocuments = function(conditions, options) {
  2666. if (typeof conditions === 'function' ||
  2667. typeof options === 'function' ||
  2668. typeof arguments[2] === 'function') {
  2669. throw new MongooseError('Query.prototype.countDocuments() no longer accepts a callback');
  2670. }
  2671. this.op = 'countDocuments';
  2672. if (canMerge(conditions)) {
  2673. this.merge(conditions);
  2674. }
  2675. if (options != null) {
  2676. this.setOptions(options);
  2677. }
  2678. return this;
  2679. };
  2680. /**
  2681. * Execute a `distinct()` query
  2682. *
  2683. * @see distinct https://www.mongodb.com/docs/manual/reference/method/db.collection.distinct/
  2684. * @api private
  2685. */
  2686. Query.prototype.__distinct = async function __distinct() {
  2687. this._applyTranslateAliases();
  2688. this._castConditions();
  2689. if (this.error()) {
  2690. throw this.error();
  2691. }
  2692. const dbOptions = this.model.db.options;
  2693. const baseOptions = this.model.base.options;
  2694. applyGlobalMaxTimeMS(this.options, dbOptions, baseOptions);
  2695. applyGlobalDiskUse(this.options, dbOptions, baseOptions);
  2696. const options = this._optionsForExec();
  2697. return this.mongooseCollection.
  2698. distinct(this._distinct, this._conditions, options);
  2699. };
  2700. /**
  2701. * Declares or executes a distinct() operation.
  2702. *
  2703. * This function does not trigger any middleware.
  2704. *
  2705. * #### Example:
  2706. *
  2707. * distinct(field, conditions, options)
  2708. * distinct(field, conditions)
  2709. * distinct(field)
  2710. * distinct()
  2711. *
  2712. * @param {String} [field]
  2713. * @param {Object|Query} [filter]
  2714. * @param {Object} [options]
  2715. * @return {Query} this
  2716. * @see distinct https://www.mongodb.com/docs/manual/reference/method/db.collection.distinct/
  2717. * @api public
  2718. */
  2719. Query.prototype.distinct = function(field, conditions, options) {
  2720. if (typeof field === 'function' ||
  2721. typeof conditions === 'function' ||
  2722. typeof options === 'function' ||
  2723. typeof arguments[3] === 'function') {
  2724. throw new MongooseError('Query.prototype.distinct() no longer accepts a callback');
  2725. }
  2726. this.op = 'distinct';
  2727. if (canMerge(conditions)) {
  2728. this.merge(conditions);
  2729. prepareDiscriminatorCriteria(this);
  2730. } else if (conditions != null) {
  2731. this.error(new ObjectParameterError(conditions, 'filter', 'distinct'));
  2732. }
  2733. if (field != null) {
  2734. this._distinct = field;
  2735. }
  2736. if (options != null) {
  2737. this.setOptions(options);
  2738. }
  2739. return this;
  2740. };
  2741. /**
  2742. * Sets the sort order
  2743. *
  2744. * If an object is passed, values allowed are `asc`, `desc`, `ascending`, `descending`, `1`, and `-1`.
  2745. *
  2746. * If a string is passed, it must be a space delimited list of path names. The
  2747. * sort order of each path is ascending unless the path name is prefixed with `-`
  2748. * which will be treated as descending.
  2749. *
  2750. * #### Example:
  2751. *
  2752. * // sort by "field" ascending and "test" descending
  2753. * query.sort({ field: 'asc', test: -1 });
  2754. *
  2755. * // equivalent
  2756. * query.sort('field -test');
  2757. *
  2758. * // also possible is to use a array with array key-value pairs
  2759. * query.sort([['field', 'asc']]);
  2760. *
  2761. * #### Note:
  2762. *
  2763. * Cannot be used with `distinct()`
  2764. *
  2765. * @param {Object|String|Array<Array<(string | number)>>} arg
  2766. * @param {Object} [options]
  2767. * @param {Boolean} [options.override=false] If true, replace existing sort options with `arg`
  2768. * @return {Query} this
  2769. * @see cursor.sort https://www.mongodb.com/docs/manual/reference/method/cursor.sort/
  2770. * @api public
  2771. */
  2772. Query.prototype.sort = function(arg, options) {
  2773. if (arguments.length > 2) {
  2774. throw new Error('sort() takes at most 2 arguments');
  2775. }
  2776. if (options != null && typeof options !== 'object') {
  2777. throw new Error('sort() options argument must be an object or nullish');
  2778. }
  2779. if (this.options.sort == null) {
  2780. this.options.sort = {};
  2781. }
  2782. if (options?.override) {
  2783. this.options.sort = {};
  2784. }
  2785. const sort = this.options.sort;
  2786. if (typeof arg === 'string') {
  2787. const properties = arg.indexOf(' ') === -1 ? [arg] : arg.split(' ');
  2788. for (let property of properties) {
  2789. const ascend = '-' == property[0] ? -1 : 1;
  2790. if (ascend === -1) {
  2791. property = property.slice(1);
  2792. }
  2793. if (specialProperties.has(property)) {
  2794. continue;
  2795. }
  2796. sort[property] = ascend;
  2797. }
  2798. } else if (Array.isArray(arg)) {
  2799. for (const pair of arg) {
  2800. if (!Array.isArray(pair)) {
  2801. throw new TypeError('Invalid sort() argument, must be array of arrays');
  2802. }
  2803. const key = '' + pair[0];
  2804. if (specialProperties.has(key)) {
  2805. continue;
  2806. }
  2807. sort[key] = _handleSortValue(pair[1], key);
  2808. }
  2809. } else if (typeof arg === 'object' && arg != null && !(arg instanceof Map)) {
  2810. for (const key of Object.keys(arg)) {
  2811. if (specialProperties.has(key)) {
  2812. continue;
  2813. }
  2814. sort[key] = _handleSortValue(arg[key], key);
  2815. }
  2816. } else if (arg instanceof Map) {
  2817. for (let key of arg.keys()) {
  2818. key = '' + key;
  2819. if (specialProperties.has(key)) {
  2820. continue;
  2821. }
  2822. sort[key] = _handleSortValue(arg.get(key), key);
  2823. }
  2824. } else if (arg != null) {
  2825. throw new TypeError('Invalid sort() argument. Must be a string, object, array, or map.');
  2826. }
  2827. return this;
  2828. };
  2829. /*!
  2830. * Convert sort values
  2831. */
  2832. function _handleSortValue(val, key) {
  2833. if (val === 1 || val === 'asc' || val === 'ascending') {
  2834. return 1;
  2835. }
  2836. if (val === -1 || val === 'desc' || val === 'descending') {
  2837. return -1;
  2838. }
  2839. if (val?.$meta != null) {
  2840. return { $meta: val.$meta };
  2841. }
  2842. throw new TypeError('Invalid sort value: { ' + key + ': ' + val + ' }');
  2843. }
  2844. /**
  2845. * Declare and/or execute this query as a `deleteOne()` operation. Works like
  2846. * remove, except it deletes at most one document regardless of the `single`
  2847. * option.
  2848. *
  2849. * This function triggers `deleteOne` middleware.
  2850. *
  2851. * #### Example:
  2852. *
  2853. * await Character.deleteOne({ name: 'Eddard Stark' });
  2854. *
  2855. * This function calls the MongoDB driver's [`Collection#deleteOne()` function](https://mongodb.github.io/node-mongodb-native/6.15/classes/Collection.html#deleteOne).
  2856. * The returned [promise](https://mongoosejs.com/docs/queries.html) resolves to an
  2857. * object that contains 2 properties:
  2858. *
  2859. * - `acknowledged`: boolean
  2860. * - `deletedCount`: the number of documents deleted
  2861. *
  2862. * #### Example:
  2863. *
  2864. * const res = await Character.deleteOne({ name: 'Eddard Stark' });
  2865. * // `1` if MongoDB deleted a doc, `0` if no docs matched the filter `{ name: ... }`
  2866. * res.deletedCount;
  2867. *
  2868. * @param {Object|Query} [filter] mongodb selector
  2869. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
  2870. * @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
  2871. * @return {Query} this
  2872. * @see DeleteResult https://mongodb.github.io/node-mongodb-native/6.15/interfaces/DeleteResult.html
  2873. * @see deleteOne https://mongodb.github.io/node-mongodb-native/6.15/classes/Collection.html#deleteOne
  2874. * @api public
  2875. */
  2876. Query.prototype.deleteOne = function deleteOne(filter, options) {
  2877. if (typeof filter === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') {
  2878. throw new MongooseError('Query.prototype.deleteOne() no longer accepts a callback');
  2879. }
  2880. this.op = 'deleteOne';
  2881. this.setOptions(options);
  2882. if (canMerge(filter)) {
  2883. this.merge(filter);
  2884. prepareDiscriminatorCriteria(this);
  2885. } else if (filter != null) {
  2886. this.error(new ObjectParameterError(filter, 'filter', 'deleteOne'));
  2887. }
  2888. return this;
  2889. };
  2890. /**
  2891. * Internal thunk for `deleteOne()`
  2892. *
  2893. * @method _deleteOne
  2894. * @instance
  2895. * @memberOf Query
  2896. * @api private
  2897. */
  2898. Query.prototype._deleteOne = async function _deleteOne() {
  2899. this._applyTranslateAliases();
  2900. this._castConditions();
  2901. // Check for empty/invalid filter with requireFilter option
  2902. checkRequireFilter(this._conditions, this.options);
  2903. if (this.error() != null) {
  2904. throw this.error();
  2905. }
  2906. const options = this._optionsForExec();
  2907. return this.mongooseCollection.deleteOne(this._conditions, options);
  2908. };
  2909. /**
  2910. * Declare and/or execute this query as a `deleteMany()` operation. Works like
  2911. * remove, except it deletes _every_ document that matches `filter` in the
  2912. * collection, regardless of the value of `single`.
  2913. *
  2914. * This function triggers `deleteMany` middleware.
  2915. *
  2916. * #### Example:
  2917. *
  2918. * await Character.deleteMany({ name: /Stark/, age: { $gte: 18 } });
  2919. *
  2920. * This function calls the MongoDB driver's [`Collection#deleteMany()` function](https://mongodb.github.io/node-mongodb-native/6.15/classes/Collection.html#deleteMany).
  2921. * The returned [promise](https://mongoosejs.com/docs/queries.html) resolves to an
  2922. * object that contains 2 properties:
  2923. *
  2924. * - `acknowledged`: boolean
  2925. * - `deletedCount`: the number of documents deleted
  2926. *
  2927. * #### Example:
  2928. *
  2929. * const res = await Character.deleteMany({ name: /Stark/, age: { $gte: 18 } });
  2930. * // `0` if no docs matched the filter, number of docs deleted otherwise
  2931. * res.deletedCount;
  2932. *
  2933. * @param {Object|Query} [filter] mongodb selector
  2934. * @param {Object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
  2935. * @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
  2936. * @return {Query} this
  2937. * @see DeleteResult https://mongodb.github.io/node-mongodb-native/6.15/interfaces/DeleteResult.html
  2938. * @see deleteMany https://mongodb.github.io/node-mongodb-native/6.15/classes/Collection.html#deleteMany
  2939. * @api public
  2940. */
  2941. Query.prototype.deleteMany = function(filter, options) {
  2942. if (typeof filter === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') {
  2943. throw new MongooseError('Query.prototype.deleteMany() no longer accepts a callback');
  2944. }
  2945. this.setOptions(options);
  2946. this.op = 'deleteMany';
  2947. if (canMerge(filter)) {
  2948. this.merge(filter);
  2949. prepareDiscriminatorCriteria(this);
  2950. } else if (filter != null) {
  2951. this.error(new ObjectParameterError(filter, 'filter', 'deleteMany'));
  2952. }
  2953. return this;
  2954. };
  2955. /**
  2956. * Execute a `deleteMany()` query
  2957. *
  2958. * @method _deleteMany
  2959. * @instance
  2960. * @memberOf Query
  2961. * @api private
  2962. */
  2963. Query.prototype._deleteMany = async function _deleteMany() {
  2964. this._applyTranslateAliases();
  2965. this._castConditions();
  2966. // Check for empty/invalid filter with requireFilter option
  2967. checkRequireFilter(this._conditions, this.options);
  2968. if (this.error() != null) {
  2969. throw this.error();
  2970. }
  2971. const options = this._optionsForExec();
  2972. return this.mongooseCollection.deleteMany(this._conditions, options);
  2973. };
  2974. /**
  2975. * hydrates a document
  2976. *
  2977. * @param {Model} model
  2978. * @param {Document} doc
  2979. * @param {Object} res 3rd parameter to callback
  2980. * @param {Object} fields
  2981. * @param {Query} self
  2982. * @param {Array} [pop] array of paths used in population
  2983. * @param {Function} callback
  2984. * @api private
  2985. */
  2986. function completeOne(model, doc, res, options, fields, userProvidedFields, pop, callback) {
  2987. if (options.includeResultMetadata && doc == null) {
  2988. _init(null);
  2989. return null;
  2990. }
  2991. helpers.createModelAndInit(model, doc, fields, userProvidedFields, options, pop, _init);
  2992. function _init(err, casted) {
  2993. if (err) {
  2994. return callback(err);
  2995. }
  2996. if (options.includeResultMetadata) {
  2997. if (doc && casted) {
  2998. if (options.session != null) {
  2999. casted.$session(options.session);
  3000. }
  3001. res.value = casted;
  3002. } else {
  3003. res.value = null;
  3004. }
  3005. return callback(null, res);
  3006. }
  3007. if (options.session != null) {
  3008. casted.$session(options.session);
  3009. }
  3010. callback(null, casted);
  3011. }
  3012. }
  3013. /**
  3014. * If the model is a discriminator type and not root, then add the key & value to the criteria.
  3015. * @param {Query} query
  3016. * @api private
  3017. */
  3018. function prepareDiscriminatorCriteria(query) {
  3019. if (!query?.model?.schema) {
  3020. return;
  3021. }
  3022. const schema = query.model.schema;
  3023. if (schema?.discriminatorMapping && !schema.discriminatorMapping.isRoot) {
  3024. query._conditions[schema.discriminatorMapping.key] = schema.discriminatorMapping.value;
  3025. }
  3026. }
  3027. /**
  3028. * Issues a mongodb `findOneAndUpdate()` command.
  3029. *
  3030. * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found
  3031. * document (if any).
  3032. *
  3033. * This function triggers the following middleware.
  3034. *
  3035. * - `findOneAndUpdate()`
  3036. *
  3037. * #### Available options
  3038. *
  3039. * - `new`: bool - if true, return the modified document rather than the original. defaults to false (changed in 4.0)
  3040. * - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
  3041. * - `fields`: {Object|String} - Field selection. Equivalent to `.select(fields).findOneAndUpdate()`
  3042. * - `sort`: if multiple docs are found by the filter, sets the sort order to choose which doc to update
  3043. * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
  3044. * - `runValidators`: if true, runs [update validators](https://mongoosejs.com/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema.
  3045. * - `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.
  3046. * - `requireFilter`: bool - if true, throws an error if the filter is empty (`{}`). Defaults to false.
  3047. *
  3048. * #### Example:
  3049. *
  3050. * query.findOneAndUpdate(filter, update, options); // returns Query
  3051. * query.findOneAndUpdate(filter, update); // returns Query
  3052. * // Note that `Query#findOneAndUpdate()` with 1 arg treats the first arg as the `update`, NOT the `filter`.
  3053. * query.findOneAndUpdate(update); // returns Query
  3054. * query.findOneAndUpdate(); // returns Query
  3055. *
  3056. * @method findOneAndUpdate
  3057. * @memberOf Query
  3058. * @instance
  3059. * @param {Object|Query} [filter]
  3060. * @param {Object} [update]
  3061. * @param {Object} [options]
  3062. * @param {Boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document
  3063. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
  3064. * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
  3065. * @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
  3066. * @param {Boolean} [options.new=false] By default, `findOneAndUpdate()` returns the document as it was **before** `update` was applied. If you set `new: true`, `findOneAndUpdate()` will instead give you the object after `update` was applied.
  3067. * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and [the Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html).
  3068. * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/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.
  3069. * @param {Boolean} [options.returnOriginal=null] An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`.
  3070. * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
  3071. * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
  3072. * @param {Boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators.
  3073. * @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
  3074. * @see Tutorial https://mongoosejs.com/docs/tutorials/findoneandupdate.html
  3075. * @see findAndModify command https://www.mongodb.com/docs/manual/reference/command/findAndModify/
  3076. * @see ModifyResult https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html
  3077. * @see findOneAndUpdate https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#findOneAndUpdate
  3078. * @return {Query} this
  3079. * @api public
  3080. */
  3081. Query.prototype.findOneAndUpdate = function(filter, update, options) {
  3082. if (typeof filter === 'function' ||
  3083. typeof update === 'function' ||
  3084. typeof options === 'function' ||
  3085. typeof arguments[3] === 'function') {
  3086. throw new MongooseError('Query.prototype.findOneAndUpdate() no longer accepts a callback');
  3087. }
  3088. this.op = 'findOneAndUpdate';
  3089. this._validate();
  3090. switch (arguments.length) {
  3091. case 2:
  3092. options = undefined;
  3093. break;
  3094. case 1:
  3095. update = filter;
  3096. filter = options = undefined;
  3097. break;
  3098. }
  3099. if (canMerge(filter)) {
  3100. this.merge(filter);
  3101. } else if (filter != null) {
  3102. this.error(
  3103. new ObjectParameterError(filter, 'filter', 'findOneAndUpdate')
  3104. );
  3105. }
  3106. options = options ? clone(options) : {};
  3107. if (options.projection) {
  3108. this.select(options.projection);
  3109. delete options.projection;
  3110. }
  3111. if (options.fields) {
  3112. this.select(options.fields);
  3113. delete options.fields;
  3114. }
  3115. const returnOriginal = this?.model?.base?.options?.returnOriginal;
  3116. if (options.new == null && options.returnDocument == null && options.returnOriginal == null && returnOriginal != null) {
  3117. options.returnOriginal = returnOriginal;
  3118. }
  3119. const updatePipeline = this?.model?.base?.options?.updatePipeline;
  3120. if (options.updatePipeline == null && updatePipeline != null) {
  3121. options.updatePipeline = updatePipeline;
  3122. }
  3123. this.setOptions(options);
  3124. // apply doc
  3125. if (update) {
  3126. this._mergeUpdate(update);
  3127. }
  3128. return this;
  3129. };
  3130. /**
  3131. * Execute a findOneAndUpdate operation
  3132. *
  3133. * @method _findOneAndUpdate
  3134. * @memberOf Query
  3135. * @api private
  3136. */
  3137. Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() {
  3138. this._applyTranslateAliases();
  3139. this._castConditions();
  3140. // Check for empty/invalid filter with requireFilter option
  3141. checkRequireFilter(this._conditions, this.options);
  3142. _castArrayFilters(this);
  3143. if (this.error()) {
  3144. throw this.error();
  3145. }
  3146. const dbOptions = this.model.db.options;
  3147. const baseOptions = this.model.base.options;
  3148. applyGlobalMaxTimeMS(this.options, dbOptions, baseOptions);
  3149. applyGlobalDiskUse(this.options, dbOptions, baseOptions);
  3150. if ('strict' in this.options) {
  3151. this._mongooseOptions.strict = this.options.strict;
  3152. }
  3153. const options = this._optionsForExec(this.model);
  3154. convertNewToReturnDocument(options);
  3155. this._update = this._castUpdate(this._update);
  3156. const _opts = Object.assign({}, options, {
  3157. setDefaultsOnInsert: this._mongooseOptions.setDefaultsOnInsert
  3158. });
  3159. this._update = setDefaultsOnInsert(this._conditions, this.model.schema,
  3160. this._update, _opts);
  3161. if (!this._update || utils.hasOwnKeys(this._update) === false) {
  3162. if (options.upsert) {
  3163. // still need to do the upsert to empty doc
  3164. const $set = clone(this._update);
  3165. delete $set._id;
  3166. this._update = { $set };
  3167. } else {
  3168. this._execCount = 0;
  3169. const res = await this._findOne();
  3170. return res;
  3171. }
  3172. } else if (this._update instanceof Error) {
  3173. throw this._update;
  3174. } else {
  3175. // In order to make MongoDB 2.6 happy (see
  3176. // https://jira.mongodb.org/browse/SERVER-12266 and related issues)
  3177. // if we have an actual update document but $set is empty, junk the $set.
  3178. if (this._update.$set && utils.hasOwnKeys(this._update.$set) === false) {
  3179. delete this._update.$set;
  3180. }
  3181. }
  3182. const runValidators = _getOption(this, 'runValidators', false);
  3183. if (runValidators) {
  3184. await this.validate(this._update, options, false);
  3185. }
  3186. if (this._update.toBSON) {
  3187. this._update = this._update.toBSON();
  3188. }
  3189. let res = await this.mongooseCollection.findOneAndUpdate(this._conditions, this._update, options);
  3190. for (const fn of this._transforms) {
  3191. res = fn(res);
  3192. }
  3193. const doc = !options.includeResultMetadata ? res : res.value;
  3194. return new Promise((resolve, reject) => {
  3195. this._completeOne(doc, res, options.projection, (err, res) => {
  3196. if (err) {
  3197. return reject(err);
  3198. }
  3199. resolve(res);
  3200. });
  3201. });
  3202. };
  3203. /**
  3204. * Issues a MongoDB [findOneAndDelete](https://www.mongodb.com/docs/manual/reference/method/db.collection.findOneAndDelete/) command.
  3205. *
  3206. * Finds a matching document, removes it, and returns the found document (if any).
  3207. *
  3208. * This function triggers the following middleware.
  3209. *
  3210. * - `findOneAndDelete()`
  3211. *
  3212. * #### Available options
  3213. *
  3214. * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
  3215. * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
  3216. * - `requireFilter`: bool - if true, throws an error if the filter is empty (`{}`). Defaults to false.
  3217. *
  3218. * #### Example:
  3219. *
  3220. * A.where().findOneAndDelete(conditions, options) // return Query
  3221. * A.where().findOneAndDelete(conditions) // returns Query
  3222. * A.where().findOneAndDelete() // returns Query
  3223. *
  3224. * @method findOneAndDelete
  3225. * @memberOf Query
  3226. * @param {Object} [filter]
  3227. * @param {Object} [options]
  3228. * @param {Boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document
  3229. * @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
  3230. * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
  3231. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
  3232. * @return {Query} this
  3233. * @see findAndModify command https://www.mongodb.com/docs/manual/reference/command/findAndModify/
  3234. * @api public
  3235. */
  3236. Query.prototype.findOneAndDelete = function(filter, options) {
  3237. if (typeof filter === 'function' ||
  3238. typeof options === 'function' ||
  3239. typeof arguments[2] === 'function') {
  3240. throw new MongooseError('Query.prototype.findOneAndDelete() no longer accepts a callback');
  3241. }
  3242. this.op = 'findOneAndDelete';
  3243. this._validate();
  3244. if (canMerge(filter)) {
  3245. this.merge(filter);
  3246. }
  3247. options && this.setOptions(options);
  3248. return this;
  3249. };
  3250. /**
  3251. * Execute a `findOneAndDelete()` query
  3252. *
  3253. * @return {Query} this
  3254. * @method _findOneAndDelete
  3255. * @memberOf Query
  3256. * @api private
  3257. */
  3258. Query.prototype._findOneAndDelete = async function _findOneAndDelete() {
  3259. this._applyTranslateAliases();
  3260. this._castConditions();
  3261. // Check for empty/invalid filter with requireFilter option
  3262. checkRequireFilter(this._conditions, this.options);
  3263. if (this.error() != null) {
  3264. throw this.error();
  3265. }
  3266. const includeResultMetadata = this.options.includeResultMetadata;
  3267. const filter = this._conditions;
  3268. const options = this._optionsForExec(this.model);
  3269. let res = await this.mongooseCollection.findOneAndDelete(filter, options);
  3270. for (const fn of this._transforms) {
  3271. res = fn(res);
  3272. }
  3273. const doc = !includeResultMetadata ? res : res.value;
  3274. return new Promise((resolve, reject) => {
  3275. this._completeOne(doc, res, options.projection, (err, res) => {
  3276. if (err) {
  3277. return reject(err);
  3278. }
  3279. resolve(res);
  3280. });
  3281. });
  3282. };
  3283. /**
  3284. * Issues a MongoDB [findOneAndReplace](https://www.mongodb.com/docs/manual/reference/method/db.collection.findOneAndReplace/) command.
  3285. *
  3286. * Finds a matching document, removes it, and returns the found document (if any).
  3287. *
  3288. * This function triggers the following middleware.
  3289. *
  3290. * - `findOneAndReplace()`
  3291. *
  3292. * #### Available options
  3293. *
  3294. * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
  3295. * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0
  3296. * - `includeResultMetadata`: if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document
  3297. * - `requireFilter`: bool - if true, throws an error if the filter is empty (`{}`). Defaults to false.
  3298. *
  3299. * #### Example:
  3300. *
  3301. * A.where().findOneAndReplace(filter, replacement, options); // return Query
  3302. * A.where().findOneAndReplace(filter); // returns Query
  3303. * A.where().findOneAndReplace(); // returns Query
  3304. *
  3305. * @method findOneAndReplace
  3306. * @memberOf Query
  3307. * @param {Object} [filter]
  3308. * @param {Object} [replacement]
  3309. * @param {Object} [options]
  3310. * @param {Boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document
  3311. * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
  3312. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
  3313. * @param {Boolean} [options.new=false] By default, `findOneAndUpdate()` returns the document as it was **before** `update` was applied. If you set `new: true`, `findOneAndUpdate()` will instead give you the object after `update` was applied.
  3314. * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and [the Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html).
  3315. * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
  3316. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
  3317. * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/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.
  3318. * @param {Boolean} [options.returnOriginal=null] An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`.
  3319. * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
  3320. * @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
  3321. * @return {Query} this
  3322. * @api public
  3323. */
  3324. Query.prototype.findOneAndReplace = function(filter, replacement, options) {
  3325. if (typeof filter === 'function' ||
  3326. typeof replacement === 'function' ||
  3327. typeof options === 'function' ||
  3328. typeof arguments[4] === 'function') {
  3329. throw new MongooseError('Query.prototype.findOneAndReplace() no longer accepts a callback');
  3330. }
  3331. this.op = 'findOneAndReplace';
  3332. this._validate();
  3333. if (canMerge(filter)) {
  3334. this.merge(filter);
  3335. } else if (filter != null) {
  3336. this.error(
  3337. new ObjectParameterError(filter, 'filter', 'findOneAndReplace')
  3338. );
  3339. }
  3340. if (replacement != null) {
  3341. this._mergeUpdate(replacement);
  3342. }
  3343. options = options || {};
  3344. const returnOriginal = this?.model?.base?.options?.returnOriginal;
  3345. if (options.new == null && options.returnDocument == null && options.returnOriginal == null && returnOriginal != null) {
  3346. options.returnOriginal = returnOriginal;
  3347. }
  3348. this.setOptions(options);
  3349. return this;
  3350. };
  3351. /**
  3352. * Execute a findOneAndReplace() query
  3353. *
  3354. * @return {Query} this
  3355. * @method _findOneAndReplace
  3356. * @instance
  3357. * @memberOf Query
  3358. * @api private
  3359. */
  3360. Query.prototype._findOneAndReplace = async function _findOneAndReplace() {
  3361. this._applyTranslateAliases();
  3362. this._castConditions();
  3363. // Check for empty/invalid filter with requireFilter option
  3364. checkRequireFilter(this._conditions, this.options);
  3365. if (this.error() != null) {
  3366. throw this.error();
  3367. }
  3368. if ('strict' in this.options) {
  3369. this._mongooseOptions.strict = this.options.strict;
  3370. delete this.options.strict;
  3371. }
  3372. const filter = this._conditions;
  3373. const options = this._optionsForExec();
  3374. convertNewToReturnDocument(options);
  3375. const includeResultMetadata = this.options.includeResultMetadata;
  3376. const modelOpts = { skipId: true };
  3377. if ('strict' in this._mongooseOptions) {
  3378. modelOpts.strict = this._mongooseOptions.strict;
  3379. }
  3380. const runValidators = _getOption(this, 'runValidators', false);
  3381. try {
  3382. const update = new this.model(this._update, null, modelOpts);
  3383. if (runValidators) {
  3384. await update.validate();
  3385. } else if (update.$__.validationError) {
  3386. throw update.$__.validationError;
  3387. }
  3388. this._update = update.toBSON();
  3389. } catch (err) {
  3390. if (err instanceof ValidationError) {
  3391. throw err;
  3392. }
  3393. const validationError = new ValidationError();
  3394. validationError.errors[err.path] = err;
  3395. throw validationError;
  3396. }
  3397. let res = await this.mongooseCollection.findOneAndReplace(filter, this._update, options);
  3398. for (const fn of this._transforms) {
  3399. res = fn(res);
  3400. }
  3401. const doc = !includeResultMetadata ? res : res.value;
  3402. return new Promise((resolve, reject) => {
  3403. this._completeOne(doc, res, options.projection, (err, res) => {
  3404. if (err) {
  3405. return reject(err);
  3406. }
  3407. resolve(res);
  3408. });
  3409. });
  3410. };
  3411. /**
  3412. * Finds a single document by its _id field. `findById(id)` is equivalent to
  3413. * `findOne({ _id: id })`.
  3414. *
  3415. * The `id` is cast based on the Schema before sending the command.
  3416. *
  3417. * This function triggers the following middleware.
  3418. *
  3419. * - `findOne()`
  3420. *
  3421. * @method findById
  3422. * @memberOf Query
  3423. * @instance
  3424. * @param {Any} id value of `_id` to query by
  3425. * @param {Object} [projection] optional fields to return
  3426. * @param {Object} [options] see [`setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions())
  3427. * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
  3428. * @return {Query} this
  3429. * @see findOne https://www.mongodb.com/docs/manual/reference/method/db.collection.findOne/
  3430. * @see Query.select https://mongoosejs.com/docs/api/query.html#Query.prototype.select()
  3431. * @api public
  3432. */
  3433. Query.prototype.findById = function(id, projection, options) {
  3434. return this.findOne({ _id: id }, projection, options);
  3435. };
  3436. /**
  3437. * Issues a mongodb findOneAndUpdate command by a document's _id field.
  3438. * `findByIdAndUpdate(id, ...)` is equivalent to `findOneAndUpdate({ _id: id }, ...)`.
  3439. *
  3440. * Finds a matching document, updates it according to the `update` arg,
  3441. * passing any `options`, and returns the found document (if any).
  3442. *
  3443. * This function triggers the following middleware.
  3444. *
  3445. * - `findOneAndUpdate()`
  3446. *
  3447. * @method findByIdAndUpdate
  3448. * @memberOf Query
  3449. * @instance
  3450. * @param {Any} id value of `_id` to query by
  3451. * @param {Object} [doc]
  3452. * @param {Object} [options]
  3453. * @param {Boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document
  3454. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
  3455. * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
  3456. * @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
  3457. * @param {Boolean} [options.new=false] By default, `findOneAndUpdate()` returns the document as it was **before** `update` was applied. If you set `new: true`, `findOneAndUpdate()` will instead give you the object after `update` was applied.
  3458. * @param {Object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and [the Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html).
  3459. * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/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.
  3460. * @param {Boolean} [options.returnOriginal=null] An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`.
  3461. * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
  3462. * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
  3463. * @param {Boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators.
  3464. * @see Tutorial https://mongoosejs.com/docs/tutorials/findoneandupdate.html
  3465. * @see findAndModify command https://www.mongodb.com/docs/manual/reference/command/findAndModify/
  3466. * @see ModifyResult https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html
  3467. * @see findOneAndUpdate https://mongodb.github.io/node-mongodb-native/4.9/classes/Collection.html#findOneAndUpdate
  3468. * @return {Query} this
  3469. * @api public
  3470. */
  3471. Query.prototype.findByIdAndUpdate = function(id, update, options) {
  3472. return this.findOneAndUpdate({ _id: id }, update, options);
  3473. };
  3474. /**
  3475. * Issue a MongoDB `findOneAndDelete()` command by a document's _id field.
  3476. * In other words, `findByIdAndDelete(id)` is a shorthand for
  3477. * `findOneAndDelete({ _id: id })`.
  3478. *
  3479. * This function triggers the following middleware.
  3480. *
  3481. * - `findOneAndDelete()`
  3482. *
  3483. * @method findByIdAndDelete
  3484. * @memberOf Query
  3485. * @param {any} id value of `_id` to query by
  3486. * @param {Object} [options]
  3487. * @param {Boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/4.9/interfaces/ModifyResult.html) rather than just the document
  3488. * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html).
  3489. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
  3490. * @return {Query} this
  3491. * @see findAndModify command https://www.mongodb.com/docs/manual/reference/command/findAndModify/
  3492. * @api public
  3493. */
  3494. Query.prototype.findByIdAndDelete = function(id, options) {
  3495. return this.findOneAndDelete({ _id: id }, options);
  3496. };
  3497. /**
  3498. * Support the `new` option as an alternative to `returnOriginal` for backwards
  3499. * compat.
  3500. * @api private
  3501. */
  3502. function convertNewToReturnDocument(options) {
  3503. if ('new' in options) {
  3504. options.returnDocument = options['new'] ? 'after' : 'before';
  3505. delete options['new'];
  3506. }
  3507. if ('returnOriginal' in options) {
  3508. options.returnDocument = options['returnOriginal'] ? 'before' : 'after';
  3509. delete options['returnOriginal'];
  3510. }
  3511. // Temporary since driver 4.0.0-beta does not support `returnDocument`
  3512. if (typeof options.returnDocument === 'string') {
  3513. options.returnOriginal = options.returnDocument === 'before';
  3514. }
  3515. }
  3516. /**
  3517. * Get options from query opts, falling back to the base mongoose object.
  3518. * @param {Query} query
  3519. * @param {Object} option
  3520. * @param {Any} def
  3521. * @api private
  3522. */
  3523. function _getOption(query, option, def) {
  3524. const opts = query._optionsForExec(query.model);
  3525. if (option in opts) {
  3526. return opts[option];
  3527. }
  3528. if (option in query.model.base.options) {
  3529. return query.model.base.options[option];
  3530. }
  3531. return def;
  3532. }
  3533. /*!
  3534. * ignore
  3535. */
  3536. function _completeOneLean(schema, doc, path, res, opts, callback) {
  3537. if (opts.lean && typeof opts.lean.transform === 'function') {
  3538. opts.lean.transform(doc);
  3539. for (let i = 0; i < schema.childSchemas.length; i++) {
  3540. const childPath = path ? path + '.' + schema.childSchemas[i].model.path : schema.childSchemas[i].model.path;
  3541. const _schema = schema.childSchemas[i].schema;
  3542. const obj = mpath.get(childPath, doc);
  3543. if (obj == null) {
  3544. continue;
  3545. }
  3546. if (Array.isArray(obj)) {
  3547. for (let i = 0; i < obj.length; i++) {
  3548. opts.lean.transform(obj[i]);
  3549. }
  3550. } else {
  3551. opts.lean.transform(obj);
  3552. }
  3553. _completeOneLean(_schema, obj, childPath, res, opts);
  3554. }
  3555. if (callback) {
  3556. return callback(null, doc);
  3557. } else {
  3558. return;
  3559. }
  3560. }
  3561. if (opts.includeResultMetadata) {
  3562. return callback(null, res);
  3563. }
  3564. return callback(null, doc);
  3565. }
  3566. /*!
  3567. * ignore
  3568. */
  3569. function _completeManyLean(schema, docs, path, opts) {
  3570. if (opts.lean && typeof opts.lean.transform === 'function') {
  3571. for (const doc of docs) {
  3572. opts.lean.transform(doc);
  3573. }
  3574. for (let i = 0; i < schema.childSchemas.length; i++) {
  3575. const childPath = path ? path + '.' + schema.childSchemas[i].model.path : schema.childSchemas[i].model.path;
  3576. const _schema = schema.childSchemas[i].schema;
  3577. let doc = mpath.get(childPath, docs);
  3578. if (doc == null) {
  3579. continue;
  3580. }
  3581. doc = doc.flat();
  3582. for (let i = 0; i < doc.length; i++) {
  3583. opts.lean.transform(doc[i]);
  3584. }
  3585. _completeManyLean(_schema, doc, childPath, opts);
  3586. }
  3587. }
  3588. return docs;
  3589. }
  3590. /**
  3591. * Override mquery.prototype._mergeUpdate to handle mongoose objects in
  3592. * updates.
  3593. *
  3594. * @param {Object} update
  3595. * @method _mergeUpdate
  3596. * @memberOf Query
  3597. * @instance
  3598. * @api private
  3599. */
  3600. Query.prototype._mergeUpdate = function(update) {
  3601. const updatePipeline = this._mongooseOptions.updatePipeline;
  3602. if (!updatePipeline && Array.isArray(update)) {
  3603. throw new MongooseError('Cannot pass an array to query updates unless the `updatePipeline` option is set.');
  3604. }
  3605. if (!this._update) {
  3606. this._update = Array.isArray(update) ? [] : {};
  3607. }
  3608. if (update == null || (typeof update === 'object' && utils.hasOwnKeys(update) === false)) {
  3609. return;
  3610. }
  3611. if (update instanceof Query) {
  3612. if (Array.isArray(this._update)) {
  3613. throw new MongooseError('Cannot mix array and object updates');
  3614. }
  3615. if (update._update) {
  3616. utils.mergeClone(this._update, update._update);
  3617. }
  3618. } else if (Array.isArray(update)) {
  3619. if (!Array.isArray(this._update)) {
  3620. // `_update` may be empty object by default, like in `doc.updateOne()`
  3621. // because we create the query first, then run hooks, then apply the update.
  3622. if (this._update == null || utils.isEmptyObject(this._update)) {
  3623. this._update = [];
  3624. } else {
  3625. throw new MongooseError('Cannot mix array and object updates');
  3626. }
  3627. }
  3628. this._update = this._update.concat(update);
  3629. } else {
  3630. if (Array.isArray(this._update)) {
  3631. throw new MongooseError('Cannot mix array and object updates');
  3632. }
  3633. utils.mergeClone(this._update, update);
  3634. }
  3635. };
  3636. /*!
  3637. * ignore
  3638. */
  3639. async function _updateThunk(op) {
  3640. this._applyTranslateAliases();
  3641. this._castConditions();
  3642. // Check for empty/invalid filter with requireFilter option
  3643. checkRequireFilter(this._conditions, this.options);
  3644. _castArrayFilters(this);
  3645. if (this.error() != null) {
  3646. throw this.error();
  3647. }
  3648. const castedQuery = this._conditions;
  3649. const options = this._optionsForExec(this.model);
  3650. this._update = clone(this._update, options);
  3651. const isOverwriting = op === 'replaceOne';
  3652. if (isOverwriting) {
  3653. this._update = new this.model(this._update, null, { skipId: true });
  3654. } else {
  3655. this._update = this._castUpdate(this._update);
  3656. if (this._update == null || utils.hasOwnKeys(this._update) === false) {
  3657. return { acknowledged: false };
  3658. }
  3659. const _opts = Object.assign({}, options, {
  3660. setDefaultsOnInsert: this._mongooseOptions.setDefaultsOnInsert
  3661. });
  3662. this._update = setDefaultsOnInsert(this._conditions, this.model.schema,
  3663. this._update, _opts);
  3664. }
  3665. if (Array.isArray(options.arrayFilters)) {
  3666. options.arrayFilters = removeUnusedArrayFilters(this._update, options.arrayFilters);
  3667. }
  3668. const runValidators = _getOption(this, 'runValidators', false);
  3669. if (runValidators) {
  3670. await this.validate(this._update, options, isOverwriting);
  3671. }
  3672. if (this._update.toBSON) {
  3673. this._update = this._update.toBSON();
  3674. }
  3675. return this.mongooseCollection[op](castedQuery, this._update, options);
  3676. }
  3677. /**
  3678. * Mongoose calls this function internally to validate the query if
  3679. * `runValidators` is set
  3680. *
  3681. * @param {Object} castedDoc the update, after casting
  3682. * @param {Object} options the options from `_optionsForExec()`
  3683. * @param {Boolean} isOverwriting
  3684. * @method validate
  3685. * @memberOf Query
  3686. * @instance
  3687. * @api private
  3688. */
  3689. Query.prototype.validate = async function validate(castedDoc, options, isOverwriting) {
  3690. if (typeof arguments[3] === 'function') {
  3691. throw new MongooseError('Query.prototype.validate() no longer accepts a callback');
  3692. }
  3693. await _executePreHooks(this, 'validate');
  3694. if (isOverwriting) {
  3695. await castedDoc.$validate();
  3696. } else {
  3697. await updateValidators(this, this.model.schema, castedDoc, options);
  3698. }
  3699. await _executePostHooks(this, null, null, 'validate');
  3700. };
  3701. /**
  3702. * Execute an updateMany query
  3703. *
  3704. * @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update()
  3705. * @method _updateMany
  3706. * @memberOf Query
  3707. * @instance
  3708. * @api private
  3709. */
  3710. Query.prototype._updateMany = async function _updateMany() {
  3711. return _updateThunk.call(this, 'updateMany');
  3712. };
  3713. /**
  3714. * Execute an updateOne query
  3715. *
  3716. * @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update()
  3717. * @method _updateOne
  3718. * @memberOf Query
  3719. * @instance
  3720. * @api private
  3721. */
  3722. Query.prototype._updateOne = async function _updateOne() {
  3723. return _updateThunk.call(this, 'updateOne');
  3724. };
  3725. /**
  3726. * Execute a replaceOne query
  3727. *
  3728. * @see Model.replaceOne https://mongoosejs.com/docs/api/model.html#Model.replaceOne()
  3729. * @method _replaceOne
  3730. * @memberOf Query
  3731. * @instance
  3732. * @api private
  3733. */
  3734. Query.prototype._replaceOne = async function _replaceOne() {
  3735. return _updateThunk.call(this, 'replaceOne');
  3736. };
  3737. /**
  3738. * Declare and/or execute this query as an updateMany() operation.
  3739. * MongoDB will update _all_ documents that match `filter` (as opposed to just the first one).
  3740. *
  3741. * **Note** updateMany will _not_ fire update middleware. Use `pre('updateMany')`
  3742. * and `post('updateMany')` instead.
  3743. *
  3744. * #### Example:
  3745. *
  3746. * const res = await Person.updateMany({ name: /Stark$/ }, { isDeleted: true });
  3747. * res.n; // Number of documents matched
  3748. * res.nModified; // Number of documents modified
  3749. *
  3750. * // Other supported syntaxes
  3751. * await Person.find({ name: /Stark$/ }).updateMany({ isDeleted: true }); // Using chaining syntax
  3752. * await Person.find().updateMany({ isDeleted: true }); // Set `isDeleted` on _all_ Person documents
  3753. *
  3754. * This function triggers the following middleware.
  3755. *
  3756. * - `updateMany()`
  3757. *
  3758. * @param {Object} [filter]
  3759. * @param {Object|Array} [update] the update command. If array, this update will be treated as an update pipeline and not casted.
  3760. * @param {Object} [options]
  3761. * @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
  3762. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
  3763. * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
  3764. * @param {Object} [options.writeConcern=null] sets the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](https://mongoosejs.com/docs/guide.html#writeConcern)
  3765. * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
  3766. * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
  3767. * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
  3768. * @param {Boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators.
  3769. * @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
  3770. * @return {Query} this
  3771. * @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update()
  3772. * @see Query docs https://mongoosejs.com/docs/queries.html
  3773. * @see update https://www.mongodb.com/docs/manual/reference/method/db.collection.update/
  3774. * @see UpdateResult https://mongodb.github.io/node-mongodb-native/4.9/interfaces/UpdateResult.html
  3775. * @see MongoDB docs https://www.mongodb.com/docs/manual/reference/command/update/#update-command-output
  3776. * @api public
  3777. */
  3778. Query.prototype.updateMany = function(conditions, doc, options, callback) {
  3779. if (typeof options === 'function') {
  3780. // .update(conditions, doc, callback)
  3781. callback = options;
  3782. options = undefined;
  3783. } else if (typeof doc === 'function') {
  3784. // .update(doc, callback);
  3785. callback = doc;
  3786. doc = conditions;
  3787. conditions = {};
  3788. options = undefined;
  3789. } else if (typeof conditions === 'function') {
  3790. // .update(callback)
  3791. callback = conditions;
  3792. conditions = undefined;
  3793. doc = undefined;
  3794. options = undefined;
  3795. } else if (typeof conditions === 'object' && !doc && !options && !callback) {
  3796. // .update(doc)
  3797. doc = conditions;
  3798. conditions = undefined;
  3799. options = undefined;
  3800. callback = undefined;
  3801. }
  3802. return _update(this, 'updateMany', conditions, doc, options, callback);
  3803. };
  3804. /**
  3805. * Declare and/or execute this query as an updateOne() operation.
  3806. * MongoDB will update _only_ the first document that matches `filter`.
  3807. *
  3808. * - Use `replaceOne()` if you want to overwrite an entire document rather than using [atomic operators](https://www.mongodb.com/docs/manual/tutorial/model-data-for-atomic-operations/#pattern) like `$set`.
  3809. *
  3810. * **Note** updateOne will _not_ fire update middleware. Use `pre('updateOne')`
  3811. * and `post('updateOne')` instead.
  3812. *
  3813. * #### Example:
  3814. *
  3815. * const res = await Person.updateOne({ name: 'Jean-Luc Picard' }, { ship: 'USS Enterprise' });
  3816. * res.acknowledged; // Indicates if this write result was acknowledged. If not, then all other members of this result will be undefined.
  3817. * res.matchedCount; // Number of documents that matched the filter
  3818. * res.modifiedCount; // Number of documents that were modified
  3819. * res.upsertedCount; // Number of documents that were upserted
  3820. * res.upsertedId; // Identifier of the inserted document (if an upsert took place)
  3821. *
  3822. * // Other supported syntaxes
  3823. * await Person.findOne({ name: 'Jean-Luc Picard' }).updateOne({ ship: 'USS Enterprise' }); // Using chaining syntax
  3824. * await Person.updateOne({ ship: 'USS Enterprise' }); // Updates first doc's `ship` property
  3825. *
  3826. * This function triggers the following middleware.
  3827. *
  3828. * - `updateOne()`
  3829. *
  3830. * @param {Object} [filter]
  3831. * @param {Object|Array} [update] the update command. If array, this update will be treated as an update pipeline and not casted.
  3832. * @param {Object} [options]
  3833. * @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
  3834. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
  3835. * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
  3836. * @param {Object} [options.writeConcern=null] sets the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](https://mongoosejs.com/docs/guide.html#writeConcern)
  3837. * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/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.
  3838. * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
  3839. * @param {Boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key
  3840. * @param {Boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators.
  3841. * @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
  3842. * @return {Query} this
  3843. * @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update()
  3844. * @see Query docs https://mongoosejs.com/docs/queries.html
  3845. * @see update https://www.mongodb.com/docs/manual/reference/method/db.collection.update/
  3846. * @see UpdateResult https://mongodb.github.io/node-mongodb-native/4.9/interfaces/UpdateResult.html
  3847. * @see MongoDB docs https://www.mongodb.com/docs/manual/reference/command/update/#update-command-output
  3848. * @api public
  3849. */
  3850. Query.prototype.updateOne = function(conditions, doc, options, callback) {
  3851. if (typeof options === 'function') {
  3852. // .update(conditions, doc, callback)
  3853. callback = options;
  3854. options = undefined;
  3855. } else if (typeof doc === 'function') {
  3856. // .update(doc, callback);
  3857. callback = doc;
  3858. doc = conditions;
  3859. conditions = {};
  3860. options = undefined;
  3861. } else if (typeof conditions === 'function') {
  3862. // .update(callback)
  3863. callback = conditions;
  3864. conditions = undefined;
  3865. doc = undefined;
  3866. options = undefined;
  3867. } else if (typeof conditions === 'object' && !doc && !options && !callback) {
  3868. // .update(doc)
  3869. doc = conditions;
  3870. conditions = undefined;
  3871. options = undefined;
  3872. callback = undefined;
  3873. }
  3874. return _update(this, 'updateOne', conditions, doc, options, callback);
  3875. };
  3876. /**
  3877. * Declare and/or execute this query as a replaceOne() operation.
  3878. * MongoDB will replace the existing document and will not accept any [atomic operators](https://www.mongodb.com/docs/manual/tutorial/model-data-for-atomic-operations/#pattern) (`$set`, etc.)
  3879. *
  3880. * **Note** replaceOne will _not_ fire update middleware. Use `pre('replaceOne')`
  3881. * and `post('replaceOne')` instead.
  3882. *
  3883. * #### Example:
  3884. *
  3885. * const res = await Person.replaceOne({ _id: 24601 }, { name: 'Jean Valjean' });
  3886. * res.acknowledged; // Indicates if this write result was acknowledged. If not, then all other members of this result will be undefined.
  3887. * res.matchedCount; // Number of documents that matched the filter
  3888. * res.modifiedCount; // Number of documents that were modified
  3889. * res.upsertedCount; // Number of documents that were upserted
  3890. * res.upsertedId; // Identifier of the inserted document (if an upsert took place)
  3891. *
  3892. * This function triggers the following middleware.
  3893. *
  3894. * - `replaceOne()`
  3895. *
  3896. * @param {Object} [filter]
  3897. * @param {Object} [doc] the update command
  3898. * @param {Object} [options]
  3899. * @param {Boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors.
  3900. * @param {Boolean|String} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict)
  3901. * @param {Boolean} [options.upsert=false] if true, and no documents found, insert a new document
  3902. * @param {Object} [options.writeConcern=null] sets the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](https://mongoosejs.com/docs/guide.html#writeConcern)
  3903. * @param {Boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set.
  3904. * @param {Boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object.
  3905. * @param {Boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`)
  3906. * @return {Query} this
  3907. * @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update()
  3908. * @see Query docs https://mongoosejs.com/docs/queries.html
  3909. * @see update https://www.mongodb.com/docs/manual/reference/method/db.collection.update/
  3910. * @see UpdateResult https://mongodb.github.io/node-mongodb-native/4.9/interfaces/UpdateResult.html
  3911. * @see MongoDB docs https://www.mongodb.com/docs/manual/reference/command/update/#update-command-output
  3912. * @api public
  3913. */
  3914. Query.prototype.replaceOne = function(conditions, doc, options, callback) {
  3915. if (typeof options === 'function') {
  3916. // .update(conditions, doc, callback)
  3917. callback = options;
  3918. options = undefined;
  3919. } else if (typeof doc === 'function') {
  3920. // .update(doc, callback);
  3921. callback = doc;
  3922. doc = conditions;
  3923. conditions = {};
  3924. options = undefined;
  3925. } else if (typeof conditions === 'function') {
  3926. // .update(callback)
  3927. callback = conditions;
  3928. conditions = undefined;
  3929. doc = undefined;
  3930. options = undefined;
  3931. } else if (typeof conditions === 'object' && !doc && !options && !callback) {
  3932. // .update(doc)
  3933. doc = conditions;
  3934. conditions = undefined;
  3935. options = undefined;
  3936. callback = undefined;
  3937. }
  3938. return _update(this, 'replaceOne', conditions, doc, options, callback);
  3939. };
  3940. /**
  3941. * Internal helper for update, updateMany, updateOne, replaceOne
  3942. * @param {Query} query
  3943. * @param {String} op
  3944. * @param {Object} filter
  3945. * @param {Document} [doc]
  3946. * @param {Object} [options]
  3947. * @param {Function} callback
  3948. * @api private
  3949. */
  3950. function _update(query, op, filter, doc, options, callback) {
  3951. // make sure we don't send in the whole Document to merge()
  3952. query.op = op;
  3953. doc = doc || {};
  3954. // strict is an option used in the update checking, make sure it gets set
  3955. if (options != null) {
  3956. if ('strict' in options) {
  3957. query._mongooseOptions.strict = options.strict;
  3958. }
  3959. }
  3960. if (!(filter instanceof Query) &&
  3961. filter != null &&
  3962. filter.toString() !== '[object Object]') {
  3963. query.error(new ObjectParameterError(filter, 'filter', op));
  3964. } else {
  3965. query.merge(filter);
  3966. }
  3967. const updatePipeline = query?.model?.base?.options?.updatePipeline;
  3968. if (updatePipeline != null && options?.updatePipeline == null) {
  3969. options = options || {};
  3970. options.updatePipeline = updatePipeline;
  3971. }
  3972. if (utils.isObject(options)) {
  3973. query.setOptions(options);
  3974. }
  3975. query._mergeUpdate(doc);
  3976. // Hooks
  3977. if (callback) {
  3978. query.exec(callback);
  3979. return query;
  3980. }
  3981. return query;
  3982. }
  3983. /**
  3984. * Runs a function `fn` and treats the return value of `fn` as the new value
  3985. * for the query to resolve to.
  3986. *
  3987. * Any functions you pass to `transform()` will run **after** any post hooks.
  3988. *
  3989. * #### Example:
  3990. *
  3991. * const res = await MyModel.findOne().transform(res => {
  3992. * // Sets a `loadedAt` property on the doc that tells you the time the
  3993. * // document was loaded.
  3994. * return res == null ?
  3995. * res :
  3996. * Object.assign(res, { loadedAt: new Date() });
  3997. * });
  3998. *
  3999. * @method transform
  4000. * @memberOf Query
  4001. * @instance
  4002. * @param {Function} fn function to run to transform the query result
  4003. * @return {Query} this
  4004. */
  4005. Query.prototype.transform = function(fn) {
  4006. this._transforms.push(fn);
  4007. return this;
  4008. };
  4009. /**
  4010. * Make this query throw an error if no documents match the given `filter`.
  4011. * This is handy for integrating with async/await, because `orFail()` saves you
  4012. * an extra `if` statement to check if no document was found.
  4013. *
  4014. * #### Example:
  4015. *
  4016. * // Throws if no doc returned
  4017. * await Model.findOne({ foo: 'bar' }).orFail();
  4018. *
  4019. * // Throws if no document was updated. Note that `orFail()` will still
  4020. * // throw if the only document that matches is `{ foo: 'bar', name: 'test' }`,
  4021. * // because `orFail()` will throw if no document was _updated_, not
  4022. * // if no document was _found_.
  4023. * await Model.updateOne({ foo: 'bar' }, { name: 'test' }).orFail();
  4024. *
  4025. * // Throws "No docs found!" error if no docs match `{ foo: 'bar' }`
  4026. * await Model.find({ foo: 'bar' }).orFail(new Error('No docs found!'));
  4027. *
  4028. * // Throws "Not found" error if no document was found
  4029. * await Model.findOneAndUpdate({ foo: 'bar' }, { name: 'test' }).
  4030. * orFail(() => Error('Not found'));
  4031. *
  4032. * @method orFail
  4033. * @memberOf Query
  4034. * @instance
  4035. * @param {Function|Error} [err] optional error to throw if no docs match `filter`. If not specified, `orFail()` will throw a `DocumentNotFoundError`
  4036. * @return {Query} this
  4037. */
  4038. Query.prototype.orFail = function(err) {
  4039. this.transform(res => {
  4040. switch (this.op) {
  4041. case 'find':
  4042. if (res.length === 0) {
  4043. throw _orFailError(err, this);
  4044. }
  4045. break;
  4046. case 'findOne':
  4047. if (res == null) {
  4048. throw _orFailError(err, this);
  4049. }
  4050. break;
  4051. case 'replaceOne':
  4052. case 'updateMany':
  4053. case 'updateOne':
  4054. if (res?.matchedCount === 0) {
  4055. throw _orFailError(err, this);
  4056. }
  4057. break;
  4058. case 'findOneAndDelete':
  4059. case 'findOneAndUpdate':
  4060. case 'findOneAndReplace':
  4061. if (this.options.includeResultMetadata && res != null && res.value == null) {
  4062. throw _orFailError(err, this);
  4063. }
  4064. if (!this.options.includeResultMetadata && res == null) {
  4065. throw _orFailError(err, this);
  4066. }
  4067. break;
  4068. case 'deleteMany':
  4069. case 'deleteOne':
  4070. if (res.deletedCount === 0) {
  4071. throw _orFailError(err, this);
  4072. }
  4073. break;
  4074. default:
  4075. break;
  4076. }
  4077. return res;
  4078. });
  4079. return this;
  4080. };
  4081. /**
  4082. * Get the error to throw for `orFail()`
  4083. * @param {Error|undefined} err
  4084. * @param {Query} query
  4085. * @api private
  4086. */
  4087. function _orFailError(err, query) {
  4088. if (typeof err === 'function') {
  4089. err = err.call(query);
  4090. }
  4091. if (err == null) {
  4092. err = new DocumentNotFoundError(query.getQuery(), query.model.modelName);
  4093. }
  4094. return err;
  4095. }
  4096. /**
  4097. * Wrapper function to call isPathSelectedInclusive on a query.
  4098. * @param {String} path
  4099. * @return {Boolean}
  4100. * @api public
  4101. */
  4102. Query.prototype.isPathSelectedInclusive = function(path) {
  4103. return isPathSelectedInclusive(this._fields, path);
  4104. };
  4105. /**
  4106. * Executes the query
  4107. *
  4108. * #### Example:
  4109. *
  4110. * const promise = query.exec();
  4111. * const promise = query.exec('update');
  4112. *
  4113. * @param {String|Function} [operation]
  4114. * @return {Promise}
  4115. * @api public
  4116. */
  4117. Query.prototype.exec = async function exec(op) {
  4118. if (typeof op === 'function' || (arguments.length >= 2 && typeof arguments[1] === 'function')) {
  4119. throw new MongooseError('Query.prototype.exec() no longer accepts a callback');
  4120. }
  4121. this._validateOp();
  4122. if (typeof op === 'string') {
  4123. this.op = op;
  4124. }
  4125. if (this.op == null) {
  4126. throw new MongooseError('Query must have `op` before executing');
  4127. }
  4128. if (this.model == null) {
  4129. throw new MongooseError('Query must have an associated model before executing');
  4130. }
  4131. const thunk = opToThunk.get(this.op);
  4132. if (!thunk) {
  4133. throw new MongooseError('Query has invalid `op`: "' + this.op + '"');
  4134. }
  4135. if (this.options?.sort && typeof this.options.sort === 'object' && Object.hasOwn(this.options.sort, '')) {
  4136. throw new Error('Invalid field "" passed to sort()');
  4137. }
  4138. if (this._execCount > 0) {
  4139. let str = this.toString();
  4140. if (str.length > 60) {
  4141. str = str.slice(0, 60) + '...';
  4142. }
  4143. throw new MongooseError('Query was already executed: ' + str);
  4144. }
  4145. this._execCount++;
  4146. let skipWrappedFunction = null;
  4147. try {
  4148. await this._hooks.execPre('exec', this, []);
  4149. } catch (err) {
  4150. if (err instanceof Kareem.skipWrappedFunction) {
  4151. skipWrappedFunction = err;
  4152. } else {
  4153. throw err;
  4154. }
  4155. }
  4156. let res;
  4157. let error = null;
  4158. try {
  4159. await _executePreHooks(this);
  4160. res = skipWrappedFunction ? skipWrappedFunction.args[0] : await this[thunk]();
  4161. for (const fn of this._transforms) {
  4162. res = fn(res);
  4163. }
  4164. } catch (err) {
  4165. if (err instanceof Kareem.skipWrappedFunction) {
  4166. res = err.args[0];
  4167. } else {
  4168. error = err;
  4169. }
  4170. error = this.model.schema._transformDuplicateKeyError(error);
  4171. }
  4172. res = await _executePostHooks(this, res, error);
  4173. await this._hooks.execPost('exec', this, []);
  4174. return res;
  4175. };
  4176. /*!
  4177. * ignore
  4178. */
  4179. function _executePostHooks(query, res, error, op) {
  4180. if (query._queryMiddleware == null) {
  4181. if (error != null) {
  4182. throw error;
  4183. }
  4184. return res;
  4185. }
  4186. const opts = error ? { error } : {};
  4187. return query._queryMiddleware.execPost(op || query.op, query, [res], opts).then((res) => {
  4188. // `res` is array of return args, but queries only return one result.
  4189. return res[0];
  4190. });
  4191. }
  4192. /*!
  4193. * ignore
  4194. */
  4195. function _executePreHooks(query, op) {
  4196. if (query._queryMiddleware == null) {
  4197. return;
  4198. }
  4199. return query._queryMiddleware.execPre(op || query.op, query, []);
  4200. }
  4201. /**
  4202. * Executes the query returning a `Promise` which will be
  4203. * resolved with either the doc(s) or rejected with the error.
  4204. *
  4205. * More about [`then()` in JavaScript](https://masteringjs.io/tutorials/fundamentals/then).
  4206. *
  4207. * @param {Function} [resolve]
  4208. * @param {Function} [reject]
  4209. * @return {Promise}
  4210. * @api public
  4211. */
  4212. Query.prototype.then = function(resolve, reject) {
  4213. return this.exec().then(resolve, reject);
  4214. };
  4215. /**
  4216. * Executes the query returning a `Promise` which will be
  4217. * resolved with either the doc(s) or rejected with the error.
  4218. * Like `.then()`, but only takes a rejection handler.
  4219. *
  4220. * More about [Promise `catch()` in JavaScript](https://masteringjs.io/tutorials/fundamentals/catch).
  4221. *
  4222. * @param {Function} [reject]
  4223. * @return {Promise}
  4224. * @api public
  4225. */
  4226. Query.prototype.catch = function(reject) {
  4227. return this.exec().then(null, reject);
  4228. };
  4229. /**
  4230. * Executes the query returning a `Promise` which will be
  4231. * resolved with `.finally()` chained.
  4232. *
  4233. * More about [Promise `finally()` in JavaScript](https://thecodebarbarian.com/using-promise-finally-in-node-js.html).
  4234. *
  4235. * @param {Function} [onFinally]
  4236. * @return {Promise}
  4237. * @api public
  4238. */
  4239. Query.prototype.finally = function(onFinally) {
  4240. return this.exec().finally(onFinally);
  4241. };
  4242. /**
  4243. * Returns a string representation of this query.
  4244. *
  4245. * More about [`toString()` in JavaScript](https://masteringjs.io/tutorials/fundamentals/tostring).
  4246. *
  4247. * #### Example:
  4248. * const q = Model.find();
  4249. * console.log(q); // Prints "Query { find }"
  4250. *
  4251. * @return {String}
  4252. * @api public
  4253. * @method [Symbol.toStringTag]
  4254. * @memberOf Query
  4255. */
  4256. Query.prototype[Symbol.toStringTag] = function toString() {
  4257. return `Query { ${this.op} }`;
  4258. };
  4259. /**
  4260. * Add pre [middleware](https://mongoosejs.com/docs/middleware.html) to this query instance. Doesn't affect
  4261. * other queries.
  4262. *
  4263. * #### Example:
  4264. *
  4265. * const q1 = Question.find({ answer: 42 });
  4266. * q1.pre(function middleware() {
  4267. * console.log(this.getFilter());
  4268. * });
  4269. * await q1.exec(); // Prints "{ answer: 42 }"
  4270. *
  4271. * // Doesn't print anything, because `middleware()` is only
  4272. * // registered on `q1`.
  4273. * await Question.find({ answer: 42 });
  4274. *
  4275. * @param {Function} fn
  4276. * @return {Promise}
  4277. * @api public
  4278. */
  4279. Query.prototype.pre = function(fn) {
  4280. this._hooks.pre('exec', fn);
  4281. return this;
  4282. };
  4283. /**
  4284. * Add post [middleware](https://mongoosejs.com/docs/middleware.html) to this query instance. Doesn't affect
  4285. * other queries.
  4286. *
  4287. * #### Example:
  4288. *
  4289. * const q1 = Question.find({ answer: 42 });
  4290. * q1.post(function middleware() {
  4291. * console.log(this.getFilter());
  4292. * });
  4293. * await q1.exec(); // Prints "{ answer: 42 }"
  4294. *
  4295. * // Doesn't print anything, because `middleware()` is only
  4296. * // registered on `q1`.
  4297. * await Question.find({ answer: 42 });
  4298. *
  4299. * @param {Function} fn
  4300. * @return {Promise}
  4301. * @api public
  4302. */
  4303. Query.prototype.post = function(fn) {
  4304. this._hooks.post('exec', fn);
  4305. return this;
  4306. };
  4307. /**
  4308. * Casts obj for an update command.
  4309. *
  4310. * @param {Object} obj
  4311. * @return {Object} obj after casting its values
  4312. * @method _castUpdate
  4313. * @memberOf Query
  4314. * @instance
  4315. * @api private
  4316. */
  4317. Query.prototype._castUpdate = function _castUpdate(obj) {
  4318. let schema = this.schema;
  4319. const discriminatorKey = schema.options.discriminatorKey;
  4320. const baseSchema = schema._baseSchema || schema;
  4321. if (this._mongooseOptions.overwriteDiscriminatorKey &&
  4322. obj[discriminatorKey] != null &&
  4323. baseSchema.discriminators) {
  4324. const _schema = Object.values(baseSchema.discriminators).find(
  4325. discriminator => discriminator.discriminatorMapping.value === obj[discriminatorKey]
  4326. );
  4327. if (_schema != null) {
  4328. schema = _schema;
  4329. }
  4330. }
  4331. let upsert;
  4332. if ('upsert' in this.options) {
  4333. upsert = this.options.upsert;
  4334. }
  4335. return castUpdate(schema, obj, {
  4336. strict: this._mongooseOptions.strict,
  4337. upsert: upsert,
  4338. arrayFilters: this.options.arrayFilters,
  4339. overwriteDiscriminatorKey: this._mongooseOptions.overwriteDiscriminatorKey,
  4340. overwriteImmutable: this._mongooseOptions.overwriteImmutable
  4341. }, this, this._conditions);
  4342. };
  4343. /**
  4344. * Specifies paths which should be populated with other documents.
  4345. *
  4346. * #### Example:
  4347. *
  4348. * let book = await Book.findOne().populate('authors');
  4349. * book.title; // 'Node.js in Action'
  4350. * book.authors[0].name; // 'TJ Holowaychuk'
  4351. * book.authors[1].name; // 'Nathan Rajlich'
  4352. *
  4353. * let books = await Book.find().populate({
  4354. * path: 'authors',
  4355. * // `match` and `sort` apply to the Author model,
  4356. * // not the Book model. These options do not affect
  4357. * // which documents are in `books`, just the order and
  4358. * // contents of each book document's `authors`.
  4359. * match: { name: new RegExp('.*h.*', 'i') },
  4360. * sort: { name: -1 }
  4361. * });
  4362. * books[0].title; // 'Node.js in Action'
  4363. * // Each book's `authors` are sorted by name, descending.
  4364. * books[0].authors[0].name; // 'TJ Holowaychuk'
  4365. * books[0].authors[1].name; // 'Marc Harter'
  4366. *
  4367. * books[1].title; // 'Professional AngularJS'
  4368. * // Empty array, no authors' name has the letter 'h'
  4369. * books[1].authors; // []
  4370. *
  4371. * Paths are populated after the query executes and a response is received. A
  4372. * separate query is then executed for each path specified for population. After
  4373. * a response for each query has also been returned, the results are passed to
  4374. * the callback.
  4375. *
  4376. * @param {Object|String|String[]} path either the path(s) to populate or an object specifying all parameters
  4377. * @param {Object|String} [select] Field selection for the population query
  4378. * @param {Model} [model] The model you wish to use for population. If not specified, populate will look up the model by the name in the Schema's `ref` field.
  4379. * @param {Object} [match] Conditions for the population query
  4380. * @param {Object} [options] Options for the population query (sort, etc)
  4381. * @param {String} [options.path=null] The path to populate.
  4382. * @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.
  4383. * @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`](https://mongoosejs.com/docs/schematypes.html#schematype-options).
  4384. * @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.
  4385. * @param {Object|Function} [options.match=null] Add an additional filter to the populate query. Can be a filter object containing [MongoDB query syntax](https://www.mongodb.com/docs/manual/tutorial/query-documents/), or a function that returns a filter object.
  4386. * @param {Function} [options.transform=null] Function that Mongoose will call on every populated document that allows you to transform the populated document.
  4387. * @param {Object} [options.options=null] Additional options like `limit` and `lean`.
  4388. * @see population https://mongoosejs.com/docs/populate.html
  4389. * @see Query#select https://mongoosejs.com/docs/api/query.html#Query.prototype.select()
  4390. * @see Model.populate https://mongoosejs.com/docs/api/model.html#Model.populate()
  4391. * @return {Query} this
  4392. * @api public
  4393. */
  4394. Query.prototype.populate = function(...args) {
  4395. // Bail when given no truthy arguments
  4396. if (!args.some(Boolean)) {
  4397. return this;
  4398. }
  4399. const res = utils.populate.apply(null, args);
  4400. const opts = this._mongooseOptions;
  4401. if (opts.lean != null) {
  4402. const lean = opts.lean;
  4403. for (const populateOptions of res) {
  4404. if (populateOptions?.options?.lean == null) {
  4405. populateOptions.options = populateOptions.options || {};
  4406. populateOptions.options.lean = lean;
  4407. }
  4408. }
  4409. }
  4410. if (!utils.isObject(opts.populate)) {
  4411. opts.populate = {};
  4412. }
  4413. const pop = opts.populate;
  4414. for (const populateOptions of res) {
  4415. const path = populateOptions.path;
  4416. if (pop[path]?.populate && populateOptions.populate) {
  4417. populateOptions.populate = pop[path].populate.concat(populateOptions.populate);
  4418. }
  4419. pop[populateOptions.path] = populateOptions;
  4420. }
  4421. return this;
  4422. };
  4423. /**
  4424. * Gets a list of paths to be populated by this query
  4425. *
  4426. * #### Example:
  4427. *
  4428. * bookSchema.pre('findOne', function() {
  4429. * let keys = this.getPopulatedPaths(); // ['author']
  4430. * });
  4431. * ...
  4432. * Book.findOne({}).populate('author');
  4433. *
  4434. * #### Example:
  4435. *
  4436. * // Deep populate
  4437. * const q = L1.find().populate({
  4438. * path: 'level2',
  4439. * populate: { path: 'level3' }
  4440. * });
  4441. * q.getPopulatedPaths(); // ['level2', 'level2.level3']
  4442. *
  4443. * @return {Array} an array of strings representing populated paths
  4444. * @api public
  4445. */
  4446. Query.prototype.getPopulatedPaths = function getPopulatedPaths() {
  4447. const obj = this._mongooseOptions.populate || {};
  4448. const ret = Object.keys(obj);
  4449. for (const path of Object.keys(obj)) {
  4450. const pop = obj[path];
  4451. if (!Array.isArray(pop.populate)) {
  4452. continue;
  4453. }
  4454. _getPopulatedPaths(ret, pop.populate, path + '.');
  4455. }
  4456. return ret;
  4457. };
  4458. /*!
  4459. * ignore
  4460. */
  4461. function _getPopulatedPaths(list, arr, prefix) {
  4462. for (const pop of arr) {
  4463. list.push(prefix + pop.path);
  4464. if (!Array.isArray(pop.populate)) {
  4465. continue;
  4466. }
  4467. _getPopulatedPaths(list, pop.populate, prefix + pop.path + '.');
  4468. }
  4469. }
  4470. /**
  4471. * Casts this query to the schema of `model`
  4472. *
  4473. * #### Note:
  4474. *
  4475. * If `obj` is present, it is cast instead of this query.
  4476. *
  4477. * @param {Model} [model] the model to cast to. If not set, defaults to `this.model`
  4478. * @param {Object} [obj]
  4479. * @return {Object}
  4480. * @api public
  4481. */
  4482. Query.prototype.cast = function(model, obj) {
  4483. obj || (obj = this._conditions);
  4484. model = model || this.model;
  4485. const discriminatorKey = model.schema.options.discriminatorKey;
  4486. if (obj != null &&
  4487. Object.hasOwn(obj, discriminatorKey)) {
  4488. model = getDiscriminatorByValue(model.discriminators, obj[discriminatorKey]) || model;
  4489. }
  4490. const opts = { upsert: this.options?.upsert };
  4491. if (this.options) {
  4492. if ('strict' in this.options) {
  4493. opts.strict = this.options.strict;
  4494. }
  4495. if ('strictQuery' in this.options) {
  4496. opts.strictQuery = this.options.strictQuery;
  4497. }
  4498. }
  4499. if ('sanitizeFilter' in this._mongooseOptions) {
  4500. opts.sanitizeFilter = this._mongooseOptions.sanitizeFilter;
  4501. }
  4502. try {
  4503. return cast(model.schema, obj, opts, this);
  4504. } catch (err) {
  4505. // CastError, assign model
  4506. if (typeof err.setModel === 'function') {
  4507. err.setModel(model);
  4508. }
  4509. throw err;
  4510. }
  4511. };
  4512. /**
  4513. * Casts selected field arguments for field selection with mongo 2.2
  4514. *
  4515. * query.select({ ids: { $elemMatch: { $in: [hexString] }})
  4516. *
  4517. * @param {Object} fields
  4518. * @see https://github.com/Automattic/mongoose/issues/1091
  4519. * @see https://www.mongodb.com/docs/manual/reference/projection/elemMatch/
  4520. * @api private
  4521. */
  4522. Query.prototype._castFields = function _castFields(fields) {
  4523. let selected,
  4524. elemMatchKeys,
  4525. keys,
  4526. key,
  4527. out;
  4528. if (fields) {
  4529. keys = Object.keys(fields);
  4530. elemMatchKeys = [];
  4531. // collect $elemMatch args
  4532. for (let i = 0; i < keys.length; ++i) {
  4533. key = keys[i];
  4534. if (fields[key].$elemMatch) {
  4535. selected || (selected = {});
  4536. selected[key] = fields[key];
  4537. elemMatchKeys.push(key);
  4538. }
  4539. }
  4540. }
  4541. if (selected) {
  4542. // they passed $elemMatch, cast em
  4543. try {
  4544. out = this.cast(this.model, selected);
  4545. } catch (err) {
  4546. return err;
  4547. }
  4548. // apply the casted field args
  4549. for (let i = 0; i < elemMatchKeys.length; ++i) {
  4550. key = elemMatchKeys[i];
  4551. fields[key] = out[key];
  4552. }
  4553. }
  4554. return fields;
  4555. };
  4556. /**
  4557. * Applies schematype selected options to this query.
  4558. * @api private
  4559. */
  4560. Query.prototype._applyPaths = function applyPaths() {
  4561. if (!this.model) {
  4562. return;
  4563. }
  4564. this._fields = this._fields || {};
  4565. let sanitizeProjection = undefined;
  4566. if (this.model != null && utils.hasUserDefinedProperty(this.model.db.options, 'sanitizeProjection')) {
  4567. sanitizeProjection = this.model.db.options.sanitizeProjection;
  4568. } else if (this.model != null && utils.hasUserDefinedProperty(this.model.base.options, 'sanitizeProjection')) {
  4569. sanitizeProjection = this.model.base.options.sanitizeProjection;
  4570. } else {
  4571. sanitizeProjection = this._mongooseOptions.sanitizeProjection;
  4572. }
  4573. const schemaLevelProjections = this._mongooseOptions.schemaLevelProjections ?? true;
  4574. if (schemaLevelProjections) {
  4575. helpers.applyPaths(this._fields, this.model.schema, sanitizeProjection);
  4576. }
  4577. let _selectPopulatedPaths = true;
  4578. if ('selectPopulatedPaths' in this.model.base.options) {
  4579. _selectPopulatedPaths = this.model.base.options.selectPopulatedPaths;
  4580. }
  4581. if ('selectPopulatedPaths' in this.model.schema.options) {
  4582. _selectPopulatedPaths = this.model.schema.options.selectPopulatedPaths;
  4583. }
  4584. if (_selectPopulatedPaths) {
  4585. selectPopulatedFields(this._fields, this._userProvidedFields, this._mongooseOptions.populate);
  4586. }
  4587. };
  4588. /**
  4589. * Returns a wrapper around a [mongodb driver cursor](https://mongodb.github.io/node-mongodb-native/4.9/classes/FindCursor.html).
  4590. * A QueryCursor exposes a Streams3 interface, as well as a `.next()` function.
  4591. *
  4592. * The `.cursor()` function triggers pre find hooks, but **not** post find hooks.
  4593. *
  4594. * #### Example:
  4595. *
  4596. * // There are 2 ways to use a cursor. First, as a stream:
  4597. * Thing.
  4598. * find({ name: /^hello/ }).
  4599. * cursor().
  4600. * on('data', function(doc) { console.log(doc); }).
  4601. * on('end', function() { console.log('Done!'); });
  4602. *
  4603. * // Or you can use `.next()` to manually get the next doc in the stream.
  4604. * // `.next()` returns a promise, so you can use promises or callbacks.
  4605. * const cursor = Thing.find({ name: /^hello/ }).cursor();
  4606. * cursor.next(function(error, doc) {
  4607. * console.log(doc);
  4608. * });
  4609. *
  4610. * // Because `.next()` returns a promise, you can use co
  4611. * // to easily iterate through all documents without loading them
  4612. * // all into memory.
  4613. * const cursor = Thing.find({ name: /^hello/ }).cursor();
  4614. * for (let doc = await cursor.next(); doc != null; doc = await cursor.next()) {
  4615. * console.log(doc);
  4616. * }
  4617. *
  4618. * #### Valid options
  4619. *
  4620. * - `transform`: optional function which accepts a mongoose document. The return value of the function will be emitted on `data` and returned by `.next()`.
  4621. *
  4622. * @return {QueryCursor}
  4623. * @param {Object} [options]
  4624. * @see QueryCursor https://mongoosejs.com/docs/api/querycursor.html
  4625. * @api public
  4626. */
  4627. Query.prototype.cursor = function cursor(opts) {
  4628. if (opts) {
  4629. this.setOptions(opts);
  4630. }
  4631. try {
  4632. this.cast(this.model);
  4633. } catch (err) {
  4634. return (new QueryCursor(this))._markError(err);
  4635. }
  4636. return new QueryCursor(this);
  4637. };
  4638. // the rest of these are basically to support older Mongoose syntax with mquery
  4639. /**
  4640. * Sets the tailable option (for use with capped collections).
  4641. *
  4642. * #### Example:
  4643. *
  4644. * query.tailable(); // true
  4645. * query.tailable(true);
  4646. * query.tailable(false);
  4647. *
  4648. * // Set both `tailable` and `awaitData` options
  4649. * query.tailable({ awaitData: true });
  4650. *
  4651. * #### Note:
  4652. *
  4653. * Cannot be used with `distinct()`
  4654. *
  4655. * @param {Boolean} bool defaults to true
  4656. * @param {Object} [opts] options to set
  4657. * @param {Boolean} [opts.awaitData] false by default. Set to true to keep the cursor open even if there's no data.
  4658. * @param {Number} [opts.maxAwaitTimeMS] the maximum amount of time for the server to wait on new documents to satisfy a tailable cursor query. Requires `tailable` and `awaitData` to be true
  4659. * @see tailable https://www.mongodb.com/docs/manual/tutorial/create-tailable-cursor/
  4660. * @api public
  4661. */
  4662. Query.prototype.tailable = function(val, opts) {
  4663. // we need to support the tailable({ awaitData : true }) as well as the
  4664. // tailable(true, {awaitData :true}) syntax that mquery does not support
  4665. if (typeof val?.constructor === 'function' && val.constructor.name === 'Object') {
  4666. opts = val;
  4667. val = true;
  4668. }
  4669. if (val === undefined) {
  4670. val = true;
  4671. }
  4672. if (opts && typeof opts === 'object') {
  4673. for (const key of Object.keys(opts)) {
  4674. if (key === 'awaitData' || key === 'awaitdata') { // backwards compat, see gh-10875
  4675. // For backwards compatibility
  4676. this.options['awaitData'] = !!opts[key];
  4677. } else {
  4678. this.options[key] = opts[key];
  4679. }
  4680. }
  4681. }
  4682. this.options.tailable = arguments.length ? !!val : true;
  4683. return this;
  4684. };
  4685. /**
  4686. * Declares an intersects query for `geometry()`.
  4687. *
  4688. * #### Example:
  4689. *
  4690. * query.where('path').intersects().geometry({
  4691. * type: 'LineString',
  4692. * coordinates: [[180.0, 11.0], [180, 9.0]]
  4693. * });
  4694. *
  4695. * query.where('path').intersects({
  4696. * type: 'LineString',
  4697. * coordinates: [[180.0, 11.0], [180, 9.0]]
  4698. * });
  4699. *
  4700. * #### Note:
  4701. *
  4702. * **MUST** be used after `where()`.
  4703. *
  4704. * #### Note:
  4705. *
  4706. * In Mongoose 3.7, `intersects` changed from a getter to a function. If you need the old syntax, use [this](https://github.com/ebensing/mongoose-within).
  4707. *
  4708. * @method intersects
  4709. * @memberOf Query
  4710. * @instance
  4711. * @param {Object} [arg]
  4712. * @return {Query} this
  4713. * @see $geometry https://www.mongodb.com/docs/manual/reference/operator/geometry/
  4714. * @see geoIntersects https://www.mongodb.com/docs/manual/reference/operator/geoIntersects/
  4715. * @api public
  4716. */
  4717. /**
  4718. * Specifies a `$geometry` condition
  4719. *
  4720. * #### Example:
  4721. *
  4722. * const polyA = [[[ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ]]]
  4723. * query.where('loc').within().geometry({ type: 'Polygon', coordinates: polyA })
  4724. *
  4725. * // or
  4726. * const polyB = [[ 0, 0 ], [ 1, 1 ]]
  4727. * query.where('loc').within().geometry({ type: 'LineString', coordinates: polyB })
  4728. *
  4729. * // or
  4730. * const polyC = [ 0, 0 ]
  4731. * query.where('loc').within().geometry({ type: 'Point', coordinates: polyC })
  4732. *
  4733. * // or
  4734. * query.where('loc').intersects().geometry({ type: 'Point', coordinates: polyC })
  4735. *
  4736. * The argument is assigned to the most recent path passed to `where()`.
  4737. *
  4738. * #### Note:
  4739. *
  4740. * `geometry()` **must** come after either `intersects()` or `within()`.
  4741. *
  4742. * The `object` argument must contain `type` and `coordinates` properties.
  4743. * - type {String}
  4744. * - coordinates {Array}
  4745. *
  4746. * @method geometry
  4747. * @memberOf Query
  4748. * @instance
  4749. * @param {Object} object Must contain a `type` property which is a String and a `coordinates` property which is an Array. See the examples.
  4750. * @return {Query} this
  4751. * @see $geometry https://www.mongodb.com/docs/manual/reference/operator/geometry/
  4752. * @see Geospatial Support Enhancements https://www.mongodb.com/docs/manual/release-notes/2.4/#geospatial-support-enhancements
  4753. * @see MongoDB Geospatial Indexing https://www.mongodb.com/docs/manual/core/geospatial-indexes/
  4754. * @api public
  4755. */
  4756. /**
  4757. * Specifies a `$near` or `$nearSphere` condition
  4758. *
  4759. * These operators return documents sorted by distance.
  4760. *
  4761. * #### Example:
  4762. *
  4763. * query.where('loc').near({ center: [10, 10] });
  4764. * query.where('loc').near({ center: [10, 10], maxDistance: 5 });
  4765. * query.where('loc').near({ center: [10, 10], maxDistance: 5, spherical: true });
  4766. * query.near('loc', { center: [10, 10], maxDistance: 5 });
  4767. *
  4768. * @method near
  4769. * @memberOf Query
  4770. * @instance
  4771. * @param {String} [path]
  4772. * @param {Object} val
  4773. * @return {Query} this
  4774. * @see $near https://www.mongodb.com/docs/manual/reference/operator/near/
  4775. * @see $nearSphere https://www.mongodb.com/docs/manual/reference/operator/nearSphere/
  4776. * @see $maxDistance https://www.mongodb.com/docs/manual/reference/operator/maxDistance/
  4777. * @see MongoDB Geospatial Indexing https://www.mongodb.com/docs/manual/core/geospatial-indexes/
  4778. * @api public
  4779. */
  4780. /**
  4781. * Overwriting mquery is needed to support a couple different near() forms found in older
  4782. * versions of mongoose
  4783. * near([1,1])
  4784. * near(1,1)
  4785. * near(field, [1,2])
  4786. * near(field, 1, 2)
  4787. * In addition to all of the normal forms supported by mquery
  4788. *
  4789. * @method near
  4790. * @memberOf Query
  4791. * @instance
  4792. * @api private
  4793. */
  4794. Query.prototype.near = function() {
  4795. const params = [];
  4796. const sphere = this._mongooseOptions.nearSphere;
  4797. // TODO refactor
  4798. if (arguments.length === 1) {
  4799. if (Array.isArray(arguments[0])) {
  4800. params.push({ center: arguments[0], spherical: sphere });
  4801. } else if (typeof arguments[0] === 'string') {
  4802. // just passing a path
  4803. params.push(arguments[0]);
  4804. } else if (utils.isObject(arguments[0])) {
  4805. if (typeof arguments[0].spherical !== 'boolean') {
  4806. arguments[0].spherical = sphere;
  4807. }
  4808. params.push(arguments[0]);
  4809. } else {
  4810. throw new TypeError('invalid argument');
  4811. }
  4812. } else if (arguments.length === 2) {
  4813. if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
  4814. params.push({ center: [arguments[0], arguments[1]], spherical: sphere });
  4815. } else if (typeof arguments[0] === 'string' && Array.isArray(arguments[1])) {
  4816. params.push(arguments[0]);
  4817. params.push({ center: arguments[1], spherical: sphere });
  4818. } else if (typeof arguments[0] === 'string' && utils.isObject(arguments[1])) {
  4819. params.push(arguments[0]);
  4820. if (typeof arguments[1].spherical !== 'boolean') {
  4821. arguments[1].spherical = sphere;
  4822. }
  4823. params.push(arguments[1]);
  4824. } else {
  4825. throw new TypeError('invalid argument');
  4826. }
  4827. } else if (arguments.length === 3) {
  4828. if (typeof arguments[0] === 'string' && typeof arguments[1] === 'number'
  4829. && typeof arguments[2] === 'number') {
  4830. params.push(arguments[0]);
  4831. params.push({ center: [arguments[1], arguments[2]], spherical: sphere });
  4832. } else {
  4833. throw new TypeError('invalid argument');
  4834. }
  4835. } else {
  4836. throw new TypeError('invalid argument');
  4837. }
  4838. return Query.base.near.apply(this, params);
  4839. };
  4840. /**
  4841. * _DEPRECATED_ Specifies a `$nearSphere` condition
  4842. *
  4843. * #### Example:
  4844. *
  4845. * query.where('loc').nearSphere({ center: [10, 10], maxDistance: 5 });
  4846. *
  4847. * **Deprecated.** Use `query.near()` instead with the `spherical` option set to `true`.
  4848. *
  4849. * #### Example:
  4850. *
  4851. * query.where('loc').near({ center: [10, 10], spherical: true });
  4852. *
  4853. * @deprecated
  4854. * @see near() https://mongoosejs.com/docs/api/query.html#Query.prototype.near()
  4855. * @see $near https://www.mongodb.com/docs/manual/reference/operator/near/
  4856. * @see $nearSphere https://www.mongodb.com/docs/manual/reference/operator/nearSphere/
  4857. * @see $maxDistance https://www.mongodb.com/docs/manual/reference/operator/maxDistance/
  4858. */
  4859. Query.prototype.nearSphere = function() {
  4860. this._mongooseOptions.nearSphere = true;
  4861. this.near.apply(this, arguments);
  4862. return this;
  4863. };
  4864. /**
  4865. * Returns an asyncIterator for use with [`for/await/of` loops](https://thecodebarbarian.com/getting-started-with-async-iterators-in-node-js)
  4866. * This function *only* works for `find()` queries.
  4867. * You do not need to call this function explicitly, the JavaScript runtime
  4868. * will call it for you.
  4869. *
  4870. * #### Example:
  4871. *
  4872. * for await (const doc of Model.aggregate([{ $sort: { name: 1 } }])) {
  4873. * console.log(doc.name);
  4874. * }
  4875. *
  4876. * @method [Symbol.asyncIterator]
  4877. * @memberOf Query
  4878. * @instance
  4879. * @api public
  4880. */
  4881. Query.prototype[Symbol.asyncIterator] = function queryAsyncIterator() {
  4882. // Set so QueryCursor knows it should transform results for async iterators into `{ value, done }` syntax
  4883. this._mongooseOptions._asyncIterator = true;
  4884. return this.cursor();
  4885. };
  4886. /**
  4887. * Specifies a `$polygon` condition
  4888. *
  4889. * #### Example:
  4890. *
  4891. * query.where('loc').within().polygon([10, 20], [13, 25], [7, 15]);
  4892. * query.polygon('loc', [10, 20], [13, 25], [7, 15]);
  4893. *
  4894. * @method polygon
  4895. * @memberOf Query
  4896. * @instance
  4897. * @param {String|Array} [path]
  4898. * @param {...Array|Object} [coordinatePairs]
  4899. * @return {Query} this
  4900. * @see $polygon https://www.mongodb.com/docs/manual/reference/operator/polygon/
  4901. * @see MongoDB Geospatial Indexing https://www.mongodb.com/docs/manual/core/geospatial-indexes/
  4902. * @api public
  4903. */
  4904. /**
  4905. * Specifies a `$box` condition
  4906. *
  4907. * #### Example:
  4908. *
  4909. * const lowerLeft = [40.73083, -73.99756]
  4910. * const upperRight= [40.741404, -73.988135]
  4911. *
  4912. * query.where('loc').within().box(lowerLeft, upperRight)
  4913. * query.box({ ll : lowerLeft, ur : upperRight })
  4914. *
  4915. * @method box
  4916. * @memberOf Query
  4917. * @instance
  4918. * @see $box https://www.mongodb.com/docs/manual/reference/operator/box/
  4919. * @see within() Query#within https://mongoosejs.com/docs/api/query.html#Query.prototype.within()
  4920. * @see MongoDB Geospatial Indexing https://www.mongodb.com/docs/manual/core/geospatial-indexes/
  4921. * @param {Object|Array<Number>} val1 Lower Left Coordinates OR a object of lower-left(ll) and upper-right(ur) Coordinates
  4922. * @param {Array<Number>} [val2] Upper Right Coordinates
  4923. * @return {Query} this
  4924. * @api public
  4925. */
  4926. /**
  4927. * this is needed to support the mongoose syntax of:
  4928. * box(field, { ll : [x,y], ur : [x2,y2] })
  4929. * box({ ll : [x,y], ur : [x2,y2] })
  4930. *
  4931. * @method box
  4932. * @memberOf Query
  4933. * @instance
  4934. * @api private
  4935. */
  4936. Query.prototype.box = function(ll, ur) {
  4937. if (!Array.isArray(ll) && utils.isObject(ll)) {
  4938. ur = ll.ur;
  4939. ll = ll.ll;
  4940. }
  4941. return Query.base.box.call(this, ll, ur);
  4942. };
  4943. /**
  4944. * Specifies a `$center` or `$centerSphere` condition.
  4945. *
  4946. * #### Example:
  4947. *
  4948. * const area = { center: [50, 50], radius: 10, unique: true }
  4949. * query.where('loc').within().circle(area)
  4950. * // alternatively
  4951. * query.circle('loc', area);
  4952. *
  4953. * // spherical calculations
  4954. * const area = { center: [50, 50], radius: 10, unique: true, spherical: true }
  4955. * query.where('loc').within().circle(area)
  4956. * // alternatively
  4957. * query.circle('loc', area);
  4958. *
  4959. * @method circle
  4960. * @memberOf Query
  4961. * @instance
  4962. * @param {String} [path]
  4963. * @param {Object} area
  4964. * @return {Query} this
  4965. * @see $center https://www.mongodb.com/docs/manual/reference/operator/center/
  4966. * @see $centerSphere https://www.mongodb.com/docs/manual/reference/operator/centerSphere/
  4967. * @see $geoWithin https://www.mongodb.com/docs/manual/reference/operator/geoWithin/
  4968. * @see MongoDB Geospatial Indexing https://www.mongodb.com/docs/manual/core/geospatial-indexes/
  4969. * @api public
  4970. */
  4971. /**
  4972. * _DEPRECATED_ Alias for [circle](https://mongoosejs.com/docs/api/query.html#Query.prototype.circle())
  4973. *
  4974. * **Deprecated.** Use [circle](https://mongoosejs.com/docs/api/query.html#Query.prototype.circle()) instead.
  4975. *
  4976. * @deprecated
  4977. * @method center
  4978. * @memberOf Query
  4979. * @instance
  4980. * @api public
  4981. */
  4982. Query.prototype.center = Query.base.circle;
  4983. /**
  4984. * _DEPRECATED_ Specifies a `$centerSphere` condition
  4985. *
  4986. * **Deprecated.** Use [circle](https://mongoosejs.com/docs/api/query.html#Query.prototype.circle()) instead.
  4987. *
  4988. * #### Example:
  4989. *
  4990. * const area = { center: [50, 50], radius: 10 };
  4991. * query.where('loc').within().centerSphere(area);
  4992. *
  4993. * @deprecated
  4994. * @param {String} [path]
  4995. * @param {Object} val
  4996. * @return {Query} this
  4997. * @see MongoDB Geospatial Indexing https://www.mongodb.com/docs/manual/core/geospatial-indexes/
  4998. * @see $centerSphere https://www.mongodb.com/docs/manual/reference/operator/centerSphere/
  4999. * @api public
  5000. */
  5001. Query.prototype.centerSphere = function() {
  5002. if (typeof arguments[0]?.constructor === 'function' && arguments[0].constructor.name === 'Object') {
  5003. arguments[0].spherical = true;
  5004. }
  5005. if (typeof arguments[1]?.constructor === 'function' && arguments[1].constructor.name === 'Object') {
  5006. arguments[1].spherical = true;
  5007. }
  5008. Query.base.circle.apply(this, arguments);
  5009. };
  5010. /**
  5011. * Determines if field selection has been made.
  5012. *
  5013. * @method selected
  5014. * @memberOf Query
  5015. * @instance
  5016. * @return {Boolean}
  5017. * @api public
  5018. */
  5019. /**
  5020. * Determines if inclusive field selection has been made.
  5021. *
  5022. * query.selectedInclusively(); // false
  5023. * query.select('name');
  5024. * query.selectedInclusively(); // true
  5025. *
  5026. * @method selectedInclusively
  5027. * @memberOf Query
  5028. * @instance
  5029. * @return {Boolean}
  5030. * @api public
  5031. */
  5032. Query.prototype.selectedInclusively = function selectedInclusively() {
  5033. return isInclusive(this._fields);
  5034. };
  5035. /**
  5036. * Determines if exclusive field selection has been made.
  5037. *
  5038. * query.selectedExclusively(); // false
  5039. * query.select('-name');
  5040. * query.selectedExclusively(); // true
  5041. * query.selectedInclusively(); // false
  5042. *
  5043. * @method selectedExclusively
  5044. * @memberOf Query
  5045. * @instance
  5046. * @return {Boolean}
  5047. * @api public
  5048. */
  5049. Query.prototype.selectedExclusively = function selectedExclusively() {
  5050. return isExclusive(this._fields);
  5051. };
  5052. /**
  5053. * The model this query is associated with.
  5054. *
  5055. * #### Example:
  5056. *
  5057. * const q = MyModel.find();
  5058. * q.model === MyModel; // true
  5059. *
  5060. * @api public
  5061. * @property model
  5062. * @memberOf Query
  5063. * @instance
  5064. */
  5065. Query.prototype.model;
  5066. /**
  5067. * Determine if we can merge the given value as a query filter. Override for mquery.canMerge() to allow null
  5068. */
  5069. function canMerge(value) {
  5070. return value instanceof Query || utils.isObject(value) || value === null;
  5071. }
  5072. /*!
  5073. * Export
  5074. */
  5075. module.exports = Query;