index.js 93 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076
  1. var mquery = require('../');
  2. var assert = require('assert');
  3. /* global Map */
  4. describe('mquery', function() {
  5. var col;
  6. before(function(done) {
  7. // get the env specific collection interface
  8. require('./env').getCollection(function(err, collection) {
  9. assert.ifError(err);
  10. col = collection;
  11. done();
  12. });
  13. });
  14. after(function(done) {
  15. require('./env').dropCollection(done);
  16. });
  17. describe('mquery', function() {
  18. it('is a function', function() {
  19. assert.equal('function', typeof mquery);
  20. });
  21. it('creates instances with the `new` keyword', function() {
  22. assert.ok(mquery() instanceof mquery);
  23. });
  24. describe('defaults', function() {
  25. it('are set', function() {
  26. var m = mquery();
  27. assert.strictEqual(undefined, m.op);
  28. assert.deepEqual({}, m.options);
  29. });
  30. });
  31. describe('criteria', function() {
  32. it('if collection-like is used as collection', function() {
  33. var m = mquery(col);
  34. assert.equal(col, m._collection.collection);
  35. });
  36. it('non-collection-like is used as criteria', function() {
  37. var m = mquery({ works: true });
  38. assert.ok(!m._collection);
  39. assert.deepEqual({ works: true }, m._conditions);
  40. });
  41. });
  42. describe('options', function() {
  43. it('are merged when passed', function() {
  44. var m;
  45. m = mquery(col, { safe: true });
  46. assert.deepEqual({ safe: true }, m.options);
  47. m = mquery({ name: 'mquery' }, { safe: true });
  48. assert.deepEqual({ safe: true }, m.options);
  49. });
  50. });
  51. });
  52. describe('toConstructor', function() {
  53. it('creates subclasses of mquery', function() {
  54. var opts = { safe: { w: 'majority' }, readPreference: 'p' };
  55. var match = { name: 'test', count: { $gt: 101 }};
  56. var select = { name: 1, count: 0 };
  57. var update = { $set: { x: true }};
  58. var path = 'street';
  59. var q = mquery().setOptions(opts);
  60. q.where(match);
  61. q.select(select);
  62. q.update(update);
  63. q.where(path);
  64. q.find();
  65. var M = q.toConstructor();
  66. var m = M();
  67. assert.ok(m instanceof mquery);
  68. assert.deepEqual(opts, m.options);
  69. assert.deepEqual(match, m._conditions);
  70. assert.deepEqual(select, m._fields);
  71. assert.deepEqual(update, m._update);
  72. assert.equal(path, m._path);
  73. assert.equal('find', m.op);
  74. });
  75. });
  76. describe('setOptions', function() {
  77. it('calls associated methods', function() {
  78. var m = mquery();
  79. assert.equal(m._collection, null);
  80. m.setOptions({ collection: col });
  81. assert.equal(m._collection.collection, col);
  82. });
  83. it('directly sets option when no method exists', function() {
  84. var m = mquery();
  85. assert.equal(m.options.woot, null);
  86. m.setOptions({ woot: 'yay' });
  87. assert.equal(m.options.woot, 'yay');
  88. });
  89. it('is chainable', function() {
  90. var m = mquery(),
  91. n;
  92. n = m.setOptions();
  93. assert.equal(m, n);
  94. n = m.setOptions({ x: 1 });
  95. assert.equal(m, n);
  96. });
  97. });
  98. describe('collection', function() {
  99. it('sets the _collection', function() {
  100. var m = mquery();
  101. m.collection(col);
  102. assert.equal(m._collection.collection, col);
  103. });
  104. it('is chainable', function() {
  105. var m = mquery();
  106. var n = m.collection(col);
  107. assert.equal(m, n);
  108. });
  109. });
  110. describe('$where', function() {
  111. it('sets the $where condition', function() {
  112. var m = mquery();
  113. function go() {}
  114. m.$where(go);
  115. assert.ok(go === m._conditions.$where);
  116. });
  117. it('is chainable', function() {
  118. var m = mquery();
  119. var n = m.$where('x');
  120. assert.equal(m, n);
  121. });
  122. });
  123. describe('where', function() {
  124. it('without arguments', function() {
  125. var m = mquery();
  126. m.where();
  127. assert.deepEqual({}, m._conditions);
  128. });
  129. it('with non-string/object argument', function() {
  130. var m = mquery();
  131. assert.throws(function() {
  132. m.where([]);
  133. }, /path must be a string or object/);
  134. });
  135. describe('with one argument', function() {
  136. it('that is an object', function() {
  137. var m = mquery();
  138. m.where({ name: 'flawed' });
  139. assert.strictEqual(m._conditions.name, 'flawed');
  140. });
  141. it('that is a query', function() {
  142. var m = mquery({ name: 'first' });
  143. var n = mquery({ name: 'changed' });
  144. m.where(n);
  145. assert.strictEqual(m._conditions.name, 'changed');
  146. });
  147. it('that is a string', function() {
  148. var m = mquery();
  149. m.where('name');
  150. assert.equal('name', m._path);
  151. assert.strictEqual(m._conditions.name, undefined);
  152. });
  153. });
  154. it('with two arguments', function() {
  155. var m = mquery();
  156. m.where('name', 'The Great Pumpkin');
  157. assert.equal('name', m._path);
  158. assert.strictEqual(m._conditions.name, 'The Great Pumpkin');
  159. });
  160. it('is chainable', function() {
  161. var m = mquery(),
  162. n;
  163. n = m.where('x', 'y');
  164. assert.equal(m, n);
  165. n = m.where();
  166. assert.equal(m, n);
  167. });
  168. });
  169. describe('equals', function() {
  170. it('must be called after where()', function() {
  171. var m = mquery();
  172. assert.throws(function() {
  173. m.equals();
  174. }, /must be used after where/);
  175. });
  176. it('sets value of path set with where()', function() {
  177. var m = mquery();
  178. m.where('age').equals(1000);
  179. assert.deepEqual({ age: 1000 }, m._conditions);
  180. });
  181. it('is chainable', function() {
  182. var m = mquery();
  183. var n = m.where('x').equals(3);
  184. assert.equal(m, n);
  185. });
  186. });
  187. describe('eq', function() {
  188. it('is alias of equals', function() {
  189. var m = mquery();
  190. m.where('age').eq(1000);
  191. assert.deepEqual({ age: 1000 }, m._conditions);
  192. });
  193. });
  194. describe('or', function() {
  195. it('pushes onto the internal $or condition', function() {
  196. var m = mquery();
  197. m.or({ 'Nightmare Before Christmas': true });
  198. assert.deepEqual([{'Nightmare Before Christmas': true }], m._conditions.$or);
  199. });
  200. it('allows passing arrays', function() {
  201. var m = mquery();
  202. var arg = [{ 'Nightmare Before Christmas': true }, { x: 1 }];
  203. m.or(arg);
  204. assert.deepEqual(arg, m._conditions.$or);
  205. });
  206. it('allows calling multiple times', function() {
  207. var m = mquery();
  208. var arg = [{ looper: true }, { x: 1 }];
  209. m.or(arg);
  210. m.or({ y: 1 });
  211. m.or([{ w: 'oo' }, { z: 'oo'} ]);
  212. assert.deepEqual([{looper:true},{x:1},{y:1},{w:'oo'},{z:'oo'}], m._conditions.$or);
  213. });
  214. it('is chainable', function() {
  215. var m = mquery();
  216. m.or({ o: 'k'}).where('name', 'table');
  217. assert.deepEqual({ name: 'table', $or: [{ o: 'k' }] }, m._conditions);
  218. });
  219. });
  220. describe('nor', function() {
  221. it('pushes onto the internal $nor condition', function() {
  222. var m = mquery();
  223. m.nor({ 'Nightmare Before Christmas': true });
  224. assert.deepEqual([{'Nightmare Before Christmas': true }], m._conditions.$nor);
  225. });
  226. it('allows passing arrays', function() {
  227. var m = mquery();
  228. var arg = [{ 'Nightmare Before Christmas': true }, { x: 1 }];
  229. m.nor(arg);
  230. assert.deepEqual(arg, m._conditions.$nor);
  231. });
  232. it('allows calling multiple times', function() {
  233. var m = mquery();
  234. var arg = [{ looper: true }, { x: 1 }];
  235. m.nor(arg);
  236. m.nor({ y: 1 });
  237. m.nor([{ w: 'oo' }, { z: 'oo'} ]);
  238. assert.deepEqual([{looper:true},{x:1},{y:1},{w:'oo'},{z:'oo'}], m._conditions.$nor);
  239. });
  240. it('is chainable', function() {
  241. var m = mquery();
  242. m.nor({ o: 'k'}).where('name', 'table');
  243. assert.deepEqual({ name: 'table', $nor: [{ o: 'k' }] }, m._conditions);
  244. });
  245. });
  246. describe('and', function() {
  247. it('pushes onto the internal $and condition', function() {
  248. var m = mquery();
  249. m.and({ 'Nightmare Before Christmas': true });
  250. assert.deepEqual([{'Nightmare Before Christmas': true }], m._conditions.$and);
  251. });
  252. it('allows passing arrays', function() {
  253. var m = mquery();
  254. var arg = [{ 'Nightmare Before Christmas': true }, { x: 1 }];
  255. m.and(arg);
  256. assert.deepEqual(arg, m._conditions.$and);
  257. });
  258. it('allows calling multiple times', function() {
  259. var m = mquery();
  260. var arg = [{ looper: true }, { x: 1 }];
  261. m.and(arg);
  262. m.and({ y: 1 });
  263. m.and([{ w: 'oo' }, { z: 'oo'} ]);
  264. assert.deepEqual([{looper:true},{x:1},{y:1},{w:'oo'},{z:'oo'}], m._conditions.$and);
  265. });
  266. it('is chainable', function() {
  267. var m = mquery();
  268. m.and({ o: 'k'}).where('name', 'table');
  269. assert.deepEqual({ name: 'table', $and: [{ o: 'k' }] }, m._conditions);
  270. });
  271. });
  272. function generalCondition(type) {
  273. return function() {
  274. it('accepts 2 args', function() {
  275. var m = mquery()[type]('count', 3);
  276. var check = {};
  277. check['$' + type] = 3;
  278. assert.deepEqual(m._conditions.count, check);
  279. });
  280. it('uses previously set `where` path if 1 arg passed', function() {
  281. var m = mquery().where('count')[type](3);
  282. var check = {};
  283. check['$' + type] = 3;
  284. assert.deepEqual(m._conditions.count, check);
  285. });
  286. it('throws if 1 arg was passed but no previous `where` was used', function() {
  287. assert.throws(function() {
  288. mquery()[type](3);
  289. }, /must be used after where/);
  290. });
  291. it('is chainable', function() {
  292. var m = mquery().where('count')[type](3).where('x', 8);
  293. var check = {x: 8, count: {}};
  294. check.count['$' + type] = 3;
  295. assert.deepEqual(m._conditions, check);
  296. });
  297. it('overwrites previous value', function() {
  298. var m = mquery().where('count')[type](3)[type](8);
  299. var check = {};
  300. check['$' + type] = 8;
  301. assert.deepEqual(m._conditions.count, check);
  302. });
  303. };
  304. }
  305. 'gt gte lt lte ne in nin regex size maxDistance minDistance'.split(' ').forEach(function(type) {
  306. describe(type, generalCondition(type));
  307. });
  308. describe('mod', function() {
  309. describe('with 1 argument', function() {
  310. it('requires a previous where()', function() {
  311. assert.throws(function() {
  312. mquery().mod([30, 10]);
  313. }, /must be used after where/);
  314. });
  315. it('works', function() {
  316. var m = mquery().where('madmen').mod([10,20]);
  317. assert.deepEqual(m._conditions, { madmen: { $mod: [10,20] }});
  318. });
  319. });
  320. describe('with 2 arguments and second is non-Array', function() {
  321. it('requires a previous where()', function() {
  322. assert.throws(function() {
  323. mquery().mod('x', 10);
  324. }, /must be used after where/);
  325. });
  326. it('works', function() {
  327. var m = mquery().where('madmen').mod(10, 20);
  328. assert.deepEqual(m._conditions, { madmen: { $mod: [10,20] }});
  329. });
  330. });
  331. it('with 2 arguments and second is an array', function() {
  332. var m = mquery().mod('madmen', [10,20]);
  333. assert.deepEqual(m._conditions, { madmen: { $mod: [10,20] }});
  334. });
  335. it('with 3 arguments', function() {
  336. var m = mquery().mod('madmen', 10, 20);
  337. assert.deepEqual(m._conditions, { madmen: { $mod: [10,20] }});
  338. });
  339. it('is chainable', function() {
  340. var m = mquery().mod('madmen', 10, 20).where('x', 8);
  341. var check = { madmen: { $mod: [10,20] }, x: 8};
  342. assert.deepEqual(m._conditions, check);
  343. });
  344. });
  345. describe('exists', function() {
  346. it('with 0 args', function() {
  347. it('throws if not used after where()', function() {
  348. assert.throws(function() {
  349. mquery().exists();
  350. }, /must be used after where/);
  351. });
  352. it('works', function() {
  353. var m = mquery().where('name').exists();
  354. var check = { name: { $exists: true }};
  355. assert.deepEqual(m._conditions, check);
  356. });
  357. });
  358. describe('with 1 arg', function() {
  359. describe('that is boolean', function() {
  360. it('throws if not used after where()', function() {
  361. assert.throws(function() {
  362. mquery().exists();
  363. }, /must be used after where/);
  364. });
  365. it('works', function() {
  366. var m = mquery().exists('name', false);
  367. var check = { name: { $exists: false }};
  368. assert.deepEqual(m._conditions, check);
  369. });
  370. });
  371. describe('that is not boolean', function() {
  372. it('sets the value to `true`', function() {
  373. var m = mquery().where('name').exists('yummy');
  374. var check = { yummy: { $exists: true }};
  375. assert.deepEqual(m._conditions, check);
  376. });
  377. });
  378. });
  379. describe('with 2 args', function() {
  380. it('works', function() {
  381. var m = mquery().exists('yummy', false);
  382. var check = { yummy: { $exists: false }};
  383. assert.deepEqual(m._conditions, check);
  384. });
  385. });
  386. it('is chainable', function() {
  387. var m = mquery().where('name').exists().find({ x: 1 });
  388. var check = { name: { $exists: true }, x: 1};
  389. assert.deepEqual(m._conditions, check);
  390. });
  391. });
  392. describe('elemMatch', function() {
  393. describe('with null/undefined first argument', function() {
  394. assert.throws(function() {
  395. mquery().elemMatch();
  396. }, /Invalid argument/);
  397. assert.throws(function() {
  398. mquery().elemMatch(null);
  399. }, /Invalid argument/);
  400. assert.doesNotThrow(function() {
  401. mquery().elemMatch('', {});
  402. });
  403. });
  404. describe('with 1 argument', function() {
  405. it('throws if not a function or object', function() {
  406. assert.throws(function() {
  407. mquery().elemMatch([]);
  408. }, /Invalid argument/);
  409. });
  410. describe('that is an object', function() {
  411. it('throws if no previous `where` was used', function() {
  412. assert.throws(function() {
  413. mquery().elemMatch({});
  414. }, /must be used after where/);
  415. });
  416. it('works', function() {
  417. var m = mquery().where('comment').elemMatch({ author: 'joe', votes: {$gte: 3 }});
  418. assert.deepEqual({ comment: { $elemMatch: { author: 'joe', votes: {$gte: 3}}}}, m._conditions);
  419. });
  420. });
  421. describe('that is a function', function() {
  422. it('throws if no previous `where` was used', function() {
  423. assert.throws(function() {
  424. mquery().elemMatch(function() {});
  425. }, /must be used after where/);
  426. });
  427. it('works', function() {
  428. var m = mquery().where('comment').elemMatch(function(query) {
  429. query.where({ author: 'joe', votes: {$gte: 3 }});
  430. });
  431. assert.deepEqual({ comment: { $elemMatch: { author: 'joe', votes: {$gte: 3}}}}, m._conditions);
  432. });
  433. });
  434. });
  435. describe('with 2 arguments', function() {
  436. describe('and the 2nd is an object', function() {
  437. it('works', function() {
  438. var m = mquery().elemMatch('comment', { author: 'joe', votes: {$gte: 3 }});
  439. assert.deepEqual({ comment: { $elemMatch: { author: 'joe', votes: {$gte: 3}}}}, m._conditions);
  440. });
  441. });
  442. describe('and the 2nd is a function', function() {
  443. it('works', function() {
  444. var m = mquery().elemMatch('comment', function(query) {
  445. query.where({ author: 'joe', votes: {$gte: 3 }});
  446. });
  447. assert.deepEqual({ comment: { $elemMatch: { author: 'joe', votes: {$gte: 3}}}}, m._conditions);
  448. });
  449. });
  450. it('and the 2nd is not a function or object', function() {
  451. assert.throws(function() {
  452. mquery().elemMatch('comment', []);
  453. }, /Invalid argument/);
  454. });
  455. });
  456. });
  457. describe('within', function() {
  458. it('is chainable', function() {
  459. var m = mquery();
  460. assert.equal(m.where('a').within(), m);
  461. });
  462. describe('when called with arguments', function() {
  463. it('must follow where()', function() {
  464. assert.throws(function() {
  465. mquery().within([]);
  466. }, /must be used after where/);
  467. });
  468. describe('of length 1', function() {
  469. it('throws if not a recognized shape', function() {
  470. assert.throws(function() {
  471. mquery().where('loc').within({});
  472. }, /Invalid argument/);
  473. assert.throws(function() {
  474. mquery().where('loc').within(null);
  475. }, /Invalid argument/);
  476. });
  477. it('delegates to circle when center exists', function() {
  478. var m = mquery().where('loc').within({ center: [10,10], radius: 3 });
  479. assert.deepEqual({ $geoWithin: {$center:[[10,10], 3]}}, m._conditions.loc);
  480. });
  481. it('delegates to box when exists', function() {
  482. var m = mquery().where('loc').within({ box: [[10,10], [11,14]] });
  483. assert.deepEqual({ $geoWithin: {$box:[[10,10], [11,14]]}}, m._conditions.loc);
  484. });
  485. it('delegates to polygon when exists', function() {
  486. var m = mquery().where('loc').within({ polygon: [[10,10], [11,14],[10,9]] });
  487. assert.deepEqual({ $geoWithin: {$polygon:[[10,10], [11,14],[10,9]]}}, m._conditions.loc);
  488. });
  489. it('delegates to geometry when exists', function() {
  490. var m = mquery().where('loc').within({ type: 'Polygon', coordinates: [[10,10], [11,14],[10,9]] });
  491. assert.deepEqual({ $geoWithin: {$geometry: {type:'Polygon', coordinates: [[10,10], [11,14],[10,9]]}}}, m._conditions.loc);
  492. });
  493. });
  494. describe('of length 2', function() {
  495. it('delegates to box()', function() {
  496. var m = mquery().where('loc').within([1,2],[2,5]);
  497. assert.deepEqual(m._conditions.loc, { $geoWithin: { $box: [[1,2],[2,5]]}});
  498. });
  499. });
  500. describe('of length > 2', function() {
  501. it('delegates to polygon()', function() {
  502. var m = mquery().where('loc').within([1,2],[2,5],[2,4],[1,3]);
  503. assert.deepEqual(m._conditions.loc, { $geoWithin: { $polygon: [[1,2],[2,5],[2,4],[1,3]]}});
  504. });
  505. });
  506. });
  507. });
  508. describe('geoWithin', function() {
  509. before(function() {
  510. mquery.use$geoWithin = false;
  511. });
  512. after(function() {
  513. mquery.use$geoWithin = true;
  514. });
  515. describe('when called with arguments', function() {
  516. describe('of length 1', function() {
  517. it('delegates to circle when center exists', function() {
  518. var m = mquery().where('loc').within({ center: [10,10], radius: 3 });
  519. assert.deepEqual({ $within: {$center:[[10,10], 3]}}, m._conditions.loc);
  520. });
  521. it('delegates to box when exists', function() {
  522. var m = mquery().where('loc').within({ box: [[10,10], [11,14]] });
  523. assert.deepEqual({ $within: {$box:[[10,10], [11,14]]}}, m._conditions.loc);
  524. });
  525. it('delegates to polygon when exists', function() {
  526. var m = mquery().where('loc').within({ polygon: [[10,10], [11,14],[10,9]] });
  527. assert.deepEqual({ $within: {$polygon:[[10,10], [11,14],[10,9]]}}, m._conditions.loc);
  528. });
  529. it('delegates to geometry when exists', function() {
  530. var m = mquery().where('loc').within({ type: 'Polygon', coordinates: [[10,10], [11,14],[10,9]] });
  531. assert.deepEqual({ $within: {$geometry: {type:'Polygon', coordinates: [[10,10], [11,14],[10,9]]}}}, m._conditions.loc);
  532. });
  533. });
  534. describe('of length 2', function() {
  535. it('delegates to box()', function() {
  536. var m = mquery().where('loc').within([1,2],[2,5]);
  537. assert.deepEqual(m._conditions.loc, { $within: { $box: [[1,2],[2,5]]}});
  538. });
  539. });
  540. describe('of length > 2', function() {
  541. it('delegates to polygon()', function() {
  542. var m = mquery().where('loc').within([1,2],[2,5],[2,4],[1,3]);
  543. assert.deepEqual(m._conditions.loc, { $within: { $polygon: [[1,2],[2,5],[2,4],[1,3]]}});
  544. });
  545. });
  546. });
  547. });
  548. describe('box', function() {
  549. describe('with 1 argument', function() {
  550. it('throws', function() {
  551. assert.throws(function() {
  552. mquery().box('sometihng');
  553. }, /Invalid argument/);
  554. });
  555. });
  556. describe('with > 3 arguments', function() {
  557. it('throws', function() {
  558. assert.throws(function() {
  559. mquery().box(1,2,3,4);
  560. }, /Invalid argument/);
  561. });
  562. });
  563. describe('with 2 arguments', function() {
  564. it('throws if not used after where()', function() {
  565. assert.throws(function() {
  566. mquery().box([],[]);
  567. }, /must be used after where/);
  568. });
  569. it('works', function() {
  570. var m = mquery().where('loc').box([1,2],[3,4]);
  571. assert.deepEqual(m._conditions.loc, { $geoWithin: { $box: [[1,2],[3,4]] }});
  572. });
  573. });
  574. describe('with 3 arguments', function() {
  575. it('works', function() {
  576. var m = mquery().box('loc', [1,2],[3,4]);
  577. assert.deepEqual(m._conditions.loc, { $geoWithin: { $box: [[1,2],[3,4]] }});
  578. });
  579. });
  580. });
  581. describe('polygon', function() {
  582. describe('when first argument is not a string', function() {
  583. it('throws if not used after where()', function() {
  584. assert.throws(function() {
  585. mquery().polygon({});
  586. }, /must be used after where/);
  587. assert.doesNotThrow(function() {
  588. mquery().where('loc').polygon([1,2], [2,3], [3,6]);
  589. });
  590. });
  591. it('assigns arguments to within polygon condition', function() {
  592. var m = mquery().where('loc').polygon([1,2], [2,3], [3,6]);
  593. assert.deepEqual(m._conditions, { loc: {$geoWithin: {$polygon: [[1,2],[2,3],[3,6]]}} });
  594. });
  595. });
  596. describe('when first arg is a string', function() {
  597. it('assigns remaining arguments to within polygon condition', function() {
  598. var m = mquery().polygon('loc', [1,2], [2,3], [3,6]);
  599. assert.deepEqual(m._conditions, { loc: {$geoWithin: {$polygon: [[1,2],[2,3],[3,6]]}} });
  600. });
  601. });
  602. });
  603. describe('circle', function() {
  604. describe('with one arg', function() {
  605. it('must follow where()', function() {
  606. assert.throws(function() {
  607. mquery().circle('x');
  608. }, /must be used after where/);
  609. assert.doesNotThrow(function() {
  610. mquery().where('loc').circle({center:[0,0], radius: 3 });
  611. });
  612. });
  613. it('works', function() {
  614. var m = mquery().where('loc').circle({center:[0,0], radius: 3 });
  615. assert.deepEqual(m._conditions, { loc: { $geoWithin: {$center: [[0,0],3] }}});
  616. });
  617. });
  618. describe('with 3 args', function() {
  619. it('throws', function() {
  620. assert.throws(function() {
  621. mquery().where('loc').circle(1,2,3);
  622. }, /Invalid argument/);
  623. });
  624. });
  625. describe('requires radius and center', function() {
  626. assert.throws(function() {
  627. mquery().circle('loc', { center: 1 });
  628. }, /center and radius are required/);
  629. assert.throws(function() {
  630. mquery().circle('loc', { radius: 1 });
  631. }, /center and radius are required/);
  632. assert.doesNotThrow(function() {
  633. mquery().circle('loc', { center: [1,2], radius: 1 });
  634. });
  635. });
  636. });
  637. describe('geometry', function() {
  638. // within + intersects
  639. var point = { type: 'Point', coordinates: [[0,0],[1,1]] };
  640. it('must be called after within or intersects', function(done) {
  641. assert.throws(function() {
  642. mquery().where('a').geometry(point);
  643. }, /must come after/);
  644. assert.doesNotThrow(function() {
  645. mquery().where('a').within().geometry(point);
  646. });
  647. assert.doesNotThrow(function() {
  648. mquery().where('a').intersects().geometry(point);
  649. });
  650. done();
  651. });
  652. describe('when called with one argument', function() {
  653. describe('after within()', function() {
  654. it('and arg quacks like geoJSON', function(done) {
  655. var m = mquery().where('a').within().geometry(point);
  656. assert.deepEqual({ a: { $geoWithin: { $geometry: point }}}, m._conditions);
  657. done();
  658. });
  659. });
  660. describe('after intersects()', function() {
  661. it('and arg quacks like geoJSON', function(done) {
  662. var m = mquery().where('a').intersects().geometry(point);
  663. assert.deepEqual({ a: { $geoIntersects: { $geometry: point }}}, m._conditions);
  664. done();
  665. });
  666. });
  667. it('and arg does not quack like geoJSON', function(done) {
  668. assert.throws(function() {
  669. mquery().where('b').within().geometry({type:1, coordinates:2});
  670. }, /Invalid argument/);
  671. done();
  672. });
  673. });
  674. describe('when called with zero arguments', function() {
  675. it('throws', function(done) {
  676. assert.throws(function() {
  677. mquery().where('a').within().geometry();
  678. }, /Invalid argument/);
  679. done();
  680. });
  681. });
  682. describe('when called with more than one arguments', function() {
  683. it('throws', function(done) {
  684. assert.throws(function() {
  685. mquery().where('a').within().geometry({type:'a',coordinates:[]}, 2);
  686. }, /Invalid argument/);
  687. done();
  688. });
  689. });
  690. });
  691. describe('intersects', function() {
  692. it('must be used after where()', function(done) {
  693. var m = mquery();
  694. assert.throws(function() {
  695. m.intersects();
  696. }, /must be used after where/);
  697. done();
  698. });
  699. it('sets geo comparison to "$intersects"', function(done) {
  700. var n = mquery().where('a').intersects();
  701. assert.equal('$geoIntersects', n._geoComparison);
  702. done();
  703. });
  704. it('is chainable', function() {
  705. var m = mquery();
  706. assert.equal(m.where('a').intersects(), m);
  707. });
  708. it('calls geometry if argument quacks like geojson', function(done) {
  709. var m = mquery();
  710. var o = { type: 'LineString', coordinates: [[0,1],[3,40]] };
  711. var ran = false;
  712. m.geometry = function(arg) {
  713. ran = true;
  714. assert.deepEqual(o, arg);
  715. };
  716. m.where('a').intersects(o);
  717. assert.ok(ran);
  718. done();
  719. });
  720. it('throws if argument is not geometry-like', function(done) {
  721. var m = mquery().where('a');
  722. assert.throws(function() {
  723. m.intersects(null);
  724. }, /Invalid argument/);
  725. assert.throws(function() {
  726. m.intersects(undefined);
  727. }, /Invalid argument/);
  728. assert.throws(function() {
  729. m.intersects(false);
  730. }, /Invalid argument/);
  731. assert.throws(function() {
  732. m.intersects({});
  733. }, /Invalid argument/);
  734. assert.throws(function() {
  735. m.intersects([]);
  736. }, /Invalid argument/);
  737. assert.throws(function() {
  738. m.intersects(function() {});
  739. }, /Invalid argument/);
  740. assert.throws(function() {
  741. m.intersects(NaN);
  742. }, /Invalid argument/);
  743. done();
  744. });
  745. });
  746. describe('near', function() {
  747. // near nearSphere
  748. describe('with 0 args', function() {
  749. it('is compatible with geometry()', function(done) {
  750. var q = mquery().where('x').near().geometry({ type: 'Point', coordinates: [180, 11] });
  751. assert.deepEqual({ $near: {$geometry: {type:'Point', coordinates: [180,11]}}}, q._conditions.x);
  752. done();
  753. });
  754. });
  755. describe('with 1 arg', function() {
  756. it('throws if not used after where()', function() {
  757. assert.throws(function() {
  758. mquery().near(1);
  759. }, /must be used after where/);
  760. });
  761. it('does not throw if used after where()', function() {
  762. assert.doesNotThrow(function() {
  763. mquery().where('loc').near({center:[1,1]});
  764. });
  765. });
  766. });
  767. describe('with > 2 args', function() {
  768. it('throws', function() {
  769. assert.throws(function() {
  770. mquery().near(1,2,3);
  771. }, /Invalid argument/);
  772. });
  773. });
  774. it('creates $geometry args for GeoJSON', function() {
  775. var m = mquery().where('loc').near({ center: { type: 'Point', coordinates: [10,10] }});
  776. assert.deepEqual({ $near: {$geometry: {type:'Point', coordinates: [10,10]}}}, m._conditions.loc);
  777. });
  778. it('expects `center`', function() {
  779. assert.throws(function() {
  780. mquery().near('loc', { maxDistance: 3 });
  781. }, /center is required/);
  782. assert.doesNotThrow(function() {
  783. mquery().near('loc', { center: [3,4] });
  784. });
  785. });
  786. it('accepts spherical conditions', function() {
  787. var m = mquery().where('loc').near({ center: [1,2], spherical: true });
  788. assert.deepEqual(m._conditions, { loc: { $nearSphere: [1,2]}});
  789. });
  790. it('is non-spherical by default', function() {
  791. var m = mquery().where('loc').near({ center: [1,2] });
  792. assert.deepEqual(m._conditions, { loc: { $near: [1,2]}});
  793. });
  794. it('supports maxDistance', function() {
  795. var m = mquery().where('loc').near({ center: [1,2], maxDistance:4 });
  796. assert.deepEqual(m._conditions, { loc: { $near: [1,2], $maxDistance: 4}});
  797. });
  798. it('supports minDistance', function() {
  799. var m = mquery().where('loc').near({ center: [1,2], minDistance:4 });
  800. assert.deepEqual(m._conditions, { loc: { $near: [1,2], $minDistance: 4}});
  801. });
  802. it('is chainable', function() {
  803. var m = mquery().where('loc').near({ center: [1,2], maxDistance:4 }).find({ x: 1 });
  804. assert.deepEqual(m._conditions, { loc: { $near: [1,2], $maxDistance: 4}, x: 1});
  805. });
  806. describe('supports passing GeoJSON, gh-13', function() {
  807. it('with center', function() {
  808. var m = mquery().where('loc').near({
  809. center: { type: 'Point', coordinates: [1,1] },
  810. maxDistance: 2
  811. });
  812. var expect = {
  813. loc: {
  814. $near: {
  815. $geometry: {
  816. type: 'Point',
  817. coordinates : [1,1]
  818. },
  819. $maxDistance : 2
  820. }
  821. }
  822. };
  823. assert.deepEqual(m._conditions, expect);
  824. });
  825. });
  826. });
  827. // fields
  828. describe('select', function() {
  829. describe('with 0 args', function() {
  830. it('is chainable', function() {
  831. var m = mquery();
  832. assert.equal(m, m.select());
  833. });
  834. });
  835. it('accepts an object', function() {
  836. var o = { x: 1, y: 1 };
  837. var m = mquery().select(o);
  838. assert.deepEqual(m._fields, o);
  839. });
  840. it('accepts a string', function() {
  841. var o = 'x -y';
  842. var m = mquery().select(o);
  843. assert.deepEqual(m._fields, { x: 1, y: 0 });
  844. });
  845. it('does accept an array', function() {
  846. var o = ['x', '-y'];
  847. var m = mquery().select(o);
  848. assert.deepEqual(m._fields, { x: 1, y: 0 });
  849. });
  850. it('merges previous arguments', function() {
  851. var o = { x: 1, y: 0, a: 1 };
  852. var m = mquery().select(o);
  853. m.select('z -u w').select({ x: 0 });
  854. assert.deepEqual(m._fields, {
  855. x: 0,
  856. y: 0,
  857. z: 1,
  858. u: 0,
  859. w: 1,
  860. a: 1
  861. });
  862. });
  863. it('rejects non-string, object, arrays', function() {
  864. assert.throws(function() {
  865. mquery().select(function() {});
  866. }, /Invalid select\(\) argument/);
  867. });
  868. it('accepts arguments objects', function() {
  869. var m = mquery();
  870. function t() {
  871. m.select(arguments);
  872. assert.deepEqual(m._fields, { x: 1, y: 0 });
  873. }
  874. t('x', '-y');
  875. });
  876. noDistinct('select');
  877. });
  878. describe('selected', function() {
  879. it('returns true when fields have been selected', function(done) {
  880. var m;
  881. m = mquery().select({ name: 1 });
  882. assert.ok(m.selected());
  883. m = mquery().select('name');
  884. assert.ok(m.selected());
  885. done();
  886. });
  887. it('returns false when no fields have been selected', function(done) {
  888. var m = mquery();
  889. assert.strictEqual(false, m.selected());
  890. done();
  891. });
  892. });
  893. describe('selectedInclusively', function() {
  894. describe('returns false', function() {
  895. it('when no fields have been selected', function(done) {
  896. assert.strictEqual(false, mquery().selectedInclusively());
  897. assert.equal(false, mquery().select({}).selectedInclusively());
  898. done();
  899. });
  900. it('when any fields have been excluded', function(done) {
  901. assert.strictEqual(false, mquery().select('-name').selectedInclusively());
  902. assert.strictEqual(false, mquery().select({ name: 0 }).selectedInclusively());
  903. assert.strictEqual(false, mquery().select('name bio -_id').selectedInclusively());
  904. assert.strictEqual(false, mquery().select({ name: 1, _id: 0 }).selectedInclusively());
  905. done();
  906. });
  907. it('when using $meta', function(done) {
  908. assert.strictEqual(false, mquery().select({ name: { $meta: 'textScore' } }).selectedInclusively());
  909. done();
  910. });
  911. });
  912. describe('returns true', function() {
  913. it('when fields have been included', function(done) {
  914. assert.equal(true, mquery().select('name').selectedInclusively());
  915. assert.equal(true, mquery().select({ name:1 }).selectedInclusively());
  916. done();
  917. });
  918. });
  919. });
  920. describe('selectedExclusively', function() {
  921. describe('returns false', function() {
  922. it('when no fields have been selected', function(done) {
  923. assert.equal(false, mquery().selectedExclusively());
  924. assert.equal(false, mquery().select({}).selectedExclusively());
  925. done();
  926. });
  927. it('when fields have only been included', function(done) {
  928. assert.equal(false, mquery().select('name').selectedExclusively());
  929. assert.equal(false, mquery().select({ name: 1 }).selectedExclusively());
  930. done();
  931. });
  932. });
  933. describe('returns true', function() {
  934. it('when any field has been excluded', function(done) {
  935. assert.equal(true, mquery().select('-name').selectedExclusively());
  936. assert.equal(true, mquery().select({ name:0 }).selectedExclusively());
  937. assert.equal(true, mquery().select('-_id').selectedExclusively());
  938. assert.strictEqual(true, mquery().select('name bio -_id').selectedExclusively());
  939. assert.strictEqual(true, mquery().select({ name: 1, _id: 0 }).selectedExclusively());
  940. done();
  941. });
  942. });
  943. });
  944. describe('slice', function() {
  945. describe('with 0 args', function() {
  946. it('is chainable', function() {
  947. var m = mquery();
  948. assert.equal(m, m.slice());
  949. });
  950. it('is a noop', function() {
  951. var m = mquery().slice();
  952. assert.deepEqual(m._fields, undefined);
  953. });
  954. });
  955. describe('with 1 arg', function() {
  956. it('throws if not called after where()', function() {
  957. assert.throws(function() {
  958. mquery().slice(1);
  959. }, /must be used after where/);
  960. assert.doesNotThrow(function() {
  961. mquery().where('a').slice(1);
  962. });
  963. });
  964. it('that is a number', function() {
  965. var query = mquery();
  966. query.where('collection').slice(5);
  967. assert.deepEqual(query._fields, {collection: {$slice: 5}});
  968. });
  969. it('that is an array', function() {
  970. var query = mquery();
  971. query.where('collection').slice([5,10]);
  972. assert.deepEqual(query._fields, {collection: {$slice: [5,10]}});
  973. });
  974. it('that is an object', function() {
  975. var query = mquery();
  976. query.slice({ collection: [5, 10] });
  977. assert.deepEqual(query._fields, {collection: {$slice: [5,10]}});
  978. });
  979. });
  980. describe('with 2 args', function() {
  981. describe('and first is a number', function() {
  982. it('throws if not called after where', function() {
  983. assert.throws(function() {
  984. mquery().slice(2,3);
  985. }, /must be used after where/);
  986. });
  987. it('does not throw if used after where', function() {
  988. var query = mquery();
  989. query.where('collection').slice(2,3);
  990. assert.deepEqual(query._fields, {collection: {$slice: [2,3]}});
  991. });
  992. });
  993. it('and first is not a number', function() {
  994. var query = mquery().slice('collection', [-5, 2]);
  995. assert.deepEqual(query._fields, {collection: {$slice: [-5,2]}});
  996. });
  997. });
  998. describe('with 3 args', function() {
  999. it('works', function() {
  1000. var query = mquery();
  1001. query.slice('collection', 14, 10);
  1002. assert.deepEqual(query._fields, {collection: {$slice: [14, 10]}});
  1003. });
  1004. });
  1005. noDistinct('slice');
  1006. no('count', 'slice');
  1007. });
  1008. // options
  1009. describe('sort', function() {
  1010. describe('with 0 args', function() {
  1011. it('chains', function() {
  1012. var m = mquery();
  1013. assert.equal(m, m.sort());
  1014. });
  1015. it('has no affect', function() {
  1016. var m = mquery();
  1017. assert.equal(m.options.sort, undefined);
  1018. });
  1019. });
  1020. it('works', function() {
  1021. var query = mquery();
  1022. query.sort('a -c b');
  1023. assert.deepEqual(query.options.sort, { a : 1, b: 1, c : -1});
  1024. query = mquery();
  1025. query.sort({'a': 1, 'c': -1, 'b': 'asc', e: 'descending', f: 'ascending'});
  1026. assert.deepEqual(query.options.sort, {'a': 1, 'c': -1, 'b': 1, 'e': -1, 'f': 1});
  1027. query = mquery();
  1028. query.sort([['a', -1], ['c', 1], ['b', 'desc'], ['e', 'ascending'], ['f', 'descending']]);
  1029. assert.deepEqual(query.options.sort, [['a', -1], ['c', 1], ['b', -1], ['e', 1], ['f', -1]]);
  1030. query = mquery();
  1031. var e = undefined;
  1032. try {
  1033. query.sort([['a', 1], { 'b': 5 }]);
  1034. } catch (err) {
  1035. e = err;
  1036. }
  1037. assert.ok(e, 'uh oh. no error was thrown');
  1038. assert.equal(e.message, 'Invalid sort() argument, must be array of arrays');
  1039. query = mquery();
  1040. e = undefined;
  1041. try {
  1042. query.sort('a', 1, 'c', -1, 'b', 1);
  1043. } catch (err) {
  1044. e = err;
  1045. }
  1046. assert.ok(e, 'uh oh. no error was thrown');
  1047. assert.equal(e.message, 'Invalid sort() argument. Must be a string, object, or array.');
  1048. });
  1049. it('handles $meta sort options', function() {
  1050. var query = mquery();
  1051. query.sort({ score: { $meta : 'textScore' } });
  1052. assert.deepEqual(query.options.sort, { score : { $meta : 'textScore' } });
  1053. });
  1054. it('array syntax', function() {
  1055. var query = mquery();
  1056. query.sort([['field', 1], ['test', -1]]);
  1057. assert.deepEqual(query.options.sort, [['field', 1], ['test', -1]]);
  1058. });
  1059. it('throws with mixed array/object syntax', function() {
  1060. var query = mquery();
  1061. assert.throws(function() {
  1062. query.sort({ field: 1 }).sort([['test', -1]]);
  1063. }, /Can't mix sort syntaxes/);
  1064. assert.throws(function() {
  1065. query.sort([['field', 1]]).sort({ test: 1 });
  1066. }, /Can't mix sort syntaxes/);
  1067. });
  1068. it('works with maps', function() {
  1069. if (typeof Map === 'undefined') {
  1070. return this.skip();
  1071. }
  1072. var query = mquery();
  1073. query.sort(new Map().set('field', 1).set('test', -1));
  1074. assert.deepEqual(query.options.sort, new Map().set('field', 1).set('test', -1));
  1075. });
  1076. });
  1077. function simpleOption(type, options) {
  1078. describe(type, function() {
  1079. it('sets the ' + type + ' option', function() {
  1080. var m = mquery()[type](2);
  1081. var optionName = options.name || type;
  1082. assert.equal(2, m.options[optionName]);
  1083. });
  1084. it('is chainable', function() {
  1085. var m = mquery();
  1086. assert.equal(m[type](3), m);
  1087. });
  1088. if (!options.distinct) noDistinct(type);
  1089. if (!options.count) no('count', type);
  1090. });
  1091. }
  1092. var negated = {
  1093. limit: {distinct: false, count: true},
  1094. skip: {distinct: false, count: true},
  1095. maxScan: {distinct: false, count: false},
  1096. batchSize: {distinct: false, count: false},
  1097. maxTime: {distinct: true, count: true, name: 'maxTimeMS' },
  1098. comment: {distinct: false, count: false}
  1099. };
  1100. Object.keys(negated).forEach(function(key) {
  1101. simpleOption(key, negated[key]);
  1102. });
  1103. describe('snapshot', function() {
  1104. it('works', function() {
  1105. var query;
  1106. query = mquery();
  1107. query.snapshot();
  1108. assert.equal(true, query.options.snapshot);
  1109. query = mquery();
  1110. query.snapshot(true);
  1111. assert.equal(true, query.options.snapshot);
  1112. query = mquery();
  1113. query.snapshot(false);
  1114. assert.equal(false, query.options.snapshot);
  1115. });
  1116. noDistinct('snapshot');
  1117. no('count', 'snapshot');
  1118. });
  1119. describe('hint', function() {
  1120. it('accepts an object', function() {
  1121. var query2 = mquery();
  1122. query2.hint({'a': 1, 'b': -1});
  1123. assert.deepEqual(query2.options.hint, {'a': 1, 'b': -1});
  1124. });
  1125. it('accepts a string', function() {
  1126. var query2 = mquery();
  1127. query2.hint('a');
  1128. assert.deepEqual(query2.options.hint, 'a');
  1129. });
  1130. it('rejects everything else', function() {
  1131. assert.throws(function() {
  1132. mquery().hint(['c']);
  1133. }, /Invalid hint./);
  1134. assert.throws(function() {
  1135. mquery().hint(1);
  1136. }, /Invalid hint./);
  1137. });
  1138. describe('does not have side affects', function() {
  1139. it('on invalid arg', function() {
  1140. var m = mquery();
  1141. try {
  1142. m.hint(1);
  1143. } catch (err) {
  1144. // ignore
  1145. }
  1146. assert.equal(undefined, m.options.hint);
  1147. });
  1148. it('on missing arg', function() {
  1149. var m = mquery().hint();
  1150. assert.equal(undefined, m.options.hint);
  1151. });
  1152. });
  1153. noDistinct('hint');
  1154. });
  1155. describe('j', function() {
  1156. it('works', function() {
  1157. var m = mquery().j(true);
  1158. assert.equal(true, m.options.j);
  1159. });
  1160. });
  1161. describe('slaveOk', function() {
  1162. it('works', function() {
  1163. var query;
  1164. query = mquery();
  1165. query.slaveOk();
  1166. assert.equal(true, query.options.slaveOk);
  1167. query = mquery();
  1168. query.slaveOk(true);
  1169. assert.equal(true, query.options.slaveOk);
  1170. query = mquery();
  1171. query.slaveOk(false);
  1172. assert.equal(false, query.options.slaveOk);
  1173. });
  1174. });
  1175. describe('read', function() {
  1176. it('sets associated readPreference option', function() {
  1177. var m = mquery();
  1178. m.read('p');
  1179. assert.equal('primary', m.options.readPreference);
  1180. });
  1181. it('is chainable', function() {
  1182. var m = mquery();
  1183. assert.equal(m, m.read('sp'));
  1184. });
  1185. });
  1186. describe('readConcern', function() {
  1187. it('sets associated readConcern option', function() {
  1188. var m;
  1189. m = mquery();
  1190. m.readConcern('s');
  1191. assert.deepEqual({ level: 'snapshot' }, m.options.readConcern);
  1192. m = mquery();
  1193. m.r('local');
  1194. assert.deepEqual({ level: 'local' }, m.options.readConcern);
  1195. });
  1196. it('is chainable', function() {
  1197. var m = mquery();
  1198. assert.equal(m, m.readConcern('lz'));
  1199. });
  1200. });
  1201. describe('tailable', function() {
  1202. it('works', function() {
  1203. var query;
  1204. query = mquery();
  1205. query.tailable();
  1206. assert.equal(true, query.options.tailable);
  1207. query = mquery();
  1208. query.tailable(true);
  1209. assert.equal(true, query.options.tailable);
  1210. query = mquery();
  1211. query.tailable(false);
  1212. assert.equal(false, query.options.tailable);
  1213. });
  1214. it('is chainable', function() {
  1215. var m = mquery();
  1216. assert.equal(m, m.tailable());
  1217. });
  1218. noDistinct('tailable');
  1219. no('count', 'tailable');
  1220. });
  1221. describe('writeConcern', function() {
  1222. it('sets associated writeConcern option', function() {
  1223. var m;
  1224. m = mquery();
  1225. m.writeConcern('majority');
  1226. assert.equal('majority', m.options.w);
  1227. m = mquery();
  1228. m.writeConcern('m'); // m is alias of majority
  1229. assert.equal('majority', m.options.w);
  1230. m = mquery();
  1231. m.writeConcern(1);
  1232. assert.equal(1, m.options.w);
  1233. });
  1234. it('accepts object', function() {
  1235. var m;
  1236. m = mquery().writeConcern({ w: 'm', j: true, wtimeout: 1000 });
  1237. assert.equal('m', m.options.w); // check it does not convert m to majority
  1238. assert.equal(true, m.options.j);
  1239. assert.equal(1000, m.options.wtimeout);
  1240. m = mquery().w('m').w({j: false, wtimeout: 0 });
  1241. assert.equal('majority', m.options.w);
  1242. assert.strictEqual(false, m.options.j);
  1243. assert.strictEqual(0, m.options.wtimeout);
  1244. });
  1245. it('is chainable', function() {
  1246. var m = mquery();
  1247. assert.equal(m, m.writeConcern('majority'));
  1248. });
  1249. });
  1250. // query utilities
  1251. describe('merge', function() {
  1252. describe('with falsy arg', function() {
  1253. it('returns itself', function() {
  1254. var m = mquery();
  1255. assert.equal(m, m.merge());
  1256. assert.equal(m, m.merge(null));
  1257. assert.equal(m, m.merge(0));
  1258. });
  1259. });
  1260. describe('with an argument', function() {
  1261. describe('that is not a query or plain object', function() {
  1262. it('throws', function() {
  1263. assert.throws(function() {
  1264. mquery().merge([]);
  1265. }, /Invalid argument/);
  1266. assert.throws(function() {
  1267. mquery().merge('merge');
  1268. }, /Invalid argument/);
  1269. assert.doesNotThrow(function() {
  1270. mquery().merge({});
  1271. }, /Invalid argument/);
  1272. });
  1273. });
  1274. describe('that is a query', function() {
  1275. it('merges conditions, field selection, and options', function() {
  1276. var m = mquery({ x: 'hi' }, { select: 'x y', another: true });
  1277. var n = mquery().merge(m);
  1278. assert.deepEqual(n._conditions, m._conditions);
  1279. assert.deepEqual(n._fields, m._fields);
  1280. assert.deepEqual(n.options, m.options);
  1281. });
  1282. it('clones update arguments', function(done) {
  1283. var original = { $set: { iTerm: true }};
  1284. var m = mquery().update(original);
  1285. var n = mquery().merge(m);
  1286. m.update({ $set: { x: 2 }});
  1287. assert.notDeepEqual(m._update, n._update);
  1288. done();
  1289. });
  1290. it('is chainable', function() {
  1291. var m = mquery({ x: 'hi' });
  1292. var n = mquery();
  1293. assert.equal(n, n.merge(m));
  1294. });
  1295. });
  1296. describe('that is an object', function() {
  1297. it('merges', function() {
  1298. var m = { x: 'hi' };
  1299. var n = mquery().merge(m);
  1300. assert.deepEqual(n._conditions, { x: 'hi' });
  1301. });
  1302. it('clones update arguments', function(done) {
  1303. var original = { $set: { iTerm: true }};
  1304. var m = mquery().update(original);
  1305. var n = mquery().merge(original);
  1306. m.update({ $set: { x: 2 }});
  1307. assert.notDeepEqual(m._update, n._update);
  1308. done();
  1309. });
  1310. it('is chainable', function() {
  1311. var m = { x: 'hi' };
  1312. var n = mquery();
  1313. assert.equal(n, n.merge(m));
  1314. });
  1315. });
  1316. });
  1317. });
  1318. // queries
  1319. describe('find', function() {
  1320. describe('with no callback', function() {
  1321. it('does not execute', function() {
  1322. var m = mquery();
  1323. assert.doesNotThrow(function() {
  1324. m.find();
  1325. });
  1326. assert.doesNotThrow(function() {
  1327. m.find({ x: 1 });
  1328. });
  1329. });
  1330. });
  1331. it('is chainable', function() {
  1332. var m = mquery().find({ x: 1 }).find().find({ y: 2 });
  1333. assert.deepEqual(m._conditions, {x:1,y:2});
  1334. });
  1335. it('merges other queries', function() {
  1336. var m = mquery({ name: 'mquery' });
  1337. m.tailable();
  1338. m.select('_id');
  1339. var a = mquery().find(m);
  1340. assert.deepEqual(a._conditions, m._conditions);
  1341. assert.deepEqual(a.options, m.options);
  1342. assert.deepEqual(a._fields, m._fields);
  1343. });
  1344. describe('executes', function() {
  1345. before(function(done) {
  1346. col.insert({ name: 'mquery' }, { safe: true }, done);
  1347. });
  1348. after(function(done) {
  1349. col.remove({ name: 'mquery' }, done);
  1350. });
  1351. it('when criteria is passed with a callback', function(done) {
  1352. mquery(col).find({ name: 'mquery' }, function(err, docs) {
  1353. assert.ifError(err);
  1354. assert.equal(1, docs.length);
  1355. done();
  1356. });
  1357. });
  1358. it('when Query is passed with a callback', function(done) {
  1359. var m = mquery({ name: 'mquery' });
  1360. mquery(col).find(m, function(err, docs) {
  1361. assert.ifError(err);
  1362. assert.equal(1, docs.length);
  1363. done();
  1364. });
  1365. });
  1366. it('when just a callback is passed', function(done) {
  1367. mquery({ name: 'mquery' }).collection(col).find(function(err, docs) {
  1368. assert.ifError(err);
  1369. assert.equal(1, docs.length);
  1370. done();
  1371. });
  1372. });
  1373. });
  1374. });
  1375. describe('findOne', function() {
  1376. describe('with no callback', function() {
  1377. it('does not execute', function() {
  1378. var m = mquery();
  1379. assert.doesNotThrow(function() {
  1380. m.findOne();
  1381. });
  1382. assert.doesNotThrow(function() {
  1383. m.findOne({ x: 1 });
  1384. });
  1385. });
  1386. });
  1387. it('is chainable', function() {
  1388. var m = mquery();
  1389. var n = m.findOne({ x: 1 }).findOne().findOne({ y: 2 });
  1390. assert.equal(m, n);
  1391. assert.deepEqual(m._conditions, {x:1,y:2});
  1392. assert.equal('findOne', m.op);
  1393. });
  1394. it('merges other queries', function() {
  1395. var m = mquery({ name: 'mquery' });
  1396. m.read('nearest');
  1397. m.select('_id');
  1398. var a = mquery().findOne(m);
  1399. assert.deepEqual(a._conditions, m._conditions);
  1400. assert.deepEqual(a.options, m.options);
  1401. assert.deepEqual(a._fields, m._fields);
  1402. });
  1403. describe('executes', function() {
  1404. before(function(done) {
  1405. col.insert({ name: 'mquery findone' }, { safe: true }, done);
  1406. });
  1407. after(function(done) {
  1408. col.remove({ name: 'mquery findone' }, done);
  1409. });
  1410. it('when criteria is passed with a callback', function(done) {
  1411. mquery(col).findOne({ name: 'mquery findone' }, function(err, doc) {
  1412. assert.ifError(err);
  1413. assert.ok(doc);
  1414. assert.equal('mquery findone', doc.name);
  1415. done();
  1416. });
  1417. });
  1418. it('when Query is passed with a callback', function(done) {
  1419. var m = mquery(col).where({ name: 'mquery findone' });
  1420. mquery(col).findOne(m, function(err, doc) {
  1421. assert.ifError(err);
  1422. assert.ok(doc);
  1423. assert.equal('mquery findone', doc.name);
  1424. done();
  1425. });
  1426. });
  1427. it('when just a callback is passed', function(done) {
  1428. mquery({ name: 'mquery findone' }).collection(col).findOne(function(err, doc) {
  1429. assert.ifError(err);
  1430. assert.ok(doc);
  1431. assert.equal('mquery findone', doc.name);
  1432. done();
  1433. });
  1434. });
  1435. });
  1436. });
  1437. describe('count', function() {
  1438. describe('with no callback', function() {
  1439. it('does not execute', function() {
  1440. var m = mquery();
  1441. assert.doesNotThrow(function() {
  1442. m.count();
  1443. });
  1444. assert.doesNotThrow(function() {
  1445. m.count({ x: 1 });
  1446. });
  1447. });
  1448. });
  1449. it('is chainable', function() {
  1450. var m = mquery();
  1451. var n = m.count({ x: 1 }).count().count({ y: 2 });
  1452. assert.equal(m, n);
  1453. assert.deepEqual(m._conditions, {x:1,y:2});
  1454. assert.equal('count', m.op);
  1455. });
  1456. it('merges other queries', function() {
  1457. var m = mquery({ name: 'mquery' });
  1458. m.read('nearest');
  1459. m.select('_id');
  1460. var a = mquery().count(m);
  1461. assert.deepEqual(a._conditions, m._conditions);
  1462. assert.deepEqual(a.options, m.options);
  1463. assert.deepEqual(a._fields, m._fields);
  1464. });
  1465. describe('executes', function() {
  1466. before(function(done) {
  1467. col.insert({ name: 'mquery count' }, { safe: true }, done);
  1468. });
  1469. after(function(done) {
  1470. col.remove({ name: 'mquery count' }, done);
  1471. });
  1472. it('when criteria is passed with a callback', function(done) {
  1473. mquery(col).count({ name: 'mquery count' }, function(err, count) {
  1474. assert.ifError(err);
  1475. assert.ok(count);
  1476. assert.ok(1 === count);
  1477. done();
  1478. });
  1479. });
  1480. it('when Query is passed with a callback', function(done) {
  1481. var m = mquery({ name: 'mquery count' });
  1482. mquery(col).count(m, function(err, count) {
  1483. assert.ifError(err);
  1484. assert.ok(count);
  1485. assert.ok(1 === count);
  1486. done();
  1487. });
  1488. });
  1489. it('when just a callback is passed', function(done) {
  1490. mquery({ name: 'mquery count' }).collection(col).count(function(err, count) {
  1491. assert.ifError(err);
  1492. assert.ok(1 === count);
  1493. done();
  1494. });
  1495. });
  1496. });
  1497. describe('validates its option', function() {
  1498. it('sort', function(done) {
  1499. assert.doesNotThrow(function() {
  1500. mquery().sort('x').count();
  1501. });
  1502. done();
  1503. });
  1504. it('select', function(done) {
  1505. assert.throws(function() {
  1506. mquery().select('x').count();
  1507. }, /field selection and slice cannot be used with count/);
  1508. done();
  1509. });
  1510. it('slice', function(done) {
  1511. assert.throws(function() {
  1512. mquery().where('x').slice(-3).count();
  1513. }, /field selection and slice cannot be used with count/);
  1514. done();
  1515. });
  1516. it('limit', function(done) {
  1517. assert.doesNotThrow(function() {
  1518. mquery().limit(3).count();
  1519. });
  1520. done();
  1521. });
  1522. it('skip', function(done) {
  1523. assert.doesNotThrow(function() {
  1524. mquery().skip(3).count();
  1525. });
  1526. done();
  1527. });
  1528. it('batchSize', function(done) {
  1529. assert.throws(function() {
  1530. mquery({}, { batchSize: 3 }).count();
  1531. }, /batchSize cannot be used with count/);
  1532. done();
  1533. });
  1534. it('comment', function(done) {
  1535. assert.throws(function() {
  1536. mquery().comment('mquery').count();
  1537. }, /comment cannot be used with count/);
  1538. done();
  1539. });
  1540. it('maxScan', function(done) {
  1541. assert.throws(function() {
  1542. mquery().maxScan(300).count();
  1543. }, /maxScan cannot be used with count/);
  1544. done();
  1545. });
  1546. it('snapshot', function(done) {
  1547. assert.throws(function() {
  1548. mquery().snapshot().count();
  1549. }, /snapshot cannot be used with count/);
  1550. done();
  1551. });
  1552. it('tailable', function(done) {
  1553. assert.throws(function() {
  1554. mquery().tailable().count();
  1555. }, /tailable cannot be used with count/);
  1556. done();
  1557. });
  1558. });
  1559. });
  1560. describe('distinct', function() {
  1561. describe('with no callback', function() {
  1562. it('does not execute', function() {
  1563. var m = mquery();
  1564. assert.doesNotThrow(function() {
  1565. m.distinct();
  1566. });
  1567. assert.doesNotThrow(function() {
  1568. m.distinct('name');
  1569. });
  1570. assert.doesNotThrow(function() {
  1571. m.distinct({ name: 'mquery distinct' });
  1572. });
  1573. assert.doesNotThrow(function() {
  1574. m.distinct({ name: 'mquery distinct' }, 'name');
  1575. });
  1576. });
  1577. });
  1578. it('is chainable', function() {
  1579. var m = mquery({x:1}).distinct('name');
  1580. var n = m.distinct({y:2});
  1581. assert.equal(m, n);
  1582. assert.deepEqual(n._conditions, {x:1, y:2});
  1583. assert.equal('name', n._distinct);
  1584. assert.equal('distinct', n.op);
  1585. });
  1586. it('overwrites field', function() {
  1587. var m = mquery({ name: 'mquery' }).distinct('name');
  1588. m.distinct('rename');
  1589. assert.equal(m._distinct, 'rename');
  1590. m.distinct({x:1}, 'renamed');
  1591. assert.equal(m._distinct, 'renamed');
  1592. });
  1593. it('merges other queries', function() {
  1594. var m = mquery().distinct({ name: 'mquery' }, 'age');
  1595. m.read('nearest');
  1596. var a = mquery().distinct(m);
  1597. assert.deepEqual(a._conditions, m._conditions);
  1598. assert.deepEqual(a.options, m.options);
  1599. assert.deepEqual(a._fields, m._fields);
  1600. assert.deepEqual(a._distinct, m._distinct);
  1601. });
  1602. describe('executes', function() {
  1603. before(function(done) {
  1604. col.insert({ name: 'mquery distinct', age: 1 }, { safe: true }, done);
  1605. });
  1606. after(function(done) {
  1607. col.remove({ name: 'mquery distinct' }, done);
  1608. });
  1609. it('when distinct arg is passed with a callback', function(done) {
  1610. mquery(col).distinct('distinct', function(err, doc) {
  1611. assert.ifError(err);
  1612. assert.ok(doc);
  1613. done();
  1614. });
  1615. });
  1616. describe('when criteria is passed with a callback', function() {
  1617. it('if distinct arg was declared', function(done) {
  1618. mquery(col).distinct('age').distinct({ name: 'mquery distinct' }, function(err, doc) {
  1619. assert.ifError(err);
  1620. assert.ok(doc);
  1621. done();
  1622. });
  1623. });
  1624. it('but not if distinct arg was not declared', function() {
  1625. assert.throws(function() {
  1626. mquery(col).distinct({ name: 'mquery distinct' }, function() {});
  1627. }, /No value for `distinct`/);
  1628. });
  1629. });
  1630. describe('when Query is passed with a callback', function() {
  1631. var m = mquery({ name: 'mquery distinct' });
  1632. it('if distinct arg was declared', function(done) {
  1633. mquery(col).distinct('age').distinct(m, function(err, doc) {
  1634. assert.ifError(err);
  1635. assert.ok(doc);
  1636. done();
  1637. });
  1638. });
  1639. it('but not if distinct arg was not declared', function() {
  1640. assert.throws(function() {
  1641. mquery(col).distinct(m, function() {});
  1642. }, /No value for `distinct`/);
  1643. });
  1644. });
  1645. describe('when just a callback is passed', function() {
  1646. it('if distinct arg was declared', function(done) {
  1647. var m = mquery({ name: 'mquery distinct' });
  1648. m.collection(col);
  1649. m.distinct('age');
  1650. m.distinct(function(err, doc) {
  1651. assert.ifError(err);
  1652. assert.ok(doc);
  1653. done();
  1654. });
  1655. });
  1656. it('but not if no distinct arg was declared', function() {
  1657. var m = mquery();
  1658. m.collection(col);
  1659. assert.throws(function() {
  1660. m.distinct(function() {});
  1661. }, /No value for `distinct`/);
  1662. });
  1663. });
  1664. });
  1665. describe('validates its option', function() {
  1666. it('sort', function(done) {
  1667. assert.throws(function() {
  1668. mquery().sort('x').distinct();
  1669. }, /sort cannot be used with distinct/);
  1670. done();
  1671. });
  1672. it('select', function(done) {
  1673. assert.throws(function() {
  1674. mquery().select('x').distinct();
  1675. }, /field selection and slice cannot be used with distinct/);
  1676. done();
  1677. });
  1678. it('slice', function(done) {
  1679. assert.throws(function() {
  1680. mquery().where('x').slice(-3).distinct();
  1681. }, /field selection and slice cannot be used with distinct/);
  1682. done();
  1683. });
  1684. it('limit', function(done) {
  1685. assert.throws(function() {
  1686. mquery().limit(3).distinct();
  1687. }, /limit cannot be used with distinct/);
  1688. done();
  1689. });
  1690. it('skip', function(done) {
  1691. assert.throws(function() {
  1692. mquery().skip(3).distinct();
  1693. }, /skip cannot be used with distinct/);
  1694. done();
  1695. });
  1696. it('batchSize', function(done) {
  1697. assert.throws(function() {
  1698. mquery({}, { batchSize: 3 }).distinct();
  1699. }, /batchSize cannot be used with distinct/);
  1700. done();
  1701. });
  1702. it('comment', function(done) {
  1703. assert.throws(function() {
  1704. mquery().comment('mquery').distinct();
  1705. }, /comment cannot be used with distinct/);
  1706. done();
  1707. });
  1708. it('maxScan', function(done) {
  1709. assert.throws(function() {
  1710. mquery().maxScan(300).distinct();
  1711. }, /maxScan cannot be used with distinct/);
  1712. done();
  1713. });
  1714. it('snapshot', function(done) {
  1715. assert.throws(function() {
  1716. mquery().snapshot().distinct();
  1717. }, /snapshot cannot be used with distinct/);
  1718. done();
  1719. });
  1720. it('hint', function(done) {
  1721. assert.throws(function() {
  1722. mquery().hint({ x: 1 }).distinct();
  1723. }, /hint cannot be used with distinct/);
  1724. done();
  1725. });
  1726. it('tailable', function(done) {
  1727. assert.throws(function() {
  1728. mquery().tailable().distinct();
  1729. }, /tailable cannot be used with distinct/);
  1730. done();
  1731. });
  1732. });
  1733. });
  1734. describe('update', function() {
  1735. describe('with no callback', function() {
  1736. it('does not execute', function() {
  1737. var m = mquery();
  1738. assert.doesNotThrow(function() {
  1739. m.update({ name: 'old' }, { name: 'updated' }, { multi: true });
  1740. });
  1741. assert.doesNotThrow(function() {
  1742. m.update({ name: 'old' }, { name: 'updated' });
  1743. });
  1744. assert.doesNotThrow(function() {
  1745. m.update({ name: 'updated' });
  1746. });
  1747. assert.doesNotThrow(function() {
  1748. m.update();
  1749. });
  1750. });
  1751. });
  1752. it('is chainable', function() {
  1753. var m = mquery({x:1}).update({ y: 2 });
  1754. var n = m.where({y:2});
  1755. assert.equal(m, n);
  1756. assert.deepEqual(n._conditions, {x:1, y:2});
  1757. assert.deepEqual({ y: 2 }, n._update);
  1758. assert.equal('update', n.op);
  1759. });
  1760. it('merges update doc arg', function() {
  1761. var a = [1,2];
  1762. var m = mquery().where({ name: 'mquery' }).update({ x: 'stuff', a: a });
  1763. m.update({ z: 'stuff' });
  1764. assert.deepEqual(m._update, { z: 'stuff', x: 'stuff', a: a });
  1765. assert.deepEqual(m._conditions, { name: 'mquery' });
  1766. assert.ok(!m.options.overwrite);
  1767. m.update({}, { z: 'renamed' }, { overwrite: true });
  1768. assert.ok(m.options.overwrite === true);
  1769. assert.deepEqual(m._conditions, { name: 'mquery' });
  1770. assert.deepEqual(m._update, { z: 'renamed', x: 'stuff', a: a });
  1771. a.push(3);
  1772. assert.notDeepEqual(m._update, { z: 'renamed', x: 'stuff', a: a });
  1773. });
  1774. it('merges other options', function() {
  1775. var m = mquery();
  1776. m.setOptions({ overwrite: true });
  1777. m.update({ age: 77 }, { name: 'pagemill' }, { multi: true });
  1778. assert.deepEqual({ age: 77 }, m._conditions);
  1779. assert.deepEqual({ name: 'pagemill' }, m._update);
  1780. assert.deepEqual({ overwrite: true, multi: true }, m.options);
  1781. });
  1782. describe('executes', function() {
  1783. var id;
  1784. before(function(done) {
  1785. col.insert({ name: 'mquery update', age: 1 }, { safe: true }, function(err, res) {
  1786. id = res.insertedIds[0];
  1787. done();
  1788. });
  1789. });
  1790. after(function(done) {
  1791. col.remove({ _id: id }, done);
  1792. });
  1793. describe('when conds + doc + opts + callback passed', function() {
  1794. it('works', function(done) {
  1795. var m = mquery(col).where({ _id: id });
  1796. m.update({}, { name: 'Sparky' }, { safe: true }, function(err, res) {
  1797. assert.ifError(err);
  1798. assert.equal(res.result.n, 1);
  1799. m.findOne(function(err, doc) {
  1800. assert.ifError(err);
  1801. assert.equal(doc.name, 'Sparky');
  1802. done();
  1803. });
  1804. });
  1805. });
  1806. });
  1807. describe('when conds + doc + callback passed', function() {
  1808. it('works', function(done) {
  1809. var m = mquery(col).update({ _id: id }, { name: 'fairgrounds' }, function(err, num) {
  1810. assert.ifError(err);
  1811. assert.ok(1, num);
  1812. m.findOne(function(err, doc) {
  1813. assert.ifError(err);
  1814. assert.equal(doc.name, 'fairgrounds');
  1815. done();
  1816. });
  1817. });
  1818. });
  1819. });
  1820. describe('when doc + callback passed', function() {
  1821. it('works', function(done) {
  1822. var m = mquery(col).where({ _id: id }).update({ name: 'changed' }, function(err, num) {
  1823. assert.ifError(err);
  1824. assert.ok(1, num);
  1825. m.findOne(function(err, doc) {
  1826. assert.ifError(err);
  1827. assert.equal(doc.name, 'changed');
  1828. done();
  1829. });
  1830. });
  1831. });
  1832. });
  1833. describe('when just callback passed', function() {
  1834. it('works', function(done) {
  1835. var m = mquery(col).where({ _id: id });
  1836. m.setOptions({ safe: true });
  1837. m.update({ name: 'Frankenweenie' });
  1838. m.update(function(err, res) {
  1839. assert.ifError(err);
  1840. assert.equal(res.result.n, 1);
  1841. m.findOne(function(err, doc) {
  1842. assert.ifError(err);
  1843. assert.equal(doc.name, 'Frankenweenie');
  1844. done();
  1845. });
  1846. });
  1847. });
  1848. });
  1849. describe('without a callback', function() {
  1850. it('when forced by exec()', function(done) {
  1851. var m = mquery(col).where({ _id: id });
  1852. m.setOptions({ safe: true, multi: true });
  1853. m.update({ name: 'forced' });
  1854. var update = m._collection.update;
  1855. m._collection.update = function(conds, doc, opts) {
  1856. m._collection.update = update;
  1857. assert.ok(opts.safe);
  1858. assert.ok(true === opts.multi);
  1859. assert.equal('forced', doc.$set.name);
  1860. done();
  1861. };
  1862. m.exec();
  1863. });
  1864. });
  1865. describe('except when update doc is empty and missing overwrite flag', function() {
  1866. it('works', function(done) {
  1867. var m = mquery(col).where({ _id: id });
  1868. m.setOptions({ safe: true });
  1869. m.update({ }, function(err, num) {
  1870. assert.ifError(err);
  1871. assert.ok(0 === num);
  1872. setTimeout(function() {
  1873. m.findOne(function(err, doc) {
  1874. assert.ifError(err);
  1875. assert.equal(3, mquery.utils.keys(doc).length);
  1876. assert.equal(id, doc._id.toString());
  1877. assert.equal('Frankenweenie', doc.name);
  1878. done();
  1879. });
  1880. }, 300);
  1881. });
  1882. });
  1883. });
  1884. describe('when update doc is set with overwrite flag', function() {
  1885. it('works', function(done) {
  1886. var m = mquery(col).where({ _id: id });
  1887. m.setOptions({ safe: true, overwrite: true });
  1888. m.update({ all: 'yep', two: 2 }, function(err, res) {
  1889. assert.ifError(err);
  1890. assert.equal(res.result.n, 1);
  1891. m.findOne(function(err, doc) {
  1892. assert.ifError(err);
  1893. assert.equal(3, mquery.utils.keys(doc).length);
  1894. assert.equal('yep', doc.all);
  1895. assert.equal(2, doc.two);
  1896. assert.equal(id, doc._id.toString());
  1897. done();
  1898. });
  1899. });
  1900. });
  1901. });
  1902. describe('when update doc is empty with overwrite flag', function() {
  1903. it('works', function(done) {
  1904. var m = mquery(col).where({ _id: id });
  1905. m.setOptions({ safe: true, overwrite: true });
  1906. m.update({ }, function(err, res) {
  1907. assert.ifError(err);
  1908. assert.equal(res.result.n, 1);
  1909. m.findOne(function(err, doc) {
  1910. assert.ifError(err);
  1911. assert.equal(1, mquery.utils.keys(doc).length);
  1912. assert.equal(id, doc._id.toString());
  1913. done();
  1914. });
  1915. });
  1916. });
  1917. });
  1918. describe('when boolean (true) - exec()', function() {
  1919. it('works', function(done) {
  1920. var m = mquery(col).where({ _id: id });
  1921. m.update({ name: 'bool' }).update(true);
  1922. setTimeout(function() {
  1923. m.findOne(function(err, doc) {
  1924. assert.ifError(err);
  1925. assert.ok(doc);
  1926. assert.equal('bool', doc.name);
  1927. done();
  1928. });
  1929. }, 300);
  1930. });
  1931. });
  1932. });
  1933. });
  1934. describe('remove', function() {
  1935. describe('with 0 args', function() {
  1936. var name = 'remove: no args test';
  1937. before(function(done) {
  1938. col.insert({ name: name }, { safe: true }, done);
  1939. });
  1940. after(function(done) {
  1941. col.remove({ name: name }, { safe: true }, done);
  1942. });
  1943. it('does not execute', function(done) {
  1944. var remove = col.remove;
  1945. col.remove = function() {
  1946. col.remove = remove;
  1947. done(new Error('remove executed!'));
  1948. };
  1949. mquery(col).where({ name: name }).remove();
  1950. setTimeout(function() {
  1951. col.remove = remove;
  1952. done();
  1953. }, 10);
  1954. });
  1955. it('chains', function() {
  1956. var m = mquery();
  1957. assert.equal(m, m.remove());
  1958. });
  1959. });
  1960. describe('with 1 argument', function() {
  1961. var name = 'remove: 1 arg test';
  1962. before(function(done) {
  1963. col.insert({ name: name }, { safe: true }, done);
  1964. });
  1965. after(function(done) {
  1966. col.remove({ name: name }, { safe: true }, done);
  1967. });
  1968. describe('that is a', function() {
  1969. it('plain object', function() {
  1970. var m = mquery(col).remove({ name: 'Whiskers' });
  1971. m.remove({ color: '#fff' });
  1972. assert.deepEqual({ name: 'Whiskers', color: '#fff' }, m._conditions);
  1973. });
  1974. it('query', function() {
  1975. var q = mquery({ color: '#fff' });
  1976. var m = mquery(col).remove({ name: 'Whiskers' });
  1977. m.remove(q);
  1978. assert.deepEqual({ name: 'Whiskers', color: '#fff' }, m._conditions);
  1979. });
  1980. it('function', function(done) {
  1981. mquery(col, { safe: true }).where({name: name}).remove(function(err) {
  1982. assert.ifError(err);
  1983. mquery(col).findOne({ name: name }, function(err, doc) {
  1984. assert.ifError(err);
  1985. assert.equal(null, doc);
  1986. done();
  1987. });
  1988. });
  1989. });
  1990. it('boolean (true) - execute', function(done) {
  1991. col.insert({ name: name }, { safe: true }, function(err) {
  1992. assert.ifError(err);
  1993. mquery(col).findOne({ name: name }, function(err, doc) {
  1994. assert.ifError(err);
  1995. assert.ok(doc);
  1996. mquery(col).remove(true);
  1997. setTimeout(function() {
  1998. mquery(col).find(function(err, docs) {
  1999. assert.ifError(err);
  2000. assert.ok(docs);
  2001. assert.equal(0, docs.length);
  2002. done();
  2003. });
  2004. }, 300);
  2005. });
  2006. });
  2007. });
  2008. });
  2009. });
  2010. describe('with 2 arguments', function() {
  2011. var name = 'remove: 2 arg test';
  2012. beforeEach(function(done) {
  2013. col.remove({}, { safe: true }, function(err) {
  2014. assert.ifError(err);
  2015. col.insert([{ name: 'shelly' }, { name: name }], { safe: true }, function(err) {
  2016. assert.ifError(err);
  2017. mquery(col).find(function(err, docs) {
  2018. assert.ifError(err);
  2019. assert.equal(2, docs.length);
  2020. done();
  2021. });
  2022. });
  2023. });
  2024. });
  2025. describe('plain object + callback', function() {
  2026. it('works', function(done) {
  2027. mquery(col).remove({ name: name }, function(err) {
  2028. assert.ifError(err);
  2029. mquery(col).find(function(err, docs) {
  2030. assert.ifError(err);
  2031. assert.ok(docs);
  2032. assert.equal(1, docs.length);
  2033. assert.equal('shelly', docs[0].name);
  2034. done();
  2035. });
  2036. });
  2037. });
  2038. });
  2039. describe('mquery + callback', function() {
  2040. it('works', function(done) {
  2041. var m = mquery({ name: name });
  2042. mquery(col).remove(m, function(err) {
  2043. assert.ifError(err);
  2044. mquery(col).find(function(err, docs) {
  2045. assert.ifError(err);
  2046. assert.ok(docs);
  2047. assert.equal(1, docs.length);
  2048. assert.equal('shelly', docs[0].name);
  2049. done();
  2050. });
  2051. });
  2052. });
  2053. });
  2054. });
  2055. });
  2056. function validateFindAndModifyOptions(method) {
  2057. describe('validates its option', function() {
  2058. it('sort', function(done) {
  2059. assert.doesNotThrow(function() {
  2060. mquery().sort('x')[method]();
  2061. });
  2062. done();
  2063. });
  2064. it('select', function(done) {
  2065. assert.doesNotThrow(function() {
  2066. mquery().select('x')[method]();
  2067. });
  2068. done();
  2069. });
  2070. it('limit', function(done) {
  2071. assert.throws(function() {
  2072. mquery().limit(3)[method]();
  2073. }, new RegExp('limit cannot be used with ' + method));
  2074. done();
  2075. });
  2076. it('skip', function(done) {
  2077. assert.throws(function() {
  2078. mquery().skip(3)[method]();
  2079. }, new RegExp('skip cannot be used with ' + method));
  2080. done();
  2081. });
  2082. it('batchSize', function(done) {
  2083. assert.throws(function() {
  2084. mquery({}, { batchSize: 3 })[method]();
  2085. }, new RegExp('batchSize cannot be used with ' + method));
  2086. done();
  2087. });
  2088. it('maxScan', function(done) {
  2089. assert.throws(function() {
  2090. mquery().maxScan(300)[method]();
  2091. }, new RegExp('maxScan cannot be used with ' + method));
  2092. done();
  2093. });
  2094. it('snapshot', function(done) {
  2095. assert.throws(function() {
  2096. mquery().snapshot()[method]();
  2097. }, new RegExp('snapshot cannot be used with ' + method));
  2098. done();
  2099. });
  2100. it('hint', function(done) {
  2101. assert.throws(function() {
  2102. mquery().hint({ x: 1 })[method]();
  2103. }, new RegExp('hint cannot be used with ' + method));
  2104. done();
  2105. });
  2106. it('tailable', function(done) {
  2107. assert.throws(function() {
  2108. mquery().tailable()[method]();
  2109. }, new RegExp('tailable cannot be used with ' + method));
  2110. done();
  2111. });
  2112. it('comment', function(done) {
  2113. assert.throws(function() {
  2114. mquery().comment('mquery')[method]();
  2115. }, new RegExp('comment cannot be used with ' + method));
  2116. done();
  2117. });
  2118. });
  2119. }
  2120. describe('findOneAndUpdate', function() {
  2121. var name = 'findOneAndUpdate + fn';
  2122. validateFindAndModifyOptions('findOneAndUpdate');
  2123. describe('with 0 args', function() {
  2124. it('makes no changes', function() {
  2125. var m = mquery();
  2126. var n = m.findOneAndUpdate();
  2127. assert.deepEqual(m, n);
  2128. });
  2129. });
  2130. describe('with 1 arg', function() {
  2131. describe('that is an object', function() {
  2132. it('updates the doc', function() {
  2133. var m = mquery();
  2134. var n = m.findOneAndUpdate({ $set: { name: '1 arg' }});
  2135. assert.deepEqual(n._update, { $set: { name: '1 arg' }});
  2136. });
  2137. });
  2138. describe('that is a query', function() {
  2139. it('updates the doc', function() {
  2140. var m = mquery({ name: name }).update({ x: 1 });
  2141. var n = mquery().findOneAndUpdate(m);
  2142. assert.deepEqual(n._update, { x: 1 });
  2143. });
  2144. });
  2145. it('that is a function', function(done) {
  2146. col.insert({ name: name }, { safe: true }, function(err) {
  2147. assert.ifError(err);
  2148. var m = mquery({ name: name }).collection(col);
  2149. name = '1 arg';
  2150. var n = m.update({ $set: { name: name }});
  2151. n.findOneAndUpdate(function(err, res) {
  2152. assert.ifError(err);
  2153. assert.ok(res.value);
  2154. assert.equal(name, res.value.name);
  2155. done();
  2156. });
  2157. });
  2158. });
  2159. });
  2160. describe('with 2 args', function() {
  2161. it('conditions + update', function() {
  2162. var m = mquery(col);
  2163. m.findOneAndUpdate({ name: name }, { age: 100 });
  2164. assert.deepEqual({ name: name }, m._conditions);
  2165. assert.deepEqual({ age: 100 }, m._update);
  2166. });
  2167. it('query + update', function() {
  2168. var n = mquery({ name: name });
  2169. var m = mquery(col);
  2170. m.findOneAndUpdate(n, { age: 100 });
  2171. assert.deepEqual({ name: name }, m._conditions);
  2172. assert.deepEqual({ age: 100 }, m._update);
  2173. });
  2174. it('update + callback', function(done) {
  2175. var m = mquery(col).where({ name: name });
  2176. m.findOneAndUpdate({}, { $inc: { age: 10 }}, { new: true }, function(err, res) {
  2177. assert.ifError(err);
  2178. assert.equal(10, res.value.age);
  2179. done();
  2180. });
  2181. });
  2182. });
  2183. describe('with 3 args', function() {
  2184. it('conditions + update + options', function() {
  2185. var m = mquery();
  2186. var n = m.findOneAndUpdate({ name: name }, { works: true }, { new: false });
  2187. assert.deepEqual({ name: name}, n._conditions);
  2188. assert.deepEqual({ works: true }, n._update);
  2189. assert.deepEqual({ new: false }, n.options);
  2190. });
  2191. it('conditions + update + callback', function(done) {
  2192. var m = mquery(col);
  2193. m.findOneAndUpdate({ name: name }, { works: true }, { new: true }, function(err, res) {
  2194. assert.ifError(err);
  2195. assert.ok(res.value);
  2196. assert.equal(name, res.value.name);
  2197. assert.ok(true === res.value.works);
  2198. done();
  2199. });
  2200. });
  2201. });
  2202. describe('with 4 args', function() {
  2203. it('conditions + update + options + callback', function(done) {
  2204. var m = mquery(col);
  2205. m.findOneAndUpdate({ name: name }, { works: false }, { new: false }, function(err, res) {
  2206. assert.ifError(err);
  2207. assert.ok(res.value);
  2208. assert.equal(name, res.value.name);
  2209. assert.ok(true === res.value.works);
  2210. done();
  2211. });
  2212. });
  2213. });
  2214. });
  2215. describe('findOneAndRemove', function() {
  2216. var name = 'findOneAndRemove';
  2217. validateFindAndModifyOptions('findOneAndRemove');
  2218. describe('with 0 args', function() {
  2219. it('makes no changes', function() {
  2220. var m = mquery();
  2221. var n = m.findOneAndRemove();
  2222. assert.deepEqual(m, n);
  2223. });
  2224. });
  2225. describe('with 1 arg', function() {
  2226. describe('that is an object', function() {
  2227. it('updates the doc', function() {
  2228. var m = mquery();
  2229. var n = m.findOneAndRemove({ name: '1 arg' });
  2230. assert.deepEqual(n._conditions, { name: '1 arg' });
  2231. });
  2232. });
  2233. describe('that is a query', function() {
  2234. it('updates the doc', function() {
  2235. var m = mquery({ name: name });
  2236. var n = m.findOneAndRemove(m);
  2237. assert.deepEqual(n._conditions, { name: name });
  2238. });
  2239. });
  2240. it('that is a function', function(done) {
  2241. col.insert({ name: name }, { safe: true }, function(err) {
  2242. assert.ifError(err);
  2243. var m = mquery({ name: name }).collection(col);
  2244. m.findOneAndRemove(function(err, res) {
  2245. assert.ifError(err);
  2246. assert.ok(res.value);
  2247. assert.equal(name, res.value.name);
  2248. done();
  2249. });
  2250. });
  2251. });
  2252. });
  2253. describe('with 2 args', function() {
  2254. it('conditions + options', function() {
  2255. var m = mquery(col);
  2256. m.findOneAndRemove({ name: name }, { new: false });
  2257. assert.deepEqual({ name: name }, m._conditions);
  2258. assert.deepEqual({ new: false }, m.options);
  2259. });
  2260. it('query + options', function() {
  2261. var n = mquery({ name: name });
  2262. var m = mquery(col);
  2263. m.findOneAndRemove(n, { sort: { x: 1 }});
  2264. assert.deepEqual({ name: name }, m._conditions);
  2265. assert.deepEqual({ sort: { 'x': 1 }}, m.options);
  2266. });
  2267. it('conditions + callback', function(done) {
  2268. col.insert({ name: name }, { safe: true }, function(err) {
  2269. assert.ifError(err);
  2270. var m = mquery(col);
  2271. m.findOneAndRemove({ name: name }, function(err, res) {
  2272. assert.ifError(err);
  2273. assert.equal(name, res.value.name);
  2274. done();
  2275. });
  2276. });
  2277. });
  2278. it('query + callback', function(done) {
  2279. col.insert({ name: name }, { safe: true }, function(err) {
  2280. assert.ifError(err);
  2281. var n = mquery({ name: name });
  2282. var m = mquery(col);
  2283. m.findOneAndRemove(n, function(err, res) {
  2284. assert.ifError(err);
  2285. assert.equal(name, res.value.name);
  2286. done();
  2287. });
  2288. });
  2289. });
  2290. });
  2291. describe('with 3 args', function() {
  2292. it('conditions + options + callback', function(done) {
  2293. name = 'findOneAndRemove + conds + options + cb';
  2294. col.insert([{ name: name }, { name: 'a' }], { safe: true }, function(err) {
  2295. assert.ifError(err);
  2296. var m = mquery(col);
  2297. m.findOneAndRemove({ name: name }, { sort: { name: 1 }}, function(err, res) {
  2298. assert.ifError(err);
  2299. assert.ok(res.value);
  2300. assert.equal(name, res.value.name);
  2301. done();
  2302. });
  2303. });
  2304. });
  2305. });
  2306. });
  2307. describe('exec', function() {
  2308. beforeEach(function(done) {
  2309. col.insert([{ name: 'exec', age: 1 }, { name: 'exec', age: 2 }], done);
  2310. });
  2311. afterEach(function(done) {
  2312. mquery(col).remove(done);
  2313. });
  2314. it('requires an op', function() {
  2315. assert.throws(function() {
  2316. mquery().exec();
  2317. }, /Missing query type/);
  2318. });
  2319. describe('find', function() {
  2320. it('works', function(done) {
  2321. var m = mquery(col).find({ name: 'exec' });
  2322. m.exec(function(err, docs) {
  2323. assert.ifError(err);
  2324. assert.equal(2, docs.length);
  2325. done();
  2326. });
  2327. });
  2328. it('works with readPreferences', function(done) {
  2329. var m = mquery(col).find({ name: 'exec' });
  2330. try {
  2331. var rp = new require('mongodb').ReadPreference('primary');
  2332. m.read(rp);
  2333. } catch (e) {
  2334. done(e.code === 'MODULE_NOT_FOUND' ? null : e);
  2335. return;
  2336. }
  2337. m.exec(function(err, docs) {
  2338. assert.ifError(err);
  2339. assert.equal(2, docs.length);
  2340. done();
  2341. });
  2342. });
  2343. it('works with hint', function(done) {
  2344. mquery(col).hint({ _id: 1 }).find({ name: 'exec' }).exec(function(err, docs) {
  2345. assert.ifError(err);
  2346. assert.equal(2, docs.length);
  2347. mquery(col).hint('_id_').find({ age: 1 }).exec(function(err, docs) {
  2348. assert.ifError(err);
  2349. assert.equal(1, docs.length);
  2350. done();
  2351. });
  2352. });
  2353. });
  2354. it('works with readConcern', function(done) {
  2355. var m = mquery(col).find({ name: 'exec' });
  2356. m.readConcern('l');
  2357. m.exec(function(err, docs) {
  2358. assert.ifError(err);
  2359. assert.equal(2, docs.length);
  2360. done();
  2361. });
  2362. });
  2363. it('works with collation', function(done) {
  2364. var m = mquery(col).find({ name: 'EXEC' });
  2365. m.collation({ locale: 'en_US', strength: 1 });
  2366. m.exec(function(err, docs) {
  2367. assert.ifError(err);
  2368. assert.equal(2, docs.length);
  2369. done();
  2370. });
  2371. });
  2372. });
  2373. it('findOne', function(done) {
  2374. var m = mquery(col).findOne({ age: 2 });
  2375. m.exec(function(err, doc) {
  2376. assert.ifError(err);
  2377. assert.equal(2, doc.age);
  2378. done();
  2379. });
  2380. });
  2381. it('count', function(done) {
  2382. var m = mquery(col).count({ name: 'exec' });
  2383. m.exec(function(err, count) {
  2384. assert.ifError(err);
  2385. assert.equal(2, count);
  2386. done();
  2387. });
  2388. });
  2389. it('distinct', function(done) {
  2390. var m = mquery({ name: 'exec' });
  2391. m.collection(col);
  2392. m.distinct('age');
  2393. m.exec(function(err, array) {
  2394. assert.ifError(err);
  2395. assert.ok(Array.isArray(array));
  2396. assert.equal(2, array.length);
  2397. assert(~array.indexOf(1));
  2398. assert(~array.indexOf(2));
  2399. done();
  2400. });
  2401. });
  2402. describe('update', function() {
  2403. var num;
  2404. it('with a callback', function(done) {
  2405. var m = mquery(col);
  2406. m.where({ name: 'exec' });
  2407. m.count(function(err, _num) {
  2408. assert.ifError(err);
  2409. num = _num;
  2410. m.setOptions({ multi: true });
  2411. m.update({ name: 'exec + update' });
  2412. m.exec(function(err, res) {
  2413. assert.ifError(err);
  2414. assert.equal(num, res.result.n);
  2415. mquery(col).find({ name: 'exec + update' }, function(err, docs) {
  2416. assert.ifError(err);
  2417. assert.equal(num, docs.length);
  2418. done();
  2419. });
  2420. });
  2421. });
  2422. });
  2423. describe('updateMany', function() {
  2424. it('works', function(done) {
  2425. mquery(col).updateMany({ name: 'exec' }, { name: 'test' }).
  2426. exec(function(error) {
  2427. assert.ifError(error);
  2428. mquery(col).count({ name: 'test' }).exec(function(error, res) {
  2429. assert.ifError(error);
  2430. assert.equal(res, 2);
  2431. done();
  2432. });
  2433. });
  2434. });
  2435. it('works with write concern', function(done) {
  2436. mquery(col).updateMany({ name: 'exec' }, { name: 'test' })
  2437. .w(1).j(true).wtimeout(1000)
  2438. .exec(function(error) {
  2439. assert.ifError(error);
  2440. mquery(col).count({ name: 'test' }).exec(function(error, res) {
  2441. assert.ifError(error);
  2442. assert.equal(res, 2);
  2443. done();
  2444. });
  2445. });
  2446. });
  2447. });
  2448. describe('updateOne', function() {
  2449. it('works', function(done) {
  2450. mquery(col).updateOne({ name: 'exec' }, { name: 'test' }).
  2451. exec(function(error) {
  2452. assert.ifError(error);
  2453. mquery(col).count({ name: 'test' }).exec(function(error, res) {
  2454. assert.ifError(error);
  2455. assert.equal(res, 1);
  2456. done();
  2457. });
  2458. });
  2459. });
  2460. });
  2461. describe('replaceOne', function() {
  2462. it('works', function(done) {
  2463. mquery(col).replaceOne({ name: 'exec' }, { name: 'test' }).
  2464. exec(function(error) {
  2465. assert.ifError(error);
  2466. mquery(col).findOne({ name: 'test' }).exec(function(error, res) {
  2467. assert.ifError(error);
  2468. assert.equal(res.name, 'test');
  2469. assert.ok(res.age == null);
  2470. done();
  2471. });
  2472. });
  2473. });
  2474. });
  2475. it('without a callback', function(done) {
  2476. var m = mquery(col);
  2477. m.where({ name: 'exec + update' }).setOptions({ multi: true });
  2478. m.update({ name: 'exec' });
  2479. // unsafe write
  2480. m.exec();
  2481. setTimeout(function() {
  2482. mquery(col).find({ name: 'exec' }, function(err, docs) {
  2483. assert.ifError(err);
  2484. assert.equal(2, docs.length);
  2485. done();
  2486. });
  2487. }, 200);
  2488. });
  2489. it('preserves key ordering', function(done) {
  2490. var m = mquery(col);
  2491. var m2 = m.update({ _id : 'something' }, { '1' : 1, '2' : 2, '3' : 3});
  2492. var doc = m2._updateForExec().$set;
  2493. var count = 0;
  2494. for (var i in doc) {
  2495. if (count == 0) {
  2496. assert.equal('1', i);
  2497. } else if (count == 1) {
  2498. assert.equal('2', i);
  2499. } else if (count == 2) {
  2500. assert.equal('3', i);
  2501. }
  2502. count++;
  2503. }
  2504. done();
  2505. });
  2506. });
  2507. describe('remove', function() {
  2508. it('with a callback', function(done) {
  2509. var m = mquery(col).where({ age: 2 }).remove();
  2510. m.exec(function(err, res) {
  2511. assert.ifError(err);
  2512. assert.equal(1, res.result.n);
  2513. done();
  2514. });
  2515. });
  2516. it('without a callback', function(done) {
  2517. var m = mquery(col).where({ age: 1 }).remove();
  2518. m.exec();
  2519. setTimeout(function() {
  2520. mquery(col).where('name', 'exec').count(function(err, num) {
  2521. assert.equal(1, num);
  2522. done();
  2523. });
  2524. }, 200);
  2525. });
  2526. });
  2527. describe('deleteOne', function() {
  2528. it('with a callback', function(done) {
  2529. var m = mquery(col).where({ age: { $gte: 0 } }).deleteOne();
  2530. m.exec(function(err, res) {
  2531. assert.ifError(err);
  2532. assert.equal(res.result.n, 1);
  2533. done();
  2534. });
  2535. });
  2536. it('with justOne set', function(done) {
  2537. var m = mquery(col).where({ age: { $gte: 0 } }).
  2538. // Should ignore `justOne`
  2539. setOptions({ justOne: false }).
  2540. deleteOne();
  2541. m.exec(function(err, res) {
  2542. assert.ifError(err);
  2543. assert.equal(res.result.n, 1);
  2544. done();
  2545. });
  2546. });
  2547. });
  2548. describe('deleteMany', function() {
  2549. it('with a callback', function(done) {
  2550. var m = mquery(col).where({ age: { $gte: 0 } }).deleteMany();
  2551. m.exec(function(err, res) {
  2552. assert.ifError(err);
  2553. assert.equal(res.result.n, 2);
  2554. done();
  2555. });
  2556. });
  2557. });
  2558. describe('findOneAndUpdate', function() {
  2559. it('with a callback', function(done) {
  2560. var m = mquery(col);
  2561. m.findOneAndUpdate({ name: 'exec', age: 1 }, { $set: { name: 'findOneAndUpdate' }});
  2562. m.exec(function(err, res) {
  2563. assert.ifError(err);
  2564. assert.equal('findOneAndUpdate', res.value.name);
  2565. done();
  2566. });
  2567. });
  2568. });
  2569. describe('findOneAndRemove', function() {
  2570. it('with a callback', function(done) {
  2571. var m = mquery(col);
  2572. m.findOneAndRemove({ name: 'exec', age: 2 });
  2573. m.exec(function(err, res) {
  2574. assert.ifError(err);
  2575. assert.equal('exec', res.value.name);
  2576. assert.equal(2, res.value.age);
  2577. mquery(col).count({ name: 'exec' }, function(err, num) {
  2578. assert.ifError(err);
  2579. assert.equal(1, num);
  2580. done();
  2581. });
  2582. });
  2583. });
  2584. });
  2585. });
  2586. describe('setTraceFunction', function() {
  2587. beforeEach(function(done) {
  2588. col.insert([{ name: 'trace', age: 93 }], done);
  2589. });
  2590. it('calls trace function when executing query', function(done) {
  2591. var m = mquery(col);
  2592. var resultTraceCalled;
  2593. m.setTraceFunction(function(method, queryInfo) {
  2594. try {
  2595. assert.equal('findOne', method);
  2596. assert.equal('trace', queryInfo.conditions.name);
  2597. } catch (e) {
  2598. done(e);
  2599. }
  2600. return function(err, result, millis) {
  2601. try {
  2602. assert.equal(93, result.age);
  2603. assert.ok(typeof millis === 'number');
  2604. } catch (e) {
  2605. done(e);
  2606. }
  2607. resultTraceCalled = true;
  2608. };
  2609. });
  2610. m.findOne({name: 'trace'}, function(err, doc) {
  2611. assert.ifError(err);
  2612. assert.equal(resultTraceCalled, true);
  2613. assert.equal(93, doc.age);
  2614. done();
  2615. });
  2616. });
  2617. it('inherits trace function when calling toConstructor', function(done) {
  2618. function traceFunction() { return function() {}; }
  2619. var tracedQuery = mquery().setTraceFunction(traceFunction).toConstructor();
  2620. var query = tracedQuery();
  2621. assert.equal(traceFunction, query._traceFunction);
  2622. done();
  2623. });
  2624. });
  2625. describe('thunk', function() {
  2626. it('returns a function', function(done) {
  2627. assert.equal('function', typeof mquery().thunk());
  2628. done();
  2629. });
  2630. it('passes the fn arg to `exec`', function(done) {
  2631. function cb() {}
  2632. var m = mquery();
  2633. m.exec = function testing(fn) {
  2634. assert.equal(this, m);
  2635. assert.equal(cb, fn);
  2636. done();
  2637. };
  2638. m.thunk()(cb);
  2639. });
  2640. });
  2641. describe('then', function() {
  2642. before(function(done) {
  2643. col.insert([{ name: 'then', age: 1 }, { name: 'then', age: 2 }], done);
  2644. });
  2645. after(function(done) {
  2646. mquery(col).remove({ name: 'then' }).exec(done);
  2647. });
  2648. it('returns a promise A+ compat object', function(done) {
  2649. var m = mquery(col).find();
  2650. assert.equal('function', typeof m.then);
  2651. done();
  2652. });
  2653. it('creates a promise that is resolved on success', function(done) {
  2654. var promise = mquery(col).count({ name: 'then' }).then();
  2655. promise.then(function(count) {
  2656. assert.equal(2, count);
  2657. done();
  2658. }, done);
  2659. });
  2660. it('supports exec() cb being called synchronously #66', function(done) {
  2661. var query = mquery(col).count({ name: 'then' });
  2662. query.exec = function(cb) {
  2663. cb(null, 66);
  2664. };
  2665. query.then(success, done);
  2666. function success(count) {
  2667. assert.equal(66, count);
  2668. done();
  2669. }
  2670. });
  2671. it('supports other Promise libs', function(done) {
  2672. var bluebird = mquery.Promise;
  2673. // hack for testing
  2674. mquery.Promise = function P() {
  2675. mquery.Promise = bluebird;
  2676. this.then = function(x, y) {
  2677. return x + y;
  2678. };
  2679. };
  2680. var val = mquery(col).count({ name: 'exec' }).then(1, 2);
  2681. assert.equal(val, 3);
  2682. done();
  2683. });
  2684. });
  2685. describe('stream', function() {
  2686. before(function(done) {
  2687. col.insert([{ name: 'stream', age: 1 }, { name: 'stream', age: 2 }], done);
  2688. });
  2689. after(function(done) {
  2690. mquery(col).remove({ name: 'stream' }).exec(done);
  2691. });
  2692. describe('throws', function() {
  2693. describe('if used with non-find operations', function() {
  2694. var ops = ['update', 'findOneAndUpdate', 'remove', 'count', 'distinct'];
  2695. ops.forEach(function(op) {
  2696. assert.throws(function() {
  2697. mquery(col)[op]().stream();
  2698. });
  2699. });
  2700. });
  2701. });
  2702. it('returns a stream', function(done) {
  2703. var stream = mquery(col).find({ name: 'stream' }).stream();
  2704. var count = 0;
  2705. var err;
  2706. stream.on('data', function(doc) {
  2707. assert.equal('stream', doc.name);
  2708. ++count;
  2709. });
  2710. stream.on('error', function(er) {
  2711. err = er;
  2712. });
  2713. stream.on('end', function() {
  2714. if (err) return done(err);
  2715. assert.equal(2, count);
  2716. done();
  2717. });
  2718. });
  2719. });
  2720. function noDistinct(type) {
  2721. it('cannot be used with distinct()', function(done) {
  2722. assert.throws(function() {
  2723. mquery().distinct('name')[type](4);
  2724. }, new RegExp(type + ' cannot be used with distinct'));
  2725. done();
  2726. });
  2727. }
  2728. function no(method, type) {
  2729. it('cannot be used with ' + method + '()', function(done) {
  2730. assert.throws(function() {
  2731. mquery()[method]()[type](4);
  2732. }, new RegExp(type + ' cannot be used with ' + method));
  2733. done();
  2734. });
  2735. }
  2736. // query internal
  2737. describe('_updateForExec', function() {
  2738. it('returns a clone of the update object with same key order #19', function(done) {
  2739. var update = {};
  2740. update.$push = { n: { $each: [{x:10}], $slice: -1, $sort: {x:1}}};
  2741. var q = mquery().update({ x: 1 }, update);
  2742. // capture original key order
  2743. var order = [];
  2744. var key;
  2745. for (key in q._update.$push.n) {
  2746. order.push(key);
  2747. }
  2748. // compare output
  2749. var doc = q._updateForExec();
  2750. var i = 0;
  2751. for (key in doc.$push.n) {
  2752. assert.equal(key, order[i]);
  2753. i++;
  2754. }
  2755. done();
  2756. });
  2757. });
  2758. });