Compreender como os filtros de coleção OData funcionam no Pesquisa de IA do Azure

Nota

O Pesquisa de IA do Azure está disponível através do portal Azure, APIs REST e SDKs do Azure. Também sustenta o Foundry IQ, a camada de conhecimento gerida que transforma conteúdos empresariais em bases de conhecimento reutilizáveis e conscientes de permissões para agentes no portal Microsoft Foundry.

Este artigo fornece informações básicas para desenvolvedores que estão escrevendo filtros avançados com expressões lambda complexas. O artigo explica por que as regras para filtros de coleção existem explorando como o Pesquisa de IA do Azure executa esses filtros.

Ao criar um filtro em campos de coleção no Pesquisa de IA do Azure, pode-se usar os e operadores junto com expressões lambda. Expressões lambda são expressões booleanas que se referem a uma variável de intervalo. Em filtros que usam uma expressão lambda, os operadores any e all são análogos a um loop for na maioria das linguagens de programação, com a variável de intervalo assumindo o papel de variável de loop e a expressão lambda como o corpo do loop. A variável range assume o valor "atual" da coleção durante a iteração do loop.

Pelo menos é assim que funciona conceitualmente. Na realidade, o Pesquisa de IA do Azure implementa filtros de uma maneira muito diferente de como for os loops funcionam. Idealmente, essa diferença seria invisível para você, mas em certas situações não é. O resultado final é que há regras que você tem que seguir ao escrever expressões lambda.

Nota

Para obter informações sobre quais são as regras para filtros de coleção, incluindo exemplos, consulte Solucionando problemas de filtros de coleção OData no Pesquisa de IA do Azure.

Por que os filtros de coleta são limitados

Há três razões subjacentes pelas quais os recursos de filtro não são totalmente suportados para todos os tipos de coleções:

  1. Apenas determinados operadores são suportados para determinados tipos de dados. Por exemplo, não faz sentido comparar os valores true booleanos e false usar operadores de comparação como lt e gt.
  2. O Pesquisa de IA do Azure suporta filtros correlacionados em campos do tipo Collection(Edm.ComplexType), mas a pesquisa de texto completo por campo nesses campos não é correlacionada.
  3. O Pesquisa de IA do Azure usa índices invertidos para executar filtros sobre todos os tipos de dados, incluindo coleções.

A primeira razão é apenas uma consequência de como a linguagem OData e o sistema de tipo EDM são definidos. Os dois últimos são explicados com mais detalhes no restante deste artigo.

Quando você aplica vários critérios de filtro sobre uma coleção de objetos complexos, os critérios são correlacionados porque se aplicam a cada objeto da coleção. Por exemplo, o filtro a seguir retorna hotéis que têm pelo menos um quarto deluxe com tarifa inferior a 100:

    Rooms/any(room: room/Type eq 'Deluxe Room' and room/BaseRate lt 100)

Se a filtragem não estiver correlacionada, o filtro acima pode retornar hotéis em que um quarto é deluxe e um quarto diferente tem uma tarifa básica inferior a 100. Isso não faria sentido, uma vez que ambas as cláusulas da expressão lambda se aplicam à mesma variável de intervalo, ou seja room, . É por isso que esses filtros estão correlacionados.

No entanto, para pesquisa de texto completo, não há como fazer referência a uma variável de intervalo específica. Se você usar a pesquisa em campo para emitir uma consulta Lucene completa como esta:

    Rooms/Type:deluxe AND Rooms/Description:"city view"

Você pode recuperar hotéis onde um quarto é deluxe, e um quarto diferente menciona "vista para a cidade" na descrição. Por exemplo, o documento abaixo com Id de 1 corresponderia à consulta:

{
  "value": [
    {
      "Id": "1",
      "Rooms": [
        { "Type": "deluxe", "Description": "Large garden view suite" },
        { "Type": "standard", "Description": "Standard city view room" }
      ]
    },
    {
      "Id": "2",
      "Rooms": [
        { "Type": "deluxe", "Description": "Courtyard motel room" }
      ]
    }
  ]
}

A razão é que Rooms/Type se refere a todos os termos analisados do Rooms/Type campo em todo o documento, e da mesma forma para Rooms/Description, como mostrado nas tabelas abaixo.

Como Rooms/Type é armazenado para pesquisa de texto completo:

Termo em Rooms/Type Identificadores de Documento
de luxo 1, 2
norma 1

Como Rooms/Description é armazenado para pesquisa de texto completo:

Termo em Rooms/Description Identificadores de Documento
pátio 2
cidade 1
jardim 1
grande 1
Motel 2
sala 1, 2
norma 1
suíte 1
vista 1

