Filtre

Qdrant ile noktaları aramak veya almak için koşullar belirleyebilirsiniz, bu da vektörler için benzerlik aramalarına ek olarak özelliklere göre filtreleme yapabileceğiniz anlamına gelir, SQL where koşullarını ayarlama gibi. Örneğin, payload ve noktanın id almak için koşullar belirleyebilirsiniz.

Bir nesnenin tüm özellikleri bir gömme içinde ifade edilemiyorsa ek koşullar belirlemek önemlidir. Örneğin, envanterin kullanılabilirliği, kullanıcı konumu veya beklenen fiyat aralığı gibi çeşitli iş gereksinimleri.

Filtre Koşulları

Qdrant, koşulları ifadelerde birleştirmenize olanak tanır. İfadeler, VEYA, VE ve DEĞİL gibi farklı mantıksal işlemlerdir. İfadeler birbirleri içinde tekrarlı bir şekilde yerleştirilebilir, böylece herhangi bir boolean ifadeyi yeniden oluşturabilirsiniz.

Qdrant'ta uygulanan ifadelere bir göz atalım.

Örneğin, payload'a sahip bir nokta kümemiz olduğunu varsayalım:

[
  { "id": 1, "city": "Londra", "color": "yeşil" },
  { "id": 2, "city": "Londra", "color": "kırmızı" },
  { "id": 3, "city": "Londra", "color": "mavi" },
  { "id": 4, "city": "Berlin", "color": "kırmızı" },
  { "id": 5, "city": "Moskova", "color": "yeşil" },
  { "id": 6, "city": "Moskova", "color": "mavi" }
]

Gereklidir (Must)

Örnek:

POST /collections/{koleksiyon_adı}/noktalar/gezmek

{
    "filter": {
        "must": [
            { "key": "city", "match": { "value": "Londra" } },
            { "key": "color", "match": { "value": "kırmızı" } }
        ]
    }
  ...
}

Filtrelenen nokta şu olacaktır:

[{ "id": 2, "city": "Londra", "color": "kırmızı" }]

Must kullanılırken, şartlar bölümündeki her koşul karşılandığında ifade sadece o zaman true olur. Bu anlamda, must, VE operatörüne eşittir.

Olmalı (Should)

Should, SQL'deki VEYA operatörüne benzer.

Örnek:

POST /collections/{koleksiyon_adı}/noktalar/gezmek

{
    "filter": {
        "should": [
            { "key": "city", "match": { "value": "Londra" } },
            { "key": "color", "match": { "value": "kırmızı" } }
        ]
    }
  ...
}

Filtrelenen noktalar şunlar olacaktır:

[
  { "id": 1, "city": "Londra", "color": "yeşil" },
  { "id": 2, "city": "Londra", "color": "kırmızı" },
  { "id": 3, "city": "Londra", "color": "mavi" },
  { "id": 4, "city": "Berlin", "color": "kırmızı" }
]

Should kullanılırken, ifade sadece en az bir koşul karşılandığında true olur. Bu anlamda, should, VEYA operatörüne eşittir.

Olmamalı (must_not)

Örnek:

POST /collections/{koleksiyon_adı}/noktalar/gezmek

{
    "filter": {
        "must_not": [
            { "key": "city", "match": { "value": "Londra" } },
            { "key": "color", "match": { "value": "kırmızı" } }
        ]
    }
  ...
}

Filtrelenen noktalar şunlar olacaktır:

[
  { "id": 5, "city": "Moskova", "color": "yeşil" },
  { "id": 6, "city": "Moskova", "color": "mavi" }
]

Must_not kullanılırken, alt ifade sadece hiçbiri karşılandığında true olur. Bu anlamda, must_not, (DEĞİL A) VE (DEĞİL B) VE (DEĞİL C) ifadesine eşittir.

Koşulların Birleştirilmesi

Çoklu koşulların aynı anda kullanılması mümkündür:

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "must": [
            { "key": "city", "match": { "value": "Londra" } }
        ],
        "must_not": [
            { "key": "color", "match": { "value": "kırmızı" } }
        ]
    }
  ...
}

Filtrelenen noktalar şunlar olacaktır:

[
  { "id": 1, "city": "Londra", "color": "yeşil" },
  { "id": 3, "city": "Londra", "color": "mavi" }
]

Bu durumda koşullar VE kullanılarak birleştirilmiştir.

Ayrıca, koşullar tekrarlı olarak iç içe yerleştirilebilir. Örneğin:

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "must_not": [
            {
                "must": [
                    { "key": "city", "match": { "value": "Londra" } },
                    { "key": "color", "match": { "value": "kırmızı" } }
                ]
            }
        ]
    }
  ...
}

