開発
Storing location data in MongoDB
denvazh
When I was looking into insights of MongoDB I’ve accidentally bumped into one very nice feature it provide out-of-the-box.
MongoDB basics
Few words about MongoDB itself. MongoDB is a document-oriented database.
Each hosted environment has a number of databases. Each database consists of a set of collections. Each collection holds a set of documents.
Each document is a set of key-value pairs.
Documents in MongoDB has dynamic schema, thus documents in same collection do not need to have same fields or structure (which is a must in relational databases).
Common fields in each document can hold different types of data.
Example:
Write operations (insert query):
> db.restaurants.insert( { name: "Coffee House", stars: 5 } ); > db.restaurants.insert( { name: "Coffee Shop", stars: 1 } ); > db.restaurants.insert( { name: "Tea Pot", stars: 5 } );
NOTE: no output is usually a good sign in unix environment.
Read operation (a kind of select query in RDB). Let’s find places with 5 star rating:
> db.restaurants.find( { stars: 5 } ); { "_id" : ObjectId("512b104fc05490d5165277bb"), "name" : "Coffee House", "stars" : 5 } { "_id" : ObjectId("512b105dc05490d5165277bd"), "name" : "Tea Pot", "stars" : 5 }
Those were MongoDB basics. Now let’s do much more interesting stuff.
2d geospatial indexes
This is very good and useful feature, because it allows to associate any given document with 2d-location data.
To actually enable spatial index, we have to do the following:
db.restaurants.ensureIndex({ loc : "2d" } );
This would enable geospatial index for the restaurants collection in “loc” field.
Adding some data:
> db.restaurants.insert( { name: "Tea Pot", stars: 5, loc: {long : 126.9, lat : 35.2} }); > db.restaurants.insert( { name: "Cold shower", stars: 5, loc: {long : 120.1, lat : 60.0} });
What can we do if we store data like this? Well, there are lot of possibilites.
For example it is possible to query if certain places are in close proximity to certain point. For the area around
that point we can use squares ( for example ).
> db.runCommand({geoNear: "restaurants", near:[ 126, 35 ], spherical: true}); { "ns" : "test.restaurants", "near" : "1110001111000010110101101000011111000010110101101000", "results" : [ { "dis" : 0.013317031524412134, "obj" : { "_id" : ObjectId("512b14b1c05490d5165277bf"), "name" : "Tea Pot", "stars" : 5, "loc" : { "long" : 126.9, "lat" : 35.2 } } }, { "dis" : 0.44143809150562696, "obj" : { "_id" : ObjectId("512b14d6c05490d5165277c0"), "name" : "Cold shower", "stars" : 5, "loc" : { "long" : 120.1, "lat" : 60 } } } ], "stats" : { "time" : 42, "btreelocs" : 0, "nscanned" : 2, "objectsLoaded" : 2, "avgDistance" : 0.22737756151501956, "maxDistance" : 0.44143830144625684 }, "ok" : 1 }
This is pretty marvelous, considering the fact, that this is done on the database level. With mongodb it is possible to do even more advanced stuff like geofencing and storing 3d geometry data.
Read more about 2d geospatial index in MongoDB here.