Qdrant 로컬 퀵스타트

Qdrant 로컬 퀵스타트 (quickstart)

짧은 예제 하나로 Qdrant를 시작해 볼게요. Python 클라이언트를 사용해 컬렉션(Collection) 만들기 → 데이터 로드 → 기본 검색 쿼리 실행까지 진행해요.

시작하기 전에 시스템에 Docker가 설치되고 실행 중인지 확인해 주세요.

출처: Qdrant 공식 문서 — quickstart

다운로드하고 실행하기

먼저 Dockerhub에서 최신 Qdrant 이미지를 받아요:

docker pull qdrant/qdrant

그다음 서비스를 실행해요:

docker run -p 6333:6333 -p 6334:6334 \
    -v "$(pwd)/qdrant_storage:/qdrant/storage:z" \
    qdrant/qdrant

Windows에서는 로컬 폴더를 마운트하는 대신 이름 있는 Docker 볼륨(named volume)을 만들어야 할 수 있어요.

기본 설정에서는 모든 데이터가 ./qdrant_storage 디렉터리에 저장돼요. 이 디렉터리는 컨테이너와 호스트 머신 양쪽이 볼 수 있는 유일한 디렉터리이기도 해요.

이제 Qdrant에 접근할 수 있어요:

  • REST API: localhost:6333
  • Web UI: localhost:6333/dashboard
  • GRPC API: localhost:6334

클라이언트 초기화하기

from qdrant_client import QdrantClient

client = QdrantClient(url="http://localhost:6333")
import { QdrantClient } from "@qdrant/js-client-rest";

const client = new QdrantClient({ host: "localhost", port: 6333 });
use qdrant_client::Qdrant;

// Rust 클라이언트는 Qdrant의 gRPC 인터페이스를 사용
let client = Qdrant::from_url("http://localhost:6334").build()?;
import "github.com/qdrant/go-client/qdrant"

// Go 클라이언트는 Qdrant의 gRPC 인터페이스를 사용
client, err := qdrant.NewClient(&qdrant.Config{
	Host: "localhost",
	Port: 6334,
})

기본적으로 Qdrant는 암호화나 인증 없이 시작돼요. 즉 네트워크로 머신에 접근할 수 있는 누구나 Qdrant 컨테이너에 접근할 수 있다는 뜻이에요. 인스턴스를 안전하게 보호하는 방법은 Security 문서를 꼭 읽어 주세요.

컬렉션 만들기

모든 벡터 데이터를 저장할 곳은 Qdrant 컬렉션이에요. test_collection이라고 이름 짓고, 벡터를 비교하는 데 dot product 거리 메트릭을 사용할게요.

from qdrant_client.models import Distance, VectorParams

client.create_collection(
    collection_name="test_collection",
    vectors_config=VectorParams(size=4, distance=Distance.DOT),
)
await client.createCollection("test_collection", {
  vectors: { size: 4, distance: "Dot" },
});
use qdrant_client::qdrant::{CreateCollectionBuilder, VectorParamsBuilder};

client
    .create_collection(
        CreateCollectionBuilder::new("test_collection")
            .vectors_config(VectorParamsBuilder::new(4, Distance::Dot)),
    )
    .await?;
import io.qdrant.client.grpc.Collections.Distance;
import io.qdrant.client.grpc.Collections.VectorParams;

client.createCollectionAsync("test_collection",
        VectorParams.newBuilder().setDistance(Distance.Dot).setSize(4).build()).get();
import (
	"context"

	"github.com/qdrant/go-client/qdrant"
)

client.CreateCollection(context.Background(), &qdrant.CreateCollection{
	CollectionName: "test_collection",
	VectorsConfig: qdrant.NewVectorsConfig(&qdrant.VectorParams{
		Size:     4,
		Distance: qdrant.Distance_Dot,
	}),
})

벡터 추가하기

이제 페이로드와 함께 벡터 몇 개를 추가해요. 페이로드(payload) 는 벡터와 함께 저장하려는 다른 데이터예요:

from qdrant_client.models import PointStruct

operation_info = client.upsert(
    collection_name="test_collection",
    wait=True,
    points=[
        PointStruct(id=1, vector=[0.05, 0.61, 0.76, 0.74], payload={"city": "Berlin"}),
        PointStruct(id=2, vector=[0.19, 0.81, 0.75, 0.11], payload={"city": "London"}),
        PointStruct(id=3, vector=[0.36, 0.55, 0.47, 0.94], payload={"city": "Moscow"}),
        PointStruct(id=4, vector=[0.18, 0.01, 0.85, 0.80], payload={"city": "New York"}),
        PointStruct(id=5, vector=[0.24, 0.18, 0.22, 0.44], payload={"city": "Beijing"}),
        PointStruct(id=6, vector=[0.35, 0.08, 0.11, 0.44], payload={"city": "Mumbai"}),
    ],
)

