Mobile GeoLocation App in 30 minutes – Part 1: Node.js and MongoDB

Overview

Are you interested in learning how to use Node.js with a NoSQL database like MongoDB to create REST services? Would you like to know how to create a mobile app with Google Maps that can perform geospatial queries with ease? Do you want to see how you can deploy all this to a PaaS with the click of a few buttons and a rudimentary understanding of Git? The answers to this and more are part of my three part series where I will cover the details on how you can create a “Pettracker” like application using Node.js, MongoDB, and Sencha Touch hosted on OpenShift. In this first part I will discuss how to build the middle tier and data store with Node.js and MongoDB.

What is Node.js and MongoDB?

Node.js is a JavaScript framework (API to a C library) for creating highly scaling, non-blocking IO applications that does not create multiple process threads for each invocation incurring a lot less O/S overhead under load. This is accomplished using an event-driven programming model that executes an IO bound call but does not block; it continues to the next line of code. It uses a callback mechanism to conclude the operation with the response once the event is complete. JavaScript is perfect to accommplish this because its native support for closures. Node by itself isn’t that interesting, but akin to Maven in the Java world, Node has npm so that you can add modules to it and expand its functionality further to all sorts of different use cases. In this article you’ll see how I leverage npm for working with MongoDB and creating a Web server router. Finally, Node together with Socket.IO can be used to create real-time Web applications (more on this in the future).

MongoDB is a highly scalable, high performance, open source NoSQL database that stores documents (rows in SQL parlance) into collections (tables in SQL) and is maximized for performance using in-memory cache, replica sets and sharding. It has a very nice query API and supports map/reduce functionality found in Hadoop/HBase. One of the pecular things with NoSQL DB’s is that there is no schema defined ahead of time. So other than installing MongoDB, there isn’t much else that you need to do. Given the fact that there’s no schema it doesn’t mean that we don’t have structure to our data. NoSQL allows for a very flexible schemas that can change easily as your application needs changing. Finally, MongoDB has built-in geospatial querying to support “near” and polygon searches. We’ll explore this in detail.

The DogTag Application

As you may be aware, Pettracker (aka Tagg) is a Qualcomm device/application that can be used to track lost Pets. I work for Qualcomm but I didn’t work on this project;however, I thought it would be interesting to see how I could replicate this application using Node.js and MongoDB, since it would be the perfect use case for something that would require high throughput and geospatial searching. Node.js will provide the middleware both to consume the JSON data from the emitters (i.e., the dog tag transponder) and it will provide the REST interface so that the mobile device can locate the dogs or perform geospatial queries of what’s near the user.

The first step to do all this is to create a Node.js application. I used the Express and Mongoose Node modules to allow me to create a website and to interface with MongoDB. You can add these modules using npm to your node install or add them to your package.json. I’ll talk more about this later in Part 3. Also, you can download the code from my GitHub account here.

Let’s take a look at the model.js code:

var mongoose = require('mongoose')
  , Schema = mongoose.Schema;

var dtSchema = new Schema({
    name:String,
    description:String,
    date: {type: Date, default: Date.now},
    longitude: Number,
    latitude: Number,
    coords: [Number, Number]
});

module.exports = mongoose.model('DogTag', dtSchema);

Mongoose is a nice Node module that allows me to work with MongoDB data in JavaScript, so in the code above you can see that I am defining “DogTag” schema (a document in Mongo) and setting fields for what should be stored. Mongoose will take care of the unmarshalling/marshalling the JSON data to MongoDB. Next, I need to define the api for the operations I wish to perform shown as follows:

var DogTag = require('../model/dogtag.js');

exports.save = function(req, res) {
    var dogtag = new DogTag({name: req.params.name, description: req.params.descr,
        longitude: req.params.longitude, latitude: req.params.latitude});
    dogtag.save(function (err) {
        if (err) throw err;
        console.log('Dogtag saved.');

        res.send('Dogtag saved.');
    });
}

exports.list = function(req, res) {
    DogTag.find(function(err, dogtag) {
          res.setHeader('Content-Type', 'text/javascript;charset=UTF-8');
        res.send(req.query["callback"] + '({"records":' +  JSON.stringify(dogtag) + '});');
    });
}

