Location-Based Search in Java With Valkey or Redis Geospatial Indexes

Published on
June 15, 2026

There was a turning point in the history of software development that happened fairly recently. When GPS receivers were suddenly everywhere, whether as a component of smartphones or specialized tracking devices, it became feasible to make applications location-aware. What was once reserved for highly specialized "geo" apps has now become commonplace.

Enterprise development teams have taken advantage of this revolution as much as commercial software makers. They can now create apps that help their fleet managers find the driver nearest to an emergency call, monitor the tracking history of important shipments, or show the locations of remote team members.

One requirement for creating geo apps is the ability to store and work with geospatial data. Many dev teams turned to PostGIS, an extension for the PostgreSQL database. However, modern distributed apps that work with potentially millions of data points soon run into the limitations of relational databases like Postgres.

The in-memory data store Redis and its open-source offshoot Valkey both offer geospatial indexes with sub-millisecond radius queries. Java developers just need an easy way to integrate Valkey and Redis geospatial into their apps. The solution for that is Redisson and its RGeo object, a familiar Java API that works with either Valkey or Redis. Here's how to get location-based search in Java with Valkey or Redis geospatial.

How Valkey/Redis Geospatial Actually Works

Redis has long shipped with the Redis geospatial data type, which Valkey inherited. While PostGIS is a separate product created to extend the functionality of a traditional RDBMS, Valkey or Redis geospatial is really just a sorted set (a standard Valkey/Redis data type) underneath, with a special hash.

When you add a longitude/latitude pair as a data point, the Valkey/Redis geospatial engine encodes it as a 52-bit geohash. This geohash interleaves the bits of the two coordinates so that points that are closer together in the real world also produce numbers that are close together numerically. The integer stored in a geohash becomes the member's score in the sorted set.

This is important because once you can express proximity in terms of numeric adjacency, a geo query such as "find everything near this point" only requires a range scan over a sorted set — just the type of operation Valkey and Redis perform extremely fast. That same sorted-set foundation powers other ranking workloads, it's also how you'd build a real-time leaderboard in Java.

Adding Location Points With GEOADD and RGeo

Redisson's RGeo object provides a friendly Java interface to the Valkey/Redis commands that drive the geospatial engine: GEOADD, GEOSEARCH, GEODIST, GEOPOS, and GEOHASH. To get started, let's look at how you add data points with GEOADD and RGeo.

The first step is adding the Redisson dependency. Here's how to do that with Maven:

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>4.6.0</version>
</dependency>

Now, you can use RGeo in your Java code to add some points. Just make sure to add longitude first, then latitude:

RedissonClient redisson = Redisson.create();
RGeo drivers = redisson.getGeo("drivers");
// add(longitude, latitude, member)
drivers.add(-122.27652,   37.805186,  "driver:1");
drivers.add(-122.2674626, 37.8062344, "driver:2");
// or batch with GeoEntry objects
drivers.add(
    new GeoEntry(-122.2469854, 37.8104049, "driver:3"),
    new GeoEntry(-122.2554, 37.8021, "driver:4"));

In this example, each call issues GEOADD against the drivers key. Because the backing structure is a sorted set, re-adding an existing member simply updates its position. This is ideal for the stream of data a large pool of moving drivers will create as they update their locations every few seconds.

Radius and Bounding-Box Search With Geosearch

In Redis 6.2 or later, and in every Valkey release, the GEOSEARCH command supports both circular and rectangular queries. Redisson's GeoSearchArgs builder is flexible enough for all kinds of searches.

Here's how to do a radius search: everyone within a circle, nearest first, with a maximum of 10 results:

List nearby = drivers.search(
    GeoSearchArgs.from(-122.27, 37.80)   // search center (lon, lat)
        .radius(3, GeoUnit.KILOMETERS)
        .order(GeoOrder.ASC)             // closest first
        .count(10));                     // top N only

Here's how to do a bounding-box search, which is everyone located inside a rectangle (defined as width x height):

List inViewport = drivers.search(
    GeoSearchArgs.from(-122.27, 37.80)
        .box(4, 4, GeoUnit.KILOMETERS));

