Sdílet prostřednictvím


Vysvětlení fungování filtrů kolekcí OData ve službě Azure AI Search

Tento článek obsahuje základní informace pro vývojáře, kteří píší pokročilé filtry pomocí složitých výrazů lambda. Článek vysvětluje, proč existují pravidla pro filtry kolekcí tím, že prozkoumáte, jak Azure AI Search tyto filtry provádí.

Když vytvoříte filtr pro pole kolekce ve službě Azure AI Search, můžete použít any operátory a all operátory společně s výrazy lambda. Výrazy lambda jsou logické výrazy, které odkazují na proměnnou rozsahu. Ve filtrech, které používají výraz lambda, any se operátory a all operátory for podobá smyčce ve většině programovacích jazyků, přičemž proměnná rozsahu přebírá roli proměnné smyčky a výraz lambda jako tělo smyčky. Proměnná rozsahu přebírá "aktuální" hodnotu kolekce během iterace smyčky.

Alespoň takto funguje koncepčně. Azure AI Search ve skutečnosti implementuje filtry velmi odlišným způsobem, jak for fungují smyčky. V ideálním případě by tento rozdíl byl pro vás neviditelný, ale v určitých situacích to není. Konečným výsledkem je, že při psaní výrazů lambda musíte dodržovat pravidla.

Poznámka:

Informace o pravidlech pro filtry kolekcí, včetně příkladů, najdete v tématu Řešení potíží s filtry kolekcí OData ve službě Azure AI Search.

Proč jsou filtry kolekcí omezené

Existují tři základní důvody, proč funkce filtru nejsou plně podporované pro všechny typy kolekcí:

  1. Pro určité datové typy jsou podporovány pouze určité operátory. Například nemá smysl porovnávat logické hodnoty true a false používat ltatd gt.
  2. Azure AI Search nepodporuje korelované hledání polí typu Collection(Edm.ComplexType).
  3. Azure AI Search používá invertované indexy ke spouštění filtrů pro všechny typy dat, včetně kolekcí.

První důvod je jen důsledkem toho, jak je definován jazyk OData a systém typů EDM. Poslední dvě jsou podrobněji vysvětleny ve zbytku tohoto článku.

Když u kolekce složitých objektů použijete více kritérií filtru, jsou kritéria korelována, protože se vztahují na každý objekt v kolekci. Následující filtr například vrátí hotely, které mají alespoň jeden pokoj typu Deluxe s sazbou nižší než 100:

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

Pokud filtrování nesouvisí, výše uvedený filtr může vrátit hotely, ve kterých je jeden pokoj deluxe a jiný pokoj má základní sazbu menší než 100. To by nemělo smysl, protože obě klauzule výrazu lambda platí pro stejnou proměnnou rozsahu, konkrétně room. Proto jsou tyto filtry korelované.

Pro fulltextové vyhledávání ale neexistuje způsob, jak odkazovat na konkrétní proměnnou rozsahu. Pokud použijete vyhledávací pole k vydání úplného dotazu Lucene, jako je tento:

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

Můžete získat hotely zpět, kde jeden pokoj je deluxe, a jiný pokoj zmíní "výhled na město" v popisu. Následující dokument by Id 1 například odpovídal dotazu:

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

Důvodem je, že Rooms/Type odkazuje na všechny analyzované termíny Rooms/Type pole v celém dokumentu a podobně pro Rooms/Description, jak je znázorněno v tabulkách níže.

Jak Rooms/Type se ukládá pro fulltextové vyhledávání:

Termín v Rooms/Type ID dokumentů
luxusní 1, 2
standard 0

Jak Rooms/Description se ukládá pro fulltextové vyhledávání:

Termín v Rooms/Description ID dokumentů
nádvoří 2
city 0
zahrada 0
velký 0
motel 2
místnost 1, 2
standard 0
apartmá 0
zobrazit 0

Na rozdíl od výše uvedeného filtru, který v podstatě říká "match documents where a room has Type equal to 'Deluxe Room' and that same room has BaseRate less than 100", the search query říká "match documents where Rooms/Type has the term "deluxe" and Rooms/Description has the phrase "city view". Neexistuje žádný koncept jednotlivých místností, jejichž pole mohou být v druhém případě korelována.

