Getting Around in MongoDB

I started working with MongoDB a few days ago. To oversimplify, think of Mongo as a really big and fast hash that gets saved to disk. It lets you query, retrieve, and manipulate data in Javascript and JSON. I had a ton of work to do, so I didn't get a chance to explore the technology as much as I would've liked. Today, after getting a solid night's sleep, I got a chance to experiment more. Read on to get some quick tips about writing Mongo queries and generating reports from the Mongo shell.

When I first started interacting with Mongo, I used the Ruby Mongoid adapter. It gave me a familiar ActiveRecord interface so I could accomplish things like:

Beer.all(:conditions => { :style => "stout" })

But it also gave me a sneak peak at Mongo's 'criteria' API for querying

Beer.criteria.where(:style => "stout")

It's worthwhile to note that criterias are lazily loaded, meaning that the query isn't performed until you actually need to access the data.

Beer.criteria.where(:style => "stout")  # doesn't hit mongo
Beer.criteria.where(:style => "stout").first.drink!  # executes actual query

This was all fine and dandy, but just like learning SQL and ActiveRecord, having an understanding of the underlying system gives you a better idea of what you can do. So I busted out the mongo shell and started running queries. The tutorial was a good starting point for familiarizing myself with how to connect to mongo, executing some queries, and printing out results. My favorite feature was that the mongo shell doubled as a Javascript interpreter. I was able to write JS to manipulate the query results:

function map(arr, func) {
  var collection = [];
  if(arr && arr.length) {
    for(var i = 0; i < arr.length; i++) {
      collection.push(func(arr[i]));
    }
  }
  return collection;
}

db.beers.find().forEach(function(beer) {
  print(beer.name);
  print('--------------');
  map(beer.ingredients, function(ingredient) {
    print(beer.ingredient.quantity + ' - ' + beer.ingredient.name);
  });
});

I got pretty tired of copying and pasting this script every time I edited it. Thankfully, mongo shell lets you pass in script files to execute:

> mongo --help
MongoDB shell version: 1.4.0
usage: mongo [options] [db address] [file names (ending in .js)

This little feature enabled me to write more complex scripts and tweak to my heart's content. Something was still missing though. I love Javascript as a language, but nowadays, I've grown so accustomed to jQuery that I'll start typing jQuery assuming it's available even when it's not.

db.beers.find().forEach(function(beer) {
  // CURSES! JQuery isn't available :(
  $(beer.ingredients).each(function() {
    // ... do something
  });
})

My next genius idea: load jQuery as the first script:

> mongo beerdb jquery-1.4.2.min.js reporting.js
JS Error: ReferenceError: window is not defined jquery-1.4.2.min.js:153
failed to load: jquery-1.4.2.min.js

Noooooo! It makes a lot of sense that jQuery would assume a browser environment, but I was hoping that it wouldn't require it for the nice utility functions.

To get the job done, I wrote myself a small utility library:

$ = {
  // > $.map([1,2,3,4], function(x) { return x*x; }
  // [1,4,9,16]
  map: function(arr, func) {
    var collection = [];
    if(arr && arr.length) {
      for(var i=0; i<arr.length; i++) {
        collection.push(func(arr[i]));
      }
    }
    return collection;
  },

  // > $.max([1,2,3,4])
  // 4
  // > $.max([1,2,3,4], function(x) { return x*2; })
  // 8
  max: function(arr, func) {
    if(arr == undefined) { return arr; }
    var max = null;
    for(var i=0; i<arr.length; i++) {
      var current = (func ? func(arr[i]) : arr[i]);
      max = (current > max ? current : max);
    }
    return max;
  },
}

This didn't buy me the full power of JQuery, but at the same time, I was pretty happy I was able to quickly whip together some JS and get the reports I needed.

Does anyone know of a JS library that buys you a lot of core library features and fixes, but doesn't assume a browser? I looked at John Resig's Env.js, but even that assumes that you're running JS in Rhino. What other Mongo tricks do people find useful?

Reference

Here's the order I recommend reading the Mongo documentation. The Advanced Queries link was especially useful.

All databases are fictional. No beers were harmed in the making of this blog post.