Skip to content

Commit

Permalink
DOC-2761 relocated and filled out the content
Browse files Browse the repository at this point in the history
  • Loading branch information
andy-stark-redis committed Dec 12, 2024
1 parent b908c65 commit 4057de1
Show file tree
Hide file tree
Showing 5 changed files with 289 additions and 132 deletions.
161 changes: 150 additions & 11 deletions content/develop/interact/search-and-query/advanced-concepts/geo.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,23 @@ weight: 14
---

Redis Query Engine supports geospatial data. This feature
lets you store geographical locations in the fields of JSON objects.
You can then use queries to find the objects by their location.
lets you store geographical locations and geometric shapes
in the fields of JSON objects.

{{< note >}}Take care not to confuse the geospatial indexing
features in Redis Query Engine with the
[Geospatial data type]({{< relref "/develop/data-types/geospatial" >}})
that Redis also supports. Although there are some similarities between
these two features, the data type is intended for simpler use
cases and doesn't have the range of format options and queries
available in Redis Query Engine.
{{< /note >}}

You can index these fields and use queries to find the objects
by their location or the relationship of their shape to other shapes.
For example, if you add the locations of a set of shops, you can
find all the shops within 5km of a user's position.
find all the shops within 5km of a user's position or determine
which ones are within the boundary of a particular town.

Redis uses coordinate points to represent geospatial locations.
You can store individual points but you can also
Expand All @@ -29,7 +42,7 @@ town, for example). You can query several types of interactions
between points and shapes, such as whether a point lies within
a shape or whether two shapes overlap.

You can interpret coordinates either as geographical longitude
Redis can interpret coordinates either as geographical longitude
and latitude or as Cartesian coordinates on a flat plane.
Geographical coordinates are ideal for large real-world locations
and areas (such as towns and countries). Cartesian coordinates
Expand All @@ -38,16 +51,142 @@ or for games, simulations, and other artificial scenarios.

## Storing geospatial data

Redis supports two different
[schema types]({{< relref "/develop/interact/search-and-query/basic-constructs/field-and-type-options" >}})
for geospatial data:

