Skip to content

Commit

Permalink
Migrating GEOADD and GEODIST command
Browse files Browse the repository at this point in the history
  • Loading branch information
Yuvraj Gosain authored and Yuvraj Gosain committed Nov 9, 2024
1 parent 44779c2 commit 5ed865a
Show file tree
Hide file tree
Showing 11 changed files with 722 additions and 180 deletions.
92 changes: 92 additions & 0 deletions docs/src/content/docs/commands/GEOADD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
title: GEOADD
description: The `GEOADD` command in DiceDB is used to add geospatial items (longitude,latitude) to a specified key, storing them as a sorted set. This would allow for efficient querying of geographical data using commands like GEOSEARCH.
---

The `GEOADD` command in DiceDB is used to add geospatial items (longitude,latitude) to a specified key, storing them as a sorted set. This would allow for efficient querying of geographical data using commands like GEOSEARCH.

## Syntax

```bash
GEOADD key [NX | XX] [CH] longitude latitude member [longitude latitude member ...]
```

## Parameters

| Parameter | Description | Type | Required |
| --------- | --------------------------------------------------------------------------------- | ------ | -------- |
| key | The name of the sorted set where the geospatial data will be stored. | string | Yes |
| NX | Only add new elements; do not update existing ones. | NONE | No |
| XX | Only update existing elements; do not add new ones. | NONE | No |
| longitude | longitude of the location (must be between -180 and 180 degrees). | float | Yes |
| latitude | latitude of the location (must be between -85.05112878 and 85.05112878 degrees). | float | Yes |
| member | A unique identifier for the location. | string | Yes |


## Return Values

| Condition | Return Value |
| ------------------------------------------------------------ | ----------------------------------------------------------- |
| For each new member added. | 1 |
| No new member is added. | 0 |
| Incorrect Argument Count |`ERR wrong number of arguments for 'geoadd' command` |
| If the longitude is not a valid number or is out of range. |`ERR invalid longitude` |
| If the latitude is not a valid number or is out of range. |`ERR invalid latitude` |

## Behaviour

When the GEOADD command is issued, DiceDB performs the following steps:

1. It checks if argument count is valid or not. If not an error is thrown.
2. It checks whether optional parameters are applied or not.
3. It checks whether longitude and latitude are valid or not. If not an error is thrown.
3. It checks whether the set exists or not.
4. If set doesn't exist new set is created or else the same set is used.
5. It adds or updates the member in the set.
6. It returns number of members added.

## Errors

1.`Wrong number of arguments for 'GEOADD' command`
- Error Message: (error) ERR wrong number of arguments for 'geoadd' command.
- Occurs when the command is executed with an incorrect number of arguments.

2. `Longitutde not a valid number or is out of range `
- Error Message: (error) ERR invalid longitude.
- Occurs when longitude is out of range(-180 to 180) or not a valid number.

3. `Latitdude not a valid number or is out of range `
- Error Message: (error) ERR invalid latitude.
- Occurs when latitude is out of range(-85.05112878 to 85.05112878) or not a valid number.

## Example Usage

Here are a few examples demonstrating the usage of the GEOADD command:

### Example 1: Adding new member to a set

```bash
127.0.0.1:7379> GEOADD locations 13.361389 38.115556 "Palermo"
1
```

### Example 2: Updating an already existing member to a set

```bash
127.0.0.1:7379> GEOADD locations 13.361389 39.115556 "Palermo"
0
```

### Example 3: Error Adding a member with invalid longitude

```bash
127.0.0.1:7379> GEOADD locations 181.120332 39.115556 "Jamaica"
(error) ERROR invalid longitude
```

### Example 4: Error Adding a member with invalid latitde

```bash
127.0.0.1:7379> GEOADD locations 13.361389 91.115556 "Venice"
(error) ERROR invalid latitude
```
72 changes: 72 additions & 0 deletions docs/src/content/docs/commands/GEODIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
title: GEODIST
description: The `GEODIST` command in Redis is used to calculate the distance between two members (geospatial points) stored in a geospatial index(set).
---