Filtrelenen noktalar şunlar olacaktır:

[
  { "id": 1, "city": "Londra", "color": "yeşil" },
  { "id": 3, "city": "Londra", "color": "mavi" },
  { "id": 4, "city": "Berlin", "color": "kırmızı" },
  { "id": 5, "city": "Moskova", "color": "yeşil" },
  { "id": 6, "city": "Moskova", "color": "mavi" }
]

Koşul Filtreleme

Payload içinde, farklı türdeki değerler, bunlara uygulanabilecek farklı türde sorgulara karşılık gelir. Mevcut koşul varyantlarına ve bunların uygulandığı veri tiplerine bir göz atalım.

Eşleştirme

{
  "key": "color",
  "match": {
    "value": "kırmızı"
  }
}

Diğer türler için, eşleştirme koşulları tamamen aynı görünür, sadece farklı türler kullanılır:

{
  "key": "count",
  "match": {
    "value": 0
  }
}

En basit koşul, depolanan değerin verilen değere eşit olup olmadığını kontrol eder. Birden fazla değer depolanmışsa, en az bir tanesi koşulu karşılamalıdır. Bu, anahtar kelime, tamsayı ve mantıksal yüklemelere uygulanabilir.

Herhangi Eşleşme

V1.1.0'dan itibaren kullanılabilir

Depolanan değerin birden fazla değerden biri olup olmadığını kontrol etmek istiyorsanız, herhangi eşleşme koşulunu kullanabilirsiniz. Herhangi eşleşme, verilen değeri mantıksal VEYA işlemi olarak ele alır. Aynı zamanda IN operatörü olarak da tanımlanabilir.

Bu, anahtar kelime ve tamsayı yüklemelere uygulanabilir.

Örnek:

{
  "key": "color",
  "match": {
    "any": ["siyah", "sarı"]
  }
}

Bu örnekte, depolanan değer siyah ya da sarı ise, koşul karşılanacaktır.

Eğer depolanan değer bir dizi ise, verilen değerlerden herhangi biriyle eşleşen en az bir değere sahip olmalıdır. Örneğin, depolanan değer ["siyah", "yeşil"] ise, koşul karşılanacaktır çünkü "siyah", ["siyah", "sarı"] içindedir.

Hariç Eşleşme

V1.2.0'dan itibaren kullanılabilir

Depolanan değerin birden fazla değerden hiçbiri olup olmadığını kontrol etmek istiyorsanız, hariç eşleşme koşulunu kullanabilirsiniz. Hariç eşleşme, verilen değeri mantıksal DEĞİL VEYA işlemi olarak ele alır. Aynı zamanda NOT IN operatörü olarak da tanımlanabilir.

Bu, anahtar kelime ve tamsayı yüklemelere uygulanabilir.

Örnek:

{
  "key": "color",
  "match": {
    "except": ["siyah", "sarı"]
  }
}

Bu örnekte, depolanan değer ne siyah ne de sarı ise, koşul karşılanacaktır.

Eğer depolanan değer bir dizi ise, verilen değerlerden herhangi biriyle eşleşmeyen en az bir değere sahip olmalıdır. Örneğin, depolanan değer ["siyah", "yeşil"] ise, koşul karşılanacaktır çünkü "yeşil", "siyah" veya "sarı" ile eşleşmez.

İç İçe Anahtarlar

V1.1.0'dan itibaren kullanılabilir

Payload'in keyfi bir JSON nesnesi olduğu için iç içe geçmiş alanları filtrelemeniz gerekebilir.

Kolaylık sağlamak için, Jq projesine benzer bir sözdizimi kullanıyoruz.

Aşağıdaki payload'a sahip bir puan setimiz olduğunu varsayalım:

[
  {
    "id": 1,
    "country": {
      "name": "Almanya",
      "cities": [
        {
          "name": "Berlin",
          "population": 3.7,
          "sightseeing": ["Brandenburg Kapısı", "Reichstag"]
        },
        {
          "name": "Münih",
          "population": 1.5,
          "sightseeing": ["Marienplatz", "Olympiapark"]
        }
      ]
    }
  },
  {
    "id": 2,
    "country": {
      "name": "Japonya",
      "cities": [
        {
          "name": "Tokyo",
          "population": 9.3,
          "sightseeing": ["Tokyo Kulesi", "Tokyo Skytree"]
        },
        {
          "name": "Osaka",
          "population": 2.7,
          "sightseeing": ["Osaka Kalesi", "Universal Studios Japonya"]
        }
      ]
    }
  }
]