exports.show = (function(req, res) {
    DogTag.findOne({name: req.params.name}, function(error, dogtag) {
        res.send([{Dog: dogtag}]);
    })
});

// first locates a dog by its name
exports.show = (function(req, res) {
    DogTag.findOne({name: req.params.name}, function(error, dogtag) {
        res.send([{Dog: dogtag}]);
    })
});

exports.near = function(req, res) {
    DogTag.find({coords : { $near : [req.params.lon, req.params.lat], $maxDistance : req.params.dist/68.91}}, 
      function (error, dogtag) {    
        res.setHeader('Content-Type', 'text/javascript;charset=UTF-8');
        res.send(req.query["callback"] +'({"records":' + JSON.stringify(dogtag) + '});');
    })
}

This code provides the CRUD operations to work with MongoDB. I used the schema I created previously to create dogtag objects and I use their save method to persist them into MongoDB. It’s that simple! If I need to find all the dogs, I use find(), or to find one dog by name, I use show(petname). Now, the last API, “near”, is doing a geospatial query using $near and $maxDistance against my “coords” field in the database. One thing to note is that you have to tell MongoDB to index fields that contain lat/lon. For details on this, see here, but all you really do is run this command from a mongo prompt: db.[collection_name].ensureIndex({ [field_name]: "2d" }).

One more thing to note is that I need to return JSONP back to the client by providing a callback name. To do that, I need the client to pass my a queryParameter (called “callback”) that will be the name of the callback method in the JSONP that is returned. I also need to call the built-in JavaScript function JSON.stringify to convert the schema object to JSON. You can see this put to use on lines 17 and 38.

The last piece is do build the node server. Yes, that’s right…you’re actually going to create the HTTP server that will respond to the request. Let’s take a look to see how:

var express = require('express');
var mongoose = require('mongoose');
var app = express();

var ipaddr  = '127.0.0.1';
var port    =  8081;

mongoose.connect('mongodb://localhost/dogtag');

app.configure(function () {
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(app.router);
});

// set up the REST API handler methods are defined in api.js
var api = require('./controller/api.js');
app.post('/dogtag', api.post);
app.get('/dogtag/near/:lon/:lat/:dist?', api.near);
app.get('/dogtag/:name/:descr/:latitude/:longitude?', api.save);
app.get('/dogtag/:name.:format?', api.show);
app.get('/dogtag', api.list);

//  And start the app on that interface (and port).
app.listen(port, ipaddr, function() {
   console.log('%s: Dogtag server started on %s:%d ...', Date(Date.now() ),
               ipaddr, port);
});

As described earlier, I need to tell node that I’m going to use express and mongoose in my application. One line 3, I simply create the HTTP server. On lines 6-9, I use mongoose to connect to the database. On lines 18-23, I configure URL routing to the controller api’s from the previous example. Note how I use colons to express parameters on the URL and then bind them to a given api. Finally, I tell the server to listen on a certain port and host spit out a start message if everything went successful.

So that’s really all there is to create the necessary middleware to support our DogTag application. It takes in JSON data from the emitters to store in MongoDB and then uses the Express framework to create a router to respond to requests for finding dogs whether by name or through geospatial searching. In the next part I’ll explore how we can use Sencha Touch to create a mobile application that will help a user locate their dog. For a little preview of that application, you can go here to experiment with it here.

Advertisements

6 comments on “Mobile GeoLocation App in 30 minutes – Part 1: Node.js and MongoDB

  1. Pingback: Mobile GeoLocation App in 30 minutes – Part 2: Sencha Touch | Loutilities

  2. Pingback: RabbitMQ, Node.js and Groovy: Making Messaging Easy | Loutilities

  3. Precisely what truly inspired u to post “Mobile GeoLocation App in 30 minutes – Part 1: Node.
    js and MongoDB | Loutilities”? I honestly loved the post!
    I appreciate it ,Irene

  4. Hi there! Would you mind if I share your blog with
    my zynga group? There’s a lot of folks that I think would really appreciate your content. Please let me know. Thanks

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s