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 i diet[1].likes z likes = true
  • "diplodocus" spełnia diet[1].food z food = mięso i diet[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" }
]