- [`GEO`](#geo): This uses a simple format where individual geospatial
points are specified as numeric longitude-latitude pairs.

- [`GEOSHAPE`](#geoshape): [Redis Stack]({{< relref "/operate/oss_and_stack" >}}) also
supports `GEOSHAPE` indexing in v7.2 and later.
This uses a subset of the
[Well-Known Text (WKT)](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry)
format to specify both points and polygons using either geographical
coordinates or Cartesian coordinates. A
`GEOSHAPE` field supports more advanced queries than `GEO`,
such as checking if one shape overlaps or contains another.

The sections below describe these schema types in more detail.

## `GEO`

A `GEO` index lets you represent geospatial data either as
a string containing a longitude-latitude pair (for example,
"-104.991531, 39.742043") or as a JSON array of these
strings. Note that the longitude value comes first in the
string.

For example, you could index the `location` fields of the
the [JSON]({{< relref "/develop/data-types/json" >}}) objects
shown below as `GEO`:

```json
{
"description": "Navy Blue Slippers",
"price": 45.99,
"city": "Denver",
"location": "-104.991531, 39.742043"
}

{
"description": "Bright Red Boots",
"price": 185.75,
"city": "Various",
"location": [
"-104.991531, 39.742043",
"-105.0618814,40.5150098"
]
}
```

`GEO` fields allow only basic point and radius queries.
For example, the query below finds products within a 100 mile radius of Colorado Springs
(Longitude=-104.800644, Latitude=38.846127).

```bash
FT.SEARCH productidx '@location:[-104.800644 38.846127 100 mi]'
```

See [Geospatial queries]({{< relref "/develop/interact/search-and-query/query/geo-spatial" >}})
for more information about the available query options and see
[Geospatial indexing]({{< relref "/develop/interact/search-and-query/indexing/geoindex" >}})
for examples of indexing `GEO` fields.

## `GEOSHAPE`

Fields indexed as `GEOSHAPE` support the `POINT` and `POLYGON` primitives from the
[Well-Known Text](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry)
representation of geometry. The `POINT` primitive defines a single point
in a similar way to a `GEO` field.
The `geom` field of the example JSON object shown below specifies a point
(in Cartesian coordinates, using the standard x,y order):

```json
{
"name": "Purple Point",
"geom": "POINT (2 2)"
}
```

The `POLYGON` primitive can approximate the outline of any shape using a
sequence of points. Specify the coordinates of the corners in the order they
occur around the shape (either clockwise or counter-clockwise) and ensure the
shape is "closed" by making the final coordinate exactly the same as the first.

Note that `POLYGON` requires double parentheses around the coordinate list.
This is because you can specify additional shapes as a comma-separated list
that define "holes" within the enclosing polygon. The holes must have the opposite
winding order to the outer polygon (so, if the outer polygon uses a clockwise winding
order, the holes must use counter-clockwise).
The `geom` field of the example JSON object shown below specifies a
square using Cartesian coordinates in a clockwise winding order:

```json
{
"name": "Green Square",
"geom": "POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))"
}
```

The following examples define one `POINT` and three `POLYGON` primitives,
which are shown in the image below:

```
POINT (2 2)
POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))
POLYGON ((2 2.5, 2 3.5, 3.5 3.5, 3.5 2.5, 2 2.5))
POLYGON ((3.5 1, 3.75 2, 4 1, 3.5 1))
```

{{< image filename="/images/dev/rqe/geoshapes.jpg" >}}

You can run various types of query against a geospatial index. For
example, the query below returns one primitive that lies within the boundary
of the green square (from the example above) but omits the square itself:

## Specifying geospatial data in queries
```bash
> FT.SEARCH geomidx "(-@name:(Green Square) @geom:[WITHIN $qshape])" PARAMS 2 qshape "POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))" RETURN 1 name DIALECT 4

Redis Query Engine uses the
[*Well-Known Text* (WKT)](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry)
format for geospatial data, specifically the `POINT` and `POLYGON`
constructs. Add fields containing WKT data to your JSON objects,
then add
1) (integer) 1
2) "shape:4"
3) 1) "name"
2) "[\"Purple Point\"]"
```

### `POINT` data
There are four query operations that you can use with `GEOSHAPE` fields:

- `WITHIN`: Find points or shapes that lie entirely within an
enclosing shape that you specify in the query.
- `CONTAINS`: Find shapes that completely contain the specified point
or shape.
- `INTERSECTS`: Find shapes whose boundary overlaps another specified
shape.
- `DISJOINT`: Find shapes whose boundary does not overlap another specified
shape.

See
[Geospatial queries]({{< relref "/develop/interact/search-and-query/query/geo-spatial" >}})
for more information about these query types and see
[Geospatial indexing]({{< relref "/develop/interact/search-and-query/indexing/geoindex" >}})
for examples of indexing `GEOSHAPE` fields.
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ You can also use the following query syntax to perform more complex numeric quer

Geo fields are used to store geographical coordinates such as longitude and latitude. They enable geospatial radius queries, which allow you to implement location-based search functionality in your applications such as finding nearby restaurants, stores, or any other points of interest.

Redis Query Engine also supports [geoshape fields](#geoshape-fields) for more advanced
geospatial queries. See the
[Geospatial]({{< relref "/develop/interact/search-and-query/advanced-concepts/geo" >}})
reference page for an introduction to the format and usage of both schema types.

You can add geo fields to the schema in [`FT.CREATE`]({{< baseurl >}}/commands/ft.create/) using this syntax:

```
Expand All @@ -82,6 +87,57 @@ You can query geo fields using the `@<field_name>:[<lon> <lat> <radius> <unit>]`
FT.SEARCH cities "@coords:[2.34 48.86 1000 km]"
```

See
[Geospatial queries]({{< relref "/develop/interact/search-and-query/query/geo-spatial" >}})
for more information and code examples.

## Geoshape fields

Geoshape fields provide more advanced functionality than [Geo](#geo-fields).
You can use them to represent locations as points but also to define
shapes and query the interactions between points and shapes (for example,
to find all points that are contained within an enclosing shape). You can
also choose between geographical coordinates (on the surface of a sphere)
or standard Cartesian coordinates. Use geoshape fields for spatial queries
such as finding all office locations in a specified region or finding
all rooms in a building that fall within range of a wi-fi router.

See the
[Geospatial]({{< relref "/develop/interact/search-and-query/advanced-concepts/geo" >}})
reference page for an introduction to the format and usage of both the
geoshape and geo schema types.

Add geoshape fields to the schema in
[`FT.CREATE`]({{< baseurl >}}/commands/ft.create/) using the following syntax:

```
FT.CREATE ... SCHEMA ... {field_name} GEOSHAPE [FLAT|SPHERICAL] [NOINDEX]
```

Where:
- `FLAT` indicates Cartesian (planar) coordinates.
- `SPHERICAL` indicates spherical (geographical) coordinates. This is the
default option if you don't specify one explicitly.
- `NOINDEX` indicates that the field is not indexed. This is useful for storing
coordinates that you don't want to search for, but that you still want to retrieve
in search results.

Note that unlike geo fields, geoshape fields don't support the `SORTABLE` option.

Query geoshape fields using the syntax `@<field_name>:[<OPERATION> <shape>]`
where `<operation>` is one of `WITHIN`, `CONTAINS`, `INTERSECTS`, or `DISJOINT`,
and `<shape>` is the shape of interest, specified in the
[Well-known text](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry)
format. For example, the query below finds shapes that contain the point (2, 2):

```
FT.SEARCH idx "(@geom:[CONTAINS $qshape])" PARAMS 2 qshape "POINT (2 2)" RETURN 1 name DIALECT 4
```

See
[Geospatial queries]({{< relref "/develop/interact/search-and-query/query/geo-spatial" >}})
for more information and code examples.

## Vector fields

Vector fields are floating-point vectors that are typically generated by external machine learning models. These vectors represent unstructured data such as text, images, or other complex features. Redis Stack allows you to search for similar vectors using vector search algorithms like cosine similarity, Euclidean distance, and inner product. This enables you to build advanced search applications, recommendation systems, or content similarity analysis.
Expand Down
88 changes: 8 additions & 80 deletions content/develop/interact/search-and-query/indexing/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,86 +311,14 @@ When JSONPath leads to multiple numerical values:
- `null` values are skipped
- Any other value type will cause an indexing failure

## Index JSON arrays as GEO

Starting with RediSearch v2.6.1, search can be done on an array of geo (geographical) values or on a JSONPath leading to multiple geo values.

Prior to RediSearch v2.6.1, only a single geo value was supported per GEO attribute. The geo value was specified using a comma delimited string in the form "longitude,latitude". For example, "15.447083,78.238306".

With RediSearch v2.6.1, a JSON array of such geo values is also supported.

In order to index multiple geo values, user either a JSONPath leading to a single array of geo values, or a JSONPath leading to multiple geo values, using JSONPath operators such as wildcard, filter, union, array slice, and/or recursive descent.

- `null` values are skipped
- Other values will cause an indexing failure (bool, number, object, array, wrongly formatted GEO string, invalid coordinates)

For example, add to the item's list the `vendor_id`, that is, where an item can be physically purchased:

```sql
127.0.0.1:6379> JSON.SET item:1 $ '{"name":"Noise-cancelling Bluetooth headphones","description":"Wireless Bluetooth headphones with noise-cancelling technology","connection":{"wireless":true,"type":"Bluetooth"},"price":99.98,"stock":25,"colors":["black","silver"], "max_level":[60, 70, 80, 90, 100], "vendor_id": [100,300]}'
OK

127.0.0.1:6379> JSON.SET item:2 $ '{"name":"Wireless earbuds","description":"Wireless Bluetooth in-ear headphones","connection":{"wireless":true,"type":"Bluetooth"},"price":64.99,"stock":17,"colors":["black","white"], "max_level":[80, 100, 120], "vendor_id": [100,200]}'
OK

127.0.0.1:6379> JSON.SET item:3 $ '{"name":"True Wireless earbuds","description":"True Wireless Bluetooth in-ear headphones","connection":{"wireless":true,"type":"Bluetooth"},"price":74.99,"stock":20,"colors":["red","light blue"], "max_level":[90, 100, 110, 120], "vendor_id": [100]}'
OK
```

Now add some vendors with their geographic locations:

```sql
127.0.0.1:6379> JSON.SET vendor:1 $ '{"id":100, "name":"Kwik-E-Mart", "location":["35.213,31.785", "35.178,31.768", "35.827,31.984"]}'
OK

127.0.0.1:6379> JSON.SET vendor:2 $ '{"id":200, "name":"Cypress Creek", "location":["34.638,31.79", "34.639,31.793"]}'
OK

127.0.0.1:6379> JSON.SET vendor:3 $ '{"id":300, "name":"Barneys", "location":["34.648,31.817", "34.638,31.806", "34.65,31.785"]}'
OK
```

To index the `vendor_id` numeric array, specify the JSONPath `$.vendor_id` in the `SCHEMA` definition during index creation:


```sql
127.0.0.1:6379> FT.CREATE itemIdx5 ON JSON PREFIX 1 item: SCHEMA $.vendor_id AS vid NUMERIC
OK
```

To index the `location` geo array, specify the JSONPath `$.location` in the `SCHEMA` definition during index creation:


```sql
127.0.0.1:6379> FT.CREATE vendorIdx ON JSON PREFIX 1 vendor: SCHEMA $.location AS loc GEO
OK
```

Now search for a vendor close to a specific location. For example, a customer is located at geo coordinates 34.5,31.5 and you want to get the vendors that are within the range of 40 km from our location:

```sql
127.0.0.1:6379> FT.SEARCH vendorIdx '@loc:[34.5 31.5 40 km]' return 1 $.id
1) (integer) 2
2) "vendor:2"
3) 1) "$.id"
1) "200"
4) "vendor:3"
5) 1) "$.id"
1) "300"
```

Now look for products offered by these vendors:

```
127.0.0.1:6379> FT.SEARCH itemIdx5 '@vid:[200 300]'
1) (integer) 2
2) "item:2"
3) 1) "$"
2) "{\"name\":\"Wireless earbuds\",\"description\":\"Wireless Bluetooth in-ear headphones\",\"connection\":{\"wireless\":true,\"type\":\"Bluetooth\"},\"price\":64.99,\"stock\":17,\"colors\":[\"black\",\"white\"],\"max_level\":[80,100,120],\"vendor_id\":[100,200]}"
4) "item:1"
5) 1) "$"
2) "{\"name\":\"Noise-cancelling Bluetooth headphones\",\"description\":\"Wireless Bluetooth headphones with noise-cancelling technology\",\"connection\":{\"wireless\":true,\"type\":\"Bluetooth\"},\"price\":99.98,\"stock\":25,\"colors\":[\"black\",\"silver\"],\"max_level\":[60,70,80,90,100],\"vendor_id\":[100,300]}"
```
## Index JSON arrays as GEO and GEOSHAPE

You can use `GEO` and `GEOSHAPE` fields to store geospatial data,
such as geographical locations and geometric shapes. See
[Geospatial indexing]({{< relref "/develop/interact/search-and-query/indexing/geoindex" >}})
to learn how to use these schema types and see the
[Geospatial]({{< relref "/develop/interact/search-and-query/advanced-concepts/geo" >}})
reference page for an introduction to their format and usage.

## Index JSON arrays as VECTOR

Expand Down
Loading

0 comments on commit 4057de1

Please sign in to comment.