The `GEODIST` command in Redis is used to calculate the distance between two members (geospatial points) stored in a geospatial index(set).

## Syntax

```bash
GEODIST key member1 member2 [m | km | ft | mi]
```

## Parameters

| Parameter | Description | Type | Required |
| --------- | --------------------------------------------------------------------------------- | ------ | -------- |
| key | The name of the sorted set where the geospatial data is stored. | string | Yes |
| member1 | The name of the member1 from where you want to measure the distance. | string | Yes |
| member2 | The name of the member2 to where you want to measure the distance. | string | Yes |
| m | The distance to be measured in meters. | NONE | NO |
| km | The distance to be measured in kilometers. | NONE | NO |
| ft | The distance to be measured in feet. | NONE | NO |
| mi | The distance to be measured in miles. | NONE | NO |


## Return Values

| Condition | Return Value |
| ------------------------------------------------------------ | ----------------------------------------------------------- |
| If both members exist in the set with no option | distance b/w them in meters |
| If both members exist in the set with option km | distance b/w them in kilometers |
| If both members exist in the set with option ft | distance b/w them in feet |
| If both members exist in the set with option mi | distance b/w them in miles |
| If any member doesn't exist in Set | nil |
| Incorrect Argument Count |`ERR wrong number of arguments for 'geodist' command` |

## Behaviour

When the GEODIST command is issued, DiceDB performs the following steps:

1. It checks if argument count is valid or not. If not an error is thrown.
2. It gets the sorted set(key).
3. It gets the scores(geohashes) from the sorted sets for both the members.
4. It calculates the distance bw them and returns it.

## Errors

1.`Wrong number of arguments for 'GEODIST' command`
- Error Message: (error) ERR wrong number of arguments for 'geodist' command.
- Occurs when the command is executed with an incorrect number of arguments.


## Example Usage

Here are a few examples demonstrating the usage of the GEODIST command:

### Example 1: Adding new member to a set

```bash
127.0.0.1:7379> GEOADD cities -74.0060 40.7128 "New York"
1
127.0.0.1:7379> GEOADD cities -79.3470 43.6510 "Toronto"
1
127.0.0.1:7379> GEODIST cities "New York" "Toronto"
"548064.1868"
127.0.0.1:7379> GEODIST cities "New York" "Toronto km"
"548.0642"
127.0.0.1:7379> GEODIST cities "New York" "Toronto mi"
"340.5521"
```

86 changes: 86 additions & 0 deletions integration_tests/commands/http/geo_tests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package http

import (
"testing"

"gotest.tools/v3/assert"
)

func TestGeoAdd(t *testing.T) {
exec := NewHTTPCommandExecutor()

testCases := []struct {
name string
commands []HTTPCommand
expected []interface{}
}{
{
name: "GEOADD with wrong number of arguments",
commands: []HTTPCommand{
{Command: "GEOADD", Body: map[string]interface{}{"key": "mygeo", "values": []interface{}{"1.2", "2.4"}}},
},
expected: []interface{}{"ERR wrong number of arguments for 'geoadd' command"},
},
{
name: "GEOADD Commands with new member and updating it",
commands: []HTTPCommand{
{Command: "GEOADD", Body: map[string]interface{}{"key": "mygeo", "values": []interface{}{"1.2", "2.4", "NJ"}}},
{Command: "GEOADD", Body: map[string]interface{}{"key": "mygeo", "values": []interface{}{"1.24", "2.48", "NJ"}}},
},
expected: []interface{}{float64(1), float64(0)},
},
{
name: "GEOADD Adding both XX and NX options together",
commands: []HTTPCommand{
{Command: "GEOADD", Body: map[string]interface{}{"key": "mygeo", "values": []interface{}{"XX", "NX", "1.2", "2.4", "NJ"}}},
},
expected: []interface{}{"ERR XX and NX options at the same time are not compatible"},
},
{
name: "GEOADD Invalid Longitude",
commands: []HTTPCommand{
{Command: "GEOADD", Body: map[string]interface{}{"key": "mygeo", "values": []interface{}{"181", "2.4", "MT"}}},
},
expected: []interface{}{"ERR invalid longitude"},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
assert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s", cmd)
}
})
}
}

