필터

Qdrant를 사용하면 점을 검색하거나 검색 조건을 설정할 수 있습니다. 이는 벡터의 유사성 검색과 마찬가지로 속성으로 필터링할 수 있음을 의미합니다. 예를 들어, 페이로드 및 포인트의 id에 대한 조건을 설정할 수 있습니다.

객체의 모든 기능을 임베딩으로 표현할 수 없는 경우 추가적인 조건을 설정하는 것이 중요합니다. 예를 들어, 재고 가용성, 사용자 위치 또는 예상 가격 범위와 같은 다양한 비즈니스 요구 사항이 있을 수 있습니다.

필터 조건

Qdrant를 사용하면 절에서 조건을 결합할 수 있습니다. 절은 OR, AND, NOT와 같은 다른 논리 연산입니다. 절은 서로 재귀적으로 중첩될 수 있으므로 모든 부울 표현식을 재생성할 수 있습니다.

Qdrant에서 구현된 절을 살펴보겠습니다.

가정해 보겠습니다. 페이로드를 포함하는 포인트 집합이 있다고 가정해 봅시다:

[
  { "id": 1, "city": "런던", "color": "녹색" },
  { "id": 2, "city": "런던", "color": "빨간색" },
  { "id": 3, "city": "런던", "color": "파란색" },
  { "id": 4, "city": "베를린", "color": "빨간색" },
  { "id": 5, "city": "모스크바", "color": "녹색" },
  { "id": 6, "city": "모스크바", "color": "파란색" }
]

반드시

예시:

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "must": [
            { "key": "city", "match": { "value": "런던" } },
            { "key": "color", "match": { "value": "빨간색" } }
        ]
    }
  ...
}

필터링된 포인트는 다음과 같습니다:

[{ "id": 2, "city": "런던", "color": "빨간색" }]

must를 사용할 때는 must에 나열된 각 조건이 만족되는 경우에만 절이 true입니다. 이런 의미에서 mustAND 연산자와 동등합니다.

해야 함

should는 SQL의 OR 연산자와 비슷합니다.

예시:

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "should": [
            { "key": "city", "match": { "value": "런던" } },
            { "key": "color", "match": { "value": "빨간색" } }
        ]
    }
  ...
}

필터링된 포인트는 다음과 같습니다:

[
  { "id": 1, "city": "런던", "color": "녹색" },
  { "id": 2, "city": "런던", "color": "빨간색" },
  { "id": 3, "city": "런던", "color": "파란색" },
  { "id": 4, "city": "베를린", "color": "빨간색" }
]

should를 사용할 때는 should에 나열된 조건 중 하나라도 만족되는 경우 절이 true입니다. 이런 의미에서 shouldOR 연산자와 동등합니다.

반드시하지 말아야 함

예시:

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "must_not": [
            { "key": "city", "match": { "value": "런던" } },
            { "key": "color", "match": { "value": "빨간색" } }
        ]
    }
  ...
}

필터링된 포인트는 다음과 같습니다:

[
  { "id": 5, "city": "모스크바", "color": "녹색" },
  { "id": 6, "city": "모스크바", "color": "파란색" }
]

must_not를 사용할 때는 must_not에 나열된 조건이 하나도 만족되지 않을 때 절이 true입니다. 이런 의미에서 must_not는 식 (NOT A) AND (NOT B) AND (NOT C)와 동등합니다.

조건의 결합

동시에 여러 조건을 사용하는 것이 가능합니다:

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "must": [
            { "key": "city", "match": { "value": "런던" } }
        ],
        "must_not": [
            { "key": "color", "match": { "value": "빨간색" } }
        ]
    }
  ...
}

필터링된 포인트는 다음과 같을 것입니다:

[
  { "id": 1, "city": "런던", "color": "초록" },
  { "id": 3, "city": "런던", "color": "파랑" }
]

이 경우, 조건들은 AND를 사용하여 결합됩니다.

또한, 조건들은 재귀적으로 중첩될 수 있습니다. 예를 들어:

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "must_not": [
            {
                "must": [
                    { "key": "city", "match": { "value": "런던" } },
                    { "key": "color", "match": { "value": "빨간색" } }
                ]
            }
        ]
    }
  ...
}

