{{announcement.body}}
{{announcement.title}}

Computing Spatial Distances in RavenDB Queries

DZone 's Guide to

Computing Spatial Distances in RavenDB Queries

Explore spatial distances in RavenDB.

· Database Zone ·
Free Resource

Image title

RavenDB has had support for spatial queries for a long time. In RavenDB 4.0 we did a whole bunch of work to make spatial queries better. In particular, we have separate the concepts of searching and ordering for spatial queries. In most cases, if you are doing a spatial query, you'll want to sort the results by their distance. The classic example is: "Give me the Pizza stores within 5 km from me". I'll usually also want to see them listed by their distance. But there are other ways to go about it. For example, if I want to see a concert by Queen, I want to sort them by distance, but I don't want to do any spatial filtering.

It gets interesting when you combine different spatial operations at the same time. For example, consider the following query. Here is how you can search for a house in a particular school district, but want to find the one that is nearest to the office. The two spatial queries aren't related at all to one another.

One thing that we did not do, however, was report the distance back to the user. In other words, during the query, we compute the distance to the provided location, but we aren't actually returning it, just use it for sorting. This is mostly an oversight, and we'll be fixing that. The thought at the time was that since you already get the document from the server, you already have the location data, and can compute it on the client side. However, that isn't ergonomic to users, who want to just get the data and be done with it. As I said, we'll be fixing this, but in the meantime, you can use another of RavenDB's capabilities to handle this, by defining a JS function and projecting that directly from the server.

Let's take a look at the following query:

declare function distance(lat1, lon1, lat2, lon2, unit) {
// from : https://www.geodatasource.com/developers/javascript
if ((lat1 == lat2) && (lon1 == lon2)) {
return 0;
}
else {
var radlat1 = Math.PI * lat1/180;
var radlat2 = Math.PI * lat2/180;
var theta = lon1-lon2;
var radtheta = Math.PI * theta/180;
var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * 
   Math.cos(radlat2) * Math.cos(radtheta);
if (dist > 1) {
dist = 1;
}
dist = Math.acos(dist);
dist = dist * 180/Math.PI;
dist = dist * 60 * 1.1515;
if (unit=="K") { dist = dist * 1.609344 }
if (unit=="N") { dist = dist * 0.8684 }
return dist;
}
}
from index 'Houses/ByLocation' as h
where spatial.within(h.Location, spatial.wkt($schoolDistrict))
order by spatial.distance(h.Location, spatial.point($office.lat, $office.lng))
select {
Distance: distance(h.Location.Lat, h.Location.Lng, $office.lat, $office.lng),
Address: h.Address.Street + " " + h.Address.Neighbourhood + ", " + h.Address.City,
Price: h.RequestedPrice
}

This query allows us to project the details we need to render the results table to the user directly, without needing to do anything on the client side. It is a good enough solution for right now, and I'm really happy that RavenDB is flexible enough to provide this to users without us needing to do anything. That said, I don't like the ergonomics, and we intend to basically make this kind of function into an intrinsic operation inside of RavenDB.

Topics:
computing spatial distances ,database ,javascript ,ravendb ,tutorial

Published at DZone with permission of Oren Eini , DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}