func TestGeoDist(t *testing.T) {
exec := NewHTTPCommandExecutor()

testCases := []struct {
name string
commands []HTTPCommand
expected []interface{}
}{
{
name: "GEODIST b/w existing points",
commands: []HTTPCommand{
{Command: "GEOADD", Body: map[string]interface{}{"key": "points", "values": []interface{}{"13.361389", "38.115556", "Palermo"}}},
{Command: "GEOADD", Body: map[string]interface{}{"key": "points", "values": []interface{}{"15.087269", "37.502669", "Catania"}}},
{Command: "GEODIST", Body: map[string]interface{}{"key": "points", "values": []interface{}{"Palermo", "Catania"}}},
{Command: "GEODIST", Body: map[string]interface{}{"key": "points", "values": []interface{}{"Palermo", "Catania", "km"}}},
},
expected: []interface{}{float64(1), float64(1), float64(166274.144), float64(166.2741)},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.commands {
result, _ := exec.FireCommand(cmd)
assert.Equal(t, tc.expected[i], result, "Value mismatch for cmd %s", cmd)
}
})
}
}
86 changes: 86 additions & 0 deletions integration_tests/commands/resp/geo_tests.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package resp

import (
"testing"
"time"

"gotest.tools/v3/assert"
)

func TestGeoAdd(t *testing.T) {
conn := getLocalConnection()
defer conn.Close()

testCases := []struct {
name string
cmds []string
expect []interface{}
}{
{
name: "GeoAdd With Wrong Number of Arguments",
cmds: []string{"GEOADD mygeo 1 2"},
expect: []interface{}{"ERR wrong number of arguments for 'geoadd' command"},
},
{
name: "GeoAdd With Adding New Member And Updating it",
cmds: []string{"GEOADD mygeo 1.21 1.44 NJ", "GEOADD mygeo 1.22 1.54 NJ"},
expect: []interface{}{int64(1), int64(0)},
},
{
name: "GeoAdd With Adding New Member And Updating it with NX",
cmds: []string{"GEOADD mygeo 1.21 1.44 MD", "GEOADD mygeo 1.22 1.54 MD"},
expect: []interface{}{int64(1), int64(0)},
},
{
name: "GEOADD with both NX and XX options",
cmds: []string{"GEOADD mygeo NX XX 1.21 1.44 MD"},
expect: []interface{}{"ERR XX and NX options at the same time are not compatible"},
},
{
name: "GEOADD invalid longitude",
cmds: []string{"GEOADD mygeo 181.0 1.44 MD"},
expect: []interface{}{"ERR invalid longitude"},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.cmds {
result := FireCommand(conn, cmd)
assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
}
})
}
}

func TestGeoDist(t *testing.T) {
conn := getLocalConnection()
defer conn.Close()

testCases := []struct {
name string
cmds []string
expect []interface{}
delays []time.Duration
}{
{
name: "GEODIST b/w existing points",
cmds: []string{
"GEOADD points 13.361389 38.115556 Palermo",
"GEOADD points 15.087269 37.502669 Catania",
"GEODIST points Palermo Catania",
"GEODIST points Palermo Catania km",
},
expect: []interface{}{int64(1), int64(1), "166274.144", "166.2741"},
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for i, cmd := range tc.cmds {
result := FireCommand(conn, cmd)
assert.Equal(t, tc.expect[i], result, "Value mismatch for cmd %s", cmd)
}
})
}
}
Loading

0 comments on commit 5ed865a

Please sign in to comment.