Skip to content

Add: Redis Geospatial Command Stages #328

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Aug 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0a0d12b
add: redis geospatial stages
UdeshyaDhungana Jul 14, 2025
e7f7f51
- Initial proposition for redis/geospatial challenge
UdeshyaDhungana Jul 16, 2025
314fced
Merge branch 'main' into redis-geospatial
UdeshyaDhungana Jul 17, 2025
ea695bb
- Apply requested changes
UdeshyaDhungana Jul 17, 2025
974c132
Remove merge conflict
UdeshyaDhungana Jul 17, 2025
98b4c6e
fix: update requested changes
UdeshyaDhungana Jul 28, 2025
fabccb8
fix: grammar
UdeshyaDhungana Jul 28, 2025
78d1c0f
fix: typos
UdeshyaDhungana Jul 28, 2025
e5ff7a8
fix: description
UdeshyaDhungana Jul 28, 2025
db308e3
fix: typo
UdeshyaDhungana Jul 28, 2025
f613c8f
fix: update tester description for ck3
UdeshyaDhungana Jul 28, 2025
eac6dc4
Fix: update instructions; fix grammar
UdeshyaDhungana Jul 28, 2025
7a6f6b0
Reorganize and reorder stages
UdeshyaDhungana Aug 1, 2025
bbf188f
- Restructured stages
UdeshyaDhungana Aug 4, 2025
f4af75c
fix: typos and add context
UdeshyaDhungana Aug 4, 2025
29e33a1
apply suggsted changes
UdeshyaDhungana Aug 8, 2025
9a6ec4c
Update geospatial stage description for the GEOADD command and its us…
rohitpaulk Aug 13, 2025
87c039a
Update geospatial stage to clarify latitude and longitude validation …
rohitpaulk Aug 13, 2025
43bd469
Update geospatial-01-zt4.md to remove reference to sorted set in GEOA…
rohitpaulk Aug 13, 2025
af148c6
Update geospatial stage descriptions with prerequisites and implement…
rohitpaulk Aug 13, 2025
bccffba
Refactor location scoring section and update algorithm description in…
rohitpaulk Aug 13, 2025
21b4c0c
Update geospatial stage description with clearer RESP string referenc…
rohitpaulk Aug 13, 2025
182e0d2
Refactor geospatial stage description for clarity and conciseness.
rohitpaulk Aug 13, 2025
ae84812
Fix typos and improve clarity in geospatial stage description for GEO…
rohitpaulk Aug 13, 2025
c1e00ac
Update geospatial stage description for GEOSEARCH command usage and e…
rohitpaulk Aug 13, 2025
fdda190
resolve merge conflicts
UdeshyaDhungana Aug 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion course-definition.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,17 @@ extensions:
[redis-zset]: https://redis.io/docs/latest/develop/data-types/sorted-sets/
[zadd-command]: https://redis.io/docs/latest/commands/zadd/
[zrange-command]: https://redis.io/docs/latest/commands/zrange/

- slug: "geospatial"
name: "Geospatial Commands"
description_markdown: |
In this challenge extension you'll add support for [Geospatial Commands][geospatial-commands] to your Redis implementation.

Along the way, you'll learn commands like [GEOADD][geoadd-command], [GEOSEARCH][geosearch-command], and more.

[geospatial-commands]: https://redis.io/docs/latest/develop/data-types/geospatial/
[geoadd-command]: https://redis.io/docs/latest/commands/geoadd/
[geosearch-command]: https://redis.io/docs/latest/commands/geosearch/

stages:
- slug: "jm1"
Expand Down Expand Up @@ -744,4 +755,53 @@ stages:
name: "Remove a member"
difficulty: easy
marketing_md: |
In this stage, you'll add support for removing a member of a sorted set using the `ZREM` command.
In this stage, you'll add support for removing a member of a sorted set using the `ZREM` command.

# Geospatial Commands
- slug: "zt4"
primary_extension_slug: "geospatial"
name: "Respond to GEOADD"
difficulty: easy
marketing_md: In this stage, you'll add support for responding to the `GEOADD` command.

- slug: "ck3"
primary_extension_slug: "geospatial"
name: "Validate coordinates"
difficulty: easy
marketing_md: In this stage, you'll add support for validating the latitude and longitude provided in the `GEOADD` command.

- slug: "tn5"
primary_extension_slug: "geospatial"
name: "Store a location"
difficulty: medium
marketing_md: In this stage, you'll add support for storing a location in a sorted set.

- slug: "cr3"
primary_extension_slug: "geospatial"
name: "Calculate location score"
difficulty: hard
marketing_md: In this stage, you'll add support for calculating the score of a location.

- slug: "xg4"
primary_extension_slug: "geospatial"
name: "Respond to GEOPOS"
difficulty: easy
marketing_md: In this stage, you'll add support for responding to the `GEOPOS` command.

- slug: "hb5"
primary_extension_slug: "geospatial"
name: "Decode coordinates"
difficulty: hard
marketing_md: In this stage, you'll add support for decoding the coordinates of a location.

