Payload
Payload
여러분, Qdrant의 강력한 기능 중 하나는 벡터와 함께 추가 정보를 저장할 수 있다는 점이에요. Qdrant 용어로 이 정보를 payload라고 불러요.
Qdrant는 JSON으로 표현할 수 있는 모든 정보를 저장할 수 있게 해줘요.
전형적인 payload 예시를 볼게요:
{
"name": "jacket",
"colors": ["red", "blue"],
"count": 10,
"price": 11.99,
"locations": [
{ "lon": 52.5200, "lat": 13.4050 }
],
"reviews": [
{ "user": "alice", "score": 4 },
{ "user": "bob", "score": 5 }
]
}
Payload 타입
payload를 저장하는 것에 더해, Qdrant는 특정 종류의 값에 기반한 검색도 지원해요. 이 기능은 검색 중 추가 필터로 구현되어, 의미론적 유사성 위에 커스텀 로직을 얹을 수 있게 해줘요.
필터링 중에 Qdrant는 필터링 조건의 타입과 일치하는 값들에 대해 조건을 확인해요. 저장된 값 타입이 필터링 조건과 맞지 않으면 그 조건은 만족하지 않은 것으로 간주돼요.
예를 들어 문자열 데이터에 범위 조건(range)을 적용하면 빈 출력을 얻게 돼요.
하지만 배열(같은 타입의 여러 값)은 조금 다르게 취급돼요. 배열에 필터를 적용하면 배열 안의 값 중 하나라도 조건을 만족하면 조건이 성립돼요.
필터링 과정은 필터링(Filtering) 섹션에서 자세히 다뤄요.
Qdrant가 검색에 지원하는 데이터 타입을 살펴볼게요:
Integer (정수)
integer — -9223372036854775808부터 9223372036854775807까지의 범위를 가진 64비트 정수.
단일 및 여러 integer 값의 예시:
{
"count": 10,
"sizes": [35, 36, 38]
}
Float (실수)
float — 64비트 부동소수점 숫자.
단일 및 여러 float 값의 예시:
{
"price": 11.99,
"ratings": [9.1, 9.2, 9.4]
}
Bool (불리언)
Bool — 이진 값. true 또는 false와 같아요.
단일 및 여러 bool 값의 예시:
{
"is_delivered": true,
"responses": [false, false, true, false]
}
Keyword (키워드)
keyword — 문자열 값.
단일 및 여러 keyword 값의 예시:
{
"name": "Alice",
"friends": ["bob", "eva", "jack"]
}
Geo (지리)
geo — 지리 좌표를 나타내는 데 사용돼요.
단일 및 여러 geo 값의 예시:
{
"location": { "lon": 52.5200, "lat": 13.4050 },
"cities": [
{ "lon": 51.5072, "lat": 0.1276 },
{ "lon": 40.7128, "lat": 74.0060 }
]
}
좌표는 lon(경도)과 lat(위도) 두 필드를 담은 객체로 표현해야 해요.
Datetime
v1.8.0부터 사용 가능
datetime — RFC 3339 형식의 날짜와 시간.
단일 및 여러 datetime 값의 예시:
{
"created_at": "2023-02-08T10:49:00Z",
"updated_at": [
"2023-02-08T13:52:00Z",
"2023-02-21T21:23:00Z"
]
}
다음 형식들이 지원돼요:
"2023-02-08T10:49:00Z"(RFC 3339, UTC)"2023-02-08T11:49:00+01:00"(RFC 3339, 시간대 포함)"2023-02-08T10:49:00"(시간대 없음, UTC로 가정)"2023-02-08T10:49"(시간대·초 없음)"2023-02-08"(날짜만, 자정 가정)
형식에 대한 참고 사항:
T는 공백으로 대체할 수 있어요.T와Z기호는 대소문자를 구분하지 않아요.- 시간대를 지정하지 않으면 항상 UTC로 가정해요.
- 시간대는
±HH:MM,±HHMM,±HH, 또는Z형식이 될 수 있어요. - 초는 소수점 6자리까지 가능하므로,
datetime의 가장 정밀한 단위는 마이크로초예요.
UUID
v1.11.0부터 사용 가능
기본 keyword 타입 외에도 Qdrant는 UUID 값을 저장하는 uuid 타입을 지원해요. 기능적으로는 keyword와 동일하게 동작하며, 내부적으로는 파싱된 UUID 값을 저장해요.
{
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"uuids": [
"550e8400-e29b-41d4-a716-446655440000",
"550e8400-e29b-41d4-a716-446655440001"
]
}
UUID의 문자열 표현(예: 550e8400-e29b-41d4-a716-446655440000)은 36바이트를 차지해요. 하지만 숫자 표현을 사용하면 128비트(16바이트)에 불과해요.
payload가 많은 컬렉션에서는 RAM을 아끼고 검색 성능을 높이기 위해 uuid 인덱스 타입 사용을 권장해요.
payload와 함께 point 만들기
REST API (스키마):
PUT /collections/{collection_name}/points
{
"points": [
{
"id": 1,
"vector": [0.05, 0.61, 0.76, 0.74],
"payload": {"city": "Berlin", "price": 1.99}
},
{
"id": 2,
"vector": [0.19, 0.81, 0.75, 0.11],
"payload": {"city": ["Berlin", "London"], "price": 1.99}
},
{
"id": 3,
"vector": [0.36, 0.55, 0.47, 0.94],
"payload": {"city": ["Berlin", "Moscow"], "price": [1.99, 2.99]}
}
]
}
from qdrant_client import QdrantClient, models
client = QdrantClient(url="http://localhost:6333")
client.upsert(
collection_name="{collection_name}",
points=[
models.PointStruct(
id=1,
vector=[0.05, 0.61, 0.76, 0.74],
payload={
"city": "Berlin",
"price": 1.99,
},
),
models.PointStruct(
id=2,
vector=[0.19, 0.81, 0.75, 0.11],
payload={
"city": ["Berlin", "London"],
"price": 1.99,
},
),
models.PointStruct(
id=3,
vector=[0.36, 0.55, 0.47, 0.94],
payload={
"city": ["Berlin", "Moscow"],
"price": [1.99, 2.99],
},
),
],
)
import { QdrantClient } from "@qdrant/js-client-rest";
const client = new QdrantClient({ host: "localhost", port: 6333 });
client.upsert("{collection_name}", {
points: [
{
id: 1,
vector: [0.05, 0.61, 0.76, 0.74],
payload: { city: "Berlin", price: 1.99 },
},
{
id: 2,
vector: [0.19, 0.81, 0.75, 0.11],
payload: { city: ["Berlin", "London"], price: 1.99 },
},
{
id: 3,
vector: [0.36, 0.55, 0.47, 0.94],
payload: { city: ["Berlin", "Moscow"], price: [1.99, 2.99] },
},
],
});
payload 업데이트
Qdrant의 payload 업데이트는 벡터 메타데이터를 관리하는 다양한 유연한 방법을 제공해요. set payload 메서드는 특정 필드만 업데이트하고 나머지는 유지하는 반면, overwrite 메서드는 전체 payload를 대체해요. 또한 clear payload로 모든 메타데이터를 제거하거나, delete 필드로 나머지에 영향 없이 특정 키만 제거할 수 있어요. 이 옵션들은 동적인 데이터셋에 적응하기 위한 정밀한 제어를 제공해요.
Set payload
point에 주어진 payload 값만 설정해요.
REST API (스키마):
POST /collections/{collection_name}/points/payload
{
"payload": {
"property1": "string",
"property2": "string"
},
"points": [0, 3, 100]
}
client.set_payload(
collection_name="{collection_name}",
payload={
"property1": "string",
"property2": "string",
},
points=[0, 3, 10],
)
client.setPayload("{collection_name}", {
payload: {
property1: "string",
property2: "string",
},
points: [0, 3, 10],
});
Facet (패싯)
v1.10.0부터 사용 가능
필드에 있는 고유 값들의 개수를 세어보고 싶을 때가 있어요. 예를 들어 제품 카탈로그에서 size 필드에 몇 개의 L, S, M이 있는지 알고 싶을 수 있죠. Facet 연산이 바로 그 역할을 해줘요 — 특정 필드의 고유 값별 개수를 반환해요. SQL의 GROUP BY와 비슷하다고 생각하면 돼요.
POST /collections/{collection_name}/facet
{
"key": "size"
}
client.facet(
collection_name="{collection_name}",
key="size",
)
client.facet("{collection_name}", { key: "size" });
응답은 그 필드의 각 고유 값에 대한 개수를 담고 있어요:
{
"response": {
"hits": [
{ "value": "L", "count": 19 },
{ "value": "S", "count": 10 },
{ "value": "M", "count": 5 },
{ "value": "XL", "count": 1 },
{ "value": "XXL", "count": 1 }
]
},
"time": 0.0001
}
결과는 개수 내림차순, 그다음 값 오름차순으로 정렬돼요. 개수가 0이 아닌 값만 반환돼요.
기본적으로 Qdrant가 각 값의 개수를 계산하는 방식은 빠른 결과를 위해 근사치예요. 대부분의 경우 이 정도로 충분하지만, 저장소를 디버깅해야 한다면 exact 매개변수를 사용해 정확한 개수를 얻을 수 있어요.
POST /collections/{collection_name}/facet
{
"key": "size",
"exact": true
}
client.facet(
collection_name="{collection_name}",
key="size",
exact=True,
)
client.facet("{collection_name}", { key: "size", exact: true });