Filtr
Dzięki Qdrantowi można ustawić warunki wyszukiwania lub pobierania punktów, co oznacza możliwość filtrowania według atrybutu, oprócz wyszukiwania podobieństw wektorów, podobnie jak w przypadku ustawiania warunków SQL „where”. Na przykład można ustawić warunki dla ładunku i id
punktu.
Ważne jest ustalenie dodatkowych warunków, gdy nie wszystkie cechy obiektu można wyrazić w osadzie. Na przykład różne wymagania biznesowe, takie jak dostępność magazynowa, lokalizacja użytkownika czy zakres cen.
Warunki Filtracji
Qdrant pozwala łączyć warunki w klauzulach. Klauzule to różne operacje logiczne, takie jak OR
, AND
i NOT
. Klauzule mogą być rekurencyjnie zagnieżdżone w sobie, dzięki czemu można odtworzyć dowolne wyrażenie boolowskie.
Przyjrzyjmy się zaimplementowanym klauzulom w Qdrancie.
Załóżmy, że mamy zbiór punktów z ładunkami:
[
{ "id": 1, "city": "London", "color": "green" },
{ "id": 2, "city": "London", "color": "red" },
{ "id": 3, "city": "London", "color": "blue" },
{ "id": 4, "city": "Berlin", "color": "red" },
{ "id": 5, "city": "Moscow", "color": "green" },
{ "id": 6, "city": "Moscow", "color": "blue" }
]
Musi
Przykład:
POST /kolekcje/{nazwa_kolekcji}/punkty/przewiń
{
"filtr": {
"musi": [
{ "klucz": "city", "pasuje": { "wartość": "London" } },
{ "klucz": "color", "pasuje": { "wartość": "red" } }
]
}
...
}
Przefiltrowany punkt będzie:
[{ "id": 2, "city": "London", "color": "red" }]
Kiedy używamy musi
, klauzula jest prawdziwa tylko wtedy, gdy każdy warunek wymieniony w musi
jest spełniony. W tym sensie musi
jest równoważne operatorowi AND
.
Powinien
Powinien
jest podobny do operatora OR
w SQL.
Przykład:
POST /kolekcje/{nazwa_kolekcji}/punkty/przewiń
{
"filtr": {
"powinien": [
{ "klucz": "city", "pasuje": { "wartość": "London" } },
{ "klucz": "color", "pasuje": { "wartość": "red" } }
]
}
...
}
Przefiltrowane punkty będą:
[
{ "id": 1, "city": "London", "color": "green" },
{ "id": 2, "city": "London", "color": "red" },
{ "id": 3, "city": "London", "color": "blue" },
{ "id": 4, "city": "Berlin", "color": "red" }
]
Kiedy używamy powinien
, klauzula jest prawdziwa, jeśli spełniony jest przynajmniej jeden warunek wymieniony w powinien
. W tym sensie powinien
jest równoważny operatorowi OR
.
nie_musi
Przykład:
POST /kolekcje/{nazwa_kolekcji}/punkty/przewiń
{
"filtr": {
"nie_musi": [
{ "klucz": "city", "pasuje": { "wartość": "London" } },
{ "klucz": "color", "pasuje": { "wartość": "red" } }
]
}
...
}
Przefiltrowane punkty będą:
[
{ "id": 5, "city": "Moscow", "color": "green" },
{ "id": 6, "city": "Moscow", "color": "blue" }
]
Kiedy używamy nie_musi
, podklauzula jest prawdziwa tylko wtedy, gdy żaden z warunków wymienionych w nie_musi
nie jest spełniony. W tym sensie nie_musi
jest równoważne wyrażeniu (NOT A) AND (NOT B) AND (NOT C)
.
Kombinacja Warunków
Jednoczesne stosowanie wielu warunków jest możliwe:
POST /collections/{nazwa_kolekcji}/points/scroll
{
"filter": {
"must": [
{ "key": "city", "match": { "value": "London" } }
],
"must_not": [
{ "key": "color", "match": { "value": "red" } }
]
}
...
}
Przefiltrowane punkty będą:
[
{ "id": 1, "city": "London", "color": "green" },
{ "id": 3, "city": "London", "color": "blue" }
]
W tym przypadku warunki są łączone za pomocą AND
.
Dodatkowo, warunki mogą być zagnieżdżone rekurencyjnie. Na przykład:
POST /collections/{nazwa_kolekcji}/points/scroll
{
"filter": {
"must_not": [
{
"must": [
{ "key": "city", "match": { "value": "London" } },
{ "key": "color", "match": { "value": "red" } }
]
}
]
}
...
}
Przefiltrowane punkty będą:
[
{ "id": 1, "city": "London", "color": "green" },
{ "id": 3, "city": "London", "color": "blue" },
{ "id": 4, "city": "Berlin", "color": "red" },
{ "id": 5, "city": "Moskwa", "color": "green" },
{ "id": 6, "city": "Moskwa", "color": "niebieski" }
]
Filtracja Warunków
W ładunku, różne rodzaje wartości odpowiadają różnym rodzajom zapytań, które można do nich zastosować. Przyjrzyjmy się istniejącym wariantom warunków i typom danych, do których się odnoszą.
Dopasowanie
{
"key": "color",
"match": {
"value": "red"
}
}
Dla innych typów danej, warunki dopasowania wyglądają dokładnie tak samo, tylko zastosowane są inne typy:
{
"key": "count",
"match": {
"value": 0
}
}
Najprostszy warunek sprawdza, czy przechowywana wartość jest równa danej wartości. Jeśli przechowywane są multiple wartości, przynajmniej jedna z nich powinna spełnić warunek. Może to być stosowane do ładunków typu słowo kluczowe, liczbowe i logiczne.
Dowolne Dopasowanie
Dostępne od wersji v1.1.0
Jeśli chcesz sprawdzić, czy przechowywana wartość jest jedną z wielu wartości, można użyć warunku dowolnego dopasowania. Dowolne dopasowanie traktuje daną wartość jako operację logicznego OR. Może to być również opisane jako operator IN
.
Można to zastosować do słów kluczowych i ładunków liczbowych.
Przykład:
{
"key": "color",
"match": {
"any": ["czarny", "żółty"]
}
}
W tym przykładzie, jeśli przechowywana wartość to czarny
lub żółty
, warunek zostanie spełniony.
Jeśli przechowywana wartość jest tablicą, przynajmniej jedna z wartości powinna pasować do któregokolwiek z podanych wartości. Na przykład, jeśli przechowywana wartość to ["czarny", "zielony"]
, wówczas warunek zostanie spełniony, ponieważ "czarny"
jest w ["czarny", "żółty"]
.
Wykluczanie Dopasowania
Dostępne od wersji v1.2.0
Jeśli chcesz sprawdzić, czy przechowywana wartość nie jest żadną z wielu wartości, możesz użyć warunku wykluczającego dopasowanie. Wykluczanie dopasowania traktuje daną wartość jako operację logicznego NOR. Może to być również opisane jako operator NOT IN
.
Można to zastosować do słów kluczowych i ładunków liczbowych.
Przykład:
{
"key": "color",
"match": {
"except": ["czarny", "żółty"]
}
}
W tym przykładzie, jeśli przechowywana wartość nie jest ani czarna
ani żółta
, warunek zostanie spełniony.
Jeśli przechowywana wartość jest tablicą, przynajmniej jedna z wartości nie powinna pasować do żadnej z podanych wartości. Na przykład, jeśli przechowywana wartość to ["czarny", "zielony"]
, wówczas warunek zostanie spełniony, ponieważ "zielony"
nie pasuje do "czarny"
ani "żółty"
.
Zagnieżdżone Klucze
Dostępne od wersji v1.1.0 i nowszych
Ponieważ ładunek jest arbitralnym obiektem JSON, możesz potrzebować filtrować zagnieżdżone pola.
Dla wygody używamy składni podobnej do projektu Jq.
Załóżmy, że mamy zestaw punktów o następującym ładunku:
[
{
"id": 1,
"country": {
"name": "Niemcy",
"cities": [
{
"name": "Berlin",
"population": 3.7,
"atrakcje": ["Brama Brandenburska", "Reichstag"]
},
{
"name": "Monachium",
"population": 1.5,
"atrakcje": ["Rynek Mariacki", "Olympiapark"]
}
]
}
},
{
"id": 2,
"country": {
"name": "Japonia",
"cities": [
{
"name": "Tokio",
"population": 9.3,
"atrakcje": ["Wieża Tokijska", "Tokyo Skytree"]
},
{
"name": "Osaka",
"population": 2.7,
"atrakcje": ["Zamek w Osace", "Universal Studios Japan"]
}
]
}
}
]
Możesz użyć notacji kropkowej do wyszukiwania zagnieżdżonych pól.
POST /kolekcje/{nazwa_kolekcji}/punkty/przewiń
{
"filtr": {
"powinien": [
{
"klucz": "country.name",
"zgodność": {
"wartość": "Niemcy"
}
}
]
}
}
Możesz również użyć notacji [ ]
do wyszukiwania tablicy poprzez wyświetlanie wewnętrznych wartości.
POST /kolekcje/{nazwa_kolekcji}/punkty/przewiń
{
"filtr": {
"powinien": [
{
"klucz": "country.cities[].population",
"zakres": {
"gte": 9.0,
}
}
]
}
}
To zapytanie wyświetla tylko punkt o id 2, ponieważ tylko Japonia ma miasto z populacją większą niż 9.0.
Zagnieżdżone pola mogą także być tablicą.
POST /kolekcje/{nazwa_kolekcji}/punkty/przewiń
{
"filtr": {
"powinien": [
{
"klucz": "country.cities[].atrakcje",
"zgodność": {
"wartość": "Zamek w Osace"
}
}
]
}
}
To zapytanie wyświetla tylko punkt o id 2, ponieważ tylko Japonia ma miasto z punktem widokowym, który zawiera "Zamek w Osace".
Filtracja zagnieżdżonych obiektów
Dostępne od wersji 1.2.0
Domyślnie warunki uwzględniają cały ładunek punktu.
Na przykład, mając dwa punkty w poniższym ładunku:
[
{
"id": 1,
"dinosaur": "t-rex",
"diet": [
{ "food": "liście", "likes": false},
{ "food": "mięso", "likes": true}
]
},
{
"id": 2,
"dinosaur": "diplodocus",
"diet": [
{ "food": "liście", "likes": true},
{ "food": "mięso", "likes": false}
]
}
]
Następujące zapytanie dopasowałoby te dwa punkty:
POST /kolekcje/{nazwa_kolekcji}/punkty/przewiń
{
"filter": {
"must": [
{
"key": "diet[].food",
"match": {
"value": "mięso"
}
},
{
"key": "diet[].likes",
"match": {
"value": true
}
}
]
}
}
Powodem dopasowania powyższych dwóch punktów jest to, że oba spełniają te dwa warunki:
- "t-rex" spełnia
diet[1].food
z food = mięso idiet[1].likes
z likes = true - "diplodocus" spełnia
diet[1].food
z food = mięso idiet[0].likes
z likes = true
Aby uzyskać tylko punkty pasujące do warunków elementów tablicy, na przykład punkt o id 1 w tym przykładzie, należy użyć filtrów zagnieżdżonych obiektów.
Filtry zagnieżdżonych obiektów pozwalają na niezależne zapytanie tablic obiektów.
Można to osiągnąć, używając typu warunku nested
, który składa się z klucza ładunku, na którym zależy oraz zastosowanego filtra.
Klucz powinien wskazywać na tablicę obiektów i opcjonalnie można użyć notacji z nawiasami kwadratowymi ("dane" lub "dane[]").
POST /kolekcje/{nazwa_kolekcji}/punkty/przewiń
{
"filter": {
"must": [
"nested": {
{
"key": "diet",
"filter": {
"must": [
{
"key": "food",
"match": {
"value": "mięso"
}
},
{
"key": "likes",
"match": {
"value": true
}
}
]
}
}
}
]
}
}
Logika dopasowywania jest zmodyfikowana tak, aby działać na poziomie elementu tablicy w ładunku.
Filtr zagnieżdżony działa tak samo jak stosowanie filtra zagnieżdżonego do pojedynczego elementu tablicy. O ile przynajmniej jeden element tablicy pasuje do filtra zagnieżdżonego, dokument nadrzędny jest uważany za pasujący do warunku.
Ograniczenie
Filtry zagnieżdżonych obiektów nie obsługują warunku has_id
. Jeśli chcesz go użyć, umieść go w sąsiedniej klauzuli must
.
POST /kolekcje/{nazwa_kolekcji}/punkty/przewiń
{
"filter": {
"must": [
"nested": {
{
"key": "diet",
"filter": {
"must": [
{
"key": "food",
"match": {
"value": "mięso"
}
},
{
"key": "likes",
"match": {
"value": true
}
}
]
}
}
},
{ "has_id": [1] }
]
}
}
Dokładne dopasowywanie tekstu
Dostępne od wersji 0.10.0
Specjalnym przypadkiem warunku match
jest warunek dopasowywania text
. Pozwala to wyszukać określone podłańcuchy, tokeny lub frazy w polu tekstowym.
Dokładne teksty spełniające ten warunek zależą od konfiguracji indeksu pełnotekstowego. Konfiguracja jest określona podczas tworzenia indeksu i jest opisana wewnątrz indeksu pełnotekstowego.
Jeśli pole nie ma indeksu pełnotekstowego, ten warunek będzie działał na podstawie dokładnego dopasowywania podłańcuchów.
{
"key": "opis",
"match": {
"text": "dobry i tani"
}
}
Jeśli zapytanie ma kilka słów, ten warunek będzie spełniony tylko wtedy, gdy wszystkie słowa pojawią się w tekście.
Zakres
{
"key": "cena",
"range": {
"gt": null,
"gte": 100.0,
"lt": null,
"lte": 450.0
}
}
Warunek range
ustawia możliwy zakres wartości dla przechowywanych danych. Jeśli przechowywane są multiple wartości, przynajmniej jedna wartość powinna spełniać warunek.
Dostępne operacje porównania obejmują:
-
gt
- większe niż -
gte
- większe niż lub równe -
lt
- mniejsze niż -
lte
- mniejsze niż lub równe
Może być stosowany do liczb zmiennoprzecinkowych i całkowitych wartości.
Prostokątne Obszary Geograficzne
{
"key": "lokalizacja",
"geo_bounding_box": {
"bottom_right": {
"lat": 52.495862,
"lon": 13.455868
},
"top_left": {
"lat": 52.520711,
"lon": 13.403683
}
}
}
Pasuje do lokalizacji
wewnątrz prostokąta o współrzędnych w prawym dolnym rogu jako bottom_right
i współrzędnych w lewym górnym rogu jako top_left
.
Obszar Geograficzny o Zadanym Promieniu
{
"key": "lokalizacja",
"geo_radius": {
"center": {
"lat": 52.520711,
"lon": 13.403683
},
"radius": 1000.0
}
}
Pasuje do lokalizacji
wewnątrz koła o środku w center
i promieniu radius
w metrach.
Jeśli przechowywane są multiple wartości, przynajmniej jedna wartość powinna spełniać warunek. Te warunki mogą być stosowane jedynie do payloadów zgodnych z formatem danych geograficznych.
Liczba Wartości
Oprócz bezpośredniego porównywania wartości, filtrowanie może być oparte również na liczbie wartości.
Na przykład, mając poniższe dane:
[
{ "id": 1, "nazwa": "Produkt A", "komentarze": ["Bardzo dobre!", "Doskonałe"] },
{ "id": 2, "nazwa": "Produkt B", "komentarze": ["Przyzwoite", "Oczekiwałem więcej", "Dobre"] }
]
Możemy wyszukiwać jedynie elementy z więcej niż dwoma komentarzami:
{
"key": "komentarze",
"values_count": {
"gt": 2
}
}
Wynikiem będzie:
[{ "id": 2, "nazwa": "Produkt B", "komentarze": ["Przyzwoite", "Oczekiwałem więcej", "Dobre"] }]
Jeśli przechowywana wartość nie jest tablicą, zakłada się, że liczba wartości wynosi 1.
Puste
Czasami przydatne jest filtrowanie rekordów, które nie posiadają pewnych wartości. Warunek IsEmpty
może pomóc w realizacji tego:
{
"is_empty": {
"key": "raporty"
}
}
Ten warunek będzie pasował do wszystkich rekordów, gdzie pole raporty
nie istnieje lub ma wartość null
lub []
.
IsEmpty jest często bardzo przydatne, gdy jest używane w połączeniu z logiczną negacją must_not. W tym przypadku, wybierze wszystkie niepuste wartości.
Null
Warunek match nie może testować wartości NULL
. Musimy użyć warunku IsNull
:
{
"is_null": {
"key": "raporty"
}
}
Ten warunek pasuje do wszystkich rekordów, gdzie pole raporty
istnieje i ma wartość NULL
.
Posiada ID
Ten rodzaj zapytania nie jest związany z payloadami, ale jest bardzo przydatny w określonych sytuacjach. Na przykład, użytkownicy mogą chcieć oznaczyć pewne wyniki wyszukiwania jako nieistotne, lub chcemy wyszukiwać jedynie pomiędzy konkretnymi punktami.
POST /kolekcje/{nazwa_kolekcji}/punkty/przewiń
{
"filter": {
"must": [
{ "has_id": [1,3,5,7,9,11] }
]
}
...
}
Wynikiem filtrowanych punktów będzie:
[
{ "id": 1, "miasto": "Londyn", "kolor": "zielony" },
{ "id": 3, "miasto": "Londyn", "kolor": "niebieski" },
{ "id": 5, "miasto": "Moskwa", "kolor": "zielony" }
]