Informacje o sposobie działania filtrów kolekcji OData w usłudze Azure AI Search

Ten artykuł zawiera podstawowe informacje dla deweloperów, którzy piszą zaawansowane filtry ze złożonymi wyrażeniami lambda. W tym artykule wyjaśniono, dlaczego istnieją reguły dotyczące filtrów kolekcji, eksplorując sposób wykonywania tych filtrów przez usługę Azure AI Search.

Podczas tworzenia filtru dla pól kolekcji w usłudze Azure AI Search można używać any operatorów i all wraz z wyrażeniami lambda. Wyrażenia lambda to wyrażenia logiczne odwołujące się do zmiennej zakresu. W filtrach używających wyrażenia any lambda operatory i all są analogiczne do for pętli w większości języków programowania, a zmienna zakresu przyjmuje rolę zmiennej pętli i wyrażenie lambda jako treść pętli. Zmienna zakresu przyjmuje wartość "current" kolekcji podczas iteracji pętli.

Przynajmniej tak działa koncepcyjnie. W rzeczywistości usługa Azure AI Search implementuje filtry w zupełnie inny sposób działania for pętli. W idealnym przypadku ta różnica byłaby dla Ciebie niewidoczna, ale w pewnych sytuacjach tak nie jest. Wynikiem końcowym jest to, że istnieją reguły, które należy przestrzegać podczas pisania wyrażeń lambda.

Uwaga

Aby uzyskać informacje na temat reguł filtrów kolekcji, w tym przykładów, zobacz Rozwiązywanie problemów z filtrami kolekcji OData w usłudze Azure AI Search.

Dlaczego filtry kolekcji są ograniczone

Istnieją trzy podstawowe przyczyny, dla których funkcje filtrowania nie są w pełni obsługiwane dla wszystkich typów kolekcji:

  1. Tylko niektóre operatory są obsługiwane w przypadku niektórych typów danych. Na przykład nie ma sensu porównywać wartości true logicznych i false używać ltwartości , gtitd.
  2. Usługa Azure AI Search nie obsługuje skorelowanego wyszukiwania w polach typu Collection(Edm.ComplexType).
  3. Usługa Azure AI Search używa odwróconych indeksów do wykonywania filtrów dla wszystkich typów danych, w tym kolekcji.

Pierwszą przyczyną jest tylko sposób definiowania języka OData i systemu typów EDM. Ostatnie dwa zostały wyjaśnione bardziej szczegółowo w pozostałej części tego artykułu.

W przypadku zastosowania wielu kryteriów filtrowania w kolekcji obiektów złożonych kryteria są skorelowane, ponieważ mają zastosowanie do każdego obiektu w kolekcji. Na przykład następujący filtr zwraca hotele, które mają co najmniej jeden pokój typu deluxe z stawką mniejszą niż 100:

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

Jeśli filtrowanie było niekorzystywane, powyższy filtr może zwrócić hotele, w których jeden pokój jest deluxe, a inny pokój ma stawkę podstawową mniejszą niż 100. Nie miałoby to sensu, ponieważ obie klauzule wyrażenia lambda mają zastosowanie do tej samej zmiennej zakresu, czyli room. Dlatego takie filtry są skorelowane.

Jednak w przypadku wyszukiwania pełnotekstowego nie ma możliwości odwoływania się do określonej zmiennej zakresu. Jeśli używasz wyszukiwania w polu, aby wydać pełne zapytanie Lucene w następujący sposób:

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

Możesz dostać hotele z powrotem, gdzie jeden pokój jest deluxe, a inny pokój wspomina "widok na miasto" w opisie. Na przykład poniższy dokument z elementem 1 będzie zgodny z Id zapytaniem:

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

Przyczyną jest to, że Rooms/Type odnosi się do wszystkich analizowanych terminów Rooms/Type pola w całym dokumencie i podobnie dla Rooms/Descriptionelementu , jak pokazano w poniższych tabelach.

Sposób Rooms/Type przechowywania wyszukiwania pełnotekstowego:

Termin w Rooms/Type Identyfikatory dokumentów
Deluxe 1, 2
standardowa 1

Sposób Rooms/Description przechowywania wyszukiwania pełnotekstowego:

Termin w Rooms/Description Identyfikatory dokumentów
Courtyard 2
miejscowość 1
Ogród 1
Dużych 1
Motel 2
Pokojach 1, 2
standardowa 1
Suite 1
wyświetl 1

Tak więc w przeciwieństwie do powyższego filtru, który w zasadzie mówi "dopasowanie dokumentów, w których pokój ma Type wartość "Deluxe Room", a ten sam pokój ma BaseRate mniej niż 100", zapytanie wyszukiwania mówi "match documents where Rooms/Type has the term "deluxe" and Rooms/Description has the phrase "city view". Nie ma pojęcia poszczególnych pomieszczeń, których pola mogą być skorelowane w tym drugim przypadku.