필터링된 포인트는 다음과 같을 것입니다:

[
  { "id": 1, "city": "런던", "color": "초록" },
  { "id": 3, "city": "런던", "color": "파랑" },
  { "id": 4, "city": "베를린", "color": "빨간색" },
  { "id": 5, "city": "모스크바", "color": "초록" },
  { "id": 6, "city": "모스크바", "color": "파랑" }
]

조건 필터링

페이로드에서 다른 유형의 값들은 그들에 적용할 수 있는 다양한 유형의 쿼리들과 대응됩니다. 기존의 조건 변형과 그들이 적용되는 데이터 유형을 살펴봅시다.

일치

{
  "key": "색깔",
  "match": {
    "value": "빨간색"
  }
}

다른 유형들에 대해서도, 일치하는 조건들은 마찬가지로 동일하게 보입니다. 사용되는 유형만 다릅니다:

{
  "key": "수량",
  "match": {
    "value": 0
  }
}

가장 간단한 조건은 저장된 값이 주어진 값과 동일한지 확인하는 것입니다. 여러 값들이 저장되어 있다면, 그 중 적어도 하나는 조건을 만족해야 합니다. 이는 키워드, 정수, 그리고 부울 페이로드에 적용될 수 있습니다.

아무 일치

v1.1.0부터 사용 가능

만약 저장된 값이 여러 값 중 하나인지 확인하고 싶다면, 아무 일치 조건을 사용할 수 있습니다. 아무 일치는 주어진 값을 논리 OR 연산으로 취급합니다. 또한 IN 연산자로 설명될 수 있습니다.

이 조건은 키워드와 정수 페이로드에 적용될 수 있습니다.

예시:

{
  "key": "색깔",
  "match": {
    "any": ["검정색", "노란색"]
  }
}

이 예시에서, 만약 저장된 값이 검정색 또는 노란색이라면 조건이 만족될 것입니다.

만약 저장된 값이 배열이라면, 적어도 하나의 값이 주어진 값들 중 아무 값과 일치해야 합니다. 예를 들어, 만약 저장된 값이 ["검정색", "초록색"]이라면, 조건이 만족될 것입니다. 왜냐하면 "검정색"["검정색", "노란색"] 안에 있기 때문입니다.

제외 일치

v1.2.0부터 사용 가능

만약 저장된 값이 여러 값 중 어느 것도 아닌지 확인하고 싶다면, 제외 일치 조건을 사용할 수 있습니다. 제외 일치는 주어진 값을 논리 NOR 연산으로 취급합니다. 또한 NOT IN 연산자로 설명될 수 있습니다.

이 조건은 키워드와 정수 페이로드에 적용될 수 있습니다.

예시:

{
  "key": "색깔",
  "match": {
    "except": ["검정색", "노란색"]
  }
}

이 예시에서, 만약 저장된 값이 검정색 또는 노란색이 아니라면 조건이 만족될 것입니다.

만약 저장된 값이 배열이라면, 주어진 값들 중 어느 값과도 일치하지 않는 값을 적어도 하나 갖고 있어야 합니다. 예를 들어, 만약 저장된 값이 ["검정색", "초록색"]이라면, 조건이 만족될 것입니다. 왜냐하면 "초록색""검정색" 또는 "노란색"과 일치하지 않기 때문입니다.

중첩된 키

v1.1.0부터 사용 가능

페이로드가 임의의 JSON 객체이므로 중첩된 필드를 필터링해야 할 수도 있습니다.

편의를 위해, Jq 프로젝트와 유사한 구문을 사용합니다.

다음과 같이 포인트 세트가 있는 경우를 가정해 봅시다.

[
  {
    "id": 1,
    "country": {
      "name": "독일",
      "cities": [
        {
          "name": "베를린",
          "population": 3.7,
          "sightseeing": ["브란덴부르크 문", "라이히스타그"]
        },
        {
          "name": "뮌헨",
          "population": 1.5,
          "sightseeing": ["마리엔플라츠", "올림픽 공원"]
        }
      ]
    }
  },
  {
    "id": 2,
    "country": {
      "name": "일본",
      "cities": [
        {
          "name": "도쿄",
          "population": 9.3,
          "sightseeing": ["도쿄 타워", "도쿄 스카이트리"]
        },
        {
          "name": "오사카",
          "population": 2.7,
          "sightseeing": ["오사카 성", "유니버설 스튜디오 재팬"]
        }
      ]
    }
  }
]

