Getting Data from a Collection

So far - the "Missing manual" looks most like the "Non Missing Manual" (with more chatty bits.) Here is some of the missing parts: How do I GET DATA FROM models?

First a brief recap - the code I use for my model class - that is the code I bury in module handlers does three things:


 * 1) Defines a schema, or a nested series of schemae.
 * 2) Ties that schema to a (collection) name. This spawns a Model.
 * 3) Returns the model that results from step 2.

That last step can be done in one of two permutations:


 * 1) mongoose.model('Robots') which returns an object to run finds from,
 * 2) new mongoose.model('Robots') which returns an active record that you can use to save new data into.

In the previous section we were concerned with the second form exclusively. Now we will start to use the first form.

Lets go over how to find data - first lets insert some robots. /** * Beginning of example */ var robots_module = require('./../models/robots'); var robots_model = robots_module.model; var micronaut = robots_module.model(true); // getting an activeRecord; // == new mongoose.model('Robots'); micronaut.name = 'Lord Luma'; micronaut.parts = [{ part_number: 526232, name: 'tiny_lance', weight: 3 }]; micronaut.save(function(err, micronaut_saved) {    if (err) {         throw err;     }     var bender = robots_module.model(true); // getting an activeRecord;     // == new mongoose.model('Robots');     bender.name = 'Bender';     bender.parts = [{         part_number: 111,         name: 'Bending Arms',         weight: 500     }, {         part_number: 15252,         name: 'Hideaway Body',         weight: 1500     }];     bender.save(function(err, bender_saved) { if (err) { throw err; }        var r2d2 = robots_module.model(true); // getting an activeRecord; // == new mongoose.model('Robots'); r2d2.name = 'R2D2'; r2d2.parts = [{ part_number: 111, name: 'Legs', quantity: 2, weight: 800 }, {            part_number: 15252, name: 'Torso', weight: 1500 }];        r2d2.save(function(err, r2d2_saved) {             if (err) {                 throw err;             }             robots_module.model.find({}, function(err, robots) { robots.forEach(function(robot) {                    console.log('robot ' + robot._id + ': ' + robot.name);                 }) })        })     }) }) The resulting input is: robot 4d84f194f813c5d54b000001: Lord Luma robot 4d84f194f813c5d54b000002: Bender robot 4d84f194f813c5d54b000003: R2D2

Why all the nested functions?
If you have not used Node.js then it will appear curious that I nest the inserts rather than just listing them horizontally as in /** * Beginning of example */ var robots_module = require('./../models/robots'); var robots_model = robots_module.model; var micronaut = robots_module.model(true); // getting an activeRecord; // == new mongoose.model('Robots'); micronaut.name = 'Lord Luma'; micronaut.parts = [{ part_number: 526232, name: 'tiny_lance', weight: 3 }]; micronaut.save(function(err){ if (err) throw err; }); var bender = robots_module.model(true); // getting an activeRecord; // == new mongoose.model('Robots'); bender.name = 'Bender'; bender.parts = [{ part_number: 111, name: 'Bending Arms', weight: 500 }, {    part_number: 15252, name: 'Hideaway Body', weight: 1500 }]; bender.save(function(err){ if (err) throw err; }); var r2d2 = robots_module.model(true); // getting an activeRecord; // == new mongoose.model('Robots'); r2d2.name = 'R2D2'; r2d2.parts = [{ part_number: 111, name: 'Legs', quantity: 2, weight: 800 }, {    part_number: 15252, name: 'Torso', weight: 1500 }]; r2d2.save(function(err){ if (err) throw err; }); Truthfully - the above code WILL WORK - in the Clintonian sense of the word "Work". However because of the asynchronous/non-blocking nature of node, we would lose track of whether all - or any - of the robots are saved. So, if we wanted to follow up with a find({}, function(err, robots) { ... }) call to list all our robots the results are unpredictable - and not in the sense of "once in a while you might get unexpected resuts" - it would be unpredictable in the sense of "You will ALMOST CERTAINLY not get all your robots in the listing and might not even get ANY."

Writing callback-centric code on Mongoose ensures that things execute and complete in the order that you want - that is by the time you hit the inner loop, the find will DEFINATELY find all the robots that have been saved.

Finding By Identity
One of the ways Mongoose makes things easier is that you can use string values of ObjectId keys in searches and other contexts; for instance to find Bender, now that we know his _id value, you can call: var robots_model = robots_module.model; // == mongoose.model('Robots'); robots_model.findById('4d83f53dd0416b3837000001', function(err, bender){   console.log('We have found ' + bender.name); } // this is exactly equal, functionally speaking to  robots_model.find({_id: '4d83f53dd0416b3837000001'}, function(err, bender){ console.log('We have found ' + bender.name); } This is in contrast to Cristkof's Mongo Native, in which you have to instantiate an ObjectId in order to find or save an ObjectID. This is one of the many benefits of using a schema-based framework.

Finding by Value
The bulk of queries are more demanding than key based retrieval. The next step up is simple key/value searches robots_model.find({name: 'Bender'}, function(err, bender){   console.log('We have found ' + bender.name); } A few things to note - having to do more with Mongo itself than Mongoose:


 * the simple (key value) query shown above is case sensitive and only returns exact matches.
 * Complex text searches require regular expressions and are a bit inefficient.
 * All members of a multi-value search {foo: 1, bar: 2} must be met for a document to be returned - that is, multiple criteria are implictly joined by 'and': ("foo is 1 and bar is 2")

Complex Finds with the Query object
Replacing the simple JSON object ({name: 'Bender'}) with a Query object allows you to add inflections (gt, sort, et all) to the Query. The Query class has a method syntax that mimics the nested syntax of a complex Mongo join, as in var q = new Query; q.where('parts.weight').gt(1000) So the complete routine for finding a robot with at least one part that weighs more than 1000 (kg?) would be: var q = new Query; q.where('parts.weight').gt(1000; q.asc('name') // sorts robots by name, ascending robots_model.find(q, function(err, robots){ for(var r in robots){ var robot = robots[r]; console.log('robot: ' + robot.name); for (var p in robots.parts){ console.log('part ' + p.name + ' weighs ' + p.weight); }  } } You'll notice our Micronaut will not be in the listing: robot: Bender part Bending Arms weighs 500 part Hideaway Body weighs 1500 robot: R2D2 part Legs weighs 800 part Torso weighs 1500 <p style="font-family: Verdana, Helvetica, Arial, sans-serif; font-size: 1em; margin-top: 0px; margin-right: 0px; margin-bottom: 1.3em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">All the great query qualifies are available in the query class as methods - check outhttps://github.com/LearnBoost/mongoose/blob/master/test/query.test.js for details.

Note - Mongoose will BLOCK if Mongo is not turned on!
<p style="font-family: Verdana, Helvetica, Arial, sans-serif; font-size: 1em; margin-top: 0px; margin-right: 0px; margin-bottom: 1.3em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; ">This isn't a "Problem" per se - it would be worse if Mongoose pretended that Mongo was working then failed to write data - but if you find your app hanging during a query, double check that mongo is running. If Mongo is not running, your callback will never get called - i.e., it won't throw an error, it just will wait for Mongo to come on line. (and even THAT won't "unhang" your app in my experience - you will probably need to restart your node app after you run mongod.)