RRedis Handbook

ORTA

Geospatial (GEO)

Konum bazlı veri depolama ve yarıçap sorguları. Restoran/mağaza arama, yakındaki kullanıcılar.

Kod örneği görünümü Bu sayfadaki eşleşen örnekleri seçilen istemciye göre gösterir.
# Konum ekle (longitude, latitude, member)
GEOADD stores:istanbul 28.9784 41.0082 "store:1"
GEOADD stores:istanbul 29.0322 41.0136 "store:2"
GEOADD stores:istanbul 28.9551 41.0186 "store:3"

# İki nokta arası mesafe
GEODIST stores:istanbul "store:1" "store:2" km    # ~4.5

# Yarıçap sorgusu (5km içindeki mağazalar)
GEOSEARCH stores:istanbul FROMLONLAT 28.98 41.01 BYRADIUS 5 km ASC COUNT 10 WITHCOORD WITHDIST

# Koordinat al
GEOPOS stores:istanbul "store:1"

# GeoHash
GEOHASH stores:istanbul "store:1"

# Üye sil (Sorted Set üzerinde çalışır)
ZREM stores:istanbul "store:1"
public class GeoLocationService
{
    private readonly IDatabase _redis;

    public GeoLocationService(IConnectionMultiplexer mux)
        => _redis = mux.GetDatabase();

    public async Task AddStoreAsync(string storeId, double longitude, double latitude)
    {
        await _redis.GeoAddAsync("stores:istanbul",
            new GeoEntry(longitude, latitude, storeId));
    }

    public async Task AddBulkAsync(IEnumerable<(string id, double lon, double lat)> stores)
    {
        var entries = stores.Select(s => new GeoEntry(s.lon, s.lat, s.id)).ToArray();
        await _redis.GeoAddAsync("stores:istanbul", entries);
    }

    // Yarıçap sorgusu — yakındaki mağazalar
    public async Task<List<NearbyStore>> GetNearbyStoresAsync(
        double longitude, double latitude, double radiusKm, int count = 10)
    {
        var results = await _redis.GeoSearchAsync("stores:istanbul",
            new GeoSearchCircle(longitude, latitude, radiusKm, GeoUnit.Kilometers),
            count: count,
            order: Order.Ascending, // en yakından uzağa
            options: GeoRadiusOptions.WithCoordinates | GeoRadiusOptions.WithDistance);

        return results.Select(r => new NearbyStore
        {
            StoreId = r.Member.ToString(),
            DistanceKm = r.Distance ?? 0,
            Longitude = r.Position?.Longitude ?? 0,
            Latitude = r.Position?.Latitude ?? 0
        }).ToList();
    }

    // İki mağaza arası mesafe
    public async Task<double?> GetDistanceAsync(string store1, string store2)
    {
        return await _redis.GeoDistanceAsync("stores:istanbul", store1, store2, GeoUnit.Kilometers);
    }

    // Kullanıcı konumunu güncelle (gerçek zamanlı takip)
    public async Task UpdateUserLocationAsync(string userId, double lon, double lat)
    {
        await _redis.GeoAddAsync("active:users", new GeoEntry(lon, lat, userId));
        await _redis.KeyExpireAsync("active:users", TimeSpan.FromMinutes(15));
    }
}

// API endpoint örneği
app.MapGet("/api/stores/nearby", async (
    [FromQuery] double lon,
    [FromQuery] double lat,
    [FromQuery] double radius,
    GeoLocationService geo) =>
{
    var stores = await geo.GetNearbyStoresAsync(lon, lat, radius);
    return Results.Ok(stores);
});

GEO aslında Sorted Set. Score olarak geohash kullanır. ZCARD ile eleman sayısı, ZREM ile silme yapabilirsin. Max hata: ~0.5m.