deno.land / x / mongoose@6.7.5 / test / model.querying.test.js
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544'use strict';
/** * Test dependencies. */
const start = require('./common');
const Query = require('../lib/query');const assert = require('assert');const random = require('./util').random;const util = require('./util');
const mongoose = start.mongoose;const Schema = mongoose.Schema;const ObjectId = Schema.Types.ObjectId;const MongooseBuffer = mongoose.Types.Buffer;const DocumentObjectId = mongoose.Types.ObjectId;
describe('model: querying:', function() { let Comments; let BlogPostB; let ModSchema; let geoSchema; let db;
before(() => { db = start(); });
beforeEach(() => db.deleteModel(/.*/)); afterEach(() => util.clearTestData(db)); afterEach(() => require('./util').stopRemainingOps(db));
beforeEach(function() { Comments = new Schema();
Comments.add({ title: String, date: Date, body: String, comments: [Comments] });
BlogPostB = new Schema({ title: String, author: String, slug: String, date: Date, meta: { date: Date, visitors: Number }, published: Boolean, mixed: {}, numbers: [Number], tags: [String], sigs: [Buffer], owners: [ObjectId], comments: [Comments], def: { type: String, default: 'kandinsky' } });
BlogPostB = db.model('BlogPost', BlogPostB);
ModSchema = Schema({ num: Number, str: String });
geoSchema = new Schema({ loc: { type: [Number], index: '2d' } }); });
let mongo26_or_greater = false; before(async function() { const version = await start.mongodVersion();
mongo26_or_greater = version[0] > 2 || (version[0] === 2 && version[1] >= 6); if (!mongo26_or_greater) { console.log('not testing mongodb 2.6 features'); } });
after(async function() { await db.close(); });
it('find returns a Query', function(done) { // query assert.ok(BlogPostB.find({}) instanceof Query);
// query, fields assert.ok(BlogPostB.find({}, {}) instanceof Query);
// query, fields (empty string) assert.ok(BlogPostB.find({}, '') instanceof Query);
// query, fields, options assert.ok(BlogPostB.find({}, {}, {}) instanceof Query);
// query, fields (null), options assert.ok(BlogPostB.find({}, null, {}) instanceof Query);
done(); });
it('findOne returns a Query', function(done) { // query assert.ok(BlogPostB.findOne({}) instanceof Query);
// query, fields assert.ok(BlogPostB.findOne({}, {}) instanceof Query);
// query, fields (empty string) assert.ok(BlogPostB.findOne({}, '') instanceof Query);
// query, fields, options assert.ok(BlogPostB.findOne({}, {}, {}) instanceof Query);
// query, fields (null), options assert.ok(BlogPostB.findOne({}, null, {}) instanceof Query);
done(); });
it('an empty find does not hang', function(done) { function fn() { done(); }
BlogPostB.find({}, fn); });
it('a query is executed when a callback is passed', function(done) { let count = 5; const q = { _id: new DocumentObjectId() }; // make sure the query is fast
function fn() { if (--count) { return; } done(); }
// query assert.ok(BlogPostB.find(q, fn) instanceof Query);
// query, fields (object) assert.ok(BlogPostB.find(q, {}, fn) instanceof Query);
// query, fields (null) assert.ok(BlogPostB.find(q, null, fn) instanceof Query);
// query, fields, options assert.ok(BlogPostB.find(q, {}, {}, fn) instanceof Query);
// query, fields (''), options assert.ok(BlogPostB.find(q, '', {}, fn) instanceof Query); });
it('query is executed where a callback for findOne', function(done) { let count = 5; const q = { _id: new DocumentObjectId() }; // make sure the query is fast
function fn() { if (--count) { return; } done(); }
// query assert.ok(BlogPostB.findOne(q, fn) instanceof Query);
// query, fields assert.ok(BlogPostB.findOne(q, {}, fn) instanceof Query);
// query, fields (empty string) assert.ok(BlogPostB.findOne(q, '', fn) instanceof Query);
// query, fields, options assert.ok(BlogPostB.findOne(q, {}, {}, fn) instanceof Query);
// query, fields (null), options assert.ok(BlogPostB.findOne(q, null, {}, fn) instanceof Query); });
describe.skip('count', function() { it('returns a Query', function(done) { assert.ok(BlogPostB.count({}) instanceof Query); done(); });
it('Query executes when you pass a callback', function(done) { let pending = 2;
function fn() { if (--pending) { return; } done(); }
assert.ok(BlogPostB.count({}, fn) instanceof Query); assert.ok(BlogPostB.count(fn) instanceof Query); });
it('counts documents', function(done) { const title = 'Wooooot ' + random();
const post = new BlogPostB(); post.set('title', title);
post.save(function(err) { assert.ifError(err);
const post = new BlogPostB(); post.set('title', title);
post.save(function(err) { assert.ifError(err);
BlogPostB.count({ title: title }, function(err, count) { assert.ifError(err);
assert.equal(typeof count, 'number'); assert.equal(count, 2);
done(); }); }); }); }); });
describe('distinct', function() { it('returns a Query', function(done) { assert.ok(BlogPostB.distinct('title', {}) instanceof Query); done(); });
it('executes when you pass a callback', function(done) { let Address = new Schema({ zip: String }); Address = db.model('Test', Address);
Address.create({ zip: '10010' }, { zip: '10010' }, { zip: '99701' }, function(err) { assert.strictEqual(null, err); const query = Address.distinct('zip', {}, function(err, results) { assert.ifError(err); assert.equal(results.length, 2); assert.ok(results.indexOf('10010') > -1); assert.ok(results.indexOf('99701') > -1); done(); }); assert.ok(query instanceof Query); }); });
it('permits excluding conditions gh-1541', function(done) { let Address = new Schema({ zip: String }); Address = db.model('Test', Address); Address.create({ zip: '10010' }, { zip: '10010' }, { zip: '99701' }, function(err) { assert.ifError(err); Address.distinct('zip', function(err, results) { assert.ifError(err); assert.equal(results.length, 2); assert.ok(results.indexOf('10010') > -1); assert.ok(results.indexOf('99701') > -1); done(); }); }); }); });
describe('update', function() { it('returns a Query', function(done) { assert.ok(BlogPostB.update({}, {}) instanceof Query); assert.ok(BlogPostB.update({}, {}, {}) instanceof Query); done(); });
it('Query executes when you pass a callback', function(done) { let count = 2;
function fn() { if (--count) { return; } done(); }
assert.ok(BlogPostB.update({ title: random() }, {}, fn) instanceof Query); assert.ok(BlogPostB.update({ title: random() }, {}, {}, fn) instanceof Query); });
it('can handle minimize option (gh-3381)', function() { const Model = db.model('Test', { name: String, mixed: Schema.Types.Mixed });
return Model.create({}). then(() => Model.replaceOne({}, { mixed: {}, name: 'abc' }, { minimize: true })). then(() => Model.collection.findOne()). then(doc => { assert.ok(doc.mixed == null); }). catch(err => { throw err; }); }); });
describe('findOne', function() { it('works', function(done) { const title = 'Wooooot ' + random();
const post = new BlogPostB(); post.set('title', title);
post.save(function(err) { assert.ifError(err);
BlogPostB.findOne({ title: title }, function(err, doc) { assert.ifError(err); assert.equal(title, doc.get('title')); assert.equal(doc.isNew, false);
done(); }); }); });
it('casts $modifiers', function(done) { const post = new BlogPostB({ meta: { visitors: -10 } });
post.save(function(err) { assert.ifError(err);
const query = { 'meta.visitors': { $gt: '-20', $lt: -1 } }; BlogPostB.findOne(query, function(err, found) { assert.ifError(err); assert.ok(found); assert.equal(found.get('meta.visitors').valueOf(), post.get('meta.visitors').valueOf()); found.id; // trigger caching assert.equal(found.get('_id').toString(), post.get('_id')); done(); }); }); });
it('querying if an array contains one of multiple members $in a set', function(done) { const post = new BlogPostB();
post.tags.push('football');
post.save(function(err) { assert.ifError(err);
BlogPostB.findOne({ tags: { $in: ['football', 'baseball'] } }, function(err, doc) { assert.ifError(err); assert.equal(doc._id.toString(), post._id);
BlogPostB.findOne({ _id: post._id, tags: /otba/i }, function(err, doc) { assert.ifError(err); assert.equal(doc._id.toString(), post._id); done(); }); }); }); });
it('querying if an array contains one of multiple members $in a set 2', function(done) { const BlogPostA = BlogPostB; const post = new BlogPostA({ tags: ['gooberOne'] });
post.save(function(err) { assert.ifError(err);
const query = { tags: { $in: ['gooberOne'] } };
BlogPostA.findOne(query, function(err, returned) { cb(); assert.ifError(err); assert.ok(!!~returned.tags.indexOf('gooberOne')); assert.equal(returned._id.toString(), post._id); }); });
const doc = { meta: { visitors: 9898, a: null } }; post.collection.insertOne(doc, {}, function(err) { assert.ifError(err);
BlogPostA.findOne({ _id: doc._id }, function(err, found) { cb(); assert.ifError(err); assert.equal(found.get('meta.visitors'), 9898); }); });
let pending = 2;
function cb() { if (--pending) { return; } done(); } });
it('querying via $where a string', function(done) { BlogPostB.create({ title: 'Steve Jobs', author: 'Steve Jobs' }, function(err, created) { assert.ifError(err);
BlogPostB.findOne({ $where: 'this.title && this.title === this.author' }, function(err, found) { assert.ifError(err);
assert.equal(found._id.toString(), created._id); done(); }); }); });
it('querying via $where a function', function(done) { BlogPostB.create({ author: 'Atari', slug: 'Atari' }, function(err, created) { assert.ifError(err);
BlogPostB.findOne({ $where: function() { return (this.author && this.slug && this.author === this.slug); } }, function(err, found) { assert.ifError(err);
assert.equal(found._id.toString(), created._id); done(); }); }); });
it('based on nested fields', function(done) { const post = new BlogPostB({ meta: { visitors: 5678 } });
post.save(function(err) { assert.ifError(err);
BlogPostB.findOne({ 'meta.visitors': 5678 }, function(err, found) { assert.ifError(err); assert.equal(found.get('meta.visitors') .valueOf(), post.get('meta.visitors').valueOf()); assert.equal(found.get('_id').toString(), post.get('_id')); done(); }); }); });
it('based on embedded doc fields (gh-242, gh-463)', function(done) { BlogPostB.create({ comments: [{ title: 'i should be queryable' }], numbers: [1, 2, 33333], tags: ['yes', 'no'] }, function(err, created) { assert.ifError(err); BlogPostB.findOne({ 'comments.title': 'i should be queryable' }, function(err, found) { assert.ifError(err); assert.equal(found._id.toString(), created._id);
BlogPostB.findOne({ 'comments.0.title': 'i should be queryable' }, function(err, found) { assert.ifError(err); assert.equal(found._id.toString(), created._id);
// GH-463 BlogPostB.findOne({ 'numbers.2': 33333 }, function(err, found) { assert.ifError(err); assert.equal(found._id.toString(), created._id);
BlogPostB.findOne({ 'tags.1': 'no' }, function(err, found) { assert.ifError(err); assert.equal(found._id.toString(), created._id); done(); }); }); }); }); }); });
it('works with nested docs and string ids (gh-389)', function(done) { BlogPostB.create({ comments: [{ title: 'i should be queryable by _id' }, { title: 'me too me too!' }] }, function(err, created) { assert.ifError(err); const id = created.comments[1]._id.toString(); BlogPostB.findOne({ 'comments._id': id }, function(err, found) { assert.ifError(err); assert.strictEqual(!!found, true, 'Find by nested doc id hex string fails'); assert.equal(found._id.toString(), created._id); done(); }); }); });
it('using #all with nested #elemMatch', function(done) { const P = BlogPostB; const post = new P({ title: 'nested elemMatch' }); post.comments.push({ title: 'comment A' }, { title: 'comment B' }, { title: 'comment C' });
const id1 = post.comments[1]._id; const id2 = post.comments[2]._id;
post.save(function(err) { assert.ifError(err);
const query0 = { $elemMatch: { _id: id1, title: 'comment B' } }; const query1 = { $elemMatch: { _id: id2.toString(), title: 'comment C' } };
P.findOne({ comments: { $all: [query0, query1] } }, function(err, p) { assert.ifError(err); assert.equal(p.id, post.id); done(); }); }); });
it('using #or with nested #elemMatch', function(done) { const P = BlogPostB; const post = new P({ title: 'nested elemMatch' }); post.comments.push({ title: 'comment D' }, { title: 'comment E' }, { title: 'comment F' });
const id1 = post.comments[1]._id;
post.save(function(err) { assert.ifError(err);
const query0 = { comments: { $elemMatch: { title: 'comment Z' } } }; const query1 = { comments: { $elemMatch: { _id: id1.toString(), title: 'comment E' } } };
P.findOne({ $or: [query0, query1] }, function(err, p) { assert.ifError(err); assert.equal(p.id, post.id); done(); }); }); });
it('buffer $in array', function(done) { BlogPostB.create({ sigs: [Buffer.from([1, 2, 3]), Buffer.from([4, 5, 6]), Buffer.from([7, 8, 9])] }, function(err, created) { assert.ifError(err); BlogPostB.findOne({ sigs: Buffer.from([1, 2, 3]) }, function(err, found) { assert.ifError(err); found.id; assert.equal(found._id.toString(), created._id); const query = { sigs: { $in: [Buffer.from([3, 3, 3]), Buffer.from([4, 5, 6])] } }; BlogPostB.findOne(query, function(err) { assert.ifError(err); done(); }); }); }); });
it('regex with Array (gh-599)', function(done) { const B = BlogPostB; B.create({ tags: 'wooof baaaark meeeeow'.split(' ') }, function(err) { assert.ifError(err); B.findOne({ tags: /ooof$/ }, function(err, doc) { assert.ifError(err); assert.strictEqual(true, !!doc); assert.ok(!!~doc.tags.indexOf('meeeeow'));
B.findOne({ tags: { $regex: 'eow$' } }, function(err, doc) { assert.ifError(err); assert.strictEqual(true, !!doc); assert.strictEqual(true, !!~doc.tags.indexOf('meeeeow')); done(); }); }); }); });
it('regex with options', function(done) { const B = BlogPostB; const post = new B({ title: '$option queries' }); post.save(function(err) { assert.ifError(err); B.findOne({ title: { $regex: ' QUERIES$', $options: 'i' } }, function(err, doc) { assert.strictEqual(null, err, err && err.stack); assert.equal(doc.id, post.id); done(); }); }); });
it('works with $elemMatch and $in combo (gh-1100)', function(done) { const id1 = new DocumentObjectId(); const id2 = new DocumentObjectId();
BlogPostB.create({ owners: [id1, id2] }, function(err, created) { assert.ifError(err); BlogPostB.findOne({ owners: { $elemMatch: { $in: [id2.toString()] } } }, function(err, found) { assert.ifError(err); assert.ok(found); assert.equal(created.id, found.id); done(); }); }); }); });
describe('findById', function() { it('handles undefined', function(done) { const title = 'Edwald ' + random();
const post = new BlogPostB(); post.set('title', title);
post.save(function(err) { assert.ifError(err);
BlogPostB.findById(undefined, function(err, doc) { assert.ifError(err); assert.equal(doc, null); done(); }); }); });
it('works', function(done) { const title = 'Edwald ' + random();
const post = new BlogPostB(); post.set('title', title);
post.save(function(err) { assert.ifError(err);
let pending = 2;
BlogPostB.findById(post.get('_id'), function(err, doc) { assert.ifError(err); assert.ok(doc instanceof BlogPostB); assert.equal(doc.get('title'), title); if (--pending) { return; } done(); });
BlogPostB.findById(post.get('_id').toHexString(), function(err, doc) { assert.ifError(err); assert.ok(doc instanceof BlogPostB); assert.equal(doc.get('title'), title); if (--pending) { return; } done(); }); }); });
it('works with partial initialization', function(done) { let queries = 5;
const post = new BlogPostB();
post.title = 'hahaha'; post.slug = 'woot'; post.meta.visitors = 53; post.tags = ['humidity', 'soggy'];
post.save(function(err) { assert.ifError(err);
BlogPostB.findById(post.get('_id'), function(err, doc) { assert.ifError(err);
assert.equal(doc.isInit('title'), true); assert.equal(doc.isInit('slug'), true); assert.equal(doc.isInit('date'), false); assert.equal(doc.isInit('meta.visitors'), true); assert.equal(doc.meta.visitors.valueOf(), 53); assert.equal(doc.tags.length, 2); if (--queries) { return; } done(); });
BlogPostB.findById(post.get('_id'), 'title', function(err, doc) { assert.ifError(err); assert.equal(doc.isInit('title'), true); assert.equal(doc.isInit('slug'), false); assert.equal(doc.isInit('date'), false); assert.equal(doc.isInit('meta.visitors'), false); assert.equal(doc.meta.visitors, undefined); assert.equal(doc.tags, undefined); if (--queries) { return; } done(); });
BlogPostB.findById(post.get('_id'), '-slug', function(err, doc) { assert.ifError(err); assert.equal(doc.isInit('title'), true); assert.equal(doc.isInit('slug'), false); assert.equal(doc.isInit('date'), false); assert.equal(doc.isInit('meta.visitors'), true); assert.equal(doc.meta.visitors, 53); assert.equal(doc.tags.length, 2); if (--queries) { return; } done(); });
BlogPostB.findById(post.get('_id'), { title: 1 }, function(err, doc) { assert.ifError(err); assert.equal(doc.isInit('title'), true); assert.equal(doc.isInit('slug'), false); assert.equal(doc.isInit('date'), false); assert.equal(doc.isInit('meta.visitors'), false); assert.equal(doc.meta.visitors, undefined); assert.equal(doc.tags, undefined); if (--queries) { return; } done(); });
BlogPostB.findById(post.get('_id'), 'slug', function(err, doc) { assert.ifError(err); assert.equal(doc.isInit('title'), false); assert.equal(doc.isInit('slug'), true); assert.equal(doc.isInit('date'), false); assert.equal(doc.isInit('meta.visitors'), false); assert.equal(doc.meta.visitors, undefined); assert.equal(doc.tags, undefined); if (--queries) { return; } done(); }); }); });
it('querying if an array contains at least a certain single member (gh-220)', function(done) { const post = new BlogPostB();
post.tags.push('cat');
post.save(function(err) { assert.ifError(err);
BlogPostB.findOne({ tags: 'cat' }, function(err, doc) { assert.ifError(err); assert.equal(doc._id.toString(), post._id); done(); }); }); });
it('where an array where the $slice operator', function(done) { BlogPostB.create({ numbers: [500, 600, 700, 800] }, function(err, created) { assert.ifError(err); BlogPostB.findById(created._id, { numbers: { $slice: 2 } }, function(err, found) { assert.ifError(err); assert.equal(found._id.toString(), created._id); assert.equal(found.numbers.length, 2); assert.equal(found.numbers[0], 500); assert.equal(found.numbers[1], 600); BlogPostB.findById(created._id, { numbers: { $slice: -2 } }, function(err, found) { assert.ifError(err); assert.equal(found._id.toString(), created._id); assert.equal(found.numbers.length, 2); assert.equal(found.numbers[0], 700); assert.equal(found.numbers[1], 800); BlogPostB.findById(created._id, { numbers: { $slice: [1, 2] } }, function(err, found) { assert.ifError(err); assert.equal(found._id.toString(), created._id); assert.equal(found.numbers.length, 2); assert.equal(found.numbers[0], 600); assert.equal(found.numbers[1], 700); done(); }); }); }); }); }); });
describe('find', function() { it('works', function(done) { const title = 'Wooooot ' + random();
const post = new BlogPostB(); post.set('title', title);
post.save(function(err) { assert.ifError(err);
const post = new BlogPostB(); post.set('title', title);
post.save(function(err) { assert.ifError(err);
BlogPostB.find({ title: title }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 2);
assert.equal(title, docs[0].get('title')); assert.equal(docs[0].isNew, false);
assert.equal(title, docs[1].get('title')); assert.equal(docs[1].isNew, false);
done(); }); }); }); });
it('returns docs where an array that contains one specific member', function(done) { BlogPostB.create({ numbers: [100, 101, 102] }, function(err, created) { assert.ifError(err); BlogPostB.find({ numbers: 100 }, function(err, found) { assert.ifError(err); assert.equal(found.length, 1); assert.equal(found[0]._id.toString(), created._id); done(); }); }); });
it('works when comparing $ne with single value against an array', function(done) { const schema = new Schema({ ids: [Schema.ObjectId], b: Schema.ObjectId });
const NE = db.model('Test', schema);
const id1 = new DocumentObjectId(); const id2 = new DocumentObjectId(); const id3 = new DocumentObjectId(); const id4 = new DocumentObjectId();
NE.create({ ids: [id1, id4], b: id3 }, function(err) { assert.ifError(err); NE.create({ ids: [id2, id4], b: id3 }, function(err) { assert.ifError(err);
const query = NE.find({ b: id3.toString(), ids: { $ne: id1 } }); query.exec(function(err, nes1) { assert.ifError(err); assert.equal(nes1.length, 1);
NE.find({ b: { $ne: [1] } }, function(err) { assert.equal(err.message, 'Cast to ObjectId failed for value "[ 1 ]" (type Array) at path "b" for model "Test"');
NE.find({ b: { $ne: 4 } }, function(err) { assert.equal(err.message, 'Cast to ObjectId failed for value "4" (type number) at path "b" for model "Test"');
NE.find({ b: id3, ids: { $ne: id4 } }, function(err, nes4) { assert.ifError(err); assert.equal(nes4.length, 0); done(); }); }); }); }); }); }); });
it('with partial initialization', function(done) { let queries = 4;
const post = new BlogPostB();
post.title = 'hahaha'; post.slug = 'woot';
post.save(function(err) { assert.ifError(err);
BlogPostB.find({ _id: post.get('_id') }, function(err, docs) { assert.ifError(err); assert.equal(docs[0].isInit('title'), true); assert.equal(docs[0].isInit('slug'), true); assert.equal(docs[0].isInit('date'), false); assert.strictEqual('kandinsky', docs[0].def); if (--queries) { return; } done(); });
BlogPostB.find({ _id: post.get('_id') }, 'title', function(err, docs) { assert.ifError(err); assert.equal(docs[0].isInit('title'), true); assert.equal(docs[0].isInit('slug'), false); assert.equal(docs[0].isInit('date'), false); assert.strictEqual(undefined, docs[0].def); if (--queries) { return; } done(); });
BlogPostB.find({ _id: post.get('_id') }, { slug: 0, def: 0 }, function(err, docs) { assert.ifError(err); assert.equal(docs[0].isInit('title'), true); assert.equal(docs[0].isInit('slug'), false); assert.equal(docs[0].isInit('date'), false); assert.strictEqual(undefined, docs[0].def); if (--queries) { return; } done(); });
BlogPostB.find({ _id: post.get('_id') }, 'slug', function(err, docs) { assert.ifError(err); assert.equal(docs[0].isInit('title'), false); assert.equal(docs[0].isInit('slug'), true); assert.equal(docs[0].isInit('date'), false); assert.strictEqual(undefined, docs[0].def); if (--queries) { return; } done(); }); }); });
it('where $exists', async function() { const ExistsSchema = new Schema({ a: Number, b: String }); const Exists = db.model('Test', ExistsSchema);
await Exists.create({ a: 1 }, { b: 'hi' }); let docs = await Exists.find({ b: { $exists: true } }); assert.equal(docs.length, 1); assert.equal(docs[0].b, 'hi');
docs = await Exists.find({ b: { $exists: 'true' } }); assert.equal(docs.length, 1); assert.equal(docs[0].b, 'hi');
let threw = false; try { await Exists.find({ b: { $exists: 'foo' } }); } catch (error) { threw = true; assert.equal(error.path, 'b'); assert.equal(error.value, 'foo'); assert.equal(error.name, 'CastError'); } assert.ok(threw); });
it('works with $elemMatch (gh-1100)', function(done) { const id1 = new DocumentObjectId(); const id2 = new DocumentObjectId();
BlogPostB.create({ owners: [id1, id2] }, function(err) { assert.ifError(err); BlogPostB.find({ owners: { $elemMatch: { $in: [id2.toString()] } } }, function(err, found) { assert.ifError(err); assert.equal(found.length, 1); done(); }); }); });
it('where $mod', function(done) { const Mod = db.model('Test', ModSchema); Mod.create({ num: 1 }, function(err, one) { assert.ifError(err); Mod.create({ num: 2 }, function(err) { assert.ifError(err); Mod.find({ num: { $mod: [2, 1] } }, function(err, found) { assert.ifError(err); assert.equal(found.length, 1); assert.equal(found[0]._id.toString(), one._id); done(); }); }); }); });
it('where $not', function(done) { const Mod = db.model('Test', ModSchema); Mod.create({ num: 1 }, function(err) { assert.ifError(err); Mod.create({ num: 2 }, function(err, two) { assert.ifError(err); Mod.find({ num: { $not: { $mod: [2, 1] } } }, function(err, found) { assert.ifError(err); assert.equal(found.length, 1); assert.equal(found[0]._id.toString(), two._id); done(); }); }); }); });
it('where or()', function(done) { const Mod = db.model('Test', ModSchema);
Mod.create({ num: 1 }, { num: 2, str: 'two' }, function(err, one, two) { assert.ifError(err);
let pending = 3; test1(); test2(); test3();
function test1() { Mod.find({ $or: [{ num: 1 }, { num: 2 }] }, function(err, found) { cb(); assert.ifError(err); assert.equal(found.length, 2);
let found1 = false; let found2 = false;
found.forEach(function(doc) { if (doc.id === one.id) { found1 = true; } else if (doc.id === two.id) { found2 = true; } });
assert.ok(found1); assert.ok(found2); }); }
function test2() { Mod.find({ $or: [{ str: 'two' }, { str: 'three' }] }, function(err, found) { cb(); assert.ifError(err); assert.equal(found.length, 1); assert.equal(found[0]._id.toString(), two._id); }); }
function test3() { Mod.find({ $or: [{ num: 1 }] }).or([{ str: 'two' }]).exec(function(err, found) { cb(); assert.ifError(err); assert.equal(found.length, 2);
let found1 = false; let found2 = false;
found.forEach(function(doc) { if (doc.id === one.id) { found1 = true; } else if (doc.id === two.id) { found2 = true; } });
assert.ok(found1); assert.ok(found2); }); }
function cb() { if (--pending) { return; } done(); } }); });
it('using $or with array of Document', function(done) { const Mod = db.model('Test', ModSchema);
Mod.create({ num: 1 }, function(err, one) { assert.ifError(err); Mod.find({ num: 1 }, function(err, found) { assert.ifError(err); Mod.find({ $or: found }, function(err, found) { assert.ifError(err); assert.equal(found.length, 1); assert.equal(found[0]._id.toString(), one._id); done(); }); }); }); });
it('where $ne', function(done) { const Mod = db.model('Test', ModSchema); Mod.create({ num: 1 }, function(err) { assert.ifError(err); Mod.create({ num: 2 }, function(err, two) { assert.ifError(err); Mod.create({ num: 3 }, function(err, three) { assert.ifError(err); Mod.find({ num: { $ne: 1 } }, function(err, found) { assert.ifError(err);
assert.equal(found.length, 2); assert.equal(found[0]._id.toString(), two._id); assert.equal(found[1]._id.toString(), three._id); done(); }); }); }); }); });
it('where $nor', function(done) { const Mod = db.model('Test', ModSchema);
Mod.create({ num: 1 }, { num: 2, str: 'two' }, function(err, one, two) { assert.ifError(err);
let pending = 3; test1(); test2(); test3();
function test1() { Mod.find({ $nor: [{ num: 1 }, { num: 3 }] }, function(err, found) { cb(); assert.ifError(err); assert.equal(found.length, 1); assert.equal(found[0]._id.toString(), two._id); }); }
function test2() { Mod.find({ $nor: [{ str: 'two' }, { str: 'three' }] }, function(err, found) { cb(); assert.ifError(err); assert.equal(found.length, 1); assert.equal(found[0]._id.toString(), one._id); }); }
function test3() { Mod.find({ $nor: [{ num: 2 }] }).nor([{ str: 'two' }]).exec(function(err, found) { cb(); assert.ifError(err); assert.equal(found.length, 1); assert.equal(found[0]._id.toString(), one._id); }); }
function cb() { if (--pending) { return; } done(); } }); });
it('STRICT null matches', function(done) { const a = { title: 'A', author: null }; const b = { title: 'B' }; BlogPostB.create(a, b, function(err, createdA) { assert.ifError(err); BlogPostB.find({ author: { $in: [null], $exists: true } }, function(err, found) { assert.ifError(err); assert.equal(found.length, 1); assert.equal(found[0]._id.toString(), createdA._id); done(); }); }); });
it('null matches null and undefined', function(done) { BlogPostB.create( { title: 'A', author: null }, { title: 'B' }, function(err) { assert.ifError(err); BlogPostB.find({ author: null }, function(err, found) { assert.ifError(err); assert.equal(found.length, 2); done(); }); }); });
it('a document whose arrays contain at least $all string values', function(done) { const post = new BlogPostB({ title: 'Aristocats' });
post.tags.push('onex'); post.tags.push('twox'); post.tags.push('threex');
post.save(function(err) { assert.ifError(err);
BlogPostB.findById(post._id, function(err, post) { assert.ifError(err);
BlogPostB.find({ title: { $all: ['Aristocats'] } }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1);
BlogPostB.find({ title: { $all: [/^Aristocats/] } }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1);
BlogPostB.find({ tags: { $all: ['onex', 'twox', 'threex'] } }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1);
BlogPostB.find({ tags: { $all: [/^onex/i] } }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1);
BlogPostB.findOne({ tags: { $all: /^two/ } }, function(err, doc) { assert.ifError(err); assert.equal(post.id, doc.id); done(); }); }); }); }); }); }); }); });
it('using #nor with nested #elemMatch', function(done) { const p0 = { title: 'nested $nor elemMatch1', comments: [] };
const p1 = { title: 'nested $nor elemMatch0', comments: [] }; p1.comments.push({ title: 'comment X' }, { title: 'comment Y' }, { title: 'comment W' });
const P = BlogPostB;
P.create(p0, p1, function(err, post0, post1) { assert.ifError(err);
const id = post1.comments[1]._id;
const query0 = { comments: { $elemMatch: { title: 'comment Z' } } }; const query1 = { comments: { $elemMatch: { _id: id.toString(), title: 'comment Y' } } };
P.find({ $nor: [query0, query1] }, function(err, posts) { assert.ifError(err); assert.equal(posts.length, 1); assert.equal(posts[0].id, post0.id); done(); }); }); });
it('strings via regexp', function(done) { BlogPostB.create({ title: 'Next to Normal' }, function(err, created) { assert.ifError(err); BlogPostB.findOne({ title: /^Next/ }, function(err, found) { assert.ifError(err); assert.equal(found._id.toString(), created._id);
const reg = '^Next to Normal$';
BlogPostB.find({ title: { $regex: reg } }, function(err, found) { assert.ifError(err); assert.equal(found.length, 1); assert.equal(found[0]._id.toString(), created._id);
BlogPostB.findOne({ title: { $regex: reg } }, function(err, found) { assert.ifError(err); assert.equal(found._id.toString(), created._id);
BlogPostB.where('title').regex(reg).findOne(function(err, found) { assert.ifError(err); assert.equal(found._id.toString(), created._id);
BlogPostB.where('title').regex(/^Next/).findOne(function(err, found) { assert.ifError(err); assert.equal(found._id.toString(), created._id); done(); }); }); }); }); }); }); });
it('a document whose arrays contain at least $all values', function(done) { const a1 = { numbers: [-1, -2, -3, -4], meta: { visitors: 4 } }; const a2 = { numbers: [0, -1, -2, -3, -4] }; BlogPostB.create(a1, a2, function(err, whereoutZero, whereZero) { assert.ifError(err);
BlogPostB.find({ numbers: { $all: [-1, -2, -3, -4] } }, function(err, found) { assert.ifError(err); assert.equal(found.length, 2); BlogPostB.find({ 'meta.visitors': { $all: [4] } }, function(err, found) { assert.ifError(err); assert.equal(found.length, 1); assert.equal(found[0]._id.toString(), whereoutZero._id); BlogPostB.find({ numbers: { $all: [0, -1] } }, function(err, found) { assert.ifError(err); assert.equal(found.length, 1); assert.equal(found[0]._id.toString(), whereZero._id); done(); }); }); }); }); });
it('where $size', function(done) { BlogPostB.create({ numbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] }, function(err) { assert.ifError(err); BlogPostB.create({ numbers: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20] }, function(err) { assert.ifError(err); BlogPostB.create({ numbers: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] }, function(err) { assert.ifError(err); BlogPostB.find({ numbers: { $size: 10 } }, function(err, found) { assert.ifError(err); assert.equal(found.length, 2); BlogPostB.find({ numbers: { $size: 11 } }, function(err, found) { assert.ifError(err); assert.equal(found.length, 1); done(); }); }); }); }); }); });
it('$gt, $lt, $lte, $gte work on strings', function(done) { const D = db.model('Test', new Schema({ dt: String }));
D.create({ dt: '2011-03-30' }, cb); D.create({ dt: '2011-03-31' }, cb); D.create({ dt: '2011-04-01' }, cb); D.create({ dt: '2011-04-02' }, cb);
let pending = 4;
function cb(err) { assert.ifError(err);
if (--pending) { return; }
pending = 2;
D.find({ dt: { $gte: '2011-03-30', $lte: '2011-04-01' } }).sort('dt').exec(function(err, docs) { if (!--pending) { done(); } assert.ifError(err); assert.equal(docs.length, 3); assert.equal(docs[0].dt, '2011-03-30'); assert.equal(docs[1].dt, '2011-03-31'); assert.equal(docs[2].dt, '2011-04-01'); assert.ok(!docs.some(function(d) { return d.dt === '2011-04-02'; })); });
D.find({ dt: { $gt: '2011-03-30', $lt: '2011-04-02' } }).sort('dt').exec(function(err, docs) { if (!--pending) { done(); } assert.ifError(err); assert.equal(docs.length, 2); assert.equal(docs[0].dt, '2011-03-31'); assert.equal(docs[1].dt, '2011-04-01'); assert.ok(!docs.some(function(d) { return d.dt === '2011-03-30'; })); assert.ok(!docs.some(function(d) { return d.dt === '2011-04-02'; })); }); } });
describe('text search indexes', function() { it('works with text search ensure indexes ', function(done) { if (!mongo26_or_greater) { return done(); }
const blogPost = BlogPostB;
blogPost.collection.createIndex({ title: 'text' }, function(error) { assert.ifError(error); const a = new blogPost({ title: 'querying in mongoose' }); const b = new blogPost({ title: 'text search in mongoose' }); a.save(function(error) { assert.ifError(error); b.save(function(error) { assert.ifError(error); blogPost. find({ $text: { $search: 'text search' } }, { score: { $meta: 'textScore' } }). limit(2). exec(function(error, documents) { assert.ifError(error); assert.equal(documents.length, 1); assert.equal(documents[0].title, 'text search in mongoose'); a.remove({}, function(error) { assert.ifError(error); b.remove({}, function(error) { assert.ifError(error); done(); }); }); }); }); }); }); });
it('works when text search is called by a schema (gh-3824) (gh-6851)', async function() { if (!mongo26_or_greater) { return this.skip(); }
const exampleSchema = new Schema({ title: String, name: { type: String, text: true }, tag: String });
const Example = db.model('Test', exampleSchema);
await Example.init(); // Wait for index build // Should not error await Example.findOne({ $text: { $search: 'text search' } });
await Example.create({ name: '1234 ABCD', tag: 'test1' }); let doc = await Example.findOne({ $text: { $search: 1234 // Will be casted to a string } }); assert.ok(doc); assert.equal(doc.tag, 'test1');
doc = await Example.findOne({ $text: { $search: 'abcd', $caseSensitive: 'no' // Casted to boolean } }); assert.ok(doc); assert.equal(doc.tag, 'test1'); }); }); });
describe('limit', function() { it('works', function(done) { BlogPostB.create({ title: 'first limit' }, function(err, first) { assert.ifError(err); BlogPostB.create({ title: 'second limit' }, function(err, second) { assert.ifError(err); BlogPostB.create({ title: 'third limit' }, function(err) { assert.ifError(err); BlogPostB.find({ title: /limit$/ }).limit(2).find(function(err, found) { assert.ifError(err); assert.equal(found.length, 2); assert.equal(found[0].id, first.id); assert.equal(found[1].id, second.id); done(); }); }); }); }); }); });
describe('skip', function() { it('works', function(done) { BlogPostB.create({ title: '1 skip' }, function(err) { assert.ifError(err); BlogPostB.create({ title: '2 skip' }, function(err, second) { assert.ifError(err); BlogPostB.create({ title: '3 skip' }, function(err, third) { assert.ifError(err); BlogPostB.find({ title: /skip$/ }).sort({ title: 1 }).skip(1).limit(2).find(function(err, found) { assert.ifError(err); assert.equal(found.length, 2); assert.equal(found[0].id, second._id); assert.equal(found[1].id, third._id); done(); }); }); }); }); }); });
describe('sort', function() { it('works', function(done) { BlogPostB.create({ meta: { visitors: 100 } }, function(err, least) { assert.ifError(err); BlogPostB.create({ meta: { visitors: 300 } }, function(err, largest) { assert.ifError(err); BlogPostB.create({ meta: { visitors: 200 } }, function(err, middle) { assert.ifError(err); BlogPostB .where('meta.visitors').gt(99).lt(301) .sort('-meta.visitors') .find(function(err, found) { assert.ifError(err); assert.equal(found.length, 3); assert.equal(found[0].id, largest._id); assert.equal(found[1].id, middle._id); assert.equal(found[2].id, least._id); done(); }); }); }); }); }); it('handles sorting by text score', function(done) { if (!mongo26_or_greater) { return done(); }
const blogPost = BlogPostB;
blogPost.collection.createIndex({ title: 'text' }, function(error) { assert.ifError(error); const a = new blogPost({ title: 'searching in mongoose' }); const b = new blogPost({ title: 'text search in mongoose' }); a.save(function(error) { assert.ifError(error); b.save(function(error) { assert.ifError(error); blogPost. find({ $text: { $search: 'text search' } }, { score: { $meta: 'textScore' } }). sort({ score: { $meta: 'textScore' } }). limit(2). exec(function(error, documents) { assert.ifError(error); assert.equal(documents.length, 2); assert.equal(documents[0].title, 'text search in mongoose'); assert.equal(documents[1].title, 'searching in mongoose'); done(); }); }); }); }); }); });
describe('nested mixed "x.y.z"', function() { it('works', function(done) { BlogPostB.find({ 'mixed.nested.stuff': 'skynet' }, function(err) { assert.ifError(err); done(); }); }); });
it('by Date (gh-336)', function(done) { const Test = db.model('Test', new Schema({ date: Date })); const now = new Date();
Test.create({ date: now }, { date: new Date(now - 10000) }, function(err) { assert.ifError(err); Test.find({ date: now }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1); done(); }); }); });
it('mixed types with $elemMatch (gh-591)', function(done) { const S = new Schema({ a: [{}], b: Number }); const M = db.model('Test', S);
const m = new M(); m.a = [1, 2, { name: 'Frodo' }, 'IDK', { name: 100 }]; m.b = 10;
m.save(function(err) { assert.ifError(err);
M.find({ a: { name: 'Frodo' }, b: '10' }, function(err, docs) { assert.ifError(err); assert.equal(docs[0].a.length, 5); assert.equal(docs[0].b.valueOf(), 10);
const query = { a: { $elemMatch: { name: 100 } } };
M.find(query, function(err, docs) { assert.ifError(err); assert.equal(docs[0].a.length, 5); done(); }); }); }); });
describe('$all', function() { it('with ObjectIds (gh-690)', function(done) { const SSchema = new Schema({ name: String }); const PSchema = new Schema({ sub: [SSchema] });
const P = db.model('Test', PSchema); const sub = [{ name: 'one' }, { name: 'two' }, { name: 'three' }];
P.create({ sub: sub }, function(err, p) { assert.ifError(err);
const o0 = p.sub[0]._id; const o1 = p.sub[1]._id; const o2 = p.sub[2]._id;
P.findOne({ 'sub._id': { $all: [o1, o2.toString()] } }, function(err, doc) { assert.ifError(err); assert.equal(doc.id, p.id);
P.findOne({ 'sub._id': { $all: [o0, new DocumentObjectId()] } }, function(err, doc) { assert.ifError(err); assert.equal(!!doc, false);
P.findOne({ 'sub._id': { $all: [o2] } }, function(err, doc) { assert.ifError(err); assert.equal(doc.id, p.id); done(); }); }); }); }); });
it('with Dates', function(done) { this.timeout(4500); const SSchema = new Schema({ d: Date }); const PSchema = new Schema({ sub: [SSchema] });
const P = db.model('Test', PSchema); const sub = [ { d: new Date() }, { d: new Date(Date.now() - 10000) }, { d: new Date(Date.now() - 30000) } ];
P.create({ sub: sub }, function(err, p) { assert.ifError(err);
const o0 = p.sub[0].d; const o1 = p.sub[1].d; const o2 = p.sub[2].d;
P.findOne({ 'sub.d': { $all: [o1, o2] } }, function(err, doc) { assert.ifError(err); assert.equal(doc.id, p.id);
P.findOne({ 'sub.d': { $all: [o0, new Date()] } }, function(err, doc) { assert.ifError(err); assert.equal(!!doc, false);
P.findOne({ 'sub.d': { $all: [o2] } }, function(err, doc) { assert.ifError(err); assert.equal(doc.id, p.id); done(); }); }); }); }); });
it('with $elemMatch (gh-3163)', async function() { const version = await start.mongodVersion();
const mongo26_or_greater = version[0] > 2 || (version[0] === 2 && version[1] >= 6); if (!mongo26_or_greater) { return; }
const schema = new Schema({ test: [String] }); const MyModel = db.model('Test', schema);
await MyModel.create({ test: ['log1', 'log2'] });
const docs = await MyModel.find({ test: { $all: [ { $elemMatch: { $regex: /log/g } } ] } });
assert.equal(docs.length, 1); }); });
describe('and', function() { it('works with queries gh-1188', function(done) { const B = BlogPostB; B.create({ title: 'and operator', published: false, author: 'Me' }, function(err) { assert.ifError(err);
B.find({ $and: [{ title: 'and operator' }] }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1);
B.find({ $and: [{ title: 'and operator' }, { published: true }] }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 0);
B.find({ $and: [{ title: 'and operator' }, { published: false }] }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1);
const query = B.find(); query.and([ { title: 'and operator', published: false }, { author: 'Me' } ]); query.exec(function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1);
const query = B.find(); query.and([ { title: 'and operator', published: false }, { author: 'You' } ]); query.exec(function(err, docs) { assert.ifError(err); assert.equal(docs.length, 0); done(); }); }); }); }); }); }); });
it('works with nested query selectors gh-1884', function(done) { const B = db.model('Test', { a: String, b: String });
B.deleteOne({ $and: [{ a: 'coffee' }, { b: { $in: ['bacon', 'eggs'] } }] }, function(error) { assert.ifError(error); done(); }); }); });
it('works with different methods and query types', function(done) { const BufSchema = new Schema({ name: String, block: Buffer }); const Test = db.model('Test', BufSchema);
const docA = { name: 'A', block: Buffer.from('über') }; const docB = { name: 'B', block: Buffer.from('buffer shtuffs are neat') }; const docC = { name: 'C', block: 'hello world' }; const docD = { name: 'D', block: { type: 'Buffer', data: [103, 104, 45, 54, 56, 54, 51] } };
Test.create(docA, docB, docC, docD, function(err, a, b, c, d) { assert.ifError(err);
assert.equal(a.block.toString('utf8'), 'über'); assert.equal(b.block.toString('utf8'), 'buffer shtuffs are neat'); assert.equal(c.block.toString('utf8'), 'hello world'); assert.equal(d.block.toString('utf8'), 'gh-6863');
Test.findById(a._id, function(err, a) { assert.ifError(err); assert.equal(a.block.toString('utf8'), 'über');
Test.findOne({ block: 'buffer shtuffs are neat' }, function(err, rb) { assert.ifError(err); assert.equal(rb.block.toString('utf8'), 'buffer shtuffs are neat');
Test.findOne({ block: /buffer/i }, function(err) { assert.equal(err.message, 'Cast to Buffer failed for value ' + '"/buffer/i" (type RegExp) at path "block" for model "Test"'); Test.findOne({ block: [195, 188, 98, 101, 114] }, function(err, rb) { assert.ifError(err); assert.equal(rb.block.toString('utf8'), 'über');
Test.findOne({ block: 'aGVsbG8gd29ybGQ=' }, function(err, rb) { assert.ifError(err); assert.strictEqual(rb, null);
Test.findOne({ block: Buffer.from('aGVsbG8gd29ybGQ=', 'base64') }, function(err, rb) { assert.ifError(err); assert.equal(rb.block.toString('utf8'), 'hello world');
Test.findOne({ block: { type: 'Buffer', data: [195, 188, 98, 101, 114] } }, function(err, rb) { assert.ifError(err); assert.equal(rb.block.toString('utf8'), 'über');
Test.deleteOne({}, function(err) { assert.ifError(err); done(); }); }); }); }); }); }); }); }); }); });
it('with conditionals', async function() { // $in $nin etc const BufSchema = new Schema({ name: String, block: Buffer }); const Test = db.model('Test', BufSchema);
const docA = { name: 'A', block: new MongooseBuffer([195, 188, 98, 101, 114]) }; // über const docB = { name: 'B', block: new MongooseBuffer('buffer shtuffs are neat') }; const docC = { name: 'C', block: new MongooseBuffer('aGVsbG8gd29ybGQ=', 'base64') }; const docD = { name: 'D', block: new MongooseBuffer({ type: 'Buffer', data: [103, 104, 45, 54, 56, 54, 51] }) };
const [a, b, c, d] = await Test.create([docA, docB, docC, docD]);
assert.equal(a.block.toString('utf8'), 'über'); assert.equal(b.block.toString('utf8'), 'buffer shtuffs are neat'); assert.equal(c.block.toString('utf8'), 'hello world'); assert.equal(d.block.toString('utf8'), 'gh-6863');
// run all of the tests in parallel await Promise.all([ Test.find({ block: { $in: [ [195, 188, 98, 101, 114], 'buffer shtuffs are neat', Buffer.from('aGVsbG8gd29ybGQ=', 'base64'), { type: 'Buffer', data: [103, 104, 45, 54, 56, 54, 51] } // gh-6863 ] } }).exec().then(tests => { assert.equal(tests.length, 4); }), Test.find({ block: { $in: ['über', 'hello world'] } }).exec().then(tests => { assert.equal(tests.length, 2); }), Test.find({ block: { $in: ['über'] } }).exec().then(tests => { assert.equal(tests.length, 1); assert.equal(tests[0].block.toString('utf8'), 'über'); }), Test.find({ block: { $nin: ['über'] } }).exec().then(tests => { assert.equal(tests.length, 3); }), Test.find({ block: { $nin: [ [195, 188, 98, 101, 114], Buffer.from('aGVsbG8gd29ybGQ=', 'base64'), { type: 'Buffer', data: [103, 104, 45, 54, 56, 54, 51] } // gh-6863 ] } }).exec().then(tests => { assert.equal(tests.length, 1); assert.equal(tests[0].block.toString('utf8'), 'buffer shtuffs are neat'); }), Test.find({ block: { $ne: 'über' } }).exec().then(tests => { assert.equal(tests.length, 3); }), Test.find({ block: { $gt: 'über' } }).exec().then(tests => { assert.equal(tests.length, 3); }), Test.find({ block: { $gte: 'über' } }).exec().then(tests => { assert.equal(tests.length, 4); }), Test.find({ block: { $lt: Buffer.from('buffer shtuffs are neat') } }).exec().then(tests => { assert.equal(tests.length, 3); const ret = {}; ret[tests[0].block.toString('utf8')] = 1; ret[tests[1].block.toString('utf8')] = 1; ret[tests[2].block.toString('utf8')] = 1;
assert.ok(ret['über'] !== undefined); }), Test.find({ block: { $lte: 'buffer shtuffs are neat' } }).exec().then(tests => { assert.equal(tests.length, 4); }), Test.find({ block: { $gt: { type: 'Buffer', data: [103, 104, 45, 54, 56, 54, 51] } } }).exec().then(tests => { assert.equal(tests.length, 2); }) ]); await Test.deleteOne({}); });
it('with previously existing null values in the db', function(done) { const post = new BlogPostB();
const doc = { meta: { visitors: 9898, a: null } }; post.collection.insertOne(doc, {}, function(err) { assert.ifError(err);
BlogPostB.findOne({ _id: doc._id }, function(err, found) { assert.ifError(err); assert.equal(found.get('meta.visitors').valueOf(), 9898); done(); }); }); });
it('with unused values in the db', function(done) { const post = new BlogPostB();
const doc = { meta: { visitors: 9898, color: 'blue' } }; post.collection.insertOne(doc, {}, function(err) { assert.ifError(err);
BlogPostB.findOne({ _id: doc._id }, function(err, found) { assert.ifError(err); assert.equal(found.get('meta.visitors').valueOf(), 9898); found.save(function(err) { assert.ifError(err); done(); }); }); }); });
describe('2d', function() { it('$near (gh-309)', function(done) { const Test = db.model('Test', geoSchema);
let pending = 2;
function complete(err) { if (complete.ran) { return; } if (err) { return done(complete.ran = err); } --pending || test(); }
Test.on('index', complete); Test.create({ loc: [10, 20] }, { loc: [40, 90] }, complete);
function test() { Test.find({ loc: { $near: [30, 40] } }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 2); done(); }); } });
it('$within arrays (gh-586)', function(done) { const Test = db.model('Test', geoSchema);
let pending = 2;
function complete(err) { if (complete.ran) { return; } if (err) { return done(complete.ran = err); } --pending || test(); }
Test.on('index', complete); Test.create({ loc: [35, 50] }, { loc: [-40, -90] }, complete);
function test() { Test.find({ loc: { $within: { $box: [[30, 40], [40, 60]] } } }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1); done(); }); } });
it('$nearSphere with arrays (gh-610)', function(done) { const Test = db.model('Test', geoSchema);
let pending = 2;
function complete(err) { if (complete.ran) { return; } if (err) { return done(complete.ran = err); } --pending || test(); }
Test.on('index', complete); Test.create({ loc: [10, 20] }, { loc: [40, 90] }, complete);
function test() { Test.find({ loc: { $nearSphere: [30, 40] } }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 2); done(); }); } });
it('$nearSphere with invalid coordinate does not crash (gh-1874)', function(done) { const geoSchema = new Schema({ loc: { type: { type: String }, coordinates: { type: [Number], index: '2dsphere' } } }); const Test = db.model('Test', geoSchema);
let pending = 2; const complete = function(err) { if (complete.ran) { return; } if (err) { done(complete.ran = err); return; } --pending || test(); };
Test.on('index', complete); Test.create( { loc: { coordinates: [30, 41] } }, { loc: { coordinates: [31, 40] } }, complete);
const test = function() { const q = new Query({}, {}, null, Test.collection); q.find({ loc: { $nearSphere: { $geometry: { type: 'Point', coordinates: [30, 40] }, $maxDistance: 10000000 } } });
assert.doesNotThrow(function() { q.cast(Test); });
done(); }; });
it('$maxDistance with arrays', function(done) { const Test = db.model('Test', geoSchema);
let pending = 2;
function complete(err) { if (complete.ran) { return; } if (err) { done(complete.ran = err); return; } --pending || test(); }
Test.on('index', complete); Test.create({ loc: [20, 80] }, { loc: [25, 30] }, complete);
function test() { Test.find({ loc: { $near: [25, 31], $maxDistance: 1 } }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1); Test.find({ loc: { $near: [25, 32], $maxDistance: 1 } }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 0); done(); }); }); } }); });
describe('2dsphere', function() { let schema2dsphere; let geoSchema; let geoMultiSchema;
before(function() { schema2dsphere = new Schema({ loc: { type: [Number], index: '2dsphere' } });
geoSchema = new Schema({ line: { type: { type: String }, coordinates: [] } }); geoSchema.index({ line: '2dsphere' });
geoMultiSchema = new Schema({ geom: [{ type: { type: String }, coordinates: [] }] }); // see mongodb issue SERVER-8907 // geoMultiSchema.index({ geom: '2dsphere' }); });
// mongodb 2.4 let mongo24_or_greater = false; before(async function() { const version = await start.mongodVersion();
mongo24_or_greater = version[0] > 2 || (version[0] === 2 && version[1] >= 4);
if (!mongo24_or_greater) { console.log('not testing mongodb 2.4 features'); } });
it('index is allowed in schema', function(done) { if (!mongo24_or_greater) { return done(); }
const ok = schema2dsphere.indexes().some(function(index) { return index[0].loc === '2dsphere'; }); assert.ok(ok); done(); });
describe('$geometry', function() { it('Polygon', async function() { if (!mongo24_or_greater) { return; }
const Test = db.model('Test', schema2dsphere); await Test.init();
const created = await Test.create({ loc: [0, 0] });
const geojsonPoly = { type: 'Polygon', coordinates: [[[-5, -5], ['-5', 5], [5, 5], [5, -5], [-5, '-5']]] };
const docs = await Test.find({ loc: { $within: { $geometry: geojsonPoly } } });
assert.equal(docs.length, 1); assert.equal(created.id, docs[0].id);
const geoDocs = await Test.where('loc').within().geometry(geojsonPoly).exec();
assert.equal(geoDocs.length, 1); assert.equal(created.id, geoDocs[0].id); }); });
describe('$geoIntersects', function() { it('LineString', async function() { if (!mongo24_or_greater) { return; }
const Test = db.model('Test', geoSchema); await Test.init();
const created = await Test.create({ line: { type: 'LineString', coordinates: [[-178.0, 10.0], [178.0, 10.0]] } });
const geojsonLine = { type: 'LineString', coordinates: [[180.0, 11.0], [180.0, '9.00']] };
const docs = await Test.find({ line: { $geoIntersects: { $geometry: geojsonLine } } });
assert.equal(docs.length, 1); assert.equal(created.id, docs[0].id);
const doc = await Test.where('line').intersects().geometry(geojsonLine).findOne();
assert.equal(created.id, doc.id); });
it('MultiLineString', function(done) { if (!mongo24_or_greater) { return done(); }
const Test = db.model('Test', geoMultiSchema);
Test.create({ geom: [{ type: 'LineString', coordinates: [[-178.0, 10.0], [178.0, 10.0]] }, { type: 'LineString', coordinates: [[-178.0, 5.0], [178.0, 5.0]] }] }, function(err, created) { assert.ifError(err);
const geojsonLine = { type: 'LineString', coordinates: [[180.0, 11.0], [180.0, '9.00']] };
Test.find({ geom: { $geoIntersects: { $geometry: geojsonLine } } }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1); assert.equal(created.id, docs[0].id);
Test.where('geom').intersects().geometry(geojsonLine).findOne(function(err, doc) { assert.ifError(err); assert.equal(created.id, doc.id); done(); }); }); }); });
it('MultiPolygon', function(done) { if (!mongo24_or_greater) { return done(); }
const Test = db.model('Test', geoMultiSchema);
Test.create({ geom: [{ type: 'Polygon', coordinates: [[[28.7, 41], [29.2, 40.9], [29.1, 41.3], [28.7, 41]]] }, { type: 'Polygon', coordinates: [[[-1, -1], [1, -1], [1, 1], [-1, 1], [-1, -1]]] }] }, function(err, created) { assert.ifError(err);
const geojsonPolygon = { type: 'Polygon', coordinates: [[[26, 36], [45, 36], [45, 42], [26, 42], [26, 36]]] };
Test.find({ geom: { $geoIntersects: { $geometry: geojsonPolygon } } }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1); assert.equal(created.id, docs[0].id);
Test.where('geom').intersects().geometry(geojsonPolygon).findOne(function(err, doc) { assert.ifError(err); assert.equal(created.id, doc.id); done(); }); }); }); }); });
describe('$near', function() { it('Point', function(done) { if (!mongo24_or_greater) { return done(); }
const Test = db.model('Test', geoSchema);
Test.on('index', function(err) { assert.ifError(err);
Test.create({ line: { type: 'Point', coordinates: [-179.0, 0.0] } }, function(err, created) { assert.ifError(err);
const geojsonPoint = { type: 'Point', coordinates: [-179.0, 0.0] };
Test.find({ line: { $near: geojsonPoint } }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1); assert.equal(created.id, docs[0].id);
Test.find({ line: { $near: { $geometry: geojsonPoint, $maxDistance: 50 } } }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1); assert.equal(created.id, docs[0].id); done(); }); }); }); }); });
it('works with GeoJSON (gh-1482)', function(done) { if (!mongo24_or_greater) { return done(); }
const geoJSONSchema = new Schema({ loc: { type: { type: String }, coordinates: [Number] } }); geoJSONSchema.index({ loc: '2dsphere' }); const Test = db.model('Test', geoJSONSchema);
let pending = 2;
function complete(err) { if (complete.ran) { return; } if (err) { return done(complete.ran = err); } --pending || test(); }
Test.on('index', complete); Test.create({ loc: { type: 'Point', coordinates: [10, 20] } }, { loc: { type: 'Point', coordinates: [40, 90] } }, complete);
function test() { // $maxDistance is in meters... so even though they aren't that far off // in lat/long, need an incredibly high number here Test.where('loc').near({ center: { type: 'Point', coordinates: [11, 20] }, maxDistance: 1000000 }).exec(function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1); done(); }); } }); it('works with legacy 2dsphere pair in schema (gh-6937)', async function() { if (!mongo24_or_greater) { return it.skip(); }
const Model = db.model('Test', schema2dsphere); await Model.init(); const model = new Model(); model.loc = [1, 2]; await model.save(); const result = await Model.where('loc').near({ center: { type: 'Point', coordinates: [1, 2] }, maxDistance: 10 }); assert.equal(result.length, 1); }); }); });
describe('hashed indexes', function() { let mongo24_or_greater = false;
before(async function() { const version = await start.mongodVersion();
mongo24_or_greater = version[0] > 2 || (version[0] === 2 && version[1] >= 4); if (!mongo24_or_greater) { console.log('not testing mongodb 2.4 features'); } });
it('work', function(done) { if (!mongo24_or_greater) { return done(); }
const schema = new Schema({ t: { type: String, index: 'hashed' } });
const H = db.model('Test', schema); H.on('index', function(err) { assert.ifError(err); H.collection.getIndexes({ full: true }, function(err, indexes) { assert.ifError(err);
const found = indexes.some(function(index) { return index.key.t === 'hashed'; }); assert.ok(found);
H.create({ t: 'hashing' }, {}, function(err, doc1, doc2) { assert.ifError(err); assert.ok(doc1); assert.ok(doc2); done(); }); }); }); }); });
describe('lean', function() { it('find', function(done) { const title = 'Wooooot ' + random();
const post = new BlogPostB(); post.set('title', title);
post.save(function(err) { assert.ifError(err); BlogPostB.find({ title: title }).lean().exec(function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1); assert.strictEqual(docs[0] instanceof mongoose.Document, false); BlogPostB.find({ title: title }, null, { lean: true }, function(err, docs) { assert.ifError(err); assert.equal(docs.length, 1); assert.strictEqual(docs[0] instanceof mongoose.Document, false); done(); }); }); }); });
it('removes the __v property if versionKey: false is set (gh-8934)', async function() { const title = 'Wooooot ' + random(); await BlogPostB.create({ title }); const foundPost = await BlogPostB.find({ title }).lean({ versionKey: false }); assert.ok(!('__v' in foundPost)); const anotherFoundPost = await BlogPostB.findOne({ title }).lean({ versionKey: false }); assert.ok(!('__v' in anotherFoundPost)); const updateFoundPost = await BlogPostB.findOneAndUpdate({ title: title }, { title: 'Woooot' }).lean({ versionKey: false }); assert.ok(!('__v' in updateFoundPost)); });
it('findOne', function(done) { const title = 'Wooooot ' + random();
const post = new BlogPostB(); post.set('title', title);
post.save(function(err) { assert.ifError(err); BlogPostB.findOne({ title: title }, null, { lean: true }, function(err, doc) { assert.ifError(err); assert.ok(doc); assert.strictEqual(false, doc instanceof mongoose.Document); done(); }); }); }); it('properly casts nested and/or queries (gh-676)', function(done) { const sch = new Schema({ num: Number, subdoc: { title: String, num: Number } });
const M = db.model('Test', sch);
const cond = { $and: [ { $or: [{ num: '23' }, { 'subdoc.num': '45' }] }, { $and: [{ 'subdoc.title': 233 }, { num: '345' }] } ] }; const q = M.find(cond); q._castConditions(); assert.equal(typeof q._conditions.$and[0].$or[0].num, 'number'); assert.equal(typeof q._conditions.$and[0].$or[1]['subdoc.num'], 'number'); assert.equal(typeof q._conditions.$and[1].$and[0]['subdoc.title'], 'string'); assert.equal(typeof q._conditions.$and[1].$and[1].num, 'number'); done(); }); it('properly casts deeply nested and/or queries (gh-676)', function(done) { const sch = new Schema({ num: Number, subdoc: { title: String, num: Number } });
const M = db.model('Test', sch);
const cond = { $and: [{ $or: [{ $and: [{ $or: [{ num: '12345' }, { 'subdoc.num': '56789' }] }] }] }] }; const q = M.find(cond); q._castConditions(); assert.equal(typeof q._conditions.$and[0].$or[0].$and[0].$or[0].num, 'number'); assert.equal(typeof q._conditions.$and[0].$or[0].$and[0].$or[1]['subdoc.num'], 'number'); done(); });
it('casts $elemMatch (gh-2199)', function(done) { const schema = new Schema({ dates: [Date] }); const Dates = db.model('Test', schema);
const array = ['2014-07-01T02:00:00.000Z', '2014-07-01T04:00:00.000Z']; Dates.create({ dates: array }, function(err) { assert.ifError(err); const elemMatch = { $gte: '2014-07-01T03:00:00.000Z' }; Dates.findOne({}, { dates: { $elemMatch: elemMatch } }, function(err, doc) { assert.ifError(err); assert.equal(doc.dates.length, 1); assert.equal(doc.dates[0].getTime(), new Date('2014-07-01T04:00:00.000Z').getTime()); done(); }); }); });
it('does not run resetId setter on query (gh-6093)', function() { const schema = new Schema({});
const Test = db.model('Test', schema);
return Test.find({ _id: { $in: [void 0] } }); });
describe('$eq', function() { let mongo26 = false;
before(async function() { const version = await start.mongodVersion();
mongo26 = version[0] > 2 || (version[0] === 2 && version[1] >= 6); });
it('casts $eq (gh-2752)', function(done) { BlogPostB.findOne( { _id: { $eq: '000000000000000000000001' }, numbers: { $eq: [1, 2] } }, function(err, doc) { if (mongo26) { assert.ifError(err); } else { assert.ok(err.toString().indexOf('MongoServerError') !== -1); }
assert.ok(!doc); done(); }); }); }); });
it('does not apply string schema setters on $regex (gh-11426)', async function() { const numbersOnlyRE = /[^\d]+/g; const getOnlyNumbers = string => string.replace(numbersOnlyRE, '');
const testSchema = new Schema({ testProp: { type: String, required: true, set: getOnlyNumbers } }, { strictQuery: false });
const Test = db.model('Test', testSchema);
await Test.collection.insertOne({ testProp: 'not numbers' });
const res = await Test.find({ testProp: /^not numbers$/ }); assert.equal(res.length, 1); assert.equal(res[0].testProp, 'not numbers');
res[0].testProp = 'something else 42'; assert.strictEqual(res[0].testProp, '42'); });});
Version Info