Nokta notasyonunu kullanarak iç içe geçmiş alanları arayabilirsiniz.

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "should": [
            {
                "key": "country.name",
                "match": {
                    "value": "Almanya"
                }
            }
        ]
    }
}

Ayrıca [ ] sözdizimini kullanarak iç içe diziyi içindeki değerleri projelendirebilirsiniz.

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "should": [
            {
                "key": "country.cities[].population",
                "range": {
                    "gte": 9.0,
                }
            }
        ]
    }
}

Bu sorgu, yalnızca 2 numaralı noktayı çıktı olarak verir, çünkü sadece Japonya'nın nüfusu 9.0'dan büyük bir şehri vardır.

İç içe alanlar aynı zamanda bir dizi olabilir.

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "should": [
            {
                "key": "country.cities[].sightseeing",
                "match": {
                    "value": "Osaka Kalesi"
                }
            }
        ]
    }
}

Bu sorgu, sadece 2 numaralı noktayı çıktı olarak verir, çünkü sadece Japonya'nın "Osaka Kalesi"ni içeren bir şehri vardır.

İç İçe Geçmiş Nesne Filtreleme

1.2.0 sürümünden itibaren kullanılabilir

Varsayılan olarak, koşullar bir noktanın tüm veri yükünü dikkate alır.

Örneğin, aşağıdaki veri yükündeki iki nokta verildiğinde:

[
  {
    "id": 1,
    "dinosaur": "t-rex",
    "diet": [
      { "food": "leaves", "likes": false},
      { "food": "meat", "likes": true}
    ]
  },
  {
    "id": 2,
    "dinosaur": "diplodocus",
    "diet": [
      { "food": "leaves", "likes": true},
      { "food": "meat", "likes": false}
    ]
  }
]

Aşağıdaki sorgu, bu iki noktayı eşleştirir:

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "must": [
            {
                "key": "diet[].food",
                  "match": {
                    "value": "meat"
                }
            },
            {
                "key": "diet[].likes",
                  "match": {
                    "value": true
                }
            }
        ]
    }
}

Yukarıdaki iki noktanın eşleştirilmesinin nedeni, her ikisinin de aşağıdaki iki koşulu karşılamasıdır:

  • "t-rex", diet[1].food ile food = meat ve diet[1].likes ile likes = true koşullarını karşılar
  • "diplodocus", diet[1].food ile food = meat ve diet[0].likes ile likes = true koşullarını karşılar

Örneğin bu örnekte id'si 1 olan noktaların koşulları karşılayan noktaları yalnızca elde etmek için iç içe geçmiş nesne filtrelerini kullanmanız gerekmektedir.

İç içe geçmiş nesne filtreleri, nesne dizilerini bağımsız olarak sorgulamayı sağlar.

Bu, ilgi alanı anahtarını ve uygulanacak filtreleri içeren nested koşul türünü kullanarak başarılabilmektedir.

Anahtar, bir nesne dizisine işaret etmeli ve opsiyonel olarak köşeli parantez notasyonu ("veri" veya "veri[]") kullanabilir.

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "must": [
            "nested": {
                {
                    "key": "diet",
                    "filter": {
                        "must": [
                            {
                                "key": "food",
                                "match": {
                                    "value": "meat"
                                }
                            },
                            {
                                "key": "likes",
                                "match": {
                                    "value": true
                                }
                            }
                        ]
                    }
                }
            }
        ]
    }
}

Eşleşme mantığı, yük içindeki dizi öğe seviyesinde uygulanacak şekilde değiştirilmiştir.

İç içe geçmiş filtre, bir dizi öğesine iç içe geçmiş bir filtre uygulandığında aynı şekilde çalışır. Dizi öğelerinden en az birinin iç içe geçmiş filtreyi karşıladığı sürece, ana belge koşulu karşılandığı kabul edilir.

Sınırlama

İç içe geçmiş nesne filtreleri, has_id koşulunu desteklemez. Onu kullanmanız gerekiyorsa, yan yana bir must kalıbına yerleştirin.

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "must": [
            "nested": {
                {
                    "key": "diet",
                    "filter": {
                        "must": [
                            {
                                "key": "food",
                                "match": {
                                    "value": "meat"
                                }
                            },
                            {
                                "key": "likes",
                                "match": {
                                    "value": true
                                }
                            }
                        ]
                    }
                }
            },
            { "has_id": [1] }
        ]
    }
}

Tam Metin Eşleşmesi

0.10.0 sürümünden itibaren kullanılabilir

match koşulunun özel bir durumu, text eşleme koşuludur. Metin alanı içinde belirli alt dizgileri, belirteçleri veya ifadeleri aramanıza olanak tanır.