print(operation_info)
const operationInfo = await client.upsert("test_collection", {
  wait: true,
  points: [
    { id: 1, vector: [0.05, 0.61, 0.76, 0.74], payload: { city: "Berlin" } },
    { id: 2, vector: [0.19, 0.81, 0.75, 0.11], payload: { city: "London" } },
    { id: 3, vector: [0.36, 0.55, 0.47, 0.94], payload: { city: "Moscow" } },
    { id: 4, vector: [0.18, 0.01, 0.85, 0.80], payload: { city: "New York" } },
    { id: 5, vector: [0.24, 0.18, 0.22, 0.44], payload: { city: "Beijing" } },
    { id: 6, vector: [0.35, 0.08, 0.11, 0.44], payload: { city: "Mumbai" } },
  ],
});

console.debug(operationInfo);

응답:

operation_id=0 status=<UpdateStatus.COMPLETED: 'completed'>
{ operation_id: 0, status: 'completed' }
operation_id:0  status:Acknowledged

쿼리 실행하기

기본 질문 하나를 던져 볼게요 — 저장된 벡터 중에 쿼리 벡터 [0.2, 0.1, 0.9, 0.7]와 가장 비슷한 것은 뭘까요?

search_result = client.query_points(
    collection_name="test_collection",
    query=[0.2, 0.1, 0.9, 0.7],
    with_payload=False,
    limit=3
).points

print(search_result)
let searchResult = await client.query(
    "test_collection", {
    query: [0.2, 0.1, 0.9, 0.7],
    limit: 3
});

console.debug(searchResult.points);
use qdrant_client::qdrant::QueryPointsBuilder;

let search_result = client
    .query(
        QueryPointsBuilder::new("test_collection")
            .query(vec![0.2, 0.1, 0.9, 0.7])
    )
    .await?;

dbg!(search_result);
import (
	"context"
	"fmt"

	"github.com/qdrant/go-client/qdrant"
)

searchResult, err := client.Query(context.Background(), &qdrant.QueryPoints{
	CollectionName: "test_collection",
	Query:          qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
})
if err != nil {
	panic(err)
}

필터 추가하기

이제 검색에 필터를 적용해요. cityLondon인 포인트만 쿼리 벡터와 비교해 볼게요:

search_result = client.query_points(
    collection_name="test_collection",
    query=[0.2, 0.1, 0.9, 0.7],
    query_filter=Filter(
        must=[FieldCondition(key="city", match=MatchValue(value="London"))]
    ),
    with_payload=True,
    limit=3,
).points

print(search_result)
searchResult = await client.query("test_collection", {
    query: [0.2, 0.1, 0.9, 0.7],
    filter: {
        must: [{ key: "city", match: { value: "London" } }],
    },
    with_payload: true,
    limit: 3,
});

console.debug(searchResult);
use qdrant_client::qdrant::{Condition, Filter, QueryPointsBuilder};

let search_result = client
    .query(
        QueryPointsBuilder::new("test_collection")
            .query(vec![0.2, 0.1, 0.9, 0.7])
            .filter(Filter::must([Condition::matches(
                "city",
                "London".to_string(),
            )]))
            .with_payload(true),
    )
    .await?;

dbg!(search_result);
import (
	"context"
	"fmt"

	"github.com/qdrant/go-client/qdrant"
)

searchResult, err := client.Query(context.Background(), &qdrant.QueryPoints{
	CollectionName: "test_collection",
	Query:          qdrant.NewQuery(0.2, 0.1, 0.9, 0.7),
	Filter: &qdrant.Filter{
		Must: []*qdrant.Condition{
			qdrant.NewMatch("city", "London"),
		},
	},
	WithPayload: qdrant.NewWithPayload(true),
})
if err != nil {
	panic(err)
}

응답:

[
    {
        "id": 2,
        "version": 0,
        "score": 0.871,
        "payload": {
            "city": "London"
        },
        "vector": null
    }
]

실데이터에서 필터 검색을 빠르게 하려면 페이로드 인덱스를 만드는 것을 강력히 권장해요!

방금 벡터 검색을 수행한 거예요. 벡터를 데이터베이스에 로드하고, 자신만의 벡터로 데이터베이스에 쿼리했어요. Qdrant는 가장 가까운 결과를 찾아 유사도 점수를 보여줬어요.

다음 단계

이제 Qdrant가 어떻게 동작하는지 알게 됐어요. Qdrant Cloud 시작도 그만큼 쉬워요. 계정을 만들고 SaaS를 무료로 사용해 보세요. 인프라 유지보수와 소프트웨어 업데이트는 알아서 처리돼요.

벡터 검색의 더 복잡한 예제로 넘어가려면 Tutorials를 읽고, Examples의 도움으로 자신만의 앱을 만들어 보세요.

배포 준비가 되면 Production Checklist를 거쳐 인스턴스가 안전하고 최적화됐는지 확인하세요.

참고: Qdrant는 별도 서버 없이 애플리케이션 안에 내장(embedded)으로도 실행할 수 있어요. Qdrant Edge는 Python과 Rust를 지원하며 프로세스 내부(in-process)에서 직접 실행되는 경량 벡터 검색 엔진이에요.

더 알아보기 (Learn more)