중첩된 필드를 검색하기 위해 점 표기법을 사용할 수 있습니다.

POST /collections/{collection_name}/points/scroll

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

내부 값으로 배열을 투영하여 [ ] 구문을 사용할 수도 있습니다.

POST /collections/{collection_name}/points/scroll

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

이 쿼리는 인구가 9.0 이상인 도시가 일본 뿐이므로 id가 2인 포인트만 출력합니다.

중첩된 필드는 배열일 수도 있습니다.

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "should": [
            {
                "key": "country.cities[].sightseeing",
                "match": {
                    "value": "오사카 성"
                }
            }
        ]
    }
}

이 쿼리는 "오사카 성"을 포함하는 관광 명소가 있는 도시가 일본 뿐이므로 id가 2인 포인트만 출력합니다.

중첩된 객체 필터링

버전 1.2.0부터 사용 가능

기본적으로 조건은 점의 전체 데이터를 고려합니다.

예를 들어, 아래 페이로드에 두 개의 점이 제공된 경우:

[
  {
    "id": 1,
    "dinosaur": "티라노사우루스",
    "식사": [
      { "음식": "나뭇잎", "좋아함": false},
      { "음식": "고기", "좋아함": true}
    ]
  },
  {
    "id": 2,
    "dinosaur": "디플로도쿠스",
    "식사": [
      { "음식": "나뭇잎", "좋아함": true},
      { "음식": "고기", "좋아함": false}
    ]
  }
]

다음 쿼리는 위의 두 점과 일치합니다:

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "must": [
            {
                "key": "식사[].음식",
                  "match": {
                    "value": "고기"
                }
            },
            {
                "key": "식사[].좋아함",
                  "match": {
                    "value": true
                }
            }
        ]
    }
}

위의 두 점이 일치하는 이유는 다음 두 조건을 모두 만족하기 때문입니다:

  • "티라노사우루스"는 음식 = 고기 및 좋아함 = true 조건을 만족합니다.
  • "디플로도쿠스"는 음식 = 고기 및 좋아함 = true 조건을 만족합니다.

예를 들어, 이 예제에서 id 1인 점과 일치하는 점만 얻으려면 중첩된 객체 필터를 사용해야 합니다.

중첩된 객체 필터를 사용하면 개별적으로 객체 배열을 쿼리할 수 있습니다.

이는 관심 페이로드 키와 적용할 필터로 구성된 nested 조건 유형을 사용하여 달성할 수 있습니다.

키는 객체 배열을 가리켜야 하며 선택적으로 괄호 표기법("data" 또는 "data[]")을 사용할 수 있습니다.

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "must": [
            "nested": {
                {
                    "key": "식사",
                    "filter": {
                        "must": [
                            {
                                "key": "음식",
                                "match": {
                                    "value": "고기"
                                }
                            },
                            {
                                "key": "좋아함",
                                "match": {
                                    "value": true
                                }
                            }
                        ]
                    }
                }
            }
        ]
    }
}

일치 로직은 페이로드 내 배열 요소 수준에 적용되도록 수정되었습니다.

중첩된 필터는 배열의 단일 요소에 중첩된 필터를 적용할 때와 마찬가지로 작동합니다. 배열의 하나 이상의 요소가 중첩된 필터와 일치하면 부모 문서는 조건을 만족하는 것으로 간주됩니다.

제한

중첩된 객체 필터는 has_id 조건을 지원하지 않습니다. 필요한 경우 인접한 must 절에 넣으십시오.

POST /collections/{collection_name}/points/scroll

{
    "filter": {
        "must": [
            "nested": {
                {
                    "key": "식사",
                    "filter": {
                        "must": [
                            {
                                "key": "음식",
                                "match": {
                                    "value": "고기"
                                }
                            },
                            {
                                "key": "좋아함",
                                "match": {
                                    "value": true
                                }
                            }
                        ]
                    }
                }
            },
            { "has_id": [1] }
        ]
    }
}

