Funções geoespaciais OData no Azure AI Search – geo.distance e geo.intersects

O Azure AI Search dá suporte a consultas geoespaciais em Expressões de filtro OData por meio das funções geo.distance e geo.intersects. A função geo.distance retorna a distância em quilômetros entre dois pontos, um deles sendo um campo ou variável de intervalo e o outro uma constante passada como parte do filtro. A função geo.intersects retorna true se um determinado ponto está dentro de um polígono determinado, em que o ponto é um campo ou variável de intervalo e o polígono é especificado como uma constante passada como parte do filtro.

A função geo.distance também pode ser usada no parâmetro $OrderBy para classificar os resultados da pesquisa por distância de um determinado ponto. A sintaxe para geo.distance em $orderby é a mesma que em $filter. Ao usar geo.distance em $orderby, o campo ao qual a operação se aplica deve ser do tipo Edm.GeographyPoint e também deve ser classificável.

Observação

Ao usar geo.distance no parâmetro $OrderBy, o campo que você passa para a função deve conter apenas um único ponto geográfico. Em outras palavras, ele deve ser do tipo Edm.GeographyPoint e não Collection(Edm.GeographyPoint). Não é possível classificar em campos de coleção no Azure AI Search.

Sintaxe

O EBNF a seguir (Formulário Backus-Naur estendido) define a gramática das funções geo.distance e geo.intersects, bem como os valores geoespaciais nos quais operam:

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)*

Um diagrama de sintaxe interativa também está disponível:

Observação

Confira Referência de sintaxe de expressão OData para Azure AI Search para ver a EBNF completa.

geo.distance

A função geo.distance usa dois parâmetros do tipo Edm.GeographyPoint e retorna um valor Edm.Double que é a distância entre eles em quilômetros. Isso difere de outros serviços compatíveis com as operações geoespaciais do OData, que normalmente retornam as distâncias em metros.

Um dos parâmetros para geo.distance deve ser uma constante de ponto de geografia e o outro deve ser um caminho de campo (ou uma variável de intervalo no caso de uma iteração de filtro sobre um campo de tipo Collection(Edm.GeographyPoint)). A ordem desses parâmetros não importa.

A constante de ponto de geografia é do formulário geography'POINT(<longitude> <latitude>)', em que a longitude e a latitude são constantes numéricas.

Observação

Ao usar geo.distance em um filtro, você deve comparar a distância retornada pela função com uma constante usando lt, le, gt ou ge. Os operadores eq e ne não são compatíveis ao comparar as distâncias. Por exemplo, este é um uso correto de geo.distance: $filter=geo.distance(location, geography'POINT(-122.131577 47.678581)') le 5.

geo.intersects

A função geo.intersects usa uma variável do tipo Edm.GeographyPoint e uma constante Edm.GeographyPolygon e retorna um Edm.Boolean -- true se o ponto estiver dentro dos limites do polígono, caso contrário, false.

O polígono é uma superfície bidimensional armazenada como uma sequência de pontos que definem um anel delimitador (veja o exemplo a seguir) (veja os exemplos abaixo). O polígono precisa estar fechado, o que significa que os conjuntos de primeiro e último pontos devem ser os mesmos. Os pontos em um polígono devem estar no sentido anti-horário.

Consultas geoespaciais e polígonos abrangendo o meridiano 180º

Para muitas bibliotecas de consulta geoespaciais, a formulação de uma consulta que inclua o meridiano 180º (próximo à hora do meridiano) está fora dos limites ou requer uma solução alternativa, como dividir o polígono em dois, um em cada lado do meridiano.

No Azure AI Search, as consultas geoespaciais que incluem longitude de 180 graus funcionam conforme o esperado se a forma de consulta for retangular e suas coordenadas se alinham a um layout de grade na longitude e na latitude (por exemplo, geo.intersects(location, geography'POLYGON((179 65, 179 66, -179 66, -179 65, 179 65))'). Caso contrário, para formas não retangulares ou desalinhadas, considere a abordagem de polígono dividido.

Funções espaciais geográficas e null

Como todos os outros campos que não são de coleção no Azure AI Search, os campos do tipo Edm.GeographyPoint podem conter valores de null. Quando o Azure AI Search avalia geo.intersects para um campo que é null, o resultado sempre será false. O comportamento de geo.distance nesse caso depende do contexto:

  • Nos filtros, geo.distance de um campo null resulta em null. Isso significa que o documento não corresponderá, pois null comparado a qualquer valor não nulo é avaliado como false.
  • Ao classificar os resultados usando $OrderBy, geo.distance de um campo null resulta na distância máxima possível. Os documentos com tal campo classificarão menos do que todos os outros quando a direção de classificação asc for usada (o padrão) e mais alta do que todas as outras quando a direção for desc.

Exemplos

Exemplos de filtro

Localizar todos os hotéis em um raio de 10 km de determinado ponto de referência (em que o local é um campo do tipo Edm.GeographyPoint):

    geo.distance(location, geography'POINT(-122.131577 47.678581)') le 10

Localizar todos os hotéis dentro de determinado visor descrito como um polígono (em que o local é um campo do tipo Edm.GeographyPoint). Observe que o polígono está fechado (o primeiro e o último pontos devem ser o mesmo) e os pontos devem estar listados no sentido anti-horário.

    geo.intersects(location, geography'POLYGON((-122.031577 47.578581, -122.031577 47.678581, -122.131577 47.678581, -122.031577 47.578581))')

Exemplos de Order-by

Organizar hotéis em ordem decrescente por rating e, em seguida, em ordem crescente por distância das coordenadas fornecidas:

    rating desc,geo.distance(location, geography'POINT(-122.131577 47.678581)') asc

Organizar em ordem decrescente por search.score e por rating e, em seguida, em ordem crescente por distância das coordenadas fornecidas, de modo que entre dois hotéis com classificações idênticas, o mais próximo seja listado primeiro:

    search.score() desc,rating desc,geo.distance(location, geography'POINT(-122.131577 47.678581)') asc

Próximas etapas