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:

  1. Alleen bepaalde operators worden ondersteund voor bepaalde gegevenstypen. Het is bijvoorbeeld niet zinvol om de Booleaanse waarden true te vergelijken en false , enzovoort te gebruikenltgt.
  2. Azure AI Search biedt geen ondersteuning voor gecorreleerde zoekopdrachten op velden van het type Collection(Edm.ComplexType).
  3. 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.

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 Id1 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
Deluxe 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
Grote 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
Voorjaar 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 ontkend
  • geo.distancemoet worden vergeleken met of ltle
  • expressies moeten worden gecombineerd met or, niet and

De omgekeerde regels zijn van alltoepassing op .

Een grotere verscheidenheid aan expressies is toegestaan bij het filteren op verzamelingen gegevenstypen die ondersteuning bieden voor, ltgtleen 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 andvan , 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 anyzijn positieve controles altijd toegestaan, zoals gelijkheid, bereikvergelijkingen, geo.intersectsof vergeleken met lt of geo.distancele (denk aan 'nabijheid' als gelijkheid als het gaat om het controleren van afstand).
  • Binnen anyis or altijd toegestaan. U kunt alleen gebruiken and voor gegevenstypen die bereikcontroles kunnen uitdrukken en alleen als u OR's van AND's (DNF) gebruikt.
  • Binnen allworden de regels omgekeerd. Alleen negatieve controles zijn toegestaan, u kunt altijd gebruiken and en u kunt alleen gebruiken or 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.

Volgende stappen