정확한 텍스트 일치

버전 0.10.0부터 사용 가능

match 조건의 특수한 경우는 text 일치 조건입니다. 이를 사용하면 텍스트 필드 내에서 특정한 부분 문자열, 토큰 또는 구를 검색할 수 있습니다.

이 조건을 만족하는 정확한 텍스트는 전체 텍스트 인덱스의 구성에 따라 다릅니다. 구성은 인덱스가 생성될 때 정의되며 전체 텍스트 인덱스 내에서 설명됩니다.

만일 필드에 전체 텍스트 인덱스가 없는 경우, 이 조건은 정확한 부분 문자열 일치를 기반으로 작동합니다.

{
  "key": "description",
  "match": {
    "text": "좋고 저렴한"
  }
}

쿼리에 여러 단어가 있는 경우, 이 조건은 모든 단어가 텍스트에 나타날 때에만 충족됩니다.

범위

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

range 조건은 저장된 페이로드의 가능한 값 범위를 설정합니다. 여러 값이 저장되어 있으면, 적어도 하나의 값이 조건과 일치해야 합니다.

사용 가능한 비교 연산에는 다음이 포함됩니다:

  • gt -보다 큼
  • gte - 크거나 같음
  • lt -보다 작음
  • lte - 작거나 같음

이는 부동 소수점 숫자 및 정수 페이로드에 적용할 수 있습니다.

지리적 경계 상자

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

geo_bounding_boxbottom_right로 지정된 좌표를 우하 꼭지점, top_left로 지정된 좌표를 좌상 꼭지점으로 하는 직사각형 내의 location과 일치합니다.

지리적 반경

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

geo_radius는 중심이 center이고 반지름이 radius 미터인 원 내의 location과 일치합니다.

여러 값이 저장되어 있으면, 적어도 하나의 값이 조건과 일치해야 합니다. 이러한 조건은 지리적 데이터 형식과 일치하는 페이로드에만 적용됩니다.

값 수

직접 값 비교 외에도 필터링은 값의 개수에 기반할 수 있습니다.

예를 들어, 다음 데이터가 주어진 경우:

[
  { "id": 1, "name": "제품 A", "comments": ["매우 좋음!", "훌륭함"] },
  { "id": 2, "name": "제품 B", "comments": ["괜찮음", "기대 이상", "좋음"] }
]

두 개 이상의 코멘트가 있는 항목만 검색할 수 있습니다:

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

결과는 다음과 같습니다:

[{ "id": 2, "name": "제품 B", "comments": ["괜찮음", "기대 이상", "좋음"] }]

저장된 값이 배열이 아닌 경우 값 수가 1인 것으로 간주됩니다.

비어 있는지

가끔, 특정 값을 가지지 않는 레코드를 필터링하는 것이 유용할 때가 있습니다. IsEmpty 조건을 사용하여 이를 달성할 수 있습니다:

{
  "is_empty": {
    "key": "reports"
  }
}

이 조건은 reports 필드가 존재하지 않거나 값이 null 또는 []인 모든 레코드와 일치합니다.

IsEmpty는 때때로 논리적 부정 must_not와 함께 사용될 때 매우 유용합니다. 이 경우 비어 있지 않은 모든 값을 선택합니다.

Null인지

match 조건은 NULL 값을 테스트할 수 없습니다. 따라서 IsNull 조건을 사용해야 합니다:

{
    "is_null": {
        "key": "reports"
    }
}

이 조건은 reports 필드가 존재하고 값이 NULL인 모든 레코드와 일치합니다.

ID가 있는지

이 유형의 쿼리는 페이로드와 관련이 없지만, 일부 상황에서 매우 유용합니다. 예를 들어 사용자는 특정 검색 결과를 관련 없는 것으로 태그하거나 특정 지점 사이에서만 검색하길 원할 수 있습니다.

POST /collections/{collection_name}/points/scroll

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

필터링된 지점은 다음과 같습니다:

[
  { "id": 1, "city": "런던", "color": "초록" },
  { "id": 3, "city": "런던", "color": "파랑" },
  { "id": 5, "city": "모스크바", "color": "초록" }
]