Présentation du fonctionnement des filtres de collection OData dans Recherche Azure AI
Cet article fournit des informations aux développeurs qui écrivent des filtres avancés avec des expressions lambda complexes. L’article explique pourquoi les règles applicables aux filtres de collection existent en explorant la façon dont Recherche Azure AI exécute ces filtres.
Lorsque vous appliquez un filtre sur des champs de collection dans Recherche Azure AI, vous pouvez utiliser les opérateurs any
et all
avec des expressions lambda. Les expressions lambda sont des expressions booléennes qui font référence à une variable de portée. Dans les filtres qui utilisent une expression lambda, les opérateurs any
et all
sont analogues à une boucle for
dans la plupart des langages de programmation, la variable de portée jouant le rôle de variable de boucle et l’expression lambda étant le corps de la boucle. La variable de portée prend la valeur « actuelle » de la collection pendant l’itération de la boucle.
Du moins, c’est ainsi qu’elle fonctionne sur le plan conceptuel. En réalité, Azure AI Search implémente les filtres d’une manière très différente du fonctionnement des boucles for
. Dans l’idéal, cette différence est invisible pour vous, mais elle ne l’est pas dans certaines situations. Le résultat final est qu’il existe des règles que vous devez suivre lors de l’écriture d’expressions lambda.
Remarque
Pour plus d’informations sur ce que sont les règles applicables aux filtres de collection, notamment des exemples, consultez Résolution des problèmes liés aux filtres de collection OData dans Azure AI Search.
Raisons de la limitation des filtres de collection
Il existe trois raisons sous-jacentes pour lesquelles les fonctionnalités de filtre ne sont pas complètement prises en charge pour tous les types de collections :
- Seuls certains opérateurs sont pris en charge pour certains types de données. Par exemple, cela n’a aucun sens de comparer les valeurs booléennes
true
etfalse
à l’aide delt
,gt
, etc. - Azure AI Search ne prend pas en charge la recherche corrélée sur des champs de type
Collection(Edm.ComplexType)
. - Azure AI Search utilise des index inversés pour exécuter des filtres sur tous les types de données, dont les collections.
La première raison est simplement une conséquence de la façon dont le langage OData et le système de type EDM sont définis. Les deux dernières sont expliquées de façon plus détaillée dans le reste de cet article.
Recherche corrélée et recherche non corrélée
Quand vous appliquez plusieurs critères de filtre sur une collection d’objets complexes, les critères sont corrélés, car ils s’appliquent à chaque objet de la collection. Par exemple, le filtre suivant retourne les hôtels ayant au moins une chambre de luxe dont le tarif est inférieur à 100 :
Rooms/any(room: room/Type eq 'Deluxe Room' and room/BaseRate lt 100)
Si le filtrage a été sans corrélation, le filtre ci-dessus peut retourner les hôtels où une chambre est de luxe (deluxe) et une autre chambre dispose d’un tarif de base inférieur à 100. Cela n’aurait pas de sens, car les deux clauses de l’expression lambda s’appliquent à la même variable de portée, à savoir room
. C’est pourquoi ces filtres sont mis en corrélation.
Toutefois, pour la recherche en texte intégral, il n’existe aucun moyen de faire référence à une variable de portée spécifique. Si vous utilisez une recherche par champ pour émettre une requête complète Lucene comme celle-ci :
Rooms/Type:deluxe AND Rooms/Description:"city view"
vous pouvez obtenir les hôtels qui compte une chambre de luxe et où une autre chambre mentionne « city view » (vue sur la ville) dans la description. Par exemple, le document ci-dessous avec comme Id
la valeur 1
correspondrait à la requête :
{
"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" }
]
}
]
}
La raison est que Rooms/Type
fait référence à tous les termes analysés du champ Rooms/Type
dans la totalité du document, et il en est de même pour Rooms/Description
, comme indiqué dans les tableaux ci-dessous.
Façon dont Rooms/Type
est stocké pour la recherche en texte intégral :
Terme dans Rooms/Type |
ID de document |
---|---|
deluxe | 1, 2 |
standard | 1 |
Façon dont Rooms/Description
est stocké pour la recherche en texte intégral :
Terme dans Rooms/Description |
ID de document |
---|---|
courtyard | 2 |
city | 1 |
garden | 1 |
large | 1 |
motel | 2 |
room | 1, 2 |
standard | 1 |
suite | 1 |
view | 1 |
Par conséquent, contrairement au filtre ci-dessus, qui dit : « Trouver une correspondance avec les documents où une chambre a un Type
égal à « Deluxe Room » (Chambre de luxe) et où cette même chambre a un BaseRate
(Tarif de base) inférieur à 100 », la requête de recherche indique « Trouver une correspondance avec les documents où Rooms/Type
(Chambres/Type) contient le terme « deluxe » (de luxe) et où Rooms/Description
(Chambres/Description) contient l’expression « city view » (vue sur la ville). Il n’existe aucun concept de chambres individuelles dont les champs peuvent être mis en corrélation dans ce dernier cas.
Index inversés et collections
Vous avez peut-être remarqué qu’il y a beaucoup moins de restrictions concernant les expressions lambda sur les collections complexes que pour les collections simples comme Collection(Edm.Int32)
, Collection(Edm.GeographyPoint)
, etc. En fait, Recherche Azure AI stocke les collections complexes en tant que collections réelles de sous-documents, tandis que les collections simples ne sont pas du tout stockées en tant que collections.
Par exemple, considérez un champ de collection de chaînes filtrable comme seasons
dans un index destiné à un détaillant en ligne. Certains documents chargés dans cet index peuvent se présenter comme suit :
{
"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"]
}
]
}
Les valeurs du champ seasons
sont stockées dans une structure appelée index inversé, qui ressemble à ceci :
Terme | ID de document |
---|---|
spring | 1, 2 |
summer | 1 |
fall | 1, 2 |
winter | 2, 3 |
Cette structure de données est conçue pour répondre très rapidement à une question : dans quels documents un terme donné apparaît-il ? Réponse à cette question équivaut davantage à un contrôle d’égalité simple qu’à une boucle sur une collection. En fait, c’est la raison pour laquelle, pour les collections de chaînes, Azure AI Search autorise uniquement eq
comme opérateur de comparaison dans une expression lambda pour any
.
Nous allons ensuite voir comment il est possible de combiner plusieurs contrôles d’égalité sur la même variable de plage avec or
. Cela fonctionne grâce à l’algèbre et à la propriété distributive des quantificateurs. Cette expression :
seasons/any(s: s eq 'winter' or s eq 'fall')
équivaut à :
seasons/any(s: s eq 'winter') or seasons/any(s: s eq 'fall')
et chacune des deux sous-expressions any
peut être exécutée efficacement à l’aide de l’index inversé. De plus, grâce à la loi de la négation des quantificateurs, cette expression :
seasons/all(s: s ne 'winter' and s ne 'fall')
équivaut à :
not seasons/any(s: s eq 'winter' or s eq 'fall')
C’est pourquoi il est possible d’utiliser all
avec ne
et and
.
Remarque
Même si ce document ne contient pas de présentation détaillée, ces mêmes principes s’étendent également aux tests de distance et d’intersection pour les collections de points de données géospatiales. C’est pourquoi, dans any
:
geo.intersects
ne peut pas être inverségeo.distance
doit être comparé à l’aide delt
ou dele
- Les expressions doivent être combinées avec
or
, et nonand
Les règles inverses s’appliquent pour all
.
Un plus large choix d’expressions sont autorisées lors du filtrage sur des collections de types de données qui prennent en charge les opérateurs lt
, gt
, le
et ge
, comme Collection(Edm.Int32)
par exemple. Plus précisément, vous pouvez utiliser and
ainsi que or
dans any
, à condition que les expressions de comparaison sous-jacentes soient combinées en comparaisons de plages à l’aide de and
, qui sont ensuite combinées à l’aide de or
. Cette structure d’expressions booléennes est appelée forme disjonctive normale (FND), également appelée « ORs de ANDs ». À l’inverse, les expressions lambda pour all
pour ces types de données doivent être en forme normale conjonctive (FNC), également appelée « ANDs de ORs ». Azure AI Search permet de telles comparaisons de plages car elle peut les exécuter efficacement à l’aide d’index inversés, tout comme elle peut effectuer une recherche de termes rapide pour les chaînes.
En résumé, voici les règles générales concernant ce qui est autorisé dans une expression lambda :
- Dans
any
, les contrôles positifs sont toujours autorisés, comme l’égalité, les comparaisons de plages,geo.intersects
ougeo.distance
comparé àlt
oule
(considérez la « proximité » comme une égalité quand il s’agit d’un contrôle de distance). - Dans
any
,or
est toujours autorisé. Vous pouvez utiliserand
uniquement pour les types de données qui peuvent exprimer des contrôles de plage, et uniquement si vous utilisez des ORs de ANDs (FND). - Dans
all
, les règles sont inversées. Seuls les contrôles négatifs sont autorisés. Vous pouvez toujours utiliserand
, et vous pouvez utiliseror
uniquement pour les contrôles de plage exprimés sous forme de AND de OR (FNC).
Dans la pratique, voici les types de filtres que vous êtes le plus susceptible d’utiliser. Il est cependant toujours utile de comprendre les limites de ce qui est possible.
Pour obtenir des exemples spécifiques des types de filtres qui sont autorisés et de ceux qui ne le sont pas, consultez Comment écrire des filtres de collection valides.
Étapes suivantes
- Résolution de problèmes liés aux filtres de collection OData dans Azure AI Search
- Filtres dans Azure AI Search
- Vue d’ensemble du langage d’expression OData pour la recherche Azure AI
- Informations de référence sur la syntaxe d’expression OData pour la recherche Azure AI
- Rechercher dans les documents (API REST recherche Azure AI)