Geospatial Query with MongoDB and Node.js

Hasn't been a long time when i started working with Wearetech.io and my first challenge was on. Sweet!

If you want to learn more about Wearetech.io, check the site and our Github repo.

Now on the fun part. We have a use case where an individual would be able to register on our site as a City Curator.

He could search for his City and if he wouldn’t find it in our database he would register it. When the next candidate comes in, he searches for his place and if it would be inside a 10 kilometers range of an already registered city we would deny it, since we wouldn’t want to have city calendars overlapping with each other.

When Thanasis started building Wearetech.io he decided to go with MongoDB. So in order to create the above scenario, MongoDB Geospatial Queries to the rescue!

MongoDB supports two types of Geospatial Queries indexing.

  • The spherical (2dSphere), that would allow you to store shapes made of points (coordinates) and then make comparisons with them like intersection, etc.
  • The flat (2d), that would store single points and then come up with their distances.
Your choice will affect your development a lot down the road, so think wisely.

Now on the code side, we will use Mongoose to shape our models. Here's the most simple one.

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

var CitySchema   = new Schema({
  name: String,
  geo: {
    type: [Number],
    index: '2d'
  }
});

module.exports = mongoose.model('City', CitySchema);

Now we can start populating our DB like this

var City = require('./app/models/city');

var cityModel     = new City(); 
cityModel.name = req.body.name; 
cityModel.geo    = [ req.body.lat, req.body.lng ]; 

cityModel.save(function (err) {
  if (err)
    res.send(err);

  res.json({});
});

And now on the fun part

var distance = 1000 / 6371;

var query = City.findOne({'geo': {
  $near: [
    req.body.lat,
    req.body.lng
  ],
  $maxDistance: distance

  }
});

query.exec(function (err, city) {
  if (err) {
    console.log(err);
    throw err;
  }

  if (!city) {
    res.json({});
  } else {
    console.log('Cant save: Found city:' + city);
    res.json(city);
 }

});

Our distance is in radians metric system and you can find more on how to calculate it here.

Due to recent Heroku pricing changes, this demo has broke. Still haven't find the time to fix it. :( Here is a demo of it. Try searching your City. If it's available register it and then try to find a place inside a 10 kilometers range of your previeus registered City.

Hope you will find it interesting. Comments?

Comments

Navigate

Removing JS from Magento CMS Pages
14 December 2014
How To Be A Programmer by Robert L Read
25 October 2014
I'm Kostas Bariotis, a web developer, a proud wanderer and a passionate doer. My mission is to write clean and efficient code, to solve problems on the Web and to learn something more. Read more about me or get in touch.