Skip to content

Commit

Permalink
ESQL: Support ST_DISJOINT (#107007)
Browse files Browse the repository at this point in the history
* WIP Started developing ST_DISJOINT

Initially based on ST_INTERSECTS

* Fix functions list and add spatial point integration tests

* Update docs/changelog/107007.yaml

* More tests for shapes and cartesian-multigeoms

* Some more tests to highlight issues with DISJOINT on cartesian point indices

* Disable Lucene push-down for DISJOINT on cartesian point indices

* Added docs for ST_DISJOINT

* Support DISJOINT in the lucene-pushdown code for cartesian point indexes

* Re-enable push-to-source for DISJOINT on cartesian_point indices

* Fix docs example

* Try fix internal docs links which are not being rendered

* Fixed disjoint on empty geometry

* Added tests on empty linestring, and changed lucene push-down to exception

In lucene code only LineString can be empty, but in Elasticsearch even that is not allowed, resulting in parsing errors. So we cannot get to this code in the lucene push-down and now throw an error instead. The tests now assert on the warnings.

Note that for any predicate DISJOINT and INTERSECTS alike, the predicate fails, because the parsing error results in null, the function returns null, the predicate interprets this as false, and no documents match. This null-in-null-out rule means that DISJOINT and INTERSECTS give the same answer on invalid geometries.
  • Loading branch information
craigtaverner committed Apr 8, 2024
1 parent 887d48d commit a7b3839
Show file tree
Hide file tree
Showing 38 changed files with 1,912 additions and 27 deletions.
5 changes: 5 additions & 0 deletions docs/changelog/107007.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 107007
summary: "ESQL: Support ST_DISJOINT"
area: ES|QL
type: enhancement
issues: []
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,3 @@
*Description*

Returns whether the first geometry contains the second geometry.

NOTE: The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.

*Description*

Returns whether the two geometries or geometry columns are disjoint.
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,3 @@
*Description*

Returns whether the two geometries or geometry columns intersect.

NOTE: The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters.
2 changes: 0 additions & 2 deletions docs/reference/esql/functions/description/st_within.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,3 @@
*Description*

Returns whether the first geometry is within the second geometry.

NOTE: The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters.
13 changes: 13 additions & 0 deletions docs/reference/esql/functions/examples/st_disjoint.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.

*Example*

[source.merge.styled,esql]
----
include::{esql-specs}/spatial_shapes.csv-spec[tag=st_disjoint-airport_city_boundaries]
----
[%header.monospaced.styled,format=dsv,separator=|]
|===
include::{esql-specs}/spatial_shapes.csv-spec[tag=st_disjoint-airport_city_boundaries-result]
|===

15 changes: 15 additions & 0 deletions docs/reference/esql/functions/layout/st_disjoint.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.

[discrete]
[[esql-st_disjoint]]
=== `ST_DISJOINT`

*Syntax*

[.text-center]
image::esql/functions/signature/st_disjoint.svg[Embedded,opts=inline]

include::../parameters/st_disjoint.asciidoc[]
include::../description/st_disjoint.asciidoc[]
include::../types/st_disjoint.asciidoc[]
include::../examples/st_disjoint.asciidoc[]
9 changes: 9 additions & 0 deletions docs/reference/esql/functions/parameters/st_disjoint.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.

*Parameters*

`geomA`::
Geometry column name or variable of geometry type

`geomB`::
Geometry column name or variable of geometry type
1 change: 1 addition & 0 deletions docs/reference/esql/functions/signature/st_disjoint.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions docs/reference/esql/functions/spatial-functions.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@

// tag::spatial_list[]
* experimental:[] <<esql-st_intersects>>
* experimental:[] <<esql-st_disjoint>>
* experimental:[] <<esql-st_contains>>
* experimental:[] <<esql-st_within>>
* experimental:[] <<esql-st_x>>
* experimental:[] <<esql-st_y>>
// end::spatial_list[]

include::st_intersects.asciidoc[]
include::st_disjoint.asciidoc[]
include::st_contains.asciidoc[]
include::st_within.asciidoc[]
include::st_x.asciidoc[]
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/esql/functions/st_contains.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The second parameter must also have the same coordinate system as the first.
This means it is not possible to combine `geo_*` and `cartesian_*` parameters.

include::description/st_contains.asciidoc[]
This is the inverse of the `<<esql-st_within,ST_WITHIN>>` function.
This is the inverse of the <<esql-st_within,ST_WITHIN>> function.

include::types/st_contains.asciidoc[]
include::examples/st_contains.asciidoc[]
27 changes: 27 additions & 0 deletions docs/reference/esql/functions/st_disjoint.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[discrete]
[[esql-st_disjoint]]
=== `ST_DISJOINT`

experimental::[]

*Syntax*

[.text-center]
image::esql/functions/signature/st_disjoint.svg[Embedded,opts=inline]

*Parameters*

`geomA`::
Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`.

`geomB`::
Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`.
The second parameter must also have the same coordinate system as the first.
This means it is not possible to combine `geo_*` and `cartesian_*` parameters.

include::description/st_disjoint.asciidoc[]
This is the inverse of the <<esql-st_intersects,ST_INTERSECTS>> function.
In mathematical terms: ST_Disjoint(A, B) ⇔ A ⋂ B = ∅

include::types/st_disjoint.asciidoc[]
include::examples/st_disjoint.asciidoc[]
1 change: 1 addition & 0 deletions docs/reference/esql/functions/st_intersects.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This means it is not possible to combine `geo_*` and `cartesian_*` parameters.
Returns true if two geometries intersect.
They intersect if they have any point in common, including their interior points
(points along lines or within polygons).
This is the inverse of the <<esql-st_disjoint,ST_DISJOINT>> function.
In mathematical terms: ST_Intersects(A, B) ⇔ A ⋂ B ≠ ∅

include::types/st_intersects.asciidoc[]
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/esql/functions/st_within.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The second parameter must also have the same coordinate system as the first.
This means it is not possible to combine `geo_*` and `cartesian_*` parameters.

include::description/st_within.asciidoc[]
This is the inverse of the `<<esql-st_contains,ST_CONTAINS>>` function.
This is the inverse of the <<esql-st_contains,ST_CONTAINS>> function.

include::types/st_within.asciidoc[]
include::examples/st_within.asciidoc[]
16 changes: 16 additions & 0 deletions docs/reference/esql/functions/types/st_disjoint.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.

*Supported types*

[%header.monospaced.styled,format=dsv,separator=|]
|===
geomA | geomB | result
cartesian_point | cartesian_point | boolean
cartesian_point | cartesian_shape | boolean
cartesian_shape | cartesian_point | boolean
cartesian_shape | cartesian_shape | boolean
geo_point | geo_point | boolean
geo_point | geo_shape | boolean
geo_shape | geo_point | boolean
geo_shape | geo_shape | boolean
|===
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,29 @@ id:l | name:keyword | shape:cartesian_shape
16 | Bottom left point | POINT(0.5 0.5)
;

whereDisjointSinglePolygon
required_feature: esql.st_disjoint

FROM cartesian_multipolygons
| WHERE ST_Disjoint(shape, TO_CARTESIANSHAPE("POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))"))
| SORT id
;

id:l | name:keyword | shape:cartesian_shape
2 | Bottom right | POLYGON((2 0, 3 0, 3 1, 2 1, 2 0))
3 | Top right | POLYGON((2 2, 3 2, 3 3, 2 3, 2 2))
4 | Top left | POLYGON((0 2, 1 2, 1 3, 0 3, 0 2))
7 | Bottom right with holes | POLYGON((2 0, 3 0, 3 1, 2 1, 2 0), (2.4 0.4, 2.6 0.4, 2.6 0.6, 2.4 0.6, 2.4 0.4))
8 | Top right with holes | POLYGON((2 2, 3 2, 3 3, 2 3, 2 2), (2.4 2.4, 2.6 2.4, 2.6 2.6, 2.4 2.6, 2.4 2.4))
9 | Top left with holes | POLYGON((0 2, 1 2, 1 3, 0 3, 0 2), (0.4 2.4, 0.6 2.4, 0.6 2.6, 0.4 2.6, 0.4 2.4))
12 | Bottom right diagonal | LINESTRING(2 0, 3 1)
13 | Top right diagonal | LINESTRING(2 2, 3 3)
14 | Top left diagonal | LINESTRING(0 2, 1 3)
17 | Bottom right point | POINT(2.5 0.5)
18 | Top right point | POINT(2.5 2.5)
19 | Top left point | POINT(0.5 2.5)
;

####################################################################################################
# Test against a polygon smaller in size to the Bottom Left polygon

Expand Down Expand Up @@ -99,6 +122,29 @@ id:l | name:keyword | shape:cartesian_shape
16 | Bottom left point | POINT(0.5 0.5)
;

whereDisjointSmallerPolygon
required_feature: esql.st_disjoint

FROM cartesian_multipolygons
| WHERE ST_Disjoint(shape, TO_CARTESIANSHAPE("POLYGON((0.2 0.2, 0.8 0.2, 0.8 0.8, 0.2 0.8, 0.2 0.2))"))
| SORT id
;

id:l | name:keyword | shape:cartesian_shape
2 | Bottom right | POLYGON((2 0, 3 0, 3 1, 2 1, 2 0))
3 | Top right | POLYGON((2 2, 3 2, 3 3, 2 3, 2 2))
4 | Top left | POLYGON((0 2, 1 2, 1 3, 0 3, 0 2))
7 | Bottom right with holes | POLYGON((2 0, 3 0, 3 1, 2 1, 2 0), (2.4 0.4, 2.6 0.4, 2.6 0.6, 2.4 0.6, 2.4 0.4))
8 | Top right with holes | POLYGON((2 2, 3 2, 3 3, 2 3, 2 2), (2.4 2.4, 2.6 2.4, 2.6 2.6, 2.4 2.6, 2.4 2.4))
9 | Top left with holes | POLYGON((0 2, 1 2, 1 3, 0 3, 0 2), (0.4 2.4, 0.6 2.4, 0.6 2.6, 0.4 2.6, 0.4 2.4))
12 | Bottom right diagonal | LINESTRING(2 0, 3 1)
13 | Top right diagonal | LINESTRING(2 2, 3 3)
14 | Top left diagonal | LINESTRING(0 2, 1 3)
17 | Bottom right point | POINT(2.5 0.5)
18 | Top right point | POINT(2.5 2.5)
19 | Top left point | POINT(0.5 2.5);
;

####################################################################################################
# Test against a polygon similar in size to the entire test data

Expand Down Expand Up @@ -175,6 +221,17 @@ id:l | name:keyword | shape:cartesian_shape
19 | Top left point | POINT(0.5 2.5)
;

whereDisjointLargerPolygon
required_feature: esql.st_disjoint

FROM cartesian_multipolygons
| WHERE ST_Disjoint(shape, TO_CARTESIANSHAPE("POLYGON((0 0, 3 0, 3 3, 0 3, 0 0))"))
| SORT id
;

id:l | name:keyword | shape:cartesian_shape
;

####################################################################################################
# Test against a polygon larger than all test data

Expand Down Expand Up @@ -250,3 +307,14 @@ id:l | name:keyword | shape:cartesian_shape
18 | Top right point | POINT(2.5 2.5)
19 | Top left point | POINT(0.5 2.5)
;

whereDisjointEvenLargerPolygon
required_feature: esql.st_disjoint

FROM cartesian_multipolygons
| WHERE ST_Disjoint(shape, TO_CARTESIANSHAPE("POLYGON((-1 -1, 4 -1, 4 4, -1 4, -1 -1))"))
| SORT id
;

id:l | name:keyword | shape:cartesian_shape
;
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ double pi()
"double sqrt(number:double|integer|long|unsigned_long)"
"geo_point|cartesian_point st_centroid(field:geo_point|cartesian_point)"
"boolean st_contains(geomA:geo_point|cartesian_point|geo_shape|cartesian_shape, geomB:geo_point|cartesian_point|geo_shape|cartesian_shape)"
"boolean st_disjoint(geomA:geo_point|cartesian_point|geo_shape|cartesian_shape, geomB:geo_point|cartesian_point|geo_shape|cartesian_shape)"
"boolean st_intersects(geomA:geo_point|cartesian_point|geo_shape|cartesian_shape, geomB:geo_point|cartesian_point|geo_shape|cartesian_shape)"
"boolean st_within(geomA:geo_point|cartesian_point|geo_shape|cartesian_shape, geomB:geo_point|cartesian_point|geo_shape|cartesian_shape)"
"double st_x(point:geo_point|cartesian_point)"
Expand Down Expand Up @@ -175,6 +176,7 @@ split |[string, delim] |["keyword|text", "keyword|te
sqrt |number |"double|integer|long|unsigned_long" |[""]
st_centroid |field |"geo_point|cartesian_point" |[""]
st_contains |[geomA, geomB] |["geo_point|cartesian_point|geo_shape|cartesian_shape", "geo_point|cartesian_point|geo_shape|cartesian_shape"] |[Geometry column name or variable of geometry type, Geometry column name or variable of geometry type]
st_disjoint |[geomA, geomB] |["geo_point|cartesian_point|geo_shape|cartesian_shape", "geo_point|cartesian_point|geo_shape|cartesian_shape"] |[Geometry column name or variable of geometry type, Geometry column name or variable of geometry type]
st_intersects |[geomA, geomB] |["geo_point|cartesian_point|geo_shape|cartesian_shape", "geo_point|cartesian_point|geo_shape|cartesian_shape"] |[Geometry column name or variable of geometry type, Geometry column name or variable of geometry type]
st_within |[geomA, geomB] |["geo_point|cartesian_point|geo_shape|cartesian_shape", "geo_point|cartesian_point|geo_shape|cartesian_shape"] |[Geometry column name or variable of geometry type, Geometry column name or variable of geometry type]
st_x |point |"geo_point|cartesian_point" |[""]
Expand Down Expand Up @@ -285,6 +287,7 @@ split |Split a single valued string into multiple strings.
sqrt |Returns the square root of a number.
st_centroid |The centroid of a spatial field.
st_contains |Returns whether the first geometry contains the second geometry.
st_disjoint |Returns whether the two geometries or geometry columns are disjoint.
st_intersects |Returns whether the two geometries or geometry columns intersect.
st_within |Returns whether the first geometry is within the second geometry.
st_x |Extracts the x-coordinate from a point geometry.
Expand Down Expand Up @@ -396,6 +399,7 @@ split |keyword
sqrt |double |false |false |false
st_centroid |"geo_point|cartesian_point" |false |false |true
st_contains |boolean |[false, false] |false |false
st_disjoint |boolean |[false, false] |false |false
st_intersects |boolean |[false, false] |false |false
st_within |boolean |[false, false] |false |false
st_x |double |false |false |false
Expand Down Expand Up @@ -451,5 +455,5 @@ countFunctions#[skip:-8.13.99]
meta functions | stats a = count(*), b = count(*), c = count(*) | mv_expand c;

a:long | b:long | c:long
101 | 101 | 101
102 | 102 | 102
;

0 comments on commit a7b3839

Please sign in to comment.