Hibernating Rhinos

Zero friction databases

Spatial Sorting

Hi all,

I’d like to share with you a new feature we were working on in the sphere of spatial queries.

It began from an issue, telling that we need to have the ability to sort query results by distance from a given point, without limiting the scope of the query. Truth to be told, it took me some time to understand what the problem was and why are the results were limited at the first place. To explain that point, I'll give an example of how spatial queries looks like in the Client API:

Let’s say that our document looks like:

public class Shop { public string Name { get; set; } public double Lat { get; set; } public double Lng { get; set; } }

And our index looks like:

public class ShopSpatialIndex : AbstractIndexCreationTask<Shop> { public ShopSpatialIndex() { Map = shops => from shop in shops select new { shop.Name, _ = SpatialGenerate(shop.Lat,shop.Lng) }; } }

Now, there are various ways to perform a spatial query, somehow I stumbled upon the most detailed and complex one:

store.DatabaseCommands.Query("ShopSpatialIndex", new SpatialIndexQuery() { QueryShape = SpatialIndexQuery.GetQueryShapeFromLatLon(36.15632, 51.70375, 100), SpatialRelation = SpatialRelation.Within, SpatialFieldName = Constants.DefaultSpatialFieldName, SortedFields = new[] { new SortedField(Constants.DistanceFieldName), } });

In that case, you can see that we are allowed to mention a shape, the relation the shape should have to the data, queried field and sorted field. The problem is that the sorting is done according to the center of the given field.

After looking a bit more, I found out that there is a special kind of relation: “Nearby”,  that one allows to do a sorting, without filtering:

store.DatabaseCommands.Query("ShopSpatialIndex", new SpatialIndexQuery() { QueryShape = SpatialIndexQuery.GetQueryShapeFromLatLon(36.15632, 51.70375, 100), SpatialRelation = SpatialRelation.Nearby, SpatialFieldName = Constants.DefaultSpatialFieldName, SortedFields = new[] { new SortedField(Constants.DistanceFieldName), } });

Now I  had a new problem: I cannot do both. Example for case when I'd like to do that is if I'm searching for Shops in a particular city, I stand inside or outside it and I want the results to be ordered by proximity:

 

image

Although the “nearby” relation inspired me, I couldn’t use it in order to solve the problem. What I chose to do is to allow passing the center of the sorting with the sorted field name, in manner of CSV parameter concatenation, no worry, I encapsulated it into a nice API. Although this solution made the complex, and most detailed writing of the query to look even less intuitive:

store.DatabaseCommands.Query("ShopSpatialIndex", new SpatialIndexQuery() { QueryShape = SpatialIndexQuery.GetQueryShapeFromLatLon(36.15632, 51.70375, 100), SpatialRelation = SpatialRelation.Nearby, SpatialFieldName = Constants.DefaultSpatialFieldName, SortedFields = new[] { new SortedField(string.Format("{0};{1};{2}", Constants.DistanceFieldName, 36.15612, 51.70355)), } });

The alternative methods to perform spatial querying now looks like:

session.Query<Shop>() .Customize(x => x.WithinRadiusOf(100, 36.15632, 51.70375)) .OrderByDistance(new SpatialSort { Latitude = 36.15612, Longitude = 51.70355, FieldName = Constants.DefaultSpatialFieldName });

or

session.Query<Shop>() .Customize(x => x.WithinRadiusOf(100, 36.15632, 51.70375)) .Customize(x => x.SortByDistance(100, 36.15632, Constants.DefaultSpatialFieldName))

or

session.Query<Shop>() .Customize(x => x.WithinRadiusOf(100, 36.15632, 51.70375)) .Customize(x => x.SortByDistance(36.15612, 51.70355))

Hope you find it useful and it will encourage you to explore and use this and other spatial functionality of RavenDB.

Tags:

Published at

Originally posted at