Förstå hur OData-samlingsfilter fungerar i Azure AI Search

Den här artikeln innehåller bakgrund för utvecklare som skriver avancerade filter med komplexa lambda-uttryck. Artikeln förklarar varför reglerna för samlingsfilter finns genom att utforska hur Azure AI Search kör dessa filter.

När du skapar ett filter på samlingsfält i Azure AI Search kan du använda operatorernaanyoch all tillsammans med lambda-uttryck. Lambda-uttryck är booleska uttryck som refererar till en intervallvariabel. I filter som använder ett lambda-uttryck any är operatorerna och all analoga med en for loop i de flesta programmeringsspråk, där intervallvariabeln tar rollen som loopvariabel och lambda-uttrycket som loopens brödtext. Intervallvariabeln använder det "aktuella" värdet för samlingen under iterationen av loopen.

Det är i alla fall så det fungerar konceptuellt. I verkligheten implementerar Azure AI Search filter på ett helt annat sätt än hur for loopar fungerar. Helst skulle den här skillnaden vara osynlig för dig, men i vissa situationer är den inte det. Slutresultatet är att det finns regler som du måste följa när du skriver lambda-uttryck.

Kommentar

Information om vad reglerna för samlingsfilter är, inklusive exempel, finns i Felsöka OData-samlingsfilter i Azure AI Search.

Varför samlingsfilter är begränsade

Det finns tre underliggande orsaker till att filterfunktioner inte stöds fullt ut för alla typer av samlingar:

  1. Endast vissa operatorer stöds för vissa datatyper. Det är till exempel inte meningsfullt att jämföra de booleska värdena true och false använda lt, gtoch så vidare.
  2. Azure AI Search stöder inte korrelerad sökning på fält av typen Collection(Edm.ComplexType).
  3. Azure AI Search använder inverterade index för att köra filter över alla typer av data, inklusive samlingar.

Den första orsaken är bara en konsekvens av hur OData-språket och EDM-typsystemet definieras. De två sista beskrivs mer detaljerat i resten av den här artikeln.

När du tillämpar flera filtervillkor för en samling komplexa objekt korreleras kriterierna eftersom de gäller för varje objekt i samlingen. Följande filter returnerar till exempel hotell som har minst ett deluxe-rum med ett pris som är mindre än 100:

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

Om filtreringen inte var korrelerad kan ovanstående filter returnera hotell där ett rum är deluxe och ett annat rum har ett baspris som är mindre än 100. Det skulle inte vara meningsfullt, eftersom båda satserna i lambda-uttrycket gäller för samma intervallvariabel, nämligen room. Det är därför sådana filter korreleras.

För fulltextsökning finns det dock inget sätt att referera till en specifik intervallvariabel. Om du använder fältsökning för att utfärda en fullständig Lucene-fråga som den här:

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

du kan få hotell tillbaka där ett rum är deluxe, och ett annat rum nämner "stadsvy" i beskrivningen. Dokumentet nedan med Id1 skulle till exempel matcha frågan:

{
  "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" }
      ]
    }
  ]
}

Anledningen är att Rooms/Type refererar till alla analyserade termer Rooms/Type i fältet i hela dokumentet och på liknande sätt för Rooms/Description, som visas i tabellerna nedan.

Hur Rooms/Type lagras för fulltextsökning:

Term i Rooms/Type Dokument-ID:t
Deluxe 1, 2
standard 1

Hur Rooms/Description lagras för fulltextsökning:

Term i Rooms/Description Dokument-ID:t
Courtyard 2
ort 1
Trädgård 1
Stora 1
Motel 2
Rum 1, 2
standard 1
Suite 1
vy 1

Så till skillnad från filtret ovan, som i princip säger "matcha dokument där ett rum har Type lika med "Deluxe Room" och samma rum har BaseRate mindre än 100", står det i sökfrågan "matcha dokument där Rooms/Type har termen "deluxe" och Rooms/Description har frasen "stadsvy". Det finns inget begrepp om enskilda rum vars fält kan korreleras i det senare fallet.

