Verstehen, wie OData-Sammlungsfilter in Azure AI Search funktionieren
Dieser Artikel enthält Hintergrundinformationen für Entwickler, die erweiterte Filter mit komplexen Lambda-Ausdrücken schreiben. In diesem Artikel wird erläutert, warum es Regeln für Sammlungsfilter gibt, indem untersucht wird, wie Azure KI Search diese Filter ausführt.
Um in Azure KI Search nach Sammlungsfeldern zu filtern, können Sie die Operatoren any
und all
zusammen mit Lambdaausdrücken verwenden. Lambdaausdrücke sind boolesche Ausdrücke, die auf eine Bereichsvariable verweisen. Die Operatoren any
und all
sind analog zu einer for
-Schleife in den meisten Programmiersprachen, wobei die Bereichsvariable die Rolle der Schleifenvariablen übernimmt und der Lambdaausdruck als Schleifenkörper fungiert. Die Bereichsvariable übernimmt den „aktuellen“ Wert der Sammlung während der Iteration durch die Schleife.
Zumindest funktioniert es so theoretisch. In der Praxis implementiert Azure KI Search Filter auf eine ganz andere Weise als for
-Schleifen. Im Idealfall wäre dieser Unterschied für Sie unsichtbar, aber unter bestimmten Umständen ist er es nicht. Das Endergebnis ist, dass es Regeln gibt, denen Sie beim Schreiben von Lambdaausdrücken folgen müssen.
Hinweis
Informationen dazu, welche Regeln für Sammlungsfilter gelten (sowie Beispiele) finden Sie unter Problembehandlung von OData-Sammlungsfiltern in Azure KI Search.
Warum Sammlungsfilter eingeschränkt sind
Es gibt drei Hauptgründe, warum nicht alle Filterfunktionen für alle Arten von Sammlungen unterstützt werden:
- Nur bestimmte Operatoren werden für bestimmte Datentypen unterstützt. Es ist beispielsweise nicht sinnvoll, die booleschen Werte
true
undfalse
mitlt
,gt
usw. zu vergleichen. - Azure KI Search unterstützt keine korrelierte Suche für Felder vom Typ
Collection(Edm.ComplexType)
. - Azure KI Search verwendet invertierte Indizes zum Ausführen von Filtern für alle Typen von Daten (einschließlich Sammlungen).
Der erste Grund ist nur eine Folge der Definition der OData-Sprache und des EDM-Typsystems. Die letzten beiden Gründe werden im weiteren Verlauf dieses Artikels näher erläutert.
Korrelierte Suche im Vergleich zu nicht korrelierter Suche
Wenn mehrere Filterkriterien auf eine Sammlung komplexer Objekte angewendet werden, sind die Kriterien korreliert, da sie sich auf jedes Objekt in der Sammlung beziehen. Der folgende Filter gibt beispielsweise Hotels zurück, die mindestens ein Luxuszimmer mit einem Preis von weniger als 100 haben:
Rooms/any(room: room/Type eq 'Deluxe Room' and room/BaseRate lt 100)
Wenn die Filterung unkorreliert wäre, kann der obige Filter Hotels zurückgeben, in denen ein Zimmer deluxe ist und ein anderes Zimmer einen Basispreis von weniger als 100 hat. Das wäre nicht sinnvoll, da beide Klauseln des Lambdaausdrucks für die gleiche Bereichsvariable gelten, nämlich für room
. Aus diesem Grund sind solche Filter korreliert.
Bei der Volltextsuche gibt es jedoch keine Möglichkeit, auf eine bestimmte Bereichsvariable zu verweisen. Wenn Sie die feldbezogene Suche verwenden, um eine vollständige Lucene-Abfrage wie diese auszugeben:
Rooms/Type:deluxe AND Rooms/Description:"city view"
erhalten Sie möglicherweise Hotels zurück, in denen ein Zimmer deluxe ist und für ein anderes Zimmer „city view“ (Aussicht auf die Stadt) in der Beschreibung erwähnt wird. Beispielsweise würde das folgende Dokument mit einer Id
von 1
der Abfrage entsprechen:
{
"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" }
]
}
]
}
Der Grund dafür ist, dass Rooms/Type
sich auf alle analysierten Begriffe des Felds Rooms/Type
im gesamten Dokument und ähnlich für Rooms/Description
bezieht, wie in den folgenden Tabellen gezeigt.
So wird Rooms/Type
für die Volltextsuche gespeichert:
Begriff in Rooms/Type |
Dokument-IDs |
---|---|
deluxe | 1, 2 |
Standard | 1 |
So wird Rooms/Description
für die Volltextsuche gespeichert:
Begriff in Rooms/Description |
Dokument-IDs |
---|---|
courtyard | 2 |
Ort | 1 |
garden | 1 |
groß | 1 |
Motel | 2 |
room | 1, 2 |
Standard | 1 |
suite | 1 |
view | 1 |
Im Gegensatz zum obigen Filter, der im Wesentlichen besagt, dass „Dokumente ermittelt werden sollen, in denen Type
für ein Zimmer gleich „Deluxe Room“ (Luxuszimmer) und BaseRate
für dieses gleiche Zimmer kleiner als 100 ist“, besagt die Suchabfrage, dass „Dokumente ermittelt werden sollen, in denen Rooms/Type
den Begriff „deluxe“ und Rooms/Description
den Begriff „city view“ (Aussicht auf die Stadt) enthält. Es gibt kein Konzept für einzelne Zimmer, deren Felder im letzteren Fall korreliert werden können.
Invertierte Indizes und Sammlungen
Sie haben vielleicht bemerkt, dass es weitaus weniger Einschränkungen für Lambdaausdrücke bei komplexen Sammlungen gibt als bei einfachen Sammlungen wie Collection(Edm.Int32)
, Collection(Edm.GeographyPoint)
usw. Dies liegt daran, dass Azure KI Search komplexe Sammlungen als tatsächliche Sammlungen von Unterdokumenten speichert, während einfache Sammlungen überhaupt nicht als Sammlungen gespeichert werden.
Betrachten Sie beispielsweise ein filterbares Zeichenfolgen-Sammlungsfeld wie seasons
in einem Index für einen Onlinehändler. Einige in diesen Index hochgeladene Dokumente könnten wie folgt aussehen:
{
"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"]
}
]
}
Die Werte des Felds seasons
werden in einer Struktur namens invertierter Index gespeichert, die in etwa so aussieht:
Begriff | Dokument-IDs |
---|---|
spring | 1, 2 |
summer | 1 |
fall | 1, 2 |
winter | 2, 3 |
Diese Datenstruktur dient der schnellen Beantwortung der Frage: In welchen Dokumenten ist ein bestimmter Begriff vorhanden? Die Beantwortung dieser Frage funktioniert eher wie eine einfache Gleichheitsprüfung und nicht wie die Ausführung einer Schleife für eine Sammlung. Genau aus diesem Grund erlaubt Azure KI Search für Zeichenfolgensammlungen nur eq
als Vergleichsoperator innerhalb eines Lambdaausdrucks für any
.
Aufbauend auf der Gleichheit untersuchen wir nun, wie es möglich ist, mehrere Gleichheitsüberprüfungen für die gleiche Bereichsvariable mit or
zu kombinieren. Dies funktioniert dank Algebra und der distributiven Eigenschaft von Quantifizierern. Der folgende Ausdruck:
seasons/any(s: s eq 'winter' or s eq 'fall')
entspricht:
seasons/any(s: s eq 'winter') or seasons/any(s: s eq 'fall')
und jeder der beiden any
-Unterausdrücke kann effizient mithilfe des invertierten Index ausgeführt werden. Dank dem Negationsgesetz von Quantifizierern ist auch dieser Ausdruck:
seasons/all(s: s ne 'winter' and s ne 'fall')
entspricht:
not seasons/any(s: s eq 'winter' or s eq 'fall')
Deshalb ist es möglich, all
mit ne
und and
zu verwenden.
Hinweis
Obwohl die Details über den Rahmen dieses Dokuments hinausgehen, erstrecken sich diese Prinzipien auch auf Abstands- und Überschneidungstests für Sammlungen von räumlichen Punkten. Deshalb kann in any
:
geo.intersects
nicht negiert werdengeo.distance
muss mitlt
oderle
verglichen werden- Ausdrücke müssen mit
or
und nicht mitand
kombiniert werden
Die entgegengesetzten Regeln gelten für all
.
Eine größere Vielfalt von Ausdrücken ist zulässig, wenn Sie nach Sammlungen von Datentypen filtern, die die Operatoren lt
, gt
, le
und ge
unterstützen, z.B. Collection(Edm.Int32)
. Insbesondere können Sie sowohl and
als auch or
in any
verwenden, solange die zugrunde liegenden Vergleichsausdrücke zu Bereichsvergleichen mit and
kombiniert werden, die dann mit or
weiter kombiniert werden. Diese Struktur von booleschen Ausdrücken wird als disjunktive Normalform (DNF) bezeichnet, auch bekannt als „ORs of ANDs“. Im Gegensatz dazu müssen Lambdaausdrücke für all
für diese Datentypen in konjunktiver Normalform (CNF) vorliegen, auch bekannt als „ANDs of ORs“. Azure KI Search ermöglicht solche Bereichsvergleiche, da Search sie effizient mit invertierten Indizes ausführen kann, genau wie die schnelle Suche nach Zeichenfolgen ermöglicht wird.
Zusammenfassend sind dies die Faustregeln für das, was in einem Lambdaausdruck zulässig ist:
- Innerhalb von
any
sind positive Überprüfungen immer zulässig, etwa Gleichheit, Bereichsvergleiche,geo.intersects
odergeo.distance
im Vergleich zult
oderle
(sehen Sie „Nähe“ als Gleichheit an, wenn es um die Überprüfung der Entfernung geht). - In
any
istor
immer zulässig. Sie könnenand
nur für Datentypen, die Bereichsüberprüfungen ausdrücken können, und nur dann verwenden, wenn Sie ORs of ANDs (DNF) verwenden. - Innerhalb
all
werden die Regeln umgekehrt. Innerhalb von sind die Regeln umgekehrt: Nur negative Überprüfungenand
sind zulässig, Sie könnenor
immer verwenden, und Sie können nur für Bereichsüberprüfungen verwenden, die als ANDs of ORs (CNF) ausgedrückt werden.
In der Praxis sind dies die Arten von Filtern, die Sie ohnehin am ehesten verwenden werden. Es ist trotzdem hilfreich, die Grenzen dessen zu verstehen, was möglich ist.
Spezifische Beispiele dafür, welche Arten von Filtern zulässig sind und welche nicht, finden Sie unter Schreiben gültiger Sammlungsfilter.