EventGet 50% off your ticket to MongoDB.local NYC on May 2. Use code Web50!Learn more >>
MongoDB Developer
Sign in to follow topics
MongoDB Developer Centerchevron-right
Developer Topicschevron-right

Introducing Sync for Geospatial Data

James Stone5 min read • Published Dec 06, 2023 • Updated Dec 06, 2023
Facebook Icontwitter iconlinkedin icon
Rate this announcement
Geospatial queries have been one of the most requested features in the Atlas Device SDKs and Realm for a long time. As of today, we have added support in Kotlin, JS, and .NET with the rest to follow soon. Geospatial queries unlock a powerful set of location-based applications, and today we will look at how to leverage the power of using them with sync to make your application both simple and efficient.
The dataset used in the following examples can be downloaded to your own database by following the instructions in the geospatial queries docs.
Let’s imagine that we want to build a “restaurants near me” application where the primary use case is to provide efficient, offline-first search for restaurants within a walkable distance of the user’s current location. How should we design such an app? Let’s consider a few options:
  1. We could send the user’s location-based queries to the server and have them processed there. This promises to deliver accurate results but is bottlenecked on the server’s performance and may not scale well. We would like to avoid the frustrating user experience of having to wait on a loading icon after entering a search.
  2. We could load relevant/nearby data onto the user’s device and do the search locally. This promises to deliver a fast search time and will be accurate to the degree that the data cached on the user’s device is up to date for the current location. But the question is, how do we decide what data to send to the device, and how do we keep it up to date?
With flexible sync and geospatial queries, we now have the tools to build the second solution, and it is much more efficient than an app that uses a REST API to fetch data.

Filtering by radius

A simple design will be to subscribe to all restaurant data that is within a reasonable walkable distance from the user’s current location — let’s say .5 kilometer (~0.31 miles). To enable geospatial queries to work in flexible sync, your data has to be in the right shape. For complete instructions on how to configure your app to support geospatial queries in sync, see the documentation. But basically, the location field has to be added to the list of queryable fields. The sync schema will look something like this:
Atlas sync schema configuration for geospatial data
Once sync has been configured to run queries on the location property, we can subscribe to a query that looks like this:
That is the syntax for the text-based “realm query language” representation (RQL) that is supported by most SDKs, but there is likely a more developer-friendly, language-specific syntax available for building up this query in the SDK of your choice. The radius of the circle is expressed in radians, and there are also SDK convenience functions to convert from the units of your choice (miles, kilometers, etc.) to radians.
With our dataset, that sync subscription will give us 321 restaurants in a circle around our example location.
Restaurants within a 0.5-km radius of the user’s location.
This design ensures that the device has all the nearby locations ready to go and allows the user to search through them even while offline. We can periodically change the sync subscription to update the user’s location in the query so that as the user walks around, the data stays up to date. When you choose to update the sync subscription with the user’s new location is up to you; you could simply use a timer, or you could calculate the delta in distance from the previous subscription location and refresh the subscription if it passes some threshold. This assumes that the user cannot walk to the edge of the results cached on the device before sync updates them. Adjusting the radius threshold in the query will be something that should be explored in user testing, given the tradeoffs of sending more data to the device versus searching stale data if the user has moved very quickly. If getting to the edge of the results is expected to be a problem, we could also subscribe to a much larger radius, and locally filter the results to a smaller radius. The advantage of this is that the app stores locally all data that the user needs as they walk around the area without needing to update the subscription at all.
Where sync really shines is that as the subscription-query changes based on the user location, the server will only send the difference of results to the device. This is much more efficient on data-in-transit and will show up in improved battery longevity on the user’s device when compared to a similar REST API design which would typically download the entire result set every time the user location changes. For example, if the user moves a few blocks to location {-73.9977962, 40.7167848}, then the updated sync query will remove 22 entries and add 85 entries.
Top Left: results at original location; Top Right: results added due to move; Bottom Left: results removed due to move; Bottom Right: final results in new location

Filtering by polygons

Let’s look at a more complex use case. What if we want our users to be able to filter by neighborhood as well as by proximity? Neighborhood boundaries are defined by a polygon in our dataset, and we can also filter by polygons. This ability unlocks a powerful feature of allowing users to explore the nearby restaurants in a specific neighborhood. For example, users can now ask for “a bakery in Chinatown near me”.
Neighborhood boundary of Chinatown
If we know that the user wants to only explore one neighborhood, we can sync that data to the device so that further local refinement is very fast.
Restaurants within the “Chinatown” neighborhood.
It is a matter of app design deciding which filter you’d like to run through sync and which you’d like to run locally later. For example, we could combine a radius filter with the neighborhood boundary to produce RQL such as this:
Note that sync schemas do not yet support nested collections. Technically, a geo polygon is a nested array because it supports “holes” within the first ring. If you are interested in learning more about the format of polygons, see the docs on GeoJSON objects . Syncing these types of shapes is in our upcoming roadmap, but until that is available, you can query the MongoDB data using the Atlas App Services API to get the BSON representation and parse that to build a GeoPolygon that Realm queries accept. Being able to filter on arbitrary shapes opens up all sorts of interesting geofencing applications, granting the app the ability to react to a change in location.
The ability to use flexible sync with geospatial queries makes it simple to design an efficient location-aware application. We are excited to see what you will use these features to create!
Ready to get started now?
Install one of our SDKs — start your journey with our docs or jump right into example projects with source code.
Then, register for Atlas to connect to Atlas Device Sync, a fully-managed mobile backend as a service. Leverage out-of-the-box infrastructure, data synchronization capabilities, network handling, and much more to quickly launch enterprise-grade mobile apps.

Facebook Icontwitter iconlinkedin icon
Rate this announcement

Easy Realm JWT Authentication with CosyncJWT

May 23, 2022 | 33 min read

Start Implementing Google Auth With MongoDB Realm in Your Android App

Mar 13, 2024 | 9 min read

Announcing the Realm C++ SDK Alpha

Apr 03, 2024 | 5 min read

SwiftUI Best Practices with Realm

Oct 19, 2022 | 33 min read
Table of Contents
  • Filtering by radius