- slug: "ek6"
primary_extension_slug: "geospatial"
name: "Calculate distance"
difficulty: medium
marketing_md: In this stage, you'll add support for calculating the distance between two locations using the `GEODIST` comamnd.

- slug: "rm9"
primary_extension_slug: "geospatial"
name: "Search within radius"
difficulty: easy
marketing_md: In this stage, you'll add support for searching locations near a coordinate within a given radius using the `GEOSEARCH` command.
47 changes: 47 additions & 0 deletions stage_descriptions/geospatial-01-zt4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
In this stage, you'll add support for responding to the `GEOADD` command.

### Extension prerequisites

This stage depends on the [**Sorted Sets**](https://redis.io/docs/latest/data-types/sorted-sets/) extension. Before attempting this extension, please make sure you've completed the Sorted Sets extension.

### The `GEOADD` command

The [`GEOADD` command](https://redis.io/docs/latest/commands/geoadd/) adds a location (with longitude, latitude, and name) to a key.

Example usage:

```bash
> GEOADD places -0.0884948 51.506479 "London"
(integer) 1
```

The arguments `GEOADD` accepts are:

1. `key`: The key to store the location in.
2. `longitude`: The longitude of the location.
3. `latitude`: The latitude of the location.
4. `member`: The name of the location.

It returns the count of elements added, encoded as a RESP Integer.

In this stage, you'll only implement the response to the `GEOADD` command. We'll get to validating arguments and storing locations in later stages.

### Tests

The tester will execute your program like this:

```bash
$ ./your_program.sh
```

It will then send a `GEOADD` command:

```bash
$ redis-cli GEOADD places 11.5030378 48.164271 Munich
```

The tester will expect the response to be `:1\r\n`, which is 1 (number of locations added) encoded as a RESP integer.

### Notes

- In this stage, you only need to implement responding to the `GEOADD` command. We'll get to validating arguments and storing locations in later stages.
63 changes: 63 additions & 0 deletions stage_descriptions/geospatial-02-ck3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
In this stage, you'll add support for validating the latitude and longitude values provided in a `GEOADD` command.

### Validating latitude and longitude values

The latitude and longitude values used in the `GEOADD` command should be in a certain range as per [EPSG:3857](https://epsg.io/3857).

- Valid longitudes are from -180° to +180°
- Both these limits are inclusive, so -180° and +180° are both valid.
- Valid latitudes are from -85.05112878° to +85.05112878°
- Both of these limits are inclusive, so -85.05112878° and +85.05112878° are both valid.
- The reason these limits are not -/+90° is because of the [Web Mercator projection](https://en.wikipedia.org/wiki/Web_Mercator_projection) that Redis uses.

If either of these values aren't within the appropriate range, `GEOADD` returns an error. Examples:

```bash
# Invalid latitude
> GEOADD places 180 90 test1
(error) ERR invalid longitude,latitude pair 180.000000,90.000000

# Invalid longitude
> GEOADD places 181 0.3 test2
(error) ERR invalid longitude,latitude pair 181.000000,0.300000
```

In this stage, you'll implement validating latitude and longitude values and returning error messages as shown above. The tester is lenient with error message formats, so you don't have to use the exact error message format mentioned above.

### Tests

The tester will execute your program like this:

```bash
$ ./your_program.sh
```

It will then send multiple `GEOADD` commands.

If the coordinates are invalid, it will expect an error response. For example:

```bash
# Expecting error
$ redis-cli GEOADD location_key 200 100 foo
(error) ERR invalid longitude,latitude pair 200,100
```

The returned value must:

- Be a [RESP simple error](https://redis.io/docs/latest/develop/reference/protocol-spec/#simple-errors) i.e. start with `-` and end with `\r\n`
- The error message must start with `ERR`, like standard Redis error messages
- The error message must contain the word "latitude" if the latitude is invalid
- The error message must contain the word "longitude" if the longitude is invalid

For example, if the latitude is invalid, valid error messages that the tester will accept are:

```bash
-ERR invalid latitude argument\r\n
-ERR invalid latitude value\r\n
-ERR latitude value (200.0) is invalid\r\n
```

### Notes

- You don't need to implement storing locations yet, we'll get to that in later stages.
- The boundary of latitude are clipped at -/+85.05112878° instead of -/+90°. This is because of the [Web Mercator projection](https://en.wikipedia.org/wiki/Web_Mercator_projection) that Redis uses.
46 changes: 46 additions & 0 deletions stage_descriptions/geospatial-03-tn5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
In this stage, you'll add support for storing locations in a sorted set.

### Storing locations in sorted set

The locations added using the `GEOADD` command are stored in a sorted set. Redis internally calculates a score for the specified location using a location's latitude and longitude.

For example, the following two commands are equivalent in Redis.

```bash
# Adding a location
$ redis-cli GEOADD places_key 2.2944692 48.8584625 location

# This command is equivalent to the command above
$ redis-cli ZADD places_key 3663832614298053 location
```

In this stage, you'll implement adding locations to a sorted set when a `GEOADD` command is run.

You can hardcode the score to be 0 for now, we'll get to calculating the score in later stages.

### Tests

The tester will execute your program like this:

```bash
$ ./your_program.sh
```

It will then send a `GEOADD` command:

```bash
$ redis-cli GEOADD places 2.2944692 48.8584625 Paris
# Expect: (integer) 1
```

The tester will then send a `ZRANGE` command to validate that the location was added to the sorted set:

```bash
$ redis-cli ZRANGE places 0 -1
# Expect RESP Array: ["Paris"]
```

### Notes

- In this stage, you can hardcode the score of the location to be 0. We'll get to calculating the value of score using latitude and longitude in later stages.
- The implementation of the `ZRANGE` command is covered in the sorted sets extension.
39 changes: 39 additions & 0 deletions stage_descriptions/geospatial-04-cr3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
In this stage, you'll add support for calculating the score of a location.

### Location scores

To store locations in a sorted set, Redis converts latitude and longitude values to a single "score".

We've created a [GitHub repository](https://github.com/codecrafters-io/redis-geocoding-algorithm) that explains how this conversion is done. It includes:

- A description of the algorithm used, along with pseudocode
- Python code that implements the algorithm
- A set of locations & scores to test against

Here's the [repository link](https://github.com/codecrafters-io/redis-geocoding-algorithm).

### Tests

The tester will execute your program like this:

```bash
$ ./your_program.sh
```

It will then send multiple `GEOADD` commands:

```bash
$ redis-cli GEOADD places 2.2944692 48.8584625 Paris
# Expect: (integer) 1
$ redis-cli GEOADD places -0.127758 51.507351 London
# Expect: (integer) 1
```

The tester will validate that scores are calculated correctly by sending multiple `ZSCORE` commands:

```bash
$ redis-cli ZSCORE places Paris
# Expecting bulk string: "3663832614298053"
```

The calculated scores must match the expected values as described in [this repository](https://github.com/codecrafters-io/redis-geocoding-algorithm).
80 changes: 80 additions & 0 deletions stage_descriptions/geospatial-05-xg4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
In this stage, you'll add support for responding to the `GEOPOS` command.

### The `GEOPOS` command

The `GEOPOS` command returns the longitude and latitude of the specified location.

Example usage:

```bash
> GEOADD places -0.0884948 51.506479 "London"
> GEOADD places 11.5030378 48.164271 "Munich"

> GEOPOS places London
1) 1) "-0.08849412202835083"
2) "51.50647814139934"

> GEOPOS places Munich
1) 1) "11.503036916255951"
2) "48.16427086232978"
```

It returns an array with one entry for each location requested.

- If the key does not exist, an empty array is returned `(*0\r\n)`
- If a location exists under the key, its entry is an array of two items:
- Longitude (Encoded as a [RESP Bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec/#bulk-strings))
- Latitude (Encoded as a [RESP Bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec/#bulk-strings))
- If a location doesn’t exist, the entry is a [null bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec/#null-bulk-strings) `($-1\r\n)`.

To return the latitude and longitude values, Redis decodes the "score" back to latitude and longitude values. We'll cover this process in later stages, for now you can hardcode the returned latitude and longitude values to be 0 (or any number).

### Tests

The tester will execute your program like this:

```bash
$ ./your_program.sh
```

It will then add multiple locations using the `GEOADD` command.

```bash
$ redis-cli
> GEOADD location_key -0.0884948 51.506479 "London"
# Expect: (integer) 1
> GEOADD location_key 11.5030378 48.164271 "Munich"
# Expect: (integer) 1
```

The tester will then send multiple `GEOPOS` commands:

```bash
> GEOPOS location_key London Munich
# Expecting: [["0", "0"], ["0", "0"]], encoded as "*2\r\n$1\r\n0\r\n$1\r\n0\r\n$1\r\n0\r\n$1\r\n0\r\n"
> GEOPOS location_key missing_location
# Expecting: [nil], encoded as "*1\r\n$-1\r\n"
```

The tester will assert that:

- The response is a RESP array that contains as many elements as the number of locations requested
- For each location requested:
- If the location exists:
- The corresponding element is a RESP array with two elements (i.e. longitude and latitude)
- Both elements are "0" (or any other valid floating point number), encoded as a RESP bulk string
- If the location doesn't exist:
- The corresponding element is a [null bulk string](https://redis.io/docs/latest/develop/reference/protocol-spec/#null-bulk-strings) `($-1\r\n)`.

The tester will also send a `GEOPOS` command using a key that doesn't exist:

```bash
> GEOPOS missing_key Foo
# Expecting: [], encoded as "*0\r\n"
```

It will expect the response to be an empty RESP array, which is encoded as `*0\r\n`.

### Notes

- In this stage, you can hardcode the returned latitude and longitude values to be 0 (or any other valid floating point number). We'll get to testing actual latitude and longitude values in later stages.
Loading
Loading