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í:
- Pro určité datové typy jsou podporovány pouze určité operátory. Například nemá smysl porovnávat logické hodnoty
true
afalse
používatlt
atdgt
. - Azure AI Search nepodporuje korelované hledání polí typu
Collection(Edm.ComplexType)
. - 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.
Korelace versus nehodnocené vyhledávání
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.distance
musí být porovnán pomocí nebolt
le
- výrazy musí být kombinovány s
or
, nikoliand
Pravidla naopak platí pro all
.
Při filtrování kolekcí datových typů, které podporují lt
operátory , gt
, le
a 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
.any
or
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.intersects
nebogeo.distance
ve srovnání slt
nebole
(myslet na "blízkost" jako rovnost, pokud jde o kontrolu vzdálenosti). - Uvnitř
any
,or
je vždy povoleno. Můžete použítand
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ř
all
jsou pravidla obrácena. Jsou povoleny pouze záporné kontroly , můžete je použítand
vždy a můžete použítor
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.