Bu koşulu karşılayan tam metinler, tam metin dizininin yapılandırmasına bağlıdır. Yapılandırma, dizin oluşturulurken tanımlanır ve tam metin dizini içinde açıklanmıştır.

Eğer alanda tam metin dizini yoksa, bu koşul tam alt dizgi eşleştirmesine dayalı olarak çalışacaktır.

{
  "key": "description",
  "match": {
    "text": "iyi ve ucuz"
  }
}

Sorguda birden fazla kelime varsa, bu koşul, tüm kelimelerin metinde göründüğü zaman yalnızca karşılanmış olacaktır.

Aralık

{
  "key": "price",
  "range": {
    "gt": null,
    "gte": 100.0,
    "lt": null,
    "lte": 450.0
  }
}

range koşulu, depolanan verilerin olası değer aralığını belirler. Birden fazla değer depolanmışsa, en az bir değer koşulu karşılamalıdır.

Kullanılabilir karşılaştırma operasyonları şunları içerir:

  • gt - büyük
  • gte - büyük veya eşit
  • lt - küçük
  • lte - küçük veya eşit

Bu, kesirli sayılar ve tam sayılı payloadlara uygulanabilir.

Coğrafi Sınırlı Kutu

{
  "key": "location",
  "geo_bounding_box": {
    "bottom_right": {
      "lat": 52.495862,
      "lon": 13.455868
    },
    "top_left": {
      "lat": 52.520711,
      "lon": 13.403683
    }
  }
}

Bu, bottom_right olarak koordinatları olan dikdörtgen içindeki locationtop_left olarak eşleştirir.

Coğrafi Yarıçap

{
  "key": "location",
  "geo_radius": {
    "center": {
      "lat": 52.520711,
      "lon": 13.403683
    },
    "radius": 1000.0
  }
}

Bu, merkezi center olan ve radius metre yarıçaplı dairenin içindeki location'ı eşleştirir.

Birden fazla değer depolanmışsa, en az bir değer koşulu karşılamalıdır. Bu koşullar yalnızca coğrafi veri formatına uyan payloadlara uygulanabilir.

Değer Sayısı

Doğrudan değer karşılaştırmasının yanı sıra, filtreleme aynı zamanda değer sayısına göre de olabilir.

Örneğin, aşağıdaki veriler verildiğinde:

[
  { "id": 1, "name": "Ürün A", "comments": ["Çok iyi!", "Mükemmel"] },
  { "id": 2, "name": "Ürün B", "comments": ["Orta", "Daha fazlasını bekliyorum", "İyi"] }
]

Sadece iki yorumdan fazlası olan öğeleri arayabiliriz:

{
  "key": "comments",
  "values_count": {
    "gt": 2
  }
}

Sonuç şu olacaktır:

[{ "id": 2, "name": "Ürün B", "comments": ["Orta", "Daha fazlasını bekliyorum", "İyi"] }]

Depolanan değer bir dizi değilse, değer sayısının 1'e eşit olduğu varsayılır.

Boş mu?

Bazı durumlarda, belirli değerlere sahip olmayan kayıtları filtrelemek faydalı olabilir. IsEmpty koşulu bunu başarmanıza yardımcı olabilir:

{
  "is_empty": {
    "key": "raporlar"
  }
}

Bu koşul, raporlar alanı mevcut değilse veya bir null veya [] değerine sahipse tüm kayıtları eşleştirir.

IsEmpty, mantıksal inkar must_not ile birlikte kullanıldığında genellikle çok faydalıdır. Bu durumda, tüm dolu olmayan değerleri seçecektir.

Null mu?

match koşulu, NULL değerlerini test edemez. IsNull koşulunu kullanmalıyız:

{
    "is_null": {
        "key": "raporlar"
    }
}

Bu koşul, raporlar alanı varsa ve bir NULL değerine sahipse tüm kayıtları eşleştirir.

ID Var mı?

Bu tür sorgu, payloadlarla ilgili olmayıp, belirli durumlarda çok kullanışlıdır. Örneğin, kullanıcılar belirli arama sonuçlarını önemsiz olarak etiketlemek isteyebilir veya belirli noktalar arasında arama yapmak isteyebiliriz.

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "must": [
            { "has_id": [1,3,5,7,9,11] }
        ]
    }
  ...
}

Filtrelenmiş noktalar:

[
  { "id": 1, "şehir": "Londra", "renk": "yeşil" },
  { "id": 3, "şehir": "Londra", "renk": "mavi" },
  { "id": 5, "şehir": "Moskova", "renk": "yeşil" }
]