And here's how to search relative to an existing member. For example, "find drivers near driver:1":

List peers = drivers.search(
    GeoSearchArgs.from("driver:1").radius(2, GeoUnit.KILOMETERS));

Calculating Distance Between Points

Knowing which drivers are nearby is one thing, but you typically also need to know how far away they are, too. RGeo offers two options for calculating the distance between points. Here's the first option:

// great-circle ("as the crow flies") distance between two members
Double meters = drivers.dist("driver:1", "driver:2", GeoUnit.METERS);
// raw coordinates for any member
Map positions = drivers.pos("driver:1", "driver:2");
GeoPosition p = positions.get("driver:1");
double lon = p.getLongitude();
double lat = p.getLatitude();

This approach finds the drivers and their distance from the search origin in a single round trip:

Map withDistance = drivers.searchWithDistance(
    GeoSearchArgs.from(-122.27, 37.80)
        .radius(3, GeoUnit.KILOMETERS)
        .order(GeoOrder.ASC));
withDistance.forEach((driver, km) ->
    System.out.printf("%s is %.2f km away%n", driver, km));

Use Case: A Ride-Share Service

As you can see, RGeo provides all the required functionality to build a location-aware app. So, let's put everything together for a real-life example: a ride-share service.

In this small bit of Java code, the ride-share drivers continuously report their locations while a rider requests a pickup:

public class RideMatcher {
    private final RGeo drivers;
    public RideMatcher(RedissonClient redisson) {
        this.drivers = redisson.getGeo("drivers");
    }
    // called on every GPS ping
    public void updateDriver(String id, double lon, double lat) {
        drivers.add(lon, lat, id);  // GEOADD upserts in place
    }
    // rider taps "request ride"
    public Map nearestDrivers(double lon, double lat) {
        return drivers.searchWithDistance(
            GeoSearchArgs.from(lon, lat)
                .radius(5, GeoUnit.KILOMETERS)
                .order(GeoOrder.ASC)
                .count(5));
    }
}
Another Use Case: A Store Finder App

The above code could be repurposed for any number of location-aware geo apps. For example, you could swap drivers for stores, load each location once, and then determine all stores within 10 km, listing the closest ones first.

Since reads and writes in either use case both hit one sorted set, you get sub-millisecond responses, even with millions of data points.

Combining Geo With RSearch

Finding all stores near a specific location is a start, but the resulting dataset obviously doesn't include any attributes about the stores themselves. If, for example, you wanted to find all coffee shops within 3 km that are rated 4 stars or higher and open right now, you need another layer of context.

For that, you can use Redisson's RSearch object, which wraps Redis 8's RediSearch engine (in Valkey, you can use the valkey-search equivalent).

To combine geo functionality with RSearch, first index your store hashes with a geo field alongside text and numeric fields:

RSearch search = redisson.getSearch();
search.createIndex("store-idx",
    IndexOptions.defaults().on(IndexType.HASH).prefix(List.of("store:")),
    FieldIndex.geo("location"),
    FieldIndex.text("name"),
    FieldIndex.numeric("rating"));

Next, run a combined query, attaching a geo filter to a normal text/numeric search:

SearchResult result = search.search("store-idx",
    "@name:coffee @rating:[4 5]",
    QueryOptions.defaults().filters(
        QueryFilter.geo("location")
            .from(-122.27, 37.80)
            .radius(3, GeoUnit.KILOMETERS)));

Now, one request returns coffee shops, rated between 4 and 5 stars, and located within 3 km. The same RSearch index can combine geo filters with full-text, numeric, and even vector-similarity queries.

Making Valkey or Redis Geospatial Easily Accessible

Location-based search in Java doesn't require a heavy tech stack loaded down with Postgres clusters and multiple PostGIS engines. Such a setup could quickly become bogged down as it tries to keep up with potentially millions of geo data points updating every few seconds.

A sorted set, a geohash, and Redisson's RGeo cover radius queries, bounding boxes, and distance calculations with incredible speed, thanks to the in-memory performance of Valkey or Redis. To get started with geospatial search in your Java apps, learn more about the features of Redisson and Redisson PRO.

Similar articles