Inverterade index och samlingar

Du kanske har märkt att det finns mycket färre begränsningar för lambda-uttryck för komplexa samlingar än för enkla samlingar som Collection(Edm.Int32), Collection(Edm.GeographyPoint)och så vidare. Det beror på att Azure AI Search lagrar komplexa samlingar som faktiska samlingar av underdokument, medan enkla samlingar inte lagras som samlingar alls.

Du kan till exempel överväga ett filterbart strängsamlingsfält som seasons i ett index för en onlineåterförsäljare. Vissa dokument som laddats upp till det här indexet kan se ut så här:

{
  "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"]
    }
  ]
}

Värdena för seasons fältet lagras i en struktur som kallas för ett inverterat index, vilket ser ut ungefär så här:

Period Dokument-ID:t
Våren 1, 2
Sommaren 1
Falla 1, 2
Vintern 2, 3

Den här datastrukturen är utformad för att besvara en fråga med hög hastighet: I vilka dokument visas en viss term? Att besvara den här frågan fungerar mer som en vanlig likhetskontroll än en loop över en samling. Det är därför för strängsamlingar endast tillåter eq Azure AI Search som jämförelseoperator i ett lambda-uttryck för any.

Därefter tittar vi på hur det är möjligt att kombinera flera likhetskontroller på samma intervallvariabel med or. Det fungerar tack vare algebra och den distribuerande egenskapen för kvantifierare. Det här uttrycket:

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

motsvarar:

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

och vart och ett av de två any underuttrycken kan effektivt köras med hjälp av det inverterade indexet. Dessutom, tack vare negationslagen för kvantifierare, detta uttryck:

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

motsvarar:

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

därför är det möjligt att använda all med ne och and.

Kommentar

Även om informationen ligger utanför det här dokumentets omfång omfattar samma principer även avstånds- och skärningstest för samlingar av geo-spatiala punkter . Det är därför, i any:

  • geo.intersects kan inte negeras
  • geo.distancemåste jämföras med eller ltle
  • uttryck måste kombineras med or, inte and

De omvända reglerna gäller för all.

En bredare mängd olika uttryck tillåts vid filtrering av samlingar av datatyper som stöder operatorerna lt, gt, leoch ge , till exempel Collection(Edm.Int32) . Mer specifikt kan du använda and såväl som or i any, så länge de underliggande jämförelseuttrycken kombineras till intervalljämförelser med hjälp av and, som sedan kombineras ytterligare med .or Den här strukturen för booleska uttryck kallas Disjunctive Normal Form (DNF), även kallat "ORs of ANDs". Omvänt måste lambda-uttryck för all för dessa datatyper vara i conjunctive Normal Form (CNF), även kallat "ANDs of ORs". Azure AI Search tillåter sådana intervalljämförelser eftersom de kan köras effektivt med hjälp av inverterade index, precis som det kan göra snabba sökningar efter strängar.

Sammanfattningsvis är här tumreglerna för vad som tillåts i ett lambda-uttryck:

  • Inuti anytillåts alltid positiva kontroller, till exempel likhet, intervalljämförelser, geo.intersects, eller geo.distance jämfört med lt eller le (tänk på "närhet" som likhet när det gäller att kontrollera avstånd).
  • Inuti anyär or alltid tillåtet. Du kan endast använda and för datatyper som kan uttrycka intervallkontroller och endast om du använder ORs för AND (DNF).
  • I allär reglerna omvända. Endast negativa kontroller tillåts, du kan alltid använda and och du kan endast använda or för intervallkontroller uttryckta som AND för ORs (CNF).

I praktiken är det här de typer av filter som du förmodligen använder ändå. Det är ändå bra att förstå gränserna för vad som är möjligt.

Specifika exempel på vilka typer av filter som tillåts och vilka som inte är det finns i Så här skriver du giltiga samlingsfilter.

Nästa steg