Begrijpen hoe OData-verzamelingsfilters werken in Azure AI Search
Dit artikel bevat achtergrondinformatie voor ontwikkelaars die geavanceerde filters schrijven met complexe lambda-expressies. In het artikel wordt uitgelegd waarom de regels voor verzamelingsfilters bestaan door te onderzoeken hoe Azure AI Search deze filters uitvoert.
Wanneer u een filter bouwt op verzamelingsvelden in Azure AI Search, kunt u de any
en all
operators samen met lambda-expressies gebruiken. Lambda-expressies zijn Booleaanse expressies die verwijzen naar een bereikvariabele. In filters die gebruikmaken van een lambda-expressie, zijn de any
en all
operators vergelijkbaar met een for
lus in de meeste programmeertalen, waarbij de bereikvariabele de rol van lusvariabele krijgt en de lambda-expressie als de hoofdtekst van de lus. De bereikvariabele neemt de 'huidige' waarde van de verzameling op tijdens de iteratie van de lus.
Dat is tenminste hoe het conceptueel werkt. In werkelijkheid implementeert Azure AI Search filters op een heel andere manier dan hoe for
lussen werken. In het ideale gevallen is dit verschil onzichtbaar voor u, maar in bepaalde situaties is het niet. Het eindresultaat is dat er regels zijn die u moet volgen bij het schrijven van lambda-expressies.
Notitie
Zie Problemen met OData-verzamelingsfilters in Azure AI Search oplossen voor informatie over wat de regels voor verzamelingsfilters zijn, inclusief voorbeelden.
Waarom verzamelingsfilters beperkt zijn
Er zijn drie onderliggende redenen waarom filterfuncties niet volledig worden ondersteund voor alle typen verzamelingen:
- Alleen bepaalde operators worden ondersteund voor bepaalde gegevenstypen. Het is bijvoorbeeld niet zinvol om de Booleaanse waarden
true
te vergelijken enfalse
, enzovoort te gebruikenlt
gt
. - Azure AI Search biedt geen ondersteuning voor gecorreleerde zoekopdrachten op velden van het type
Collection(Edm.ComplexType)
. - Azure AI Search maakt gebruik van omgekeerde indexen om filters uit te voeren voor alle typen gegevens, inclusief verzamelingen.
De eerste reden is slechts een gevolg van de manier waarop de OData-taal en het EDM-typesysteem worden gedefinieerd. De laatste twee worden uitgebreid beschreven in de rest van dit artikel.
Gecorreleerde versus niet-gerelateerde zoekopdracht
Wanneer u meerdere filtercriteria toepast op een verzameling complexe objecten, worden de criteria gecorreleerd omdat deze van toepassing zijn op elk object in de verzameling. Het volgende filter retourneert bijvoorbeeld hotels met ten minste één deluxe kamer met een tarief kleiner dan 100:
Rooms/any(room: room/Type eq 'Deluxe Room' and room/BaseRate lt 100)
Als filteren niet-gerelateerd was, kan het bovenstaande filter hotels retourneren waarbij één kamer deluxe is en een andere kamer een basistarief heeft van minder dan 100. Dat zou niet logisch zijn, omdat beide componenten van de lambda-expressie van toepassing zijn op dezelfde bereikvariabele, namelijk room
. Daarom worden dergelijke filters gecorreleerd.
Voor zoeken in volledige tekst is er echter geen manier om te verwijzen naar een specifieke bereikvariabele. Als u een veldzoekopdracht gebruikt om een volledige Lucene-query uit te geven, zoals deze:
Rooms/Type:deluxe AND Rooms/Description:"city view"
U krijgt mogelijk hotels terug waar één kamer deluxe is, en een andere kamer vermeldt "uitzicht op de stad" in de beschrijving. Het onderstaande document met de query komt bijvoorbeeld overeen met Id
1
de query:
{
"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" }
]
}
]
}
De reden hiervoor is dat Rooms/Type
verwijst naar alle geanalyseerde termen van het Rooms/Type
veld in het hele document, en op dezelfde manier voor Rooms/Description
, zoals wordt weergegeven in de onderstaande tabellen.
Hoe Rooms/Type
wordt opgeslagen voor zoeken in volledige tekst:
Term in Rooms/Type |
Document-id's |
---|---|
luxueus | 1, 2 |
standard | 1 |
Hoe Rooms/Description
wordt opgeslagen voor zoeken in volledige tekst:
Term in Rooms/Description |
Document-id's |
---|---|
binnenplaats | 2 |
plaats | 1 |
tuin | 1 |
groot | 1 |
motel | 2 |
kamer | 1, 2 |
standard | 1 |
suite | 1 |
weergeven | 1 |
In tegenstelling tot het bovenstaande filter, dat in feite 'overeenkomende documenten waar een ruimte gelijk is Type
aan 'Deluxe Room' en diezelfde ruimte minder dan 100 heeft BaseRate
, zegt de zoekquery 'match documents where Rooms/Type
has the term 'deluxe' en Rooms/Description
heeft de woordgroep 'city view'. Er is geen concept van afzonderlijke ruimten waarvan de velden in het laatste geval kunnen worden gecorreleerd.
Omgekeerde indexen en verzamelingen
Mogelijk hebt u gemerkt dat er veel minder beperkingen gelden voor lambda-expressies ten opzichte van complexe verzamelingen dan voor eenvoudige verzamelingen, zoals Collection(Edm.Int32)
, Collection(Edm.GeographyPoint)
enzovoort. Dit komt doordat in Azure AI Search complexe verzamelingen worden opgeslagen als werkelijke verzamelingen van subdocumenten, terwijl eenvoudige verzamelingen helemaal niet worden opgeslagen als verzamelingen.
Denk bijvoorbeeld aan een filterbaar tekenreeksverzamelingsveld, zoals seasons
in een index voor een onlinewinkel. Sommige documenten die naar deze index zijn geüpload, kunnen er als volgt uitzien:
{
"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"]
}
]
}
De waarden van het seasons
veld worden opgeslagen in een structuur die een omgekeerde index wordt genoemd, die er ongeveer als volgt uitziet:
Term | Document-id's |
---|---|
lente | 1, 2 |
zomer | 1 |
vallen | 1, 2 |
winter | 2, 3 |
Deze gegevensstructuur is ontworpen om één vraag met grote snelheid te beantwoorden: In welke documenten wordt een bepaalde term weergegeven? Het beantwoorden van deze vraag werkt meer als een gewone gelijkheidscontrole dan een lus over een verzameling. In feite is dit de reden waarom voor tekenreeksverzamelingen Azure AI Search alleen als vergelijkingsoperator in een lambda-expressie is toegestaaneq
.any
Vervolgens kijken we hoe het mogelijk is om meerdere gelijkheidscontroles op dezelfde bereikvariabele te combineren met or
. Het werkt dankzij algebra en de verdelingseigenschap van kwantificatoren. Deze expressie:
seasons/any(s: s eq 'winter' or s eq 'fall')
is gelijk aan:
seasons/any(s: s eq 'winter') or seasons/any(s: s eq 'fall')
en elk van de twee any
subexpressies kan efficiënt worden uitgevoerd met behulp van de omgekeerde index. Ook, dankzij de negatiewet van kwantificatoren, deze expressie:
seasons/all(s: s ne 'winter' and s ne 'fall')
is gelijk aan:
not seasons/any(s: s eq 'winter' or s eq 'fall')
daarom is het mogelijk om te gebruiken all
met ne
en and
.
Notitie
Hoewel de details buiten het bereik van dit document vallen, zijn dezelfde principes ook van toepassing op afstands- en snijpunttests voor verzamelingen georuimtelijke punten . Dit is de reden waarom, in any
:
geo.intersects
kan niet worden ontkendgeo.distance
moet worden vergeleken met oflt
le
- expressies moeten worden gecombineerd met
or
, nietand
De omgekeerde regels zijn van all
toepassing op .
Een grotere verscheidenheid aan expressies is toegestaan bij het filteren op verzamelingen gegevenstypen die ondersteuning bieden voor, lt
gt
le
en ge
operators, zoals Collection(Edm.Int32)
bijvoorbeeld. U kunt met name ook gebruiken and
als in any
, zolang de onderliggende vergelijkingsexpressies worden gecombineerd tot bereikvergelijkingen met behulp and
van , die vervolgens verder worden gecombineerd met behulp van or
.or
Deze structuur van Boole-expressies wordt Disjunctive Normal Form (DNF) genoemd, ook wel bekend als 'OR's van AND's'. Daarentegen moeten lambda-expressies voor all
deze gegevenstypen zich in De Normale Vorm (CNF) bevinden, ook wel bekend als 'AND's van OR's'. Azure AI Search maakt dergelijke bereikvergelijkingen mogelijk omdat deze efficiënt kunnen worden uitgevoerd met omgekeerde indexen, net zoals het snel opzoeken van termen voor tekenreeksen kan uitvoeren.
Kortom, hier volgen de vuistregels voor wat is toegestaan in een lambda-expressie:
- Binnen
any
zijn positieve controles altijd toegestaan, zoals gelijkheid, bereikvergelijkingen,geo.intersects
of vergeleken metlt
ofgeo.distance
le
(denk aan 'nabijheid' als gelijkheid als het gaat om het controleren van afstand). - Binnen
any
isor
altijd toegestaan. U kunt alleen gebruikenand
voor gegevenstypen die bereikcontroles kunnen uitdrukken en alleen als u OR's van AND's (DNF) gebruikt. - Binnen
all
worden de regels omgekeerd. Alleen negatieve controles zijn toegestaan, u kunt altijd gebruikenand
en u kunt alleen gebruikenor
voor bereikcontroles die worden uitgedrukt als AND's van OR's (CNF).
In de praktijk zijn dit de typen filters die u waarschijnlijk toch zult gebruiken. Het is nog steeds handig om de grenzen te begrijpen van wat er mogelijk is.
Zie Hoe u geldige verzamelingsfilters schrijft voor specifieke voorbeelden van welke typen filters zijn toegestaan en welke niet.