December 26, 2024

RavenDB spatial data – fast and proper way to query coordinates

Hello there and thank you for your visit! I’m going to delve a bit into a RavenDB feature which I wanted to explore for a long time now, that is spatial coordinate querying. Ever wondered how algorithms in apps like Google Maps manage to find places near you in a snappy manner? The good news is that, if you use RavenDB in your app you can add support for this too 😉

Let’s find some shops

Now we need to have some context, let’s say we want to find nearby shops depending on our location. We will just use the RavenDB Studio in order to familiarize ourselves with the concept. Luckily, our majestic RavenDB makes it really easy to implement this feature, albeit you need to learn a bit about indexes.

As for the data, I created some documents representing shops around an area. Each document contains the location of the shop expresses in latitude and longitude as decimal numbers. I also added the type of the shop and name in order to make it feel real. The raw JSON data itself looks like this:

{
    "name": "Catena",
    "type": "pharmacy",
    "location": {
        "latitude": 45.518488638431,
        "longitude": 25.36989384496497
    },
    "@metadata": {
        "@collection": "Shops"
    }
}
{
    "name": "Free spirit",
    "type": "alcohol",
    "location": {
        "latitude": 45.67150004752834,
        "longitude": 25.313239911728434
    },
    "@metadata": {
        "@collection": "Shops"
    }
}
{
    "name": "LIDL",
    "type": "supermarket",
    "location": {
        "latitude": 45.52627894738562,
        "longitude": 25.296296679277805
    },
    "@metadata": {
        "@collection": "Shops"
    }
}
{
    "name": "La tantica",
    "type": "groceries",
    "location": {
        "latitude": 45.56002448709781,
        "longitude": 25.325417860052323
    },
    "@metadata": {
        "@collection": "Shops"
    }
}

All of the coordinates are in the vicinity of my hometown in central Romania, for demonstration purposes.

I marked the shops location with A,B,C,D which correspond like below:

  • A – “Catena”
  • B – “Free spirit”
  • C – “LIDL”
  • D – “La tantica”

The (much) necessary index

For this geospatial feature to work, as it seems you cannot query directly into the documents, an index has to be created. The purpose of the index is to correctly ingest the coordinate data and make it available for querying operations.


map('Shops', (shop) => {
    return {
        name: shop.name,
        type: shop.type,
        location: createSpatialField(
            shop.location.latitude,
            shop.location.longitude
        )
    }
})

That’s pretty much it for the index, the JS map function is returning an exact copy of the document, except for the location field for which we call the predefined createSpatialField function to let RavenDB know that this field is a spatial one.

The query in action

It’s high time to see how to query this index we just created! If we go to the Query tab in the Raven Studio, we can type this query in:


from index 'ShopsWithSpatial' as s
where spatial.within(
    s.location,
    spatial.circle(8, 45.60190009658405, 25.30741567557353)
)

Now the novelty in this query is the spatial.circle function. This predefined function requires 3 parameters:

  1. The radius of the circle expressed in kilometers
  2. The latitude
  3. The longitude

Here we’ve got a visual representation of the circle area of the above query:

If we run the query in the Studio we will get just 2 results, the shops for B and D respectively:

Try to increase the range from 8 to 10 in the query. You’ll notice another shop returned by the query. How about 12? No problem, all of the shops are found within this range and will be returned accordingly 😉

You can also play with the coordinates if you like. Also, lest to forget, if you feel adventurous you can also order the results by distance:


from index 'ShopsWithSpatial' as s
where spatial.within(
    s.location,
    spatial.circle(12, 45.60190009658405, 25.30741567557353)
)
order by spatial.distance(
    location,
    spatial.point(45.60190009658405, 25.30741567557353)
) 

This way you also get results closer to you 😀

You can find the database I used on my Github page.

Conclusion

This article showcased the powerful geospatial capabilities that RavenDB has with an exciting shops example which simulates a read-world scenario. This can be adapted to suit your needs in no time.

I guess this will be the last article of the year due to End-of-Year Celebrations so I wish you Happy Celebrations and looking forward to an exciting 2024! 🎅

Thanks for reading, I hope you found this article useful and interesting. If you have any suggestions don’t hesitate to contact me. If you found my content useful please consider a small donation. Any support is greatly appreciated! Cheers  😉

afivan

Enthusiast adventurer, software developer with a high sense of creativity, discipline and achievement. I like to travel, I like music and outdoor sports. Because I have a broken ligament, I prefer safer activities like running or biking. In a couple of years, my ambition is to become a good technical lead with entrepreneurial mindset. From a personal point of view, I’d like to establish my own family, so I’ll have lots of things to do, there’s never time to get bored 😂

View all posts by afivan →