Invertované indexy a kolekce

Možná jste si všimli, že u výrazů lambda ve složitých kolekcích existuje mnohem méně omezení, než je u jednoduchých kolekcí, jako Collection(Edm.Int32)je , Collection(Edm.GeographyPoint)atd. Důvodem je, že Azure AI Search ukládá složité kolekce jako skutečné kolekce vnořených dokumentů, zatímco jednoduché kolekce se vůbec neukládají jako kolekce.

Zvažte například filtrovatelné pole kolekce řetězců, například seasons v indexu pro online prodejce. Některé dokumenty nahrané do tohoto indexu můžou vypadat takto:

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

Hodnoty seasons pole jsou uloženy ve struktuře označované jako invertovaný index, který vypadá nějak takto:

Období ID dokumentů
jaro 1, 2
léto 0
podzim 1, 2
zima 2, 3

Tato datová struktura je navržená tak, aby odpovídala na jednu otázku s velkou rychlostí: Ve kterých dokumentech se daný termín zobrazuje? Odpověď na tuto otázku funguje spíše jako kontrola prosté rovnosti, než smyčka nad kolekcí. Ve skutečnosti to je důvod, proč azure AI Search umožňuje eq pouze jako relační operátor uvnitř výrazu lambda pro any.

Dále se podíváme na to, jak je možné zkombinovat více kontrol rovnosti u stejné proměnné rozsahu s or. Funguje díky algebrě a distribuovatelnosti kvantifikátorů. Tento výraz:

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

odpovídá:

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

a každý ze dvou any dílčích výrazů se dá efektivně spouštět pomocí invertovaného indexu. Díky zákonu negace kvantifikátorů tento výraz:

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

odpovídá:

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

to je důvod, proč je možné použít all s ne a and.

Poznámka:

I když jsou podrobnosti nad rámec tohoto dokumentu, tyto stejné principy se rozšiřují i na testy vzdálenosti a průsečíku pro kolekce geoprostorových bodů . To je důvod, proč v any:

  • geo.intersects nelze negovat.
  • geo.distancemusí být porovnán pomocí nebo ltle
  • výrazy musí být kombinovány s or, nikoli and

Pravidla naopak platí pro all.

Při filtrování kolekcí datových typů, které podporují ltoperátory , gt, lea ge operátory, jako Collection(Edm.Int32) je například, jsou povoleny širší škály výrazů. Konkrétně můžete použít i v , pokud se podkladové porovnávací výrazy zkombinují do porovnání rozsahů pomocí and, které se pak dále zkombinují pomocí or.anyor and Tato struktura logických výrazů se nazývá Disjunktive Normal Form (DNF), jinak se označuje jako "ORS of AND". Naopak výrazy lambda pro all tyto datové typy musí být ve formátu CNF (Conjunctive Normal Form), jinak označované jako AND ORS. Azure AI Search umožňuje taková porovnání rozsahů, protože je může efektivně spouštět pomocí invertovaných indexů, stejně jako může provádět rychlé vyhledávání termínů pro řetězce.

Stručně řečeno, tady jsou pravidla toho, co je povolené ve výrazu lambda:

  • Uvnitř any, pozitivní kontroly jsou vždy povoleny, jako je rovnost, porovnání rozsahů, geo.intersectsnebo geo.distance ve srovnání s lt nebo le (myslet na "blízkost" jako rovnost, pokud jde o kontrolu vzdálenosti).
  • Uvnitř any, or je vždy povoleno. Můžete použít and pouze pro datové typy, které můžou vyjádřit kontroly rozsahu, a pouze v případě, že používáte ORS identifikátorů AND (DNF).
  • Uvnitř alljsou pravidla obrácena. Jsou povoleny pouze záporné kontroly , můžete je použít and vždy a můžete použít or pouze pro kontroly rozsahu vyjádřené jako identifikátory RK (CNF).

V praxi se jedná o typy filtrů, které budete pravděpodobně přesto používat. Přesto je užitečné pochopit hranice toho, co je možné.

Konkrétní příklady toho, které typy filtrů jsou povolené a které nejsou, najdete v tématu Postup zápisu platných filtrů kolekce.

Další kroky