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 die Regeln für Sammlungsfilter vorhanden sind, indem sie untersuchen, wie Azure AI Search diese Filter ausführt.
Wenn Sie einen Filter für Sammlungsfelder in Azure AI Search erstellen, können Sie die any
Und-Operatorenall
zusammen mit Lambda-Ausdrücken verwenden. Lambdaausdrücke sind boolesche Ausdrücke, die auf eine Bereichsvariable verweisen. In Filtern, die einen Lambda-Ausdruck verwenden, sind die und all
die any
Operatoren analog zu einer for
Schleife in den meisten Programmiersprachen, wobei die Bereichsvariable die Rolle der Schleifenvariablen und den Lambda-Ausdruck als Textkörper der Schleife verwendet. 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 zugrunde liegende Gründe, warum Filterfeatures für alle Arten von Auflistungen nicht vollständig 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 Sie mehrere Filterkriterien auf eine Auflistung komplexer Objekte anwenden, werden die Kriterien korreliert, da sie für jedes Objekt in der Auflistung gelten. Der folgende Filter gibt beispielsweise Hotels zurück, die mindestens ein Deluxe-Zimmer 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"
Sie können Hotels zurückkommen, wo ein Zimmer deluxe ist, und ein anderes Zimmer Erwähnung s "Stadtansicht" in der Beschreibung. 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
Möglicherweise haben Sie bemerkt, dass es viel weniger Einschränkungen für Lambda-Ausdrücke über komplexe Auflistungen gibt, als für einfache Auflistungen wie Collection(Edm.Int32)
, Collection(Edm.GeographyPoint)
usw. Dies liegt daran, dass Azure AI Search komplexe Sammlungen als tatsächliche Sammlungen von Filialdokumenten 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
.
Als Nächstes betrachten wir, wie es möglich ist, mehrere Gleichheitsprüfungen für dieselbe 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. Es sind nur negative Prüfungen zulässig, Sie können immer verwendenand
, und Sie können nur für Bereichsprüfungen verwendetor
werden, die als ANDs von 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.