Odwrócone indeksy i kolekcje

Być może zauważysz, że istnieje znacznie mniej ograniczeń dotyczących wyrażeń lambda w przypadku złożonych kolekcji, niż w przypadku prostych kolekcji, takich jak Collection(Edm.Int32), Collection(Edm.GeographyPoint)i tak dalej. Dzieje się tak, ponieważ usługa Azure AI Search przechowuje złożone kolekcje jako rzeczywiste kolekcje dokumentów podrzędnych, podczas gdy proste kolekcje nie są w ogóle przechowywane jako kolekcje.

Rozważmy na przykład pole kolekcji ciągów z możliwością filtrowania, takie jak seasons w indeksie dla sprzedawcy internetowego. Niektóre dokumenty przekazane do tego indeksu mogą wyglądać następująco:

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

Wartości seasons pola są przechowywane w strukturze nazywanej indeksem odwróconym, który wygląda następująco:

Termin Identyfikatory dokumentów
Wiosna 1, 2
Letnich 1
Jesień 1, 2
Zimowe 2, 3

Ta struktura danych została zaprojektowana tak, aby odpowiedzieć na jedno pytanie z dużą szybkością: w jakich dokumentach pojawia się dany termin? Odpowiadanie na to pytanie działa bardziej jak zwykłe sprawdzanie równości niż pętla w kolekcji. W rzeczywistości dlatego w przypadku kolekcji ciągów usługa Azure AI Search zezwala eq tylko jako operator porównania wewnątrz wyrażenia lambda dla elementu any.

Następnie przyjrzymy się, jak można połączyć wiele kontroli równości dla tej samej zmiennej zakresu za pomocą polecenia or. Działa dzięki algebra i właściwości dystrybucyjnej kwantyfikatorów. To wyrażenie:

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

jest odpowiednikiem:

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

a każde z dwóch any wyrażeń podrzędnych można wydajnie wykonać przy użyciu odwróconego indeksu. Ponadto dzięki negacji prawa kwantyfikatorów to wyrażenie:

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

jest odpowiednikiem:

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

dlatego można używać z all usługami ne i and.

Uwaga

Chociaż szczegóły wykraczają poza zakres tego dokumentu, te same zasady rozszerzają się na odległość i testy przecięcia dla kolekcji punktów geoprzestrzeniowych. Dlatego w pliku :any

  • geo.intersects nie można negować
  • geo.distance należy porównać przy użyciu polecenia lt lub le
  • wyrażenia muszą być łączone z wyrażeniem or, a nie and

Reguły odwrotne dotyczą elementu all.

W przypadku filtrowania kolekcji typów danych obsługujących ltoperatory , , gt, lei ge , na Collection(Edm.Int32) przykład, dozwolone są szersze wyrażenia. W szczególności można użyć and funkcji , jak również or w anysystemie , o ile bazowe wyrażenia porównania są łączone w porównania zakresów przy użyciu polecenia , które następnie są dalej łączone przy użyciu andpolecenia or. Ta struktura wyrażeń logicznych nosi nazwę Disjunctive Normal Form (DNF), inaczej znaną jako "ORs of ANDs". Z drugiej strony wyrażenia lambda dla all tych typów danych muszą znajdować się w postaci standardowej (CNF), inaczej znanej jako "ANDs of ORs". Usługa Azure AI Search umożliwia takie porównania zakresów, ponieważ może je wykonywać przy użyciu indeksów odwróconych wydajnie, podobnie jak w przypadku szybkiego wyszukiwania terminów dla ciągów.

Podsumowując, poniżej przedstawiono reguły kciuka dotyczące tego, co jest dozwolone w wyrażeniu lambda:

  • W systemie anytesty dodatnie są zawsze dozwolone, takie jak równość, porównania zakresów, geo.intersectslub geo.distance w porównaniu z lt lub le (pomyśl o "zbliżeniu" jako o równości, jeśli chodzi o sprawdzanie odległości).
  • Wewnątrz anyelementu or jest zawsze dozwolone. Można użyć and tylko dla typów danych, które mogą wyrażać kontrole zakresów, i tylko wtedy, gdy używasz jednostek ORS identyfikatorów AND (DNF).
  • Wewnątrz allreguły są odwrócone. Dozwolone są tylko testy ujemne , można używać and zawsze i można użyć or tylko do sprawdzania zakresu wyrażonego jako AND jednostek ORS (CNF).

W praktyce są to typy filtrów, których najprawdopodobniej używasz mimo to. Nadal warto zrozumieć granice tego, co jest możliwe.

Aby zapoznać się z konkretnymi przykładami dozwolonych filtrów i których nie ma, zobacz Jak napisać prawidłowe filtry kolekcji.

Następne kroki