OData geo-spatial functions in Azure AI Search - geo.distance
and geo.intersects
Azure AI Search supports geo-spatial queries in OData filter expressions via the geo.distance
and geo.intersects
functions. The geo.distance
function returns the distance in kilometers between two points, one being a field or range variable, and one being a constant passed as part of the filter. The geo.intersects
function returns true
if a given point is within a given polygon, where the point is a field or range variable and the polygon is specified as a constant passed as part of the filter.
The geo.distance
function can also be used in the $orderby parameter to sort search results by distance from a given point. The syntax for geo.distance
in $orderby is the same as it is in $filter. When using geo.distance
in $orderby, the field to which it applies must be of type Edm.GeographyPoint
and it must also be sortable.
Note
When using geo.distance
in the $orderby parameter, the field you pass to the function must contain only a single geo-point. In other words, it must be of type Edm.GeographyPoint
and not Collection(Edm.GeographyPoint)
. It is not possible to sort on collection fields in Azure AI Search.
Syntax
The following EBNF (Extended Backus-Naur Form) defines the grammar of the geo.distance
and geo.intersects
functions, as well as the geo-spatial values on which they operate:
geo_distance_call ::=
'geo.distance(' variable ',' geo_point ')'
| 'geo.distance(' geo_point ',' variable ')'
geo_point ::= "geography'POINT(" lon_lat ")'"
lon_lat ::= float_literal ' ' float_literal
geo_intersects_call ::=
'geo.intersects(' variable ',' geo_polygon ')'
/* You need at least four points to form a polygon, where the first and
last points are the same. */
geo_polygon ::=
"geography'POLYGON((" lon_lat ',' lon_lat ',' lon_lat ',' lon_lat_list "))'"
lon_lat_list ::= lon_lat(',' lon_lat)*
An interactive syntax diagram is also available:
Note
See OData expression syntax reference for Azure AI Search for the complete EBNF.
geo.distance
The geo.distance
function takes two parameters of type Edm.GeographyPoint
and returns an Edm.Double
value that is the distance between them in kilometers. This differs from other services that support OData geo-spatial operations, which typically return distances in meters.
One of the parameters to geo.distance
must be a geography point constant, and the other must be a field path (or a range variable in the case of a filter iterating over a field of type Collection(Edm.GeographyPoint)
). The order of these parameters doesn't matter.
The geography point constant is of the form geography'POINT(<longitude> <latitude>)'
, where the longitude and latitude are numeric constants.
Note
When using geo.distance
in a filter, you must compare the distance returned by the function with a constant using lt
, le
, gt
, or ge
. The operators eq
and ne
are not supported when comparing distances. For example, this is a correct usage of geo.distance
: $filter=geo.distance(location, geography'POINT(-122.131577 47.678581)') le 5
.
geo.intersects
The geo.intersects
function takes a variable of type Edm.GeographyPoint
and a constant Edm.GeographyPolygon
and returns an Edm.Boolean
-- true
if the point is within the bounds of the polygon, false
otherwise.
The polygon is a two-dimensional surface stored as a sequence of points defining a bounding ring (see the examples below). The polygon needs to be closed, meaning the first and last point sets must be the same. Points in a polygon must be in counterclockwise order.
Geo-spatial queries and polygons spanning the 180th meridian
For many geo-spatial query libraries formulating a query that includes the 180th meridian (near the dateline) is either off-limits or requires a workaround, such as splitting the polygon into two, one on either side of the meridian.
In Azure AI Search, geo-spatial queries that include 180-degree longitude will work as expected if the query shape is rectangular and your coordinates align to a grid layout along longitude and latitude (for example, geo.intersects(location, geography'POLYGON((179 65, 179 66, -179 66, -179 65, 179 65))'
). Otherwise, for non-rectangular or unaligned shapes, consider the split polygon approach.
Geo-spatial functions and null
Like all other non-collection fields in Azure AI Search, fields of type Edm.GeographyPoint
can contain null
values. When Azure AI Search evaluates geo.intersects
for a field that is null
, the result will always be false
. The behavior of geo.distance
in this case depends on the context:
- In filters,
geo.distance
of anull
field results innull
. This means the document will not match becausenull
compared to any non-null value evaluates tofalse
. - When sorting results using $orderby,
geo.distance
of anull
field results in the maximum possible distance. Documents with such a field will sort lower than all others when the sort directionasc
is used (the default), and higher than all others when the direction isdesc
.
Examples
Filter examples
Find all hotels within 10 kilometers of a given reference point (where location is a field of type Edm.GeographyPoint
):
geo.distance(location, geography'POINT(-122.131577 47.678581)') le 10
Find all hotels within a given viewport described as a polygon (where location is a field of type Edm.GeographyPoint
). Note that the polygon is closed (the first and last point sets must be the same) and the points must be listed in counterclockwise order.
geo.intersects(location, geography'POLYGON((-122.031577 47.578581, -122.031577 47.678581, -122.131577 47.678581, -122.031577 47.578581))')
Order-by examples
Sort hotels descending by rating
, then ascending by distance from the given coordinates:
rating desc,geo.distance(location, geography'POINT(-122.131577 47.678581)') asc
Sort hotels in descending order by search.score
and rating
, and then in ascending order by distance from the given coordinates so that between two hotels with identical ratings, the closest one is listed first:
search.score() desc,rating desc,geo.distance(location, geography'POINT(-122.131577 47.678581)') asc