GeoCouch, a CouchDB index that lets you query documents based on their geographic location, is more useful than you think. It’s not just for plotting points on a map; it can efficiently find all documents within a given radius, bounding box, or even a complex polygon.

Let’s see it in action. Imagine you have a places database in CouchDB with documents like this:

{
  "_id": "london",
  "_rev": "1-a1b2c3d4e5f678901234567890abcdef",
  "name": "London",
  "location": {
    "lat": 51.5074,
    "lon": -0.1278
  }
}

To enable GeoCouch, you need to create a design document with a geospatial view. Here’s a basic setup:

// Design Document: _design/geo
{
  "_id": "_design/geo",
  "_rev": "1-abcdef...",
  "views": {
    "locations_by_latlon": {
      "map": "function(doc) { if (doc.location && doc.location.lat && doc.location.lon) { emit([doc.location.lon, doc.location.lat], null); } }"
    }
  },
  "lists": {},
  "shows": {},
  "filters": {},
  "spatial": {
    "locations_by_latlon": "function(doc) { if (doc.location && doc.location.lat && doc.location.lon) { emit({latitude: doc.location.lat, longitude: doc.location.lon}, null); } }"
  }
}

The spatial function is where the magic happens. It takes the latitude and longitude from your document and emits them in a format GeoCouch understands. The key here is that the spatial function must emit an object with latitude and longitude keys.

Now, to query for places within a bounding box around London (let’s say a 10km radius, which is roughly 0.1 degrees latitude/longitude):

You’d send a GET request to:

GET /places/_design/geo/_spatial/locations_by_latlon?bbox=-0.2278,51.4574,-0.0278,51.5574

The bbox parameter takes minLon,minLat,maxLon,maxLat. GeoCouch will then return all documents whose location falls within this rectangular area.

For a circular query, you’d use the georadius parameter:

GET /places/_design/geo/_spatial/locations_by_latlon?georadius=-0.1278,51.5074,10000

Here, georadius takes lon,lat,radius_in_meters. This is incredibly powerful because it does the spherical geometry calculations for you, returning all points within that radius.

The mental model for GeoCouch is that it doesn’t just store your coordinates; it builds a specialized R-tree index. When you query with bbox or georadius, CouchDB traverses this R-tree to efficiently find only the relevant spatial regions, and then filters the documents within those regions. This is far more performant than a full scan of all documents and checking each one’s coordinates.

What most people miss is that the spatial function’s output must be an object with latitude and longitude keys. If you emit [lon, lat] or something else, it won’t be recognized as valid geospatial data by the GeoCouch index, and your queries will fail silently or return no results.

The next step is to explore polygon queries with GeoCouch, which allows for even more complex spatial filtering.

Want structured learning?

Take the full Couchdb course →