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:

  1. Nur bestimmte Operatoren werden für bestimmte Datentypen unterstützt. Es ist beispielsweise nicht sinnvoll, die booleschen Werte true und false mit lt, gt usw. zu vergleichen.
  2. Azure KI Search unterstützt keine korrelierte Suche für Felder vom Typ Collection(Edm.ComplexType).
  3. 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.

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 orzu 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 werden
  • geo.distance muss mit lt oder le verglichen werden
  • Ausdrücke müssen mit or und nicht mit and 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 oder geo.distance im Vergleich zu lt oder le (sehen Sie „Nähe“ als Gleichheit an, wenn es um die Überprüfung der Entfernung geht).
  • In any ist or immer zulässig. Sie können and nur für Datentypen, die Bereichsüberprüfungen ausdrücken können, und nur dann verwenden, wenn Sie ORs of ANDs (DNF) verwenden.
  • Innerhalb allwerden die Regeln umgekehrt. Es sind nur negative Prüfungen zulässig, Sie können immer verwenden and , und Sie können nur für Bereichsprüfungen verwendet or 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.

Nächste Schritte