diff --git a/content/develop/data-types/geospatial.md b/content/develop/data-types/geospatial.md index c92d5d8bc..bd23f06bc 100644 --- a/content/develop/data-types/geospatial.md +++ b/content/develop/data-types/geospatial.md @@ -20,6 +20,14 @@ weight: 80 Redis geospatial indexes let you store coordinates and search for them. This data structure is useful for finding nearby points within a given radius or bounding box. +{{< note >}}Take care not to confuse the Geospatial data type with the +[Geospatial]({{< relref "/develop/interact/search-and-query/advanced-concepts/geo" >}}) +features in [Redis Query Engine]({{< relref "/develop/interact/search-and-query" >}}). +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 >}} + ## Basic commands * [`GEOADD`]({{< relref "/commands/geoadd" >}}) adds a location to a given geospatial index (note that longitude comes before latitude with this command). diff --git a/content/develop/interact/search-and-query/advanced-concepts/geo.md b/content/develop/interact/search-and-query/advanced-concepts/geo.md new file mode 100644 index 000000000..5f28a13d2 --- /dev/null +++ b/content/develop/interact/search-and-query/advanced-concepts/geo.md @@ -0,0 +1,202 @@ +--- +categories: +- docs +- develop +- stack +- oss +- rs +- rc +- oss +- kubernetes +- clients +description: Learn how to use geospatial fields and perform geospatial queries in Redis +linkTitle: Geospatial +math: true +title: Geospatial +weight: 14 +--- + +Redis Query Engine supports geospatial data. This feature +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 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 +use a set of points to define a polygon shape (the shape of a +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. + +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 +are more suitable for smaller areas (such as rooms in a building) +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 Community Edition]({{< 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 queries 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: + +```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 + +1) (integer) 1 +2) "shape:4" +3) 1) "name" + 2) "[\"Purple Point\"]" +``` + +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. + +## Limitations of geographical coordinates + +Planet Earth is actually shaped more like an +[ellipsoid](https://en.wikipedia.org/wiki/Earth_ellipsoid) than a perfect sphere. +The spherical coordinate system used by Redis Query Engine is a close +approximation to the shape of the Earth but not exact. For most practical +uses of geospatial queries, the approximation works very well, but you +shouldn't rely on it if you need very precise location data (for example, to track +the GPS locations of boats in an emergency response system). diff --git a/content/develop/interact/search-and-query/basic-constructs/field-and-type-options.md b/content/develop/interact/search-and-query/basic-constructs/field-and-type-options.md index 3e7b990c1..9866d8185 100644 --- a/content/develop/interact/search-and-query/basic-constructs/field-and-type-options.md +++ b/content/develop/interact/search-and-query/basic-constructs/field-and-type-options.md @@ -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: ``` @@ -82,6 +87,57 @@ You can query geo fields using the `@:[ ]` 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 `@:[ ]` +where `` is one of `WITHIN`, `CONTAINS`, `INTERSECTS`, or `DISJOINT`, +and `` 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. diff --git a/content/develop/interact/search-and-query/indexing/_index.md b/content/develop/interact/search-and-query/indexing/_index.md index 513ce9000..6a88b310d 100644 --- a/content/develop/interact/search-and-query/indexing/_index.md +++ b/content/develop/interact/search-and-query/indexing/_index.md @@ -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 diff --git a/content/develop/interact/search-and-query/indexing/geoindex.md b/content/develop/interact/search-and-query/indexing/geoindex.md new file mode 100644 index 000000000..3df944e47 --- /dev/null +++ b/content/develop/interact/search-and-query/indexing/geoindex.md @@ -0,0 +1,110 @@ +--- +categories: +- docs +- develop +- stack +- oss +- rs +- rc +- oss +- kubernetes +- clients +description: Options for indexing geospatial data +linkTitle: Geospatial +title: Geospatial indexing +weight: 3 +--- + +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): 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. + +The sections below explain how to index these schema types. See the +[Geospatial]({{< relref "/develop/interact/search-and-query/advanced-concepts/geo" >}}) +reference page for a full description of both types. + +## `GEO` + +The following command creates a `GEO` index for JSON objects that contain +the geospatial data in a field called `location`: + +```bash +> FT.CREATE productidx ON JSON PREFIX 1 product: SCHEMA $.location AS location GEO +OK +``` + +If you now add JSON objects with the `product:` prefix and a `location` field, +they will be added to the index automatically: + +```bash +> JSON.SET product:46885 $ '{"description": "Navy Blue Slippers","price": 45.99,"city": "Denver","location": "-104.991531, 39.742043"}' +OK +> JSON.SET product:46886 $ '{"description": "Bright Green Socks","price": 25.50,"city": "Fort Collins","location": "-105.0618814,40.5150098"}' +OK +``` + +The query below finds products within a 100 mile radius of Colorado Springs +(Longitude=-104.800644, Latitude=38.846127). This returns only the location in +Denver, but a radius of 200 miles would also include the location in Fort Collins: + +```bash +> FT.SEARCH productidx '@location:[-104.800644 38.846127 100 mi]' +1) "1" +2) "product:46885" +3) 1) "$" + 2) "{\"description\":\"Navy Blue Slippers\",\"price\":45.99,\"city\":\"Denver\",\"location\":\"-104.991531, 39.742043\"}" +``` + +See [Geospatial queries]({{< relref "/develop/interact/search-and-query/query/geo-spatial" >}}) +for more information about the available options. + +## `GEOSHAPE` + +The following command creates an index for JSON objects that include +geospatial data in a field called `geom`. The `FLAT` option at the end +of the field definition specifies Cartesian coordinates instead of +the default spherical geographical coordinates. Use `SPHERICAL` in +place of `FLAT` to choose the coordinate space explicitly. + +```bash +> FT.CREATE geomidx ON JSON PREFIX 1 shape: SCHEMA $.name AS name TEXT $.geom AS geom GEOSHAPE FLAT +OK +``` + +Use the `shape:` prefix for the JSON objects to add them to the index: + +```bash +> JSON.SET shape:1 $ '{"name": "Green Square", "geom": "POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))"}' +OK +> JSON.SET shape:2 $ '{"name": "Red Rectangle", "geom": "POLYGON ((2 2.5, 2 3.5, 3.5 3.5, 3.5 2.5, 2 2.5))"}' +OK +> JSON.SET shape:3 $ '{"name": "Blue Triangle", "geom": "POLYGON ((3.5 1, 3.75 2, 4 1, 3.5 1))"}' +OK +> JSON.SET shape:4 $ '{"name": "Purple Point", "geom": "POINT (2 2)"}' +OK +``` + +You can now run various geospatial queries against the index. For +example, the query below returns any shapes within the boundary +of the green square but omits the green square itself: + +```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 + +1) (integer) 1 +2) "shape:4" +3) 1) "name" + 2) "[\"Purple Point\"]" +``` + +You can also run queries to find whether shapes in the index completely contain +or overlap each other. See +[Geospatial queries]({{< relref "/develop/interact/search-and-query/query/geo-spatial" >}}) +for more information. diff --git a/static/images/dev/rqe/geoshapes.jpg b/static/images/dev/rqe/geoshapes.jpg new file mode 100644 index 000000000..401e39096 Binary files /dev/null and b/static/images/dev/rqe/geoshapes.jpg differ