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 operatorernaany
och 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:
- Endast vissa operatorer stöds för vissa datatyper. Det är till exempel inte meningsfullt att jämföra de booleska värdena
true
ochfalse
användalt
,gt
och så vidare. - Azure AI Search stöder inte korrelerad sökning på fält av typen
Collection(Edm.ComplexType)
. - 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.
Korrelerad eller icke-korrelerad sökning
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 Id
1
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 |
---|---|
gårdsplan | 2 |
ort | 1 |
trädgård | 1 |
stor | 1 |
motell | 2 |
rum | 1, 2 |
standard | 1 |
svit | 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:
Term | Dokument-ID:t |
---|---|
vår | 1, 2 |
sommar | 1 |
falla | 1, 2 |
vinter | 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 negerasgeo.distance
måste jämföras med ellerlt
le
- uttryck måste kombineras med
or
, inteand
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
, le
och 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
any
tillåts alltid positiva kontroller, till exempel likhet, intervalljämförelser,geo.intersects
, ellergeo.distance
jämfört medlt
ellerle
(tänk på "närhet" som likhet när det gäller att kontrollera avstånd). - Inuti
any
äror
alltid tillåtet. Du kan endast användaand
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ändaand
och du kan endast användaor
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.