mquery.js 80 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253
  1. 'use strict';
  2. /**
  3. * Dependencies
  4. */
  5. var slice = require('sliced');
  6. var assert = require('assert');
  7. var util = require('util');
  8. var utils = require('./utils');
  9. var debug = require('debug')('mquery');
  10. /* global Map */
  11. /**
  12. * Query constructor used for building queries.
  13. *
  14. * ####Example:
  15. *
  16. * var query = new Query({ name: 'mquery' });
  17. * query.setOptions({ collection: moduleCollection })
  18. * query.where('age').gte(21).exec(callback);
  19. *
  20. * @param {Object} [criteria]
  21. * @param {Object} [options]
  22. * @api public
  23. */
  24. function Query(criteria, options) {
  25. if (!(this instanceof Query))
  26. return new Query(criteria, options);
  27. var proto = this.constructor.prototype;
  28. this.op = proto.op || undefined;
  29. this.options = {};
  30. this.setOptions(proto.options);
  31. this._conditions = proto._conditions
  32. ? utils.clone(proto._conditions)
  33. : {};
  34. this._fields = proto._fields
  35. ? utils.clone(proto._fields)
  36. : undefined;
  37. this._update = proto._update
  38. ? utils.clone(proto._update)
  39. : undefined;
  40. this._path = proto._path || undefined;
  41. this._distinct = proto._distinct || undefined;
  42. this._collection = proto._collection || undefined;
  43. this._traceFunction = proto._traceFunction || undefined;
  44. if (options) {
  45. this.setOptions(options);
  46. }
  47. if (criteria) {
  48. if (criteria.find && criteria.remove && criteria.update) {
  49. // quack quack!
  50. this.collection(criteria);
  51. } else {
  52. this.find(criteria);
  53. }
  54. }
  55. }
  56. /**
  57. * This is a parameter that the user can set which determines if mquery
  58. * uses $within or $geoWithin for queries. It defaults to true which
  59. * means $geoWithin will be used. If using MongoDB < 2.4 you should
  60. * set this to false.
  61. *
  62. * @api public
  63. * @property use$geoWithin
  64. */
  65. var $withinCmd = '$geoWithin';
  66. Object.defineProperty(Query, 'use$geoWithin', {
  67. get: function( ) { return $withinCmd == '$geoWithin'; },
  68. set: function(v) {
  69. if (true === v) {
  70. // mongodb >= 2.4
  71. $withinCmd = '$geoWithin';
  72. } else {
  73. $withinCmd = '$within';
  74. }
  75. }
  76. });
  77. /**
  78. * Converts this query to a constructor function with all arguments and options retained.
  79. *
  80. * ####Example
  81. *
  82. * // Create a query that will read documents with a "video" category from
  83. * // `aCollection` on the primary node in the replica-set unless it is down,
  84. * // in which case we'll read from a secondary node.
  85. * var query = mquery({ category: 'video' })
  86. * query.setOptions({ collection: aCollection, read: 'primaryPreferred' });
  87. *
  88. * // create a constructor based off these settings
  89. * var Video = query.toConstructor();
  90. *
  91. * // Video is now a subclass of mquery() and works the same way but with the
  92. * // default query parameters and options set.
  93. *
  94. * // run a query with the previous settings but filter for movies with names
  95. * // that start with "Life".
  96. * Video().where({ name: /^Life/ }).exec(cb);
  97. *
  98. * @return {Query} new Query
  99. * @api public
  100. */
  101. Query.prototype.toConstructor = function toConstructor() {
  102. function CustomQuery(criteria, options) {
  103. if (!(this instanceof CustomQuery))
  104. return new CustomQuery(criteria, options);
  105. Query.call(this, criteria, options);
  106. }
  107. utils.inherits(CustomQuery, Query);
  108. // set inherited defaults
  109. var p = CustomQuery.prototype;
  110. p.options = {};
  111. p.setOptions(this.options);
  112. p.op = this.op;
  113. p._conditions = utils.clone(this._conditions);
  114. p._fields = utils.clone(this._fields);
  115. p._update = utils.clone(this._update);
  116. p._path = this._path;
  117. p._distinct = this._distinct;
  118. p._collection = this._collection;
  119. p._traceFunction = this._traceFunction;
  120. return CustomQuery;
  121. };
  122. /**
  123. * Sets query options.
  124. *
  125. * ####Options:
  126. *
  127. * - [tailable](http://www.mongodb.org/display/DOCS/Tailable+Cursors) *
  128. * - [sort](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsort(\)%7D%7D) *
  129. * - [limit](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Blimit%28%29%7D%7D) *
  130. * - [skip](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bskip%28%29%7D%7D) *
  131. * - [maxScan](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24maxScan) *
  132. * - [maxTime](http://docs.mongodb.org/manual/reference/operator/meta/maxTimeMS/#op._S_maxTimeMS) *
  133. * - [batchSize](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7BbatchSize%28%29%7D%7D) *
  134. * - [comment](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24comment) *
  135. * - [snapshot](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsnapshot%28%29%7D%7D) *
  136. * - [hint](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24hint) *
  137. * - [slaveOk](http://docs.mongodb.org/manual/applications/replication/#read-preference) *
  138. * - [safe](http://www.mongodb.org/display/DOCS/getLastError+Command)
  139. * - collection the collection to query against
  140. *
  141. * _* denotes a query helper method is also available_
  142. *
  143. * @param {Object} options
  144. * @api public
  145. */
  146. Query.prototype.setOptions = function(options) {
  147. if (!(options && utils.isObject(options)))
  148. return this;
  149. // set arbitrary options
  150. var methods = utils.keys(options),
  151. method;
  152. for (var i = 0; i < methods.length; ++i) {
  153. method = methods[i];
  154. // use methods if exist (safer option manipulation)
  155. if ('function' == typeof this[method]) {
  156. var args = utils.isArray(options[method])
  157. ? options[method]
  158. : [options[method]];
  159. this[method].apply(this, args);
  160. } else {
  161. this.options[method] = options[method];
  162. }
  163. }
  164. return this;
  165. };
  166. /**
  167. * Sets this Querys collection.
  168. *
  169. * @param {Collection} coll
  170. * @return {Query} this
  171. */
  172. Query.prototype.collection = function collection(coll) {
  173. this._collection = new Query.Collection(coll);
  174. return this;
  175. };
  176. /**
  177. * Adds a collation to this op (MongoDB 3.4 and up)
  178. *
  179. * ####Example
  180. *
  181. * query.find().collation({ locale: "en_US", strength: 1 })
  182. *
  183. * @param {Object} value
  184. * @return {Query} this
  185. * @see MongoDB docs https://docs.mongodb.com/manual/reference/method/cursor.collation/#cursor.collation
  186. * @api public
  187. */
  188. Query.prototype.collation = function(value) {
  189. this.options.collation = value;
  190. return this;
  191. };
  192. /**
  193. * Specifies a `$where` condition
  194. *
  195. * Use `$where` when you need to select documents using a JavaScript expression.
  196. *
  197. * ####Example
  198. *
  199. * query.$where('this.comments.length > 10 || this.name.length > 5')
  200. *
  201. * query.$where(function () {
  202. * return this.comments.length > 10 || this.name.length > 5;
  203. * })
  204. *
  205. * @param {String|Function} js javascript string or function
  206. * @return {Query} this
  207. * @memberOf Query
  208. * @method $where
  209. * @api public
  210. */
  211. Query.prototype.$where = function(js) {
  212. this._conditions.$where = js;
  213. return this;
  214. };
  215. /**
  216. * Specifies a `path` for use with chaining.
  217. *
  218. * ####Example
  219. *
  220. * // instead of writing:
  221. * User.find({age: {$gte: 21, $lte: 65}}, callback);
  222. *
  223. * // we can instead write:
  224. * User.where('age').gte(21).lte(65);
  225. *
  226. * // passing query conditions is permitted
  227. * User.find().where({ name: 'vonderful' })
  228. *
  229. * // chaining
  230. * User
  231. * .where('age').gte(21).lte(65)
  232. * .where('name', /^vonderful/i)
  233. * .where('friends').slice(10)
  234. * .exec(callback)
  235. *
  236. * @param {String} [path]
  237. * @param {Object} [val]
  238. * @return {Query} this
  239. * @api public
  240. */
  241. Query.prototype.where = function() {
  242. if (!arguments.length) return this;
  243. if (!this.op) this.op = 'find';
  244. var type = typeof arguments[0];
  245. if ('string' == type) {
  246. this._path = arguments[0];
  247. if (2 === arguments.length) {
  248. this._conditions[this._path] = arguments[1];
  249. }
  250. return this;
  251. }
  252. if ('object' == type && !Array.isArray(arguments[0])) {
  253. return this.merge(arguments[0]);
  254. }
  255. throw new TypeError('path must be a string or object');
  256. };
  257. /**
  258. * Specifies the complementary comparison value for paths specified with `where()`
  259. *
  260. * ####Example
  261. *
  262. * User.where('age').equals(49);
  263. *
  264. * // is the same as
  265. *
  266. * User.where('age', 49);
  267. *
  268. * @param {Object} val
  269. * @return {Query} this
  270. * @api public
  271. */
  272. Query.prototype.equals = function equals(val) {
  273. this._ensurePath('equals');
  274. var path = this._path;
  275. this._conditions[path] = val;
  276. return this;
  277. };
  278. /**
  279. * Specifies the complementary comparison value for paths specified with `where()`
  280. * This is alias of `equals`
  281. *
  282. * ####Example
  283. *
  284. * User.where('age').eq(49);
  285. *
  286. * // is the same as
  287. *
  288. * User.shere('age').equals(49);
  289. *
  290. * // is the same as
  291. *
  292. * User.where('age', 49);
  293. *
  294. * @param {Object} val
  295. * @return {Query} this
  296. * @api public
  297. */
  298. Query.prototype.eq = function eq(val) {
  299. this._ensurePath('eq');
  300. var path = this._path;
  301. this._conditions[path] = val;
  302. return this;
  303. };
  304. /**
  305. * Specifies arguments for an `$or` condition.
  306. *
  307. * ####Example
  308. *
  309. * query.or([{ color: 'red' }, { status: 'emergency' }])
  310. *
  311. * @param {Array} array array of conditions
  312. * @return {Query} this
  313. * @api public
  314. */
  315. Query.prototype.or = function or(array) {
  316. var or = this._conditions.$or || (this._conditions.$or = []);
  317. if (!utils.isArray(array)) array = [array];
  318. or.push.apply(or, array);
  319. return this;
  320. };
  321. /**
  322. * Specifies arguments for a `$nor` condition.
  323. *
  324. * ####Example
  325. *
  326. * query.nor([{ color: 'green' }, { status: 'ok' }])
  327. *
  328. * @param {Array} array array of conditions
  329. * @return {Query} this
  330. * @api public
  331. */
  332. Query.prototype.nor = function nor(array) {
  333. var nor = this._conditions.$nor || (this._conditions.$nor = []);
  334. if (!utils.isArray(array)) array = [array];
  335. nor.push.apply(nor, array);
  336. return this;
  337. };
  338. /**
  339. * Specifies arguments for a `$and` condition.
  340. *
  341. * ####Example
  342. *
  343. * query.and([{ color: 'green' }, { status: 'ok' }])
  344. *
  345. * @see $and http://docs.mongodb.org/manual/reference/operator/and/
  346. * @param {Array} array array of conditions
  347. * @return {Query} this
  348. * @api public
  349. */
  350. Query.prototype.and = function and(array) {
  351. var and = this._conditions.$and || (this._conditions.$and = []);
  352. if (!Array.isArray(array)) array = [array];
  353. and.push.apply(and, array);
  354. return this;
  355. };
  356. /**
  357. * Specifies a $gt query condition.
  358. *
  359. * When called with one argument, the most recent path passed to `where()` is used.
  360. *
  361. * ####Example
  362. *
  363. * Thing.find().where('age').gt(21)
  364. *
  365. * // or
  366. * Thing.find().gt('age', 21)
  367. *
  368. * @method gt
  369. * @memberOf Query
  370. * @param {String} [path]
  371. * @param {Number} val
  372. * @api public
  373. */
  374. /**
  375. * Specifies a $gte query condition.
  376. *
  377. * When called with one argument, the most recent path passed to `where()` is used.
  378. *
  379. * @method gte
  380. * @memberOf Query
  381. * @param {String} [path]
  382. * @param {Number} val
  383. * @api public
  384. */
  385. /**
  386. * Specifies a $lt query condition.
  387. *
  388. * When called with one argument, the most recent path passed to `where()` is used.
  389. *
  390. * @method lt
  391. * @memberOf Query
  392. * @param {String} [path]
  393. * @param {Number} val
  394. * @api public
  395. */
  396. /**
  397. * Specifies a $lte query condition.
  398. *
  399. * When called with one argument, the most recent path passed to `where()` is used.
  400. *
  401. * @method lte
  402. * @memberOf Query
  403. * @param {String} [path]
  404. * @param {Number} val
  405. * @api public
  406. */
  407. /**
  408. * Specifies a $ne query condition.
  409. *
  410. * When called with one argument, the most recent path passed to `where()` is used.
  411. *
  412. * @method ne
  413. * @memberOf Query
  414. * @param {String} [path]
  415. * @param {Number} val
  416. * @api public
  417. */
  418. /**
  419. * Specifies an $in query condition.
  420. *
  421. * When called with one argument, the most recent path passed to `where()` is used.
  422. *
  423. * @method in
  424. * @memberOf Query
  425. * @param {String} [path]
  426. * @param {Number} val
  427. * @api public
  428. */
  429. /**
  430. * Specifies an $nin query condition.
  431. *
  432. * When called with one argument, the most recent path passed to `where()` is used.
  433. *
  434. * @method nin
  435. * @memberOf Query
  436. * @param {String} [path]
  437. * @param {Number} val
  438. * @api public
  439. */
  440. /**
  441. * Specifies an $all query condition.
  442. *
  443. * When called with one argument, the most recent path passed to `where()` is used.
  444. *
  445. * @method all
  446. * @memberOf Query
  447. * @param {String} [path]
  448. * @param {Number} val
  449. * @api public
  450. */
  451. /**
  452. * Specifies a $size query condition.
  453. *
  454. * When called with one argument, the most recent path passed to `where()` is used.
  455. *
  456. * @method size
  457. * @memberOf Query
  458. * @param {String} [path]
  459. * @param {Number} val
  460. * @api public
  461. */
  462. /**
  463. * Specifies a $regex query condition.
  464. *
  465. * When called with one argument, the most recent path passed to `where()` is used.
  466. *
  467. * @method regex
  468. * @memberOf Query
  469. * @param {String} [path]
  470. * @param {String|RegExp} val
  471. * @api public
  472. */
  473. /**
  474. * Specifies a $maxDistance query condition.
  475. *
  476. * When called with one argument, the most recent path passed to `where()` is used.
  477. *
  478. * @method maxDistance
  479. * @memberOf Query
  480. * @param {String} [path]
  481. * @param {Number} val
  482. * @api public
  483. */
  484. /*!
  485. * gt, gte, lt, lte, ne, in, nin, all, regex, size, maxDistance
  486. *
  487. * Thing.where('type').nin(array)
  488. */
  489. 'gt gte lt lte ne in nin all regex size maxDistance minDistance'.split(' ').forEach(function($conditional) {
  490. Query.prototype[$conditional] = function() {
  491. var path, val;
  492. if (1 === arguments.length) {
  493. this._ensurePath($conditional);
  494. val = arguments[0];
  495. path = this._path;
  496. } else {
  497. val = arguments[1];
  498. path = arguments[0];
  499. }
  500. var conds = this._conditions[path] === null || typeof this._conditions[path] === 'object' ?
  501. this._conditions[path] :
  502. (this._conditions[path] = {});
  503. conds['$' + $conditional] = val;
  504. return this;
  505. };
  506. });
  507. /**
  508. * Specifies a `$mod` condition
  509. *
  510. * @param {String} [path]
  511. * @param {Number} val
  512. * @return {Query} this
  513. * @api public
  514. */
  515. Query.prototype.mod = function() {
  516. var val, path;
  517. if (1 === arguments.length) {
  518. this._ensurePath('mod');
  519. val = arguments[0];
  520. path = this._path;
  521. } else if (2 === arguments.length && !utils.isArray(arguments[1])) {
  522. this._ensurePath('mod');
  523. val = slice(arguments);
  524. path = this._path;
  525. } else if (3 === arguments.length) {
  526. val = slice(arguments, 1);
  527. path = arguments[0];
  528. } else {
  529. val = arguments[1];
  530. path = arguments[0];
  531. }
  532. var conds = this._conditions[path] || (this._conditions[path] = {});
  533. conds.$mod = val;
  534. return this;
  535. };
  536. /**
  537. * Specifies an `$exists` condition
  538. *
  539. * ####Example
  540. *
  541. * // { name: { $exists: true }}
  542. * Thing.where('name').exists()
  543. * Thing.where('name').exists(true)
  544. * Thing.find().exists('name')
  545. *
  546. * // { name: { $exists: false }}
  547. * Thing.where('name').exists(false);
  548. * Thing.find().exists('name', false);
  549. *
  550. * @param {String} [path]
  551. * @param {Number} val
  552. * @return {Query} this
  553. * @api public
  554. */
  555. Query.prototype.exists = function() {
  556. var path, val;
  557. if (0 === arguments.length) {
  558. this._ensurePath('exists');
  559. path = this._path;
  560. val = true;
  561. } else if (1 === arguments.length) {
  562. if ('boolean' === typeof arguments[0]) {
  563. this._ensurePath('exists');
  564. path = this._path;
  565. val = arguments[0];
  566. } else {
  567. path = arguments[0];
  568. val = true;
  569. }
  570. } else if (2 === arguments.length) {
  571. path = arguments[0];
  572. val = arguments[1];
  573. }
  574. var conds = this._conditions[path] || (this._conditions[path] = {});
  575. conds.$exists = val;
  576. return this;
  577. };
  578. /**
  579. * Specifies an `$elemMatch` condition
  580. *
  581. * ####Example
  582. *
  583. * query.elemMatch('comment', { author: 'autobot', votes: {$gte: 5}})
  584. *
  585. * query.where('comment').elemMatch({ author: 'autobot', votes: {$gte: 5}})
  586. *
  587. * query.elemMatch('comment', function (elem) {
  588. * elem.where('author').equals('autobot');
  589. * elem.where('votes').gte(5);
  590. * })
  591. *
  592. * query.where('comment').elemMatch(function (elem) {
  593. * elem.where({ author: 'autobot' });
  594. * elem.where('votes').gte(5);
  595. * })
  596. *
  597. * @param {String|Object|Function} path
  598. * @param {Object|Function} criteria
  599. * @return {Query} this
  600. * @api public
  601. */
  602. Query.prototype.elemMatch = function() {
  603. if (null == arguments[0])
  604. throw new TypeError('Invalid argument');
  605. var fn, path, criteria;
  606. if ('function' === typeof arguments[0]) {
  607. this._ensurePath('elemMatch');
  608. path = this._path;
  609. fn = arguments[0];
  610. } else if (utils.isObject(arguments[0])) {
  611. this._ensurePath('elemMatch');
  612. path = this._path;
  613. criteria = arguments[0];
  614. } else if ('function' === typeof arguments[1]) {
  615. path = arguments[0];
  616. fn = arguments[1];
  617. } else if (arguments[1] && utils.isObject(arguments[1])) {
  618. path = arguments[0];
  619. criteria = arguments[1];
  620. } else {
  621. throw new TypeError('Invalid argument');
  622. }
  623. if (fn) {
  624. criteria = new Query;
  625. fn(criteria);
  626. criteria = criteria._conditions;
  627. }
  628. var conds = this._conditions[path] || (this._conditions[path] = {});
  629. conds.$elemMatch = criteria;
  630. return this;
  631. };
  632. // Spatial queries
  633. /**
  634. * Sugar for geo-spatial queries.
  635. *
  636. * ####Example
  637. *
  638. * query.within().box()
  639. * query.within().circle()
  640. * query.within().geometry()
  641. *
  642. * query.where('loc').within({ center: [50,50], radius: 10, unique: true, spherical: true });
  643. * query.where('loc').within({ box: [[40.73, -73.9], [40.7, -73.988]] });
  644. * query.where('loc').within({ polygon: [[],[],[],[]] });
  645. *
  646. * query.where('loc').within([], [], []) // polygon
  647. * query.where('loc').within([], []) // box
  648. * query.where('loc').within({ type: 'LineString', coordinates: [...] }); // geometry
  649. *
  650. * ####NOTE:
  651. *
  652. * Must be used after `where()`.
  653. *
  654. * @memberOf Query
  655. * @return {Query} this
  656. * @api public
  657. */
  658. Query.prototype.within = function within() {
  659. // opinionated, must be used after where
  660. this._ensurePath('within');
  661. this._geoComparison = $withinCmd;
  662. if (0 === arguments.length) {
  663. return this;
  664. }
  665. if (2 === arguments.length) {
  666. return this.box.apply(this, arguments);
  667. } else if (2 < arguments.length) {
  668. return this.polygon.apply(this, arguments);
  669. }
  670. var area = arguments[0];
  671. if (!area)
  672. throw new TypeError('Invalid argument');
  673. if (area.center)
  674. return this.circle(area);
  675. if (area.box)
  676. return this.box.apply(this, area.box);
  677. if (area.polygon)
  678. return this.polygon.apply(this, area.polygon);
  679. if (area.type && area.coordinates)
  680. return this.geometry(area);
  681. throw new TypeError('Invalid argument');
  682. };
  683. /**
  684. * Specifies a $box condition
  685. *
  686. * ####Example
  687. *
  688. * var lowerLeft = [40.73083, -73.99756]
  689. * var upperRight= [40.741404, -73.988135]
  690. *
  691. * query.where('loc').within().box(lowerLeft, upperRight)
  692. * query.box('loc', lowerLeft, upperRight )
  693. *
  694. * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
  695. * @see Query#within #query_Query-within
  696. * @param {String} path
  697. * @param {Object} val
  698. * @return {Query} this
  699. * @api public
  700. */
  701. Query.prototype.box = function() {
  702. var path, box;
  703. if (3 === arguments.length) {
  704. // box('loc', [], [])
  705. path = arguments[0];
  706. box = [arguments[1], arguments[2]];
  707. } else if (2 === arguments.length) {
  708. // box([], [])
  709. this._ensurePath('box');
  710. path = this._path;
  711. box = [arguments[0], arguments[1]];
  712. } else {
  713. throw new TypeError('Invalid argument');
  714. }
  715. var conds = this._conditions[path] || (this._conditions[path] = {});
  716. conds[this._geoComparison || $withinCmd] = { '$box': box };
  717. return this;
  718. };
  719. /**
  720. * Specifies a $polygon condition
  721. *
  722. * ####Example
  723. *
  724. * query.where('loc').within().polygon([10,20], [13, 25], [7,15])
  725. * query.polygon('loc', [10,20], [13, 25], [7,15])
  726. *
  727. * @param {String|Array} [path]
  728. * @param {Array|Object} [val]
  729. * @return {Query} this
  730. * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
  731. * @api public
  732. */
  733. Query.prototype.polygon = function() {
  734. var val, path;
  735. if ('string' == typeof arguments[0]) {
  736. // polygon('loc', [],[],[])
  737. path = arguments[0];
  738. val = slice(arguments, 1);
  739. } else {
  740. // polygon([],[],[])
  741. this._ensurePath('polygon');
  742. path = this._path;
  743. val = slice(arguments);
  744. }
  745. var conds = this._conditions[path] || (this._conditions[path] = {});
  746. conds[this._geoComparison || $withinCmd] = { '$polygon': val };
  747. return this;
  748. };
  749. /**
  750. * Specifies a $center or $centerSphere condition.
  751. *
  752. * ####Example
  753. *
  754. * var area = { center: [50, 50], radius: 10, unique: true }
  755. * query.where('loc').within().circle(area)
  756. * query.center('loc', area);
  757. *
  758. * // for spherical calculations
  759. * var area = { center: [50, 50], radius: 10, unique: true, spherical: true }
  760. * query.where('loc').within().circle(area)
  761. * query.center('loc', area);
  762. *
  763. * @param {String} [path]
  764. * @param {Object} area
  765. * @return {Query} this
  766. * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
  767. * @api public
  768. */
  769. Query.prototype.circle = function() {
  770. var path, val;
  771. if (1 === arguments.length) {
  772. this._ensurePath('circle');
  773. path = this._path;
  774. val = arguments[0];
  775. } else if (2 === arguments.length) {
  776. path = arguments[0];
  777. val = arguments[1];
  778. } else {
  779. throw new TypeError('Invalid argument');
  780. }
  781. if (!('radius' in val && val.center))
  782. throw new Error('center and radius are required');
  783. var conds = this._conditions[path] || (this._conditions[path] = {});
  784. var type = val.spherical
  785. ? '$centerSphere'
  786. : '$center';
  787. var wKey = this._geoComparison || $withinCmd;
  788. conds[wKey] = {};
  789. conds[wKey][type] = [val.center, val.radius];
  790. if ('unique' in val)
  791. conds[wKey].$uniqueDocs = !!val.unique;
  792. return this;
  793. };
  794. /**
  795. * Specifies a `$near` or `$nearSphere` condition
  796. *
  797. * These operators return documents sorted by distance.
  798. *
  799. * ####Example
  800. *
  801. * query.where('loc').near({ center: [10, 10] });
  802. * query.where('loc').near({ center: [10, 10], maxDistance: 5 });
  803. * query.where('loc').near({ center: [10, 10], maxDistance: 5, spherical: true });
  804. * query.near('loc', { center: [10, 10], maxDistance: 5 });
  805. * query.near({ center: { type: 'Point', coordinates: [..] }})
  806. * query.near().geometry({ type: 'Point', coordinates: [..] })
  807. *
  808. * @param {String} [path]
  809. * @param {Object} val
  810. * @return {Query} this
  811. * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
  812. * @api public
  813. */
  814. Query.prototype.near = function near() {
  815. var path, val;
  816. this._geoComparison = '$near';
  817. if (0 === arguments.length) {
  818. return this;
  819. } else if (1 === arguments.length) {
  820. this._ensurePath('near');
  821. path = this._path;
  822. val = arguments[0];
  823. } else if (2 === arguments.length) {
  824. path = arguments[0];
  825. val = arguments[1];
  826. } else {
  827. throw new TypeError('Invalid argument');
  828. }
  829. if (!val.center) {
  830. throw new Error('center is required');
  831. }
  832. var conds = this._conditions[path] || (this._conditions[path] = {});
  833. var type = val.spherical
  834. ? '$nearSphere'
  835. : '$near';
  836. // center could be a GeoJSON object or an Array
  837. if (Array.isArray(val.center)) {
  838. conds[type] = val.center;
  839. var radius = 'maxDistance' in val
  840. ? val.maxDistance
  841. : null;
  842. if (null != radius) {
  843. conds.$maxDistance = radius;
  844. }
  845. if (null != val.minDistance) {
  846. conds.$minDistance = val.minDistance;
  847. }
  848. } else {
  849. // GeoJSON?
  850. if (val.center.type != 'Point' || !Array.isArray(val.center.coordinates)) {
  851. throw new Error(util.format('Invalid GeoJSON specified for %s', type));
  852. }
  853. conds[type] = { $geometry : val.center };
  854. // MongoDB 2.6 insists on maxDistance being in $near / $nearSphere
  855. if ('maxDistance' in val) {
  856. conds[type]['$maxDistance'] = val.maxDistance;
  857. }
  858. if ('minDistance' in val) {
  859. conds[type]['$minDistance'] = val.minDistance;
  860. }
  861. }
  862. return this;
  863. };
  864. /**
  865. * Declares an intersects query for `geometry()`.
  866. *
  867. * ####Example
  868. *
  869. * query.where('path').intersects().geometry({
  870. * type: 'LineString'
  871. * , coordinates: [[180.0, 11.0], [180, 9.0]]
  872. * })
  873. *
  874. * query.where('path').intersects({
  875. * type: 'LineString'
  876. * , coordinates: [[180.0, 11.0], [180, 9.0]]
  877. * })
  878. *
  879. * @param {Object} [arg]
  880. * @return {Query} this
  881. * @api public
  882. */
  883. Query.prototype.intersects = function intersects() {
  884. // opinionated, must be used after where
  885. this._ensurePath('intersects');
  886. this._geoComparison = '$geoIntersects';
  887. if (0 === arguments.length) {
  888. return this;
  889. }
  890. var area = arguments[0];
  891. if (null != area && area.type && area.coordinates)
  892. return this.geometry(area);
  893. throw new TypeError('Invalid argument');
  894. };
  895. /**
  896. * Specifies a `$geometry` condition
  897. *
  898. * ####Example
  899. *
  900. * var polyA = [[[ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ]]]
  901. * query.where('loc').within().geometry({ type: 'Polygon', coordinates: polyA })
  902. *
  903. * // or
  904. * var polyB = [[ 0, 0 ], [ 1, 1 ]]
  905. * query.where('loc').within().geometry({ type: 'LineString', coordinates: polyB })
  906. *
  907. * // or
  908. * var polyC = [ 0, 0 ]
  909. * query.where('loc').within().geometry({ type: 'Point', coordinates: polyC })
  910. *
  911. * // or
  912. * query.where('loc').intersects().geometry({ type: 'Point', coordinates: polyC })
  913. *
  914. * ####NOTE:
  915. *
  916. * `geometry()` **must** come after either `intersects()` or `within()`.
  917. *
  918. * The `object` argument must contain `type` and `coordinates` properties.
  919. * - type {String}
  920. * - coordinates {Array}
  921. *
  922. * The most recent path passed to `where()` is used.
  923. *
  924. * @param {Object} object Must contain a `type` property which is a String and a `coordinates` property which is an Array. See the examples.
  925. * @return {Query} this
  926. * @see http://docs.mongodb.org/manual/release-notes/2.4/#new-geospatial-indexes-with-geojson-and-improved-spherical-geometry
  927. * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing
  928. * @see $geometry http://docs.mongodb.org/manual/reference/operator/geometry/
  929. * @api public
  930. */
  931. Query.prototype.geometry = function geometry() {
  932. if (!('$within' == this._geoComparison ||
  933. '$geoWithin' == this._geoComparison ||
  934. '$near' == this._geoComparison ||
  935. '$geoIntersects' == this._geoComparison)) {
  936. throw new Error('geometry() must come after `within()`, `intersects()`, or `near()');
  937. }
  938. var val, path;
  939. if (1 === arguments.length) {
  940. this._ensurePath('geometry');
  941. path = this._path;
  942. val = arguments[0];
  943. } else {
  944. throw new TypeError('Invalid argument');
  945. }
  946. if (!(val.type && Array.isArray(val.coordinates))) {
  947. throw new TypeError('Invalid argument');
  948. }
  949. var conds = this._conditions[path] || (this._conditions[path] = {});
  950. conds[this._geoComparison] = { $geometry: val };
  951. return this;
  952. };
  953. // end spatial
  954. /**
  955. * Specifies which document fields to include or exclude
  956. *
  957. * ####String syntax
  958. *
  959. * When passing a string, prefixing a path with `-` will flag that path as excluded. When a path does not have the `-` prefix, it is included.
  960. *
  961. * ####Example
  962. *
  963. * // include a and b, exclude c
  964. * query.select('a b -c');
  965. *
  966. * // or you may use object notation, useful when
  967. * // you have keys already prefixed with a "-"
  968. * query.select({a: 1, b: 1, c: 0});
  969. *
  970. * ####Note
  971. *
  972. * Cannot be used with `distinct()`
  973. *
  974. * @param {Object|String} arg
  975. * @return {Query} this
  976. * @see SchemaType
  977. * @api public
  978. */
  979. Query.prototype.select = function select() {
  980. var arg = arguments[0];
  981. if (!arg) return this;
  982. if (arguments.length !== 1) {
  983. throw new Error('Invalid select: select only takes 1 argument');
  984. }
  985. this._validate('select');
  986. var fields = this._fields || (this._fields = {});
  987. var type = typeof arg;
  988. var i, len;
  989. if (('string' == type || utils.isArgumentsObject(arg)) &&
  990. 'number' == typeof arg.length || Array.isArray(arg)) {
  991. if ('string' == type)
  992. arg = arg.split(/\s+/);
  993. for (i = 0, len = arg.length; i < len; ++i) {
  994. var field = arg[i];
  995. if (!field) continue;
  996. var include = '-' == field[0] ? 0 : 1;
  997. if (include === 0) field = field.substring(1);
  998. fields[field] = include;
  999. }
  1000. return this;
  1001. }
  1002. if (utils.isObject(arg)) {
  1003. var keys = utils.keys(arg);
  1004. for (i = 0; i < keys.length; ++i) {
  1005. fields[keys[i]] = arg[keys[i]];
  1006. }
  1007. return this;
  1008. }
  1009. throw new TypeError('Invalid select() argument. Must be string or object.');
  1010. };
  1011. /**
  1012. * Specifies a $slice condition for a `path`
  1013. *
  1014. * ####Example
  1015. *
  1016. * query.slice('comments', 5)
  1017. * query.slice('comments', -5)
  1018. * query.slice('comments', [10, 5])
  1019. * query.where('comments').slice(5)
  1020. * query.where('comments').slice([-10, 5])
  1021. *
  1022. * @param {String} [path]
  1023. * @param {Number} val number/range of elements to slice
  1024. * @return {Query} this
  1025. * @see mongodb http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields#RetrievingaSubsetofFields-RetrievingaSubrangeofArrayElements
  1026. * @api public
  1027. */
  1028. Query.prototype.slice = function() {
  1029. if (0 === arguments.length)
  1030. return this;
  1031. this._validate('slice');
  1032. var path, val;
  1033. if (1 === arguments.length) {
  1034. var arg = arguments[0];
  1035. if (typeof arg === 'object' && !Array.isArray(arg)) {
  1036. var keys = Object.keys(arg);
  1037. var numKeys = keys.length;
  1038. for (var i = 0; i < numKeys; ++i) {
  1039. this.slice(keys[i], arg[keys[i]]);
  1040. }
  1041. return this;
  1042. }
  1043. this._ensurePath('slice');
  1044. path = this._path;
  1045. val = arguments[0];
  1046. } else if (2 === arguments.length) {
  1047. if ('number' === typeof arguments[0]) {
  1048. this._ensurePath('slice');
  1049. path = this._path;
  1050. val = slice(arguments);
  1051. } else {
  1052. path = arguments[0];
  1053. val = arguments[1];
  1054. }
  1055. } else if (3 === arguments.length) {
  1056. path = arguments[0];
  1057. val = slice(arguments, 1);
  1058. }
  1059. var myFields = this._fields || (this._fields = {});
  1060. myFields[path] = { '$slice': val };
  1061. return this;
  1062. };
  1063. /**
  1064. * Sets the sort order
  1065. *
  1066. * If an object is passed, values allowed are 'asc', 'desc', 'ascending', 'descending', 1, and -1.
  1067. *
  1068. * If a string is passed, it must be a space delimited list of path names. The sort order of each path is ascending unless the path name is prefixed with `-` which will be treated as descending.
  1069. *
  1070. * ####Example
  1071. *
  1072. * // these are equivalent
  1073. * query.sort({ field: 'asc', test: -1 });
  1074. * query.sort('field -test');
  1075. * query.sort([['field', 1], ['test', -1]]);
  1076. *
  1077. * ####Note
  1078. *
  1079. * - The array syntax `.sort([['field', 1], ['test', -1]])` can only be used with [mongodb driver >= 2.0.46](https://github.com/mongodb/node-mongodb-native/blob/2.1/HISTORY.md#2046-2015-10-15).
  1080. * - Cannot be used with `distinct()`
  1081. *
  1082. * @param {Object|String|Array} arg
  1083. * @return {Query} this
  1084. * @api public
  1085. */
  1086. Query.prototype.sort = function(arg) {
  1087. if (!arg) return this;
  1088. var i, len, field;
  1089. this._validate('sort');
  1090. var type = typeof arg;
  1091. // .sort([['field', 1], ['test', -1]])
  1092. if (Array.isArray(arg)) {
  1093. len = arg.length;
  1094. for (i = 0; i < arg.length; ++i) {
  1095. if (!Array.isArray(arg[i])) {
  1096. throw new Error('Invalid sort() argument, must be array of arrays');
  1097. }
  1098. _pushArr(this.options, arg[i][0], arg[i][1]);
  1099. }
  1100. return this;
  1101. }
  1102. // .sort('field -test')
  1103. if (1 === arguments.length && 'string' == type) {
  1104. arg = arg.split(/\s+/);
  1105. len = arg.length;
  1106. for (i = 0; i < len; ++i) {
  1107. field = arg[i];
  1108. if (!field) continue;
  1109. var ascend = '-' == field[0] ? -1 : 1;
  1110. if (ascend === -1) field = field.substring(1);
  1111. push(this.options, field, ascend);
  1112. }
  1113. return this;
  1114. }
  1115. // .sort({ field: 1, test: -1 })
  1116. if (utils.isObject(arg)) {
  1117. var keys = utils.keys(arg);
  1118. for (i = 0; i < keys.length; ++i) {
  1119. field = keys[i];
  1120. push(this.options, field, arg[field]);
  1121. }
  1122. return this;
  1123. }
  1124. if (typeof Map !== 'undefined' && arg instanceof Map) {
  1125. _pushMap(this.options, arg);
  1126. return this;
  1127. }
  1128. throw new TypeError('Invalid sort() argument. Must be a string, object, or array.');
  1129. };
  1130. /*!
  1131. * @ignore
  1132. */
  1133. var _validSortValue = {
  1134. '1': 1,
  1135. '-1': -1,
  1136. 'asc': 1,
  1137. 'ascending': 1,
  1138. 'desc': -1,
  1139. 'descending': -1
  1140. };
  1141. function push(opts, field, value) {
  1142. if (Array.isArray(opts.sort)) {
  1143. throw new TypeError('Can\'t mix sort syntaxes. Use either array or object:' +
  1144. '\n- `.sort([[\'field\', 1], [\'test\', -1]])`' +
  1145. '\n- `.sort({ field: 1, test: -1 })`');
  1146. }
  1147. var s;
  1148. if (value && value.$meta) {
  1149. s = opts.sort || (opts.sort = {});
  1150. s[field] = { $meta : value.$meta };
  1151. return;
  1152. }
  1153. s = opts.sort || (opts.sort = {});
  1154. var val = String(value || 1).toLowerCase();
  1155. val = _validSortValue[val];
  1156. if (!val) throw new TypeError('Invalid sort value: { ' + field + ': ' + value + ' }');
  1157. s[field] = val;
  1158. }
  1159. function _pushArr(opts, field, value) {
  1160. opts.sort = opts.sort || [];
  1161. if (!Array.isArray(opts.sort)) {
  1162. throw new TypeError('Can\'t mix sort syntaxes. Use either array or object:' +
  1163. '\n- `.sort([[\'field\', 1], [\'test\', -1]])`' +
  1164. '\n- `.sort({ field: 1, test: -1 })`');
  1165. }
  1166. var val = String(value || 1).toLowerCase();
  1167. val = _validSortValue[val];
  1168. if (!val) throw new TypeError('Invalid sort value: [ ' + field + ', ' + value + ' ]');
  1169. opts.sort.push([field, val]);
  1170. }
  1171. function _pushMap(opts, map) {
  1172. opts.sort = opts.sort || new Map();
  1173. if (!(opts.sort instanceof Map)) {
  1174. throw new TypeError('Can\'t mix sort syntaxes. Use either array or ' +
  1175. 'object or map consistently');
  1176. }
  1177. map.forEach(function(value, key) {
  1178. var val = String(value || 1).toLowerCase();
  1179. val = _validSortValue[val];
  1180. if (!val) throw new TypeError('Invalid sort value: < ' + key + ': ' + value + ' >');
  1181. opts.sort.set(key, val);
  1182. });
  1183. }
  1184. /**
  1185. * Specifies the limit option.
  1186. *
  1187. * ####Example
  1188. *
  1189. * query.limit(20)
  1190. *
  1191. * ####Note
  1192. *
  1193. * Cannot be used with `distinct()`
  1194. *
  1195. * @method limit
  1196. * @memberOf Query
  1197. * @param {Number} val
  1198. * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Blimit%28%29%7D%7D
  1199. * @api public
  1200. */
  1201. /**
  1202. * Specifies the skip option.
  1203. *
  1204. * ####Example
  1205. *
  1206. * query.skip(100).limit(20)
  1207. *
  1208. * ####Note
  1209. *
  1210. * Cannot be used with `distinct()`
  1211. *
  1212. * @method skip
  1213. * @memberOf Query
  1214. * @param {Number} val
  1215. * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bskip%28%29%7D%7D
  1216. * @api public
  1217. */
  1218. /**
  1219. * Specifies the maxScan option.
  1220. *
  1221. * ####Example
  1222. *
  1223. * query.maxScan(100)
  1224. *
  1225. * ####Note
  1226. *
  1227. * Cannot be used with `distinct()`
  1228. *
  1229. * @method maxScan
  1230. * @memberOf Query
  1231. * @param {Number} val
  1232. * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24maxScan
  1233. * @api public
  1234. */
  1235. /**
  1236. * Specifies the batchSize option.
  1237. *
  1238. * ####Example
  1239. *
  1240. * query.batchSize(100)
  1241. *
  1242. * ####Note
  1243. *
  1244. * Cannot be used with `distinct()`
  1245. *
  1246. * @method batchSize
  1247. * @memberOf Query
  1248. * @param {Number} val
  1249. * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7BbatchSize%28%29%7D%7D
  1250. * @api public
  1251. */
  1252. /**
  1253. * Specifies the `comment` option.
  1254. *
  1255. * ####Example
  1256. *
  1257. * query.comment('login query')
  1258. *
  1259. * ####Note
  1260. *
  1261. * Cannot be used with `distinct()`
  1262. *
  1263. * @method comment
  1264. * @memberOf Query
  1265. * @param {Number} val
  1266. * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24comment
  1267. * @api public
  1268. */
  1269. /*!
  1270. * limit, skip, maxScan, batchSize, comment
  1271. *
  1272. * Sets these associated options.
  1273. *
  1274. * query.comment('feed query');
  1275. */
  1276. ['limit', 'skip', 'maxScan', 'batchSize', 'comment'].forEach(function(method) {
  1277. Query.prototype[method] = function(v) {
  1278. this._validate(method);
  1279. this.options[method] = v;
  1280. return this;
  1281. };
  1282. });
  1283. /**
  1284. * Specifies the maxTimeMS option.
  1285. *
  1286. * ####Example
  1287. *
  1288. * query.maxTime(100)
  1289. * query.maxTimeMS(100)
  1290. *
  1291. * @method maxTime
  1292. * @memberOf Query
  1293. * @param {Number} ms
  1294. * @see mongodb http://docs.mongodb.org/manual/reference/operator/meta/maxTimeMS/#op._S_maxTimeMS
  1295. * @api public
  1296. */
  1297. Query.prototype.maxTime = Query.prototype.maxTimeMS = function(ms) {
  1298. this._validate('maxTime');
  1299. this.options.maxTimeMS = ms;
  1300. return this;
  1301. };
  1302. /**
  1303. * Specifies this query as a `snapshot` query.
  1304. *
  1305. * ####Example
  1306. *
  1307. * mquery().snapshot() // true
  1308. * mquery().snapshot(true)
  1309. * mquery().snapshot(false)
  1310. *
  1311. * ####Note
  1312. *
  1313. * Cannot be used with `distinct()`
  1314. *
  1315. * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsnapshot%28%29%7D%7D
  1316. * @return {Query} this
  1317. * @api public
  1318. */
  1319. Query.prototype.snapshot = function() {
  1320. this._validate('snapshot');
  1321. this.options.snapshot = arguments.length
  1322. ? !!arguments[0]
  1323. : true;
  1324. return this;
  1325. };
  1326. /**
  1327. * Sets query hints.
  1328. *
  1329. * ####Example
  1330. *
  1331. * query.hint({ indexA: 1, indexB: -1});
  1332. * query.hint('indexA_1_indexB_1');
  1333. *
  1334. * ####Note
  1335. *
  1336. * Cannot be used with `distinct()`
  1337. *
  1338. * @param {Object|string} val a hint object or the index name
  1339. * @return {Query} this
  1340. * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24hint
  1341. * @api public
  1342. */
  1343. Query.prototype.hint = function() {
  1344. if (0 === arguments.length) return this;
  1345. this._validate('hint');
  1346. var arg = arguments[0];
  1347. if (utils.isObject(arg)) {
  1348. var hint = this.options.hint || (this.options.hint = {});
  1349. // must keep object keys in order so don't use Object.keys()
  1350. for (var k in arg) {
  1351. hint[k] = arg[k];
  1352. }
  1353. return this;
  1354. }
  1355. if (typeof arg === 'string') {
  1356. this.options.hint = arg;
  1357. return this;
  1358. }
  1359. throw new TypeError('Invalid hint. ' + arg);
  1360. };
  1361. /**
  1362. * Requests acknowledgement that this operation has been persisted to MongoDB's
  1363. * on-disk journal.
  1364. * This option is only valid for operations that write to the database:
  1365. *
  1366. * - `deleteOne()`
  1367. * - `deleteMany()`
  1368. * - `findOneAndDelete()`
  1369. * - `findOneAndUpdate()`
  1370. * - `remove()`
  1371. * - `update()`
  1372. * - `updateOne()`
  1373. * - `updateMany()`
  1374. *
  1375. * Defaults to the `j` value if it is specified in writeConcern options
  1376. *
  1377. * ####Example:
  1378. *
  1379. * mquery().w(2).j(true).wtimeout(2000);
  1380. *
  1381. * @method j
  1382. * @memberOf Query
  1383. * @instance
  1384. * @param {boolean} val
  1385. * @see mongodb https://docs.mongodb.com/manual/reference/write-concern/#j-option
  1386. * @return {Query} this
  1387. * @api public
  1388. */
  1389. Query.prototype.j = function j(val) {
  1390. this.options.j = val;
  1391. return this;
  1392. };
  1393. /**
  1394. * Sets the slaveOk option. _Deprecated_ in MongoDB 2.2 in favor of read preferences.
  1395. *
  1396. * ####Example:
  1397. *
  1398. * query.slaveOk() // true
  1399. * query.slaveOk(true)
  1400. * query.slaveOk(false)
  1401. *
  1402. * @deprecated use read() preferences instead if on mongodb >= 2.2
  1403. * @param {Boolean} v defaults to true
  1404. * @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference
  1405. * @see read()
  1406. * @return {Query} this
  1407. * @api public
  1408. */
  1409. Query.prototype.slaveOk = function(v) {
  1410. this.options.slaveOk = arguments.length ? !!v : true;
  1411. return this;
  1412. };
  1413. /**
  1414. * Sets the readPreference option for the query.
  1415. *
  1416. * ####Example:
  1417. *
  1418. * new Query().read('primary')
  1419. * new Query().read('p') // same as primary
  1420. *
  1421. * new Query().read('primaryPreferred')
  1422. * new Query().read('pp') // same as primaryPreferred
  1423. *
  1424. * new Query().read('secondary')
  1425. * new Query().read('s') // same as secondary
  1426. *
  1427. * new Query().read('secondaryPreferred')
  1428. * new Query().read('sp') // same as secondaryPreferred
  1429. *
  1430. * new Query().read('nearest')
  1431. * new Query().read('n') // same as nearest
  1432. *
  1433. * // you can also use mongodb.ReadPreference class to also specify tags
  1434. * new Query().read(mongodb.ReadPreference('secondary', [{ dc:'sf', s: 1 },{ dc:'ma', s: 2 }]))
  1435. *
  1436. * new Query().setReadPreference('primary') // alias of .read()
  1437. *
  1438. * ####Preferences:
  1439. *
  1440. * primary - (default) Read from primary only. Operations will produce an error if primary is unavailable. Cannot be combined with tags.
  1441. * secondary Read from secondary if available, otherwise error.
  1442. * primaryPreferred Read from primary if available, otherwise a secondary.
  1443. * secondaryPreferred Read from a secondary if available, otherwise read from the primary.
  1444. * 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.
  1445. *
  1446. * Aliases
  1447. *
  1448. * p primary
  1449. * pp primaryPreferred
  1450. * s secondary
  1451. * sp secondaryPreferred
  1452. * n nearest
  1453. *
  1454. * Read more about how to use read preferences [here](http://docs.mongodb.org/manual/applications/replication/#read-preference) and [here](http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences).
  1455. *
  1456. * @param {String|ReadPreference} pref one of the listed preference options or their aliases
  1457. * @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference
  1458. * @see driver http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences
  1459. * @return {Query} this
  1460. * @api public
  1461. */
  1462. Query.prototype.read = Query.prototype.setReadPreference = function(pref) {
  1463. if (arguments.length > 1 && !Query.prototype.read.deprecationWarningIssued) {
  1464. console.error('Deprecation warning: \'tags\' argument is not supported anymore in Query.read() method. Please use mongodb.ReadPreference object instead.');
  1465. Query.prototype.read.deprecationWarningIssued = true;
  1466. }
  1467. this.options.readPreference = utils.readPref(pref);
  1468. return this;
  1469. };
  1470. /**
  1471. * Sets the readConcern option for the query.
  1472. *
  1473. * ####Example:
  1474. *
  1475. * new Query().readConcern('local')
  1476. * new Query().readConcern('l') // same as local
  1477. *
  1478. * new Query().readConcern('available')
  1479. * new Query().readConcern('a') // same as available
  1480. *
  1481. * new Query().readConcern('majority')
  1482. * new Query().readConcern('m') // same as majority
  1483. *
  1484. * new Query().readConcern('linearizable')
  1485. * new Query().readConcern('lz') // same as linearizable
  1486. *
  1487. * new Query().readConcern('snapshot')
  1488. * new Query().readConcern('s') // same as snapshot
  1489. *
  1490. * new Query().r('s') // r is alias of readConcern
  1491. *
  1492. *
  1493. * ####Read Concern Level:
  1494. *
  1495. * 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).
  1496. * 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).
  1497. * 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.
  1498. * 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.
  1499. * 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.
  1500. *
  1501. *
  1502. * Aliases
  1503. *
  1504. * l local
  1505. * a available
  1506. * m majority
  1507. * lz linearizable
  1508. * s snapshot
  1509. *
  1510. * Read more about how to use read concern [here](https://docs.mongodb.com/manual/reference/read-concern/).
  1511. *
  1512. * @param {String} level one of the listed read concern level or their aliases
  1513. * @see mongodb https://docs.mongodb.com/manual/reference/read-concern/
  1514. * @return {Query} this
  1515. * @api public
  1516. */
  1517. Query.prototype.readConcern = Query.prototype.r = function(level) {
  1518. this.options.readConcern = utils.readConcern(level);
  1519. return this;
  1520. };
  1521. /**
  1522. * Sets tailable option.
  1523. *
  1524. * ####Example
  1525. *
  1526. * query.tailable() <== true
  1527. * query.tailable(true)
  1528. * query.tailable(false)
  1529. *
  1530. * ####Note
  1531. *
  1532. * Cannot be used with `distinct()`
  1533. *
  1534. * @param {Boolean} v defaults to true
  1535. * @see mongodb http://www.mongodb.org/display/DOCS/Tailable+Cursors
  1536. * @api public
  1537. */
  1538. Query.prototype.tailable = function() {
  1539. this._validate('tailable');
  1540. this.options.tailable = arguments.length
  1541. ? !!arguments[0]
  1542. : true;
  1543. return this;
  1544. };
  1545. /**
  1546. * Sets the specified number of `mongod` servers, or tag set of `mongod` servers,
  1547. * that must acknowledge this write before this write is considered successful.
  1548. * This option is only valid for operations that write to the database:
  1549. *
  1550. * - `deleteOne()`
  1551. * - `deleteMany()`
  1552. * - `findOneAndDelete()`
  1553. * - `findOneAndUpdate()`
  1554. * - `remove()`
  1555. * - `update()`
  1556. * - `updateOne()`
  1557. * - `updateMany()`
  1558. *
  1559. * Defaults to the `w` value if it is specified in writeConcern options
  1560. *
  1561. * ####Example:
  1562. *
  1563. * mquery().writeConcern(0)
  1564. * mquery().writeConcern(1)
  1565. * mquery().writeConcern({ w: 1, j: true, wtimeout: 2000 })
  1566. * mquery().writeConcern('majority')
  1567. * mquery().writeConcern('m') // same as majority
  1568. * mquery().writeConcern('tagSetName') // if the tag set is 'm', use .writeConcern({ w: 'm' }) instead
  1569. * mquery().w(1) // w is alias of writeConcern
  1570. *
  1571. * @method writeConcern
  1572. * @memberOf Query
  1573. * @instance
  1574. * @param {String|number|object} concern 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://docs.mongodb.com/manual/reference/write-concern/#w-option).
  1575. * @see mongodb https://docs.mongodb.com/manual/reference/write-concern/#w-option
  1576. * @return {Query} this
  1577. * @api public
  1578. */
  1579. Query.prototype.writeConcern = Query.prototype.w = function writeConcern(concern) {
  1580. if ('object' === typeof concern) {
  1581. if ('undefined' !== typeof concern.j) this.options.j = concern.j;
  1582. if ('undefined' !== typeof concern.w) this.options.w = concern.w;
  1583. if ('undefined' !== typeof concern.wtimeout) this.options.wtimeout = concern.wtimeout;
  1584. } else {
  1585. this.options.w = 'm' === concern ? 'majority' : concern;
  1586. }
  1587. return this;
  1588. };
  1589. /**
  1590. * Specifies a time limit, in milliseconds, for the write concern.
  1591. * If `ms > 1`, it is maximum amount of time to wait for this write
  1592. * to propagate through the replica set before this operation fails.
  1593. * The default is `0`, which means no timeout.
  1594. *
  1595. * This option is only valid for operations that write to the database:
  1596. *
  1597. * - `deleteOne()`
  1598. * - `deleteMany()`
  1599. * - `findOneAndDelete()`
  1600. * - `findOneAndUpdate()`
  1601. * - `remove()`
  1602. * - `update()`
  1603. * - `updateOne()`
  1604. * - `updateMany()`
  1605. *
  1606. * Defaults to `wtimeout` value if it is specified in writeConcern
  1607. *
  1608. * ####Example:
  1609. *
  1610. * mquery().w(2).j(true).wtimeout(2000)
  1611. *
  1612. * @method wtimeout
  1613. * @memberOf Query
  1614. * @instance
  1615. * @param {number} ms number of milliseconds to wait
  1616. * @see mongodb https://docs.mongodb.com/manual/reference/write-concern/#wtimeout
  1617. * @return {Query} this
  1618. * @api public
  1619. */
  1620. Query.prototype.wtimeout = Query.prototype.wTimeout = function wtimeout(ms) {
  1621. this.options.wtimeout = ms;
  1622. return this;
  1623. };
  1624. /**
  1625. * Merges another Query or conditions object into this one.
  1626. *
  1627. * When a Query is passed, conditions, field selection and options are merged.
  1628. *
  1629. * @param {Query|Object} source
  1630. * @return {Query} this
  1631. */
  1632. Query.prototype.merge = function(source) {
  1633. if (!source)
  1634. return this;
  1635. if (!Query.canMerge(source))
  1636. throw new TypeError('Invalid argument. Expected instanceof mquery or plain object');
  1637. if (source instanceof Query) {
  1638. // if source has a feature, apply it to ourselves
  1639. if (source._conditions) {
  1640. utils.merge(this._conditions, source._conditions);
  1641. }
  1642. if (source._fields) {
  1643. this._fields || (this._fields = {});
  1644. utils.merge(this._fields, source._fields);
  1645. }
  1646. if (source.options) {
  1647. this.options || (this.options = {});
  1648. utils.merge(this.options, source.options);
  1649. }
  1650. if (source._update) {
  1651. this._update || (this._update = {});
  1652. utils.mergeClone(this._update, source._update);
  1653. }
  1654. if (source._distinct) {
  1655. this._distinct = source._distinct;
  1656. }
  1657. return this;
  1658. }
  1659. // plain object
  1660. utils.merge(this._conditions, source);
  1661. return this;
  1662. };
  1663. /**
  1664. * Finds documents.
  1665. *
  1666. * Passing a `callback` executes the query.
  1667. *
  1668. * ####Example
  1669. *
  1670. * query.find()
  1671. * query.find(callback)
  1672. * query.find({ name: 'Burning Lights' }, callback)
  1673. *
  1674. * @param {Object} [criteria] mongodb selector
  1675. * @param {Function} [callback]
  1676. * @return {Query} this
  1677. * @api public
  1678. */
  1679. Query.prototype.find = function(criteria, callback) {
  1680. this.op = 'find';
  1681. if ('function' === typeof criteria) {
  1682. callback = criteria;
  1683. criteria = undefined;
  1684. } else if (Query.canMerge(criteria)) {
  1685. this.merge(criteria);
  1686. }
  1687. if (!callback) return this;
  1688. var conds = this._conditions;
  1689. var options = this._optionsForExec();
  1690. if (this.$useProjection) {
  1691. options.projection = this._fieldsForExec();
  1692. } else {
  1693. options.fields = this._fieldsForExec();
  1694. }
  1695. debug('find', this._collection.collectionName, conds, options);
  1696. callback = this._wrapCallback('find', callback, {
  1697. conditions: conds,
  1698. options: options
  1699. });
  1700. this._collection.find(conds, options, utils.tick(callback));
  1701. return this;
  1702. };
  1703. /**
  1704. * Returns the query cursor
  1705. *
  1706. * ####Examples
  1707. *
  1708. * query.find().cursor();
  1709. * query.cursor({ name: 'Burning Lights' });
  1710. *
  1711. * @param {Object} [criteria] mongodb selector
  1712. * @return {Object} cursor
  1713. * @api public
  1714. */
  1715. Query.prototype.cursor = function cursor(criteria) {
  1716. if (this.op) {
  1717. if (this.op !== 'find') {
  1718. throw new TypeError('.cursor only support .find method');
  1719. }
  1720. } else {
  1721. this.find(criteria);
  1722. }
  1723. var conds = this._conditions;
  1724. var options = this._optionsForExec();
  1725. if (this.$useProjection) {
  1726. options.projection = this._fieldsForExec();
  1727. } else {
  1728. options.fields = this._fieldsForExec();
  1729. }
  1730. debug('findCursor', this._collection.collectionName, conds, options);
  1731. return this._collection.findCursor(conds, options);
  1732. };
  1733. /**
  1734. * Executes the query as a findOne() operation.
  1735. *
  1736. * Passing a `callback` executes the query.
  1737. *
  1738. * ####Example
  1739. *
  1740. * query.findOne().where('name', /^Burning/);
  1741. *
  1742. * query.findOne({ name: /^Burning/ })
  1743. *
  1744. * query.findOne({ name: /^Burning/ }, callback); // executes
  1745. *
  1746. * query.findOne(function (err, doc) {
  1747. * if (err) return handleError(err);
  1748. * if (doc) {
  1749. * // doc may be null if no document matched
  1750. *
  1751. * }
  1752. * });
  1753. *
  1754. * @param {Object|Query} [criteria] mongodb selector
  1755. * @param {Function} [callback]
  1756. * @return {Query} this
  1757. * @api public
  1758. */
  1759. Query.prototype.findOne = function(criteria, callback) {
  1760. this.op = 'findOne';
  1761. if ('function' === typeof criteria) {
  1762. callback = criteria;
  1763. criteria = undefined;
  1764. } else if (Query.canMerge(criteria)) {
  1765. this.merge(criteria);
  1766. }
  1767. if (!callback) return this;
  1768. var conds = this._conditions;
  1769. var options = this._optionsForExec();
  1770. if (this.$useProjection) {
  1771. options.projection = this._fieldsForExec();
  1772. } else {
  1773. options.fields = this._fieldsForExec();
  1774. }
  1775. debug('findOne', this._collection.collectionName, conds, options);
  1776. callback = this._wrapCallback('findOne', callback, {
  1777. conditions: conds,
  1778. options: options
  1779. });
  1780. this._collection.findOne(conds, options, utils.tick(callback));
  1781. return this;
  1782. };
  1783. /**
  1784. * Exectues the query as a count() operation.
  1785. *
  1786. * Passing a `callback` executes the query.
  1787. *
  1788. * ####Example
  1789. *
  1790. * query.count().where('color', 'black').exec(callback);
  1791. *
  1792. * query.count({ color: 'black' }).count(callback)
  1793. *
  1794. * query.count({ color: 'black' }, callback)
  1795. *
  1796. * query.where('color', 'black').count(function (err, count) {
  1797. * if (err) return handleError(err);
  1798. * console.log('there are %d kittens', count);
  1799. * })
  1800. *
  1801. * @param {Object} [criteria] mongodb selector
  1802. * @param {Function} [callback]
  1803. * @return {Query} this
  1804. * @see mongodb http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Count
  1805. * @api public
  1806. */
  1807. Query.prototype.count = function(criteria, callback) {
  1808. this.op = 'count';
  1809. this._validate();
  1810. if ('function' === typeof criteria) {
  1811. callback = criteria;
  1812. criteria = undefined;
  1813. } else if (Query.canMerge(criteria)) {
  1814. this.merge(criteria);
  1815. }
  1816. if (!callback) return this;
  1817. var conds = this._conditions,
  1818. options = this._optionsForExec();
  1819. debug('count', this._collection.collectionName, conds, options);
  1820. callback = this._wrapCallback('count', callback, {
  1821. conditions: conds,
  1822. options: options
  1823. });
  1824. this._collection.count(conds, options, utils.tick(callback));
  1825. return this;
  1826. };
  1827. /**
  1828. * Declares or executes a distinct() operation.
  1829. *
  1830. * Passing a `callback` executes the query.
  1831. *
  1832. * ####Example
  1833. *
  1834. * distinct(criteria, field, fn)
  1835. * distinct(criteria, field)
  1836. * distinct(field, fn)
  1837. * distinct(field)
  1838. * distinct(fn)
  1839. * distinct()
  1840. *
  1841. * @param {Object|Query} [criteria]
  1842. * @param {String} [field]
  1843. * @param {Function} [callback]
  1844. * @return {Query} this
  1845. * @see mongodb http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Distinct
  1846. * @api public
  1847. */
  1848. Query.prototype.distinct = function(criteria, field, callback) {
  1849. this.op = 'distinct';
  1850. this._validate();
  1851. if (!callback) {
  1852. switch (typeof field) {
  1853. case 'function':
  1854. callback = field;
  1855. if ('string' == typeof criteria) {
  1856. field = criteria;
  1857. criteria = undefined;
  1858. }
  1859. break;
  1860. case 'undefined':
  1861. case 'string':
  1862. break;
  1863. default:
  1864. throw new TypeError('Invalid `field` argument. Must be string or function');
  1865. }
  1866. switch (typeof criteria) {
  1867. case 'function':
  1868. callback = criteria;
  1869. criteria = field = undefined;
  1870. break;
  1871. case 'string':
  1872. field = criteria;
  1873. criteria = undefined;
  1874. break;
  1875. }
  1876. }
  1877. if ('string' == typeof field) {
  1878. this._distinct = field;
  1879. }
  1880. if (Query.canMerge(criteria)) {
  1881. this.merge(criteria);
  1882. }
  1883. if (!callback) {
  1884. return this;
  1885. }
  1886. if (!this._distinct) {
  1887. throw new Error('No value for `distinct` has been declared');
  1888. }
  1889. var conds = this._conditions,
  1890. options = this._optionsForExec();
  1891. debug('distinct', this._collection.collectionName, conds, options);
  1892. callback = this._wrapCallback('distinct', callback, {
  1893. conditions: conds,
  1894. options: options
  1895. });
  1896. this._collection.distinct(this._distinct, conds, options, utils.tick(callback));
  1897. return this;
  1898. };
  1899. /**
  1900. * Declare and/or execute this query as an update() operation. By default,
  1901. * `update()` only modifies the _first_ document that matches `criteria`.
  1902. *
  1903. * _All paths passed that are not $atomic operations will become $set ops._
  1904. *
  1905. * ####Example
  1906. *
  1907. * mquery({ _id: id }).update({ title: 'words' }, ...)
  1908. *
  1909. * becomes
  1910. *
  1911. * collection.update({ _id: id }, { $set: { title: 'words' }}, ...)
  1912. *
  1913. * ####Note
  1914. *
  1915. * Passing an empty object `{}` as the doc will result in a no-op unless the `overwrite` option is passed. Without the `overwrite` option set, the update operation will be ignored and the callback executed without sending the command to MongoDB so as to prevent accidently overwritting documents in the collection.
  1916. *
  1917. * ####Note
  1918. *
  1919. * The operation is only executed when a callback is passed. To force execution without a callback (which would be an unsafe write), we must first call update() and then execute it by using the `exec()` method.
  1920. *
  1921. * var q = mquery(collection).where({ _id: id });
  1922. * q.update({ $set: { name: 'bob' }}).update(); // not executed
  1923. *
  1924. * var q = mquery(collection).where({ _id: id });
  1925. * q.update({ $set: { name: 'bob' }}).exec(); // executed as unsafe
  1926. *
  1927. * // keys that are not $atomic ops become $set.
  1928. * // this executes the same command as the previous example.
  1929. * q.update({ name: 'bob' }).where({ _id: id }).exec();
  1930. *
  1931. * var q = mquery(collection).update(); // not executed
  1932. *
  1933. * // overwriting with empty docs
  1934. * var q.where({ _id: id }).setOptions({ overwrite: true })
  1935. * q.update({ }, callback); // executes
  1936. *
  1937. * // multi update with overwrite to empty doc
  1938. * var q = mquery(collection).where({ _id: id });
  1939. * q.setOptions({ multi: true, overwrite: true })
  1940. * q.update({ });
  1941. * q.update(callback); // executed
  1942. *
  1943. * // multi updates
  1944. * mquery()
  1945. * .collection(coll)
  1946. * .update({ name: /^match/ }, { $set: { arr: [] }}, { multi: true }, callback)
  1947. * // more multi updates
  1948. * mquery({ })
  1949. * .collection(coll)
  1950. * .setOptions({ multi: true })
  1951. * .update({ $set: { arr: [] }}, callback)
  1952. *
  1953. * // single update by default
  1954. * mquery({ email: 'address@example.com' })
  1955. * .collection(coll)
  1956. * .update({ $inc: { counter: 1 }}, callback)
  1957. *
  1958. * // summary
  1959. * update(criteria, doc, opts, cb) // executes
  1960. * update(criteria, doc, opts)
  1961. * update(criteria, doc, cb) // executes
  1962. * update(criteria, doc)
  1963. * update(doc, cb) // executes
  1964. * update(doc)
  1965. * update(cb) // executes
  1966. * update(true) // executes (unsafe write)
  1967. * update()
  1968. *
  1969. * @param {Object} [criteria]
  1970. * @param {Object} [doc] the update command
  1971. * @param {Object} [options]
  1972. * @param {Function} [callback]
  1973. * @return {Query} this
  1974. * @api public
  1975. */
  1976. Query.prototype.update = function update(criteria, doc, options, callback) {
  1977. var force;
  1978. switch (arguments.length) {
  1979. case 3:
  1980. if ('function' == typeof options) {
  1981. callback = options;
  1982. options = undefined;
  1983. }
  1984. break;
  1985. case 2:
  1986. if ('function' == typeof doc) {
  1987. callback = doc;
  1988. doc = criteria;
  1989. criteria = undefined;
  1990. }
  1991. break;
  1992. case 1:
  1993. switch (typeof criteria) {
  1994. case 'function':
  1995. callback = criteria;
  1996. criteria = options = doc = undefined;
  1997. break;
  1998. case 'boolean':
  1999. // execution with no callback (unsafe write)
  2000. force = criteria;
  2001. criteria = undefined;
  2002. break;
  2003. default:
  2004. doc = criteria;
  2005. criteria = options = undefined;
  2006. break;
  2007. }
  2008. }
  2009. return _update(this, 'update', criteria, doc, options, force, callback);
  2010. };
  2011. /**
  2012. * Declare and/or execute this query as an `updateMany()` operation. Identical
  2013. * to `update()` except `updateMany()` will update _all_ documents that match
  2014. * `criteria`, rather than just the first one.
  2015. *
  2016. * _All paths passed that are not $atomic operations will become $set ops._
  2017. *
  2018. * ####Example
  2019. *
  2020. * // Update every document whose `title` contains 'test'
  2021. * mquery().updateMany({ title: /test/ }, { year: 2017 })
  2022. *
  2023. * @param {Object} [criteria]
  2024. * @param {Object} [doc] the update command
  2025. * @param {Object} [options]
  2026. * @param {Function} [callback]
  2027. * @return {Query} this
  2028. * @api public
  2029. */
  2030. Query.prototype.updateMany = function updateMany(criteria, doc, options, callback) {
  2031. var force;
  2032. switch (arguments.length) {
  2033. case 3:
  2034. if ('function' == typeof options) {
  2035. callback = options;
  2036. options = undefined;
  2037. }
  2038. break;
  2039. case 2:
  2040. if ('function' == typeof doc) {
  2041. callback = doc;
  2042. doc = criteria;
  2043. criteria = undefined;
  2044. }
  2045. break;
  2046. case 1:
  2047. switch (typeof criteria) {
  2048. case 'function':
  2049. callback = criteria;
  2050. criteria = options = doc = undefined;
  2051. break;
  2052. case 'boolean':
  2053. // execution with no callback (unsafe write)
  2054. force = criteria;
  2055. criteria = undefined;
  2056. break;
  2057. default:
  2058. doc = criteria;
  2059. criteria = options = undefined;
  2060. break;
  2061. }
  2062. }
  2063. return _update(this, 'updateMany', criteria, doc, options, force, callback);
  2064. };
  2065. /**
  2066. * Declare and/or execute this query as an `updateOne()` operation. Identical
  2067. * to `update()` except `updateOne()` will _always_ update just one document,
  2068. * regardless of the `multi` option.
  2069. *
  2070. * _All paths passed that are not $atomic operations will become $set ops._
  2071. *
  2072. * ####Example
  2073. *
  2074. * // Update the first document whose `title` contains 'test'
  2075. * mquery().updateMany({ title: /test/ }, { year: 2017 })
  2076. *
  2077. * @param {Object} [criteria]
  2078. * @param {Object} [doc] the update command
  2079. * @param {Object} [options]
  2080. * @param {Function} [callback]
  2081. * @return {Query} this
  2082. * @api public
  2083. */
  2084. Query.prototype.updateOne = function updateOne(criteria, doc, options, callback) {
  2085. var force;
  2086. switch (arguments.length) {
  2087. case 3:
  2088. if ('function' == typeof options) {
  2089. callback = options;
  2090. options = undefined;
  2091. }
  2092. break;
  2093. case 2:
  2094. if ('function' == typeof doc) {
  2095. callback = doc;
  2096. doc = criteria;
  2097. criteria = undefined;
  2098. }
  2099. break;
  2100. case 1:
  2101. switch (typeof criteria) {
  2102. case 'function':
  2103. callback = criteria;
  2104. criteria = options = doc = undefined;
  2105. break;
  2106. case 'boolean':
  2107. // execution with no callback (unsafe write)
  2108. force = criteria;
  2109. criteria = undefined;
  2110. break;
  2111. default:
  2112. doc = criteria;
  2113. criteria = options = undefined;
  2114. break;
  2115. }
  2116. }
  2117. return _update(this, 'updateOne', criteria, doc, options, force, callback);
  2118. };
  2119. /**
  2120. * Declare and/or execute this query as an `replaceOne()` operation. Similar
  2121. * to `updateOne()`, except `replaceOne()` is not allowed to use atomic
  2122. * modifiers (`$set`, `$push`, etc.). Calling `replaceOne()` will always
  2123. * replace the existing doc.
  2124. *
  2125. * ####Example
  2126. *
  2127. * // Replace the document with `_id` 1 with `{ _id: 1, year: 2017 }`
  2128. * mquery().replaceOne({ _id: 1 }, { year: 2017 })
  2129. *
  2130. * @param {Object} [criteria]
  2131. * @param {Object} [doc] the update command
  2132. * @param {Object} [options]
  2133. * @param {Function} [callback]
  2134. * @return {Query} this
  2135. * @api public
  2136. */
  2137. Query.prototype.replaceOne = function replaceOne(criteria, doc, options, callback) {
  2138. var force;
  2139. switch (arguments.length) {
  2140. case 3:
  2141. if ('function' == typeof options) {
  2142. callback = options;
  2143. options = undefined;
  2144. }
  2145. break;
  2146. case 2:
  2147. if ('function' == typeof doc) {
  2148. callback = doc;
  2149. doc = criteria;
  2150. criteria = undefined;
  2151. }
  2152. break;
  2153. case 1:
  2154. switch (typeof criteria) {
  2155. case 'function':
  2156. callback = criteria;
  2157. criteria = options = doc = undefined;
  2158. break;
  2159. case 'boolean':
  2160. // execution with no callback (unsafe write)
  2161. force = criteria;
  2162. criteria = undefined;
  2163. break;
  2164. default:
  2165. doc = criteria;
  2166. criteria = options = undefined;
  2167. break;
  2168. }
  2169. }
  2170. this.setOptions({ overwrite: true });
  2171. return _update(this, 'replaceOne', criteria, doc, options, force, callback);
  2172. };
  2173. /*!
  2174. * Internal helper for update, updateMany, updateOne
  2175. */
  2176. function _update(query, op, criteria, doc, options, force, callback) {
  2177. query.op = op;
  2178. if (Query.canMerge(criteria)) {
  2179. query.merge(criteria);
  2180. }
  2181. if (doc) {
  2182. query._mergeUpdate(doc);
  2183. }
  2184. if (utils.isObject(options)) {
  2185. // { overwrite: true }
  2186. query.setOptions(options);
  2187. }
  2188. // we are done if we don't have callback and they are
  2189. // not forcing an unsafe write.
  2190. if (!(force || callback)) {
  2191. return query;
  2192. }
  2193. if (!query._update ||
  2194. !query.options.overwrite && 0 === utils.keys(query._update).length) {
  2195. callback && utils.soon(callback.bind(null, null, 0));
  2196. return query;
  2197. }
  2198. options = query._optionsForExec();
  2199. if (!callback) options.safe = false;
  2200. criteria = query._conditions;
  2201. doc = query._updateForExec();
  2202. debug('update', query._collection.collectionName, criteria, doc, options);
  2203. callback = query._wrapCallback(op, callback, {
  2204. conditions: criteria,
  2205. doc: doc,
  2206. options: options
  2207. });
  2208. query._collection[op](criteria, doc, options, utils.tick(callback));
  2209. return query;
  2210. }
  2211. /**
  2212. * Declare and/or execute this query as a remove() operation.
  2213. *
  2214. * ####Example
  2215. *
  2216. * mquery(collection).remove({ artist: 'Anne Murray' }, callback)
  2217. *
  2218. * ####Note
  2219. *
  2220. * The operation is only executed when a callback is passed. To force execution without a callback (which would be an unsafe write), we must first call remove() and then execute it by using the `exec()` method.
  2221. *
  2222. * // not executed
  2223. * var query = mquery(collection).remove({ name: 'Anne Murray' })
  2224. *
  2225. * // executed
  2226. * mquery(collection).remove({ name: 'Anne Murray' }, callback)
  2227. * mquery(collection).remove({ name: 'Anne Murray' }).remove(callback)
  2228. *
  2229. * // executed without a callback (unsafe write)
  2230. * query.exec()
  2231. *
  2232. * // summary
  2233. * query.remove(conds, fn); // executes
  2234. * query.remove(conds)
  2235. * query.remove(fn) // executes
  2236. * query.remove()
  2237. *
  2238. * @param {Object|Query} [criteria] mongodb selector
  2239. * @param {Function} [callback]
  2240. * @return {Query} this
  2241. * @api public
  2242. */
  2243. Query.prototype.remove = function(criteria, callback) {
  2244. this.op = 'remove';
  2245. var force;
  2246. if ('function' === typeof criteria) {
  2247. callback = criteria;
  2248. criteria = undefined;
  2249. } else if (Query.canMerge(criteria)) {
  2250. this.merge(criteria);
  2251. } else if (true === criteria) {
  2252. force = criteria;
  2253. criteria = undefined;
  2254. }
  2255. if (!(force || callback))
  2256. return this;
  2257. var options = this._optionsForExec();
  2258. if (!callback) options.safe = false;
  2259. var conds = this._conditions;
  2260. debug('remove', this._collection.collectionName, conds, options);
  2261. callback = this._wrapCallback('remove', callback, {
  2262. conditions: conds,
  2263. options: options
  2264. });
  2265. this._collection.remove(conds, options, utils.tick(callback));
  2266. return this;
  2267. };
  2268. /**
  2269. * Declare and/or execute this query as a `deleteOne()` operation. Behaves like
  2270. * `remove()`, except for ignores the `justOne` option and always deletes at
  2271. * most one document.
  2272. *
  2273. * ####Example
  2274. *
  2275. * mquery(collection).deleteOne({ artist: 'Anne Murray' }, callback)
  2276. *
  2277. * @param {Object|Query} [criteria] mongodb selector
  2278. * @param {Function} [callback]
  2279. * @return {Query} this
  2280. * @api public
  2281. */
  2282. Query.prototype.deleteOne = function(criteria, callback) {
  2283. this.op = 'deleteOne';
  2284. var force;
  2285. if ('function' === typeof criteria) {
  2286. callback = criteria;
  2287. criteria = undefined;
  2288. } else if (Query.canMerge(criteria)) {
  2289. this.merge(criteria);
  2290. } else if (true === criteria) {
  2291. force = criteria;
  2292. criteria = undefined;
  2293. }
  2294. if (!(force || callback))
  2295. return this;
  2296. var options = this._optionsForExec();
  2297. if (!callback) options.safe = false;
  2298. delete options.justOne;
  2299. var conds = this._conditions;
  2300. debug('deleteOne', this._collection.collectionName, conds, options);
  2301. callback = this._wrapCallback('deleteOne', callback, {
  2302. conditions: conds,
  2303. options: options
  2304. });
  2305. this._collection.deleteOne(conds, options, utils.tick(callback));
  2306. return this;
  2307. };
  2308. /**
  2309. * Declare and/or execute this query as a `deleteMany()` operation. Behaves like
  2310. * `remove()`, except for ignores the `justOne` option and always deletes
  2311. * _every_ document that matches `criteria`.
  2312. *
  2313. * ####Example
  2314. *
  2315. * mquery(collection).deleteMany({ artist: 'Anne Murray' }, callback)
  2316. *
  2317. * @param {Object|Query} [criteria] mongodb selector
  2318. * @param {Function} [callback]
  2319. * @return {Query} this
  2320. * @api public
  2321. */
  2322. Query.prototype.deleteMany = function(criteria, callback) {
  2323. this.op = 'deleteMany';
  2324. var force;
  2325. if ('function' === typeof criteria) {
  2326. callback = criteria;
  2327. criteria = undefined;
  2328. } else if (Query.canMerge(criteria)) {
  2329. this.merge(criteria);
  2330. } else if (true === criteria) {
  2331. force = criteria;
  2332. criteria = undefined;
  2333. }
  2334. if (!(force || callback))
  2335. return this;
  2336. var options = this._optionsForExec();
  2337. if (!callback) options.safe = false;
  2338. delete options.justOne;
  2339. var conds = this._conditions;
  2340. debug('deleteOne', this._collection.collectionName, conds, options);
  2341. callback = this._wrapCallback('deleteOne', callback, {
  2342. conditions: conds,
  2343. options: options
  2344. });
  2345. this._collection.deleteMany(conds, options, utils.tick(callback));
  2346. return this;
  2347. };
  2348. /**
  2349. * Issues a mongodb [findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) update command.
  2350. *
  2351. * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any) to the callback. The query executes immediately if `callback` is passed.
  2352. *
  2353. * ####Available options
  2354. *
  2355. * - `new`: bool - true to return the modified document rather than the original. defaults to true
  2356. * - `upsert`: bool - creates the object if it doesn't exist. defaults to false.
  2357. * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
  2358. *
  2359. * ####Examples
  2360. *
  2361. * query.findOneAndUpdate(conditions, update, options, callback) // executes
  2362. * query.findOneAndUpdate(conditions, update, options) // returns Query
  2363. * query.findOneAndUpdate(conditions, update, callback) // executes
  2364. * query.findOneAndUpdate(conditions, update) // returns Query
  2365. * query.findOneAndUpdate(update, callback) // returns Query
  2366. * query.findOneAndUpdate(update) // returns Query
  2367. * query.findOneAndUpdate(callback) // executes
  2368. * query.findOneAndUpdate() // returns Query
  2369. *
  2370. * @param {Object|Query} [query]
  2371. * @param {Object} [doc]
  2372. * @param {Object} [options]
  2373. * @param {Function} [callback]
  2374. * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
  2375. * @return {Query} this
  2376. * @api public
  2377. */
  2378. Query.prototype.findOneAndUpdate = function(criteria, doc, options, callback) {
  2379. this.op = 'findOneAndUpdate';
  2380. this._validate();
  2381. switch (arguments.length) {
  2382. case 3:
  2383. if ('function' == typeof options) {
  2384. callback = options;
  2385. options = {};
  2386. }
  2387. break;
  2388. case 2:
  2389. if ('function' == typeof doc) {
  2390. callback = doc;
  2391. doc = criteria;
  2392. criteria = undefined;
  2393. }
  2394. options = undefined;
  2395. break;
  2396. case 1:
  2397. if ('function' == typeof criteria) {
  2398. callback = criteria;
  2399. criteria = options = doc = undefined;
  2400. } else {
  2401. doc = criteria;
  2402. criteria = options = undefined;
  2403. }
  2404. }
  2405. if (Query.canMerge(criteria)) {
  2406. this.merge(criteria);
  2407. }
  2408. // apply doc
  2409. if (doc) {
  2410. this._mergeUpdate(doc);
  2411. }
  2412. options && this.setOptions(options);
  2413. if (!callback) return this;
  2414. return this._findAndModify('update', callback);
  2415. };
  2416. /**
  2417. * Issues a mongodb [findAndModify](http://www.mongodb.org/display/DOCS/findAndModify+Command) remove command.
  2418. *
  2419. * Finds a matching document, removes it, passing the found document (if any) to the callback. Executes immediately if `callback` is passed.
  2420. *
  2421. * ####Available options
  2422. *
  2423. * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update
  2424. *
  2425. * ####Examples
  2426. *
  2427. * A.where().findOneAndRemove(conditions, options, callback) // executes
  2428. * A.where().findOneAndRemove(conditions, options) // return Query
  2429. * A.where().findOneAndRemove(conditions, callback) // executes
  2430. * A.where().findOneAndRemove(conditions) // returns Query
  2431. * A.where().findOneAndRemove(callback) // executes
  2432. * A.where().findOneAndRemove() // returns Query
  2433. * A.where().findOneAndDelete() // alias of .findOneAndRemove()
  2434. *
  2435. * @param {Object} [conditions]
  2436. * @param {Object} [options]
  2437. * @param {Function} [callback]
  2438. * @return {Query} this
  2439. * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command
  2440. * @api public
  2441. */
  2442. Query.prototype.findOneAndRemove = Query.prototype.findOneAndDelete = function(conditions, options, callback) {
  2443. this.op = 'findOneAndRemove';
  2444. this._validate();
  2445. if ('function' == typeof options) {
  2446. callback = options;
  2447. options = undefined;
  2448. } else if ('function' == typeof conditions) {
  2449. callback = conditions;
  2450. conditions = undefined;
  2451. }
  2452. // apply conditions
  2453. if (Query.canMerge(conditions)) {
  2454. this.merge(conditions);
  2455. }
  2456. // apply options
  2457. options && this.setOptions(options);
  2458. if (!callback) return this;
  2459. return this._findAndModify('remove', callback);
  2460. };
  2461. /**
  2462. * _findAndModify
  2463. *
  2464. * @param {String} type - either "remove" or "update"
  2465. * @param {Function} callback
  2466. * @api private
  2467. */
  2468. Query.prototype._findAndModify = function(type, callback) {
  2469. assert.equal('function', typeof callback);
  2470. var options = this._optionsForExec();
  2471. var fields;
  2472. var doc;
  2473. if ('remove' == type) {
  2474. options.remove = true;
  2475. } else {
  2476. if (!('new' in options)) options.new = true;
  2477. if (!('upsert' in options)) options.upsert = false;
  2478. doc = this._updateForExec();
  2479. if (!doc) {
  2480. if (options.upsert) {
  2481. // still need to do the upsert to empty doc
  2482. doc = { $set: {} };
  2483. } else {
  2484. return this.findOne(callback);
  2485. }
  2486. }
  2487. }
  2488. fields = this._fieldsForExec();
  2489. if (fields != null) {
  2490. if (this.$useProjection) {
  2491. options.projection = this._fieldsForExec();
  2492. } else {
  2493. options.fields = this._fieldsForExec();
  2494. }
  2495. }
  2496. var conds = this._conditions;
  2497. debug('findAndModify', this._collection.collectionName, conds, doc, options);
  2498. callback = this._wrapCallback('findAndModify', callback, {
  2499. conditions: conds,
  2500. doc: doc,
  2501. options: options
  2502. });
  2503. this._collection.findAndModify(conds, doc, options, utils.tick(callback));
  2504. return this;
  2505. };
  2506. /**
  2507. * Wrap callback to add tracing
  2508. *
  2509. * @param {Function} callback
  2510. * @param {Object} [queryInfo]
  2511. * @api private
  2512. */
  2513. Query.prototype._wrapCallback = function(method, callback, queryInfo) {
  2514. var traceFunction = this._traceFunction || Query.traceFunction;
  2515. if (traceFunction) {
  2516. queryInfo.collectionName = this._collection.collectionName;
  2517. var traceCallback = traceFunction &&
  2518. traceFunction.call(null, method, queryInfo, this);
  2519. var startTime = new Date().getTime();
  2520. return function wrapperCallback(err, result) {
  2521. if (traceCallback) {
  2522. var millis = new Date().getTime() - startTime;
  2523. traceCallback.call(null, err, result, millis);
  2524. }
  2525. if (callback) {
  2526. callback.apply(null, arguments);
  2527. }
  2528. };
  2529. }
  2530. return callback;
  2531. };
  2532. /**
  2533. * Add trace function that gets called when the query is executed.
  2534. * The function will be called with (method, queryInfo, query) and
  2535. * should return a callback function which will be called
  2536. * with (err, result, millis) when the query is complete.
  2537. *
  2538. * queryInfo is an object containing: {
  2539. * collectionName: <name of the collection>,
  2540. * conditions: <query criteria>,
  2541. * options: <comment, fields, readPreference, etc>,
  2542. * doc: [document to update, if applicable]
  2543. * }
  2544. *
  2545. * NOTE: Does not trace stream queries.
  2546. *
  2547. * @param {Function} traceFunction
  2548. * @return {Query} this
  2549. * @api public
  2550. */
  2551. Query.prototype.setTraceFunction = function(traceFunction) {
  2552. this._traceFunction = traceFunction;
  2553. return this;
  2554. };
  2555. /**
  2556. * Executes the query
  2557. *
  2558. * ####Examples
  2559. *
  2560. * query.exec();
  2561. * query.exec(callback);
  2562. * query.exec('update');
  2563. * query.exec('find', callback);
  2564. *
  2565. * @param {String|Function} [operation]
  2566. * @param {Function} [callback]
  2567. * @api public
  2568. */
  2569. Query.prototype.exec = function exec(op, callback) {
  2570. switch (typeof op) {
  2571. case 'function':
  2572. callback = op;
  2573. op = null;
  2574. break;
  2575. case 'string':
  2576. this.op = op;
  2577. break;
  2578. }
  2579. assert.ok(this.op, 'Missing query type: (find, update, etc)');
  2580. if ('update' == this.op || 'remove' == this.op) {
  2581. callback || (callback = true);
  2582. }
  2583. var _this = this;
  2584. if ('function' == typeof callback) {
  2585. this[this.op](callback);
  2586. } else {
  2587. return new Query.Promise(function(success, error) {
  2588. _this[_this.op](function(err, val) {
  2589. if (err) error(err);
  2590. else success(val);
  2591. success = error = null;
  2592. });
  2593. });
  2594. }
  2595. };
  2596. /**
  2597. * Returns a thunk which when called runs this.exec()
  2598. *
  2599. * The thunk receives a callback function which will be
  2600. * passed to `this.exec()`
  2601. *
  2602. * @return {Function}
  2603. * @api public
  2604. */
  2605. Query.prototype.thunk = function() {
  2606. var _this = this;
  2607. return function(cb) {
  2608. _this.exec(cb);
  2609. };
  2610. };
  2611. /**
  2612. * Executes the query returning a `Promise` which will be
  2613. * resolved with either the doc(s) or rejected with the error.
  2614. *
  2615. * @param {Function} [resolve]
  2616. * @param {Function} [reject]
  2617. * @return {Promise}
  2618. * @api public
  2619. */
  2620. Query.prototype.then = function(resolve, reject) {
  2621. var _this = this;
  2622. var promise = new Query.Promise(function(success, error) {
  2623. _this.exec(function(err, val) {
  2624. if (err) error(err);
  2625. else success(val);
  2626. success = error = null;
  2627. });
  2628. });
  2629. return promise.then(resolve, reject);
  2630. };
  2631. /**
  2632. * Returns a stream for the given find query.
  2633. *
  2634. * @throws Error if operation is not a find
  2635. * @returns {Stream} Node 0.8 style
  2636. */
  2637. Query.prototype.stream = function(streamOptions) {
  2638. if ('find' != this.op)
  2639. throw new Error('stream() is only available for find');
  2640. var conds = this._conditions;
  2641. var options = this._optionsForExec();
  2642. if (this.$useProjection) {
  2643. options.projection = this._fieldsForExec();
  2644. } else {
  2645. options.fields = this._fieldsForExec();
  2646. }
  2647. debug('stream', this._collection.collectionName, conds, options, streamOptions);
  2648. return this._collection.findStream(conds, options, streamOptions);
  2649. };
  2650. /**
  2651. * Determines if field selection has been made.
  2652. *
  2653. * @return {Boolean}
  2654. * @api public
  2655. */
  2656. Query.prototype.selected = function selected() {
  2657. return !!(this._fields && Object.keys(this._fields).length > 0);
  2658. };
  2659. /**
  2660. * Determines if inclusive field selection has been made.
  2661. *
  2662. * query.selectedInclusively() // false
  2663. * query.select('name')
  2664. * query.selectedInclusively() // true
  2665. * query.selectedExlusively() // false
  2666. *
  2667. * @returns {Boolean}
  2668. */
  2669. Query.prototype.selectedInclusively = function selectedInclusively() {
  2670. if (!this._fields) return false;
  2671. var keys = Object.keys(this._fields);
  2672. if (0 === keys.length) return false;
  2673. for (var i = 0; i < keys.length; ++i) {
  2674. var key = keys[i];
  2675. if (0 === this._fields[key]) return false;
  2676. if (this._fields[key] &&
  2677. typeof this._fields[key] === 'object' &&
  2678. this._fields[key].$meta) {
  2679. return false;
  2680. }
  2681. }
  2682. return true;
  2683. };
  2684. /**
  2685. * Determines if exclusive field selection has been made.
  2686. *
  2687. * query.selectedExlusively() // false
  2688. * query.select('-name')
  2689. * query.selectedExlusively() // true
  2690. * query.selectedInclusively() // false
  2691. *
  2692. * @returns {Boolean}
  2693. */
  2694. Query.prototype.selectedExclusively = function selectedExclusively() {
  2695. if (!this._fields) return false;
  2696. var keys = Object.keys(this._fields);
  2697. if (0 === keys.length) return false;
  2698. for (var i = 0; i < keys.length; ++i) {
  2699. var key = keys[i];
  2700. if (0 === this._fields[key]) return true;
  2701. }
  2702. return false;
  2703. };
  2704. /**
  2705. * Merges `doc` with the current update object.
  2706. *
  2707. * @param {Object} doc
  2708. */
  2709. Query.prototype._mergeUpdate = function(doc) {
  2710. if (!this._update) this._update = {};
  2711. if (doc instanceof Query) {
  2712. if (doc._update) {
  2713. utils.mergeClone(this._update, doc._update);
  2714. }
  2715. } else {
  2716. utils.mergeClone(this._update, doc);
  2717. }
  2718. };
  2719. /**
  2720. * Returns default options.
  2721. *
  2722. * @return {Object}
  2723. * @api private
  2724. */
  2725. Query.prototype._optionsForExec = function() {
  2726. var options = utils.clone(this.options);
  2727. return options;
  2728. };
  2729. /**
  2730. * Returns fields selection for this query.
  2731. *
  2732. * @return {Object}
  2733. * @api private
  2734. */
  2735. Query.prototype._fieldsForExec = function() {
  2736. return utils.clone(this._fields);
  2737. };
  2738. /**
  2739. * Return an update document with corrected $set operations.
  2740. *
  2741. * @api private
  2742. */
  2743. Query.prototype._updateForExec = function() {
  2744. var update = utils.clone(this._update),
  2745. ops = utils.keys(update),
  2746. i = ops.length,
  2747. ret = {};
  2748. while (i--) {
  2749. var op = ops[i];
  2750. if (this.options.overwrite) {
  2751. ret[op] = update[op];
  2752. continue;
  2753. }
  2754. if ('$' !== op[0]) {
  2755. // fix up $set sugar
  2756. if (!ret.$set) {
  2757. if (update.$set) {
  2758. ret.$set = update.$set;
  2759. } else {
  2760. ret.$set = {};
  2761. }
  2762. }
  2763. ret.$set[op] = update[op];
  2764. ops.splice(i, 1);
  2765. if (!~ops.indexOf('$set')) ops.push('$set');
  2766. } else if ('$set' === op) {
  2767. if (!ret.$set) {
  2768. ret[op] = update[op];
  2769. }
  2770. } else {
  2771. ret[op] = update[op];
  2772. }
  2773. }
  2774. this._compiledUpdate = ret;
  2775. return ret;
  2776. };
  2777. /**
  2778. * Make sure _path is set.
  2779. *
  2780. * @parmam {String} method
  2781. */
  2782. Query.prototype._ensurePath = function(method) {
  2783. if (!this._path) {
  2784. var msg = method + '() must be used after where() '
  2785. + 'when called with these arguments';
  2786. throw new Error(msg);
  2787. }
  2788. };
  2789. /*!
  2790. * Permissions
  2791. */
  2792. Query.permissions = require('./permissions');
  2793. Query._isPermitted = function(a, b) {
  2794. var denied = Query.permissions[b];
  2795. if (!denied) return true;
  2796. return true !== denied[a];
  2797. };
  2798. Query.prototype._validate = function(action) {
  2799. var fail;
  2800. var validator;
  2801. if (undefined === action) {
  2802. validator = Query.permissions[this.op];
  2803. if ('function' != typeof validator) return true;
  2804. fail = validator(this);
  2805. } else if (!Query._isPermitted(action, this.op)) {
  2806. fail = action;
  2807. }
  2808. if (fail) {
  2809. throw new Error(fail + ' cannot be used with ' + this.op);
  2810. }
  2811. };
  2812. /**
  2813. * Determines if `conds` can be merged using `mquery().merge()`
  2814. *
  2815. * @param {Object} conds
  2816. * @return {Boolean}
  2817. */
  2818. Query.canMerge = function(conds) {
  2819. return conds instanceof Query || utils.isObject(conds);
  2820. };
  2821. /**
  2822. * Set a trace function that will get called whenever a
  2823. * query is executed.
  2824. *
  2825. * See `setTraceFunction()` for details.
  2826. *
  2827. * @param {Object} conds
  2828. * @return {Boolean}
  2829. */
  2830. Query.setGlobalTraceFunction = function(traceFunction) {
  2831. Query.traceFunction = traceFunction;
  2832. };
  2833. /*!
  2834. * Exports.
  2835. */
  2836. Query.utils = utils;
  2837. Query.env = require('./env');
  2838. Query.Collection = require('./collection');
  2839. Query.BaseCollection = require('./collection/collection');
  2840. Query.Promise = require('bluebird');
  2841. module.exports = exports = Query;
  2842. // TODO
  2843. // test utils