Assim, ao contrário do filtro acima, que basicamente diz "combinar documentos onde um quarto tem Type igual a 'Quarto Deluxe' e esse mesmo quarto tem BaseRate menos de 100", a consulta de pesquisa diz "combinar documentos onde Rooms/Type tem o termo "deluxe" e Rooms/Description tem a frase "vista da cidade". Não há conceito de salas individuais cujos campos possam ser correlacionados neste último caso.

Índices invertidos e coleções

Pode notar que as expressões lambda têm muito menos restrições sobre coleções complexas do que sobre coleções simples, como Collection(Edm.Int32) e Collection(Edm.GeographyPoint). Esta diferença existe porque o Pesquisa de IA do Azure armazena coleções complexas como coleções reais de subdocumentos, enquanto coleções simples não são armazenadas como coleções de todo.

Por exemplo, considere um campo de coleção de cadeia de caracteres filtrável como seasons em um índice para um varejista online. Alguns documentos carregados para este índice podem ter esta aparência:

{
  "value": [
    {
      "id": "1",
      "name": "Hiking boots",
      "seasons": ["spring", "summer", "fall"]
    },
    {
      "id": "2",
      "name": "Rain jacket",
      "seasons": ["spring", "fall", "winter"]
    },
    {
      "id": "3",
      "name": "Parka",
      "seasons": ["winter"]
    }
  ]
}

Os valores do campo seasons são armazenados numa estrutura chamada de índice invertido, que parece-se com o seguinte:

Termo Identificadores de Documento
primavera 1, 2
verão 1
queda 1, 2
inverno 2, 3

Esta estrutura de dados foi concebida para responder a uma pergunta com grande rapidez: em que documentos aparece um determinado termo? Responder a esta pergunta funciona mais como uma simples verificação de igualdade do que um loop sobre uma coleção. Na verdade, é por isso que, para coleções de cadeia de caracteres, o Pesquisa de IA do Azure só permite eq como um operador de comparação dentro de uma expressão lambda para any.

Em seguida, analisamos como é possível combinar várias verificações de igualdade na mesma variável de intervalo com or. Funciona graças à álgebra e à propriedade distributiva dos quantificadores. Esta expressão:

    seasons/any(s: s eq 'winter' or s eq 'fall')

é equivalente a:

    seasons/any(s: s eq 'winter') or seasons/any(s: s eq 'fall')

e cada uma das duas any subexpressões pode ser executada eficientemente usando o índice invertido. Além disso, graças à lei de negação dos quantificadores, esta expressão:

    seasons/all(s: s ne 'winter' and s ne 'fall')

é equivalente a:

    not seasons/any(s: s eq 'winter' or s eq 'fall')

é por isso que é possível usar all com ne e and.

Nota

Embora os detalhes estejam além do escopo deste documento, esses mesmos princípios se estendem a testes de distância e interseção para coleções de pontos geoespaciais também. É por isso que, em any:

  • geo.intersects não pode ser negado
  • geo.distance devem ser comparados utilizando lt ou le
  • as expressões devem ser combinadas com or, não and

As regras inversas aplicam-se a all.

Uma maior variedade de expressões é permitida ao filtrar coleções de tipos de dados que suportam os operadores lt, gt, le e ge, como Collection(Edm.Int32), por exemplo. Especificamente, você pode utilizar tanto and quanto or em any, desde que as expressões subjacentes de comparação sejam combinadas em comparações de intervalos utilizando and, que por sua vez são combinadas usando or. Esta estrutura de expressões booleanas é chamada de Forma Normal Disjuntiva (DNF), também conhecida como "ORs de ANDs". Por outro lado, as expressões lambda para all para esses tipos de dados devem estar na Forma Normal Conjuntiva (CNF), também conhecida como "ANDs de OUs". O Pesquisa de IA do Azure permite essas comparações de intervalo porque pode realizá-las usando índices invertidos de forma eficiente, tal como pode fazer pesquisa rápida de termos para cadeias de caracteres.

Em resumo, aqui estão as regras básicas para o que é permitido em uma expressão lambda:

  • No interior any, verificações positivas são sempre permitidas, como igualdade, comparações de intervalo, geo.intersects, ou geo.distance comparadas com lt ou le (considere "proximidade" como semelhante à igualdade quando se trata de verificar distância).
  • Dentro de any, or é sempre permitido. Você pode usar and somente para tipos de dados que podem expressar verificações de intervalo e somente se usar ORs de ANDs (DNF).
  • No interior all, as regras invertem-se. Apenas verificações negativas são permitidas, pode usar and sempre, e pode usar or apenas para verificações de intervalo expressas como ANDs de ORs (CNF).

Na prática, esses são os tipos de filtros que você provavelmente usará de qualquer maneira. No entanto, ainda é útil entender os limites do que é possível.

Para obter exemplos específicos de quais tipos de filtros são permitidos e quais não são, consulte Como escrever filtros de coleção válidos.

Próximos passos