Redis Vishwaroopam Part 2: Exploring Geospatial Commands

Redis Geospatial data title cover

Today, we’ll explore the Geospatial data type in Redis. This data structure is particularly useful for fetching locations, such as restaurants, within a given radius or box from your location. Redis stores geographic locations very efficiently using the Geohash algorithm with sorted sets as the underlying data structure. The Geohash encodes a geographic location into a short string of letters and digits.

In this post, we’ll dive into some of Redis’s geospatial commands by building a minimalistic backend for a hotel booking platform. We’ll use the “Indian Hotels on GoIbibo” dataset from Kaggle, import it into Redis, and create listing APIs in Node.js. So buckle up!

I’ve used the “ioredis” package to connect to Redis. You can find the source code for this build here. For demonstration purposes, I’ve used the POST method for most endpoints. Ideally, you should use GET when retrieving resources and POST to create resources. I’ve also avoided any basic code like setting up app, routes as well.

GEOADD

This command adds data to the geospatial indices. We’ll use it to import our dataset for the application. Here’s my script:

router.post("/import", async (_req, res) => {
try {
const hotels = [];
const stream = fs
.createReadStream("./routes/data-import/hotel-data/goibibo_com-travel_sample.csv")
.pipe(csv());

for await (const row of stream) {
const { property_name: HotelName, longitude, latitude, province, city, address, state } = row;
hotels.push({
name: HotelName,
longitude,
latitude,
province,
city,
address,
state,
});
}

for (const hotel of hotels) {
await redisClient.geoadd(
"hotels",
hotel.longitude,
hotel.latitude,
hotel.name
);
console.log("Hotel added:", hotel.name);
}

console.log("CSV file successfully processed");
return res.status(201).json({ ok: true, size: hotels.length });
} catch (error) {
console.error("Error processing CSV file:", error);
return res.status(400).json({ ok: false, message: error.message });
}
});

GEOHASH

This command returns the Geohash string of a coordinate. The string is always 11 characters long. The format is standard and works with the geohash.org website as https://geohash.org/<geohash>

You can start removing the characters from the right but as we remove more characters, the precision is lost but points to the same area!

router.post("/hotels/geohash", async (req, res) => {
const { name } = req.body;
try {
const response = await redisClient.geohash("hotels", name);
return res.json(response);
} catch (error) {
res.status(400).send(error.message);
}
});
POSTMAN: GeoHash

GEOPOS

To show users nearby attractions from their stay, we’d use the GEORADIUS or GEOSEARCH command. GEOPOS returns a 52-bit representation of the longitude and latitude.

router.post("/hotels/position", async (req, res) => {
const { name } = req.body;
try {
const response = await redisClient.geopos("hotels", name);
return res.json(response);
} catch (error) {
res.status(500).send(error.message);
}
});
POSTMAN: GeoPos

GEODIST

To show approximate distances to different points of attraction from the user’s hotel, GEODIST is useful. This is a good place to start to figure out your requirements before subscribing to 3rd Party APIs like Google Maps. It’s worth noting that Redis computes the distance assuming the Earth is a perfect sphere, so errors up to 0.5% are possible in edge cases.

router.post("/hotels/distance", async (req, res) => {
const { hotel1, hotel2, unit = 'km' } = req.body;
try {
const response = await redisClient.geodist("hotels", hotel1, hotel2, unit);
return res.json(response);
} catch (error) {
res.status(500).send(error.message);
}
});
POSTMAN: GeoDist

GEOSEARCH

This is my favorite command available. Let’s say you want to search nearby hotels around “Manali” or search nearby tourist attractions from your stay, this command is there for you. GEOSEARCH will take the member name (Hotel Name in our case) or longitude or latitude and then all you have to do is define the radius or boundary. We’ll use radius for easiness.

router.post("/hotels/search", async (req, res) => {
const { name, lat, lng, radius, unit = "km" } = req.body;
try {
const args = ["hotels"];
if (lat && lng) {
args.push("FROMLONLAT", lng, lat);
} else {
args.push("FROMMEMBER", name);
}
args.push("BYRADIUS", radius, unit);
const response = await redisClient.geosearch(...args);
return res.json(response);
} catch (error) {
return res.status(500).send(error.message);
}
});
POSTMAN: GEOSEARCH using coordinates
POSTMAN: GEOSEARCH using Member

Conclusion

Redis’s geospatial data type and commands are powerful tools for building location-based applications. This is particularly handy when you need to prototype things fast and later settle on the right tools and databases. Whether you’re creating a hotel booking platform or any other application that requires location-based searches, by leveraging the various geospatial commands, developers can efficiently manage and query geographic data and Redis does offer geospatial capabilities with ease of scalability.

--

--