これは、なにをしたくて書いたもの?
前に少し、ベクトルデータベースにはどのようなものがあるのか調べてみました。
ベクトルデータベースってどういうものがある? - CLOVER🍀
そろそろ、この中からなにか選んで、ベクトルデータベースというものを試してみようかなと思いまして。
Qdrantを試してみることにしました。
Qdrant
QdrantのWebサイトはこちら。
Webサイトのトップページによると、特徴は以下のようです。
- OpenAPI 3仕様に沿ったドキュメントを提供し、多くのプログラミング言語に対するクライアントを作成可能
- ANN Search(Approximate Nearest Neighbor Search)のためのHNSWアルゴリズムを独自にカスタム実装
- ベクトルに関連付けられたペイロードの追加をサポートし、ペイロードを保存するだけではなくペイロードの値に基づいて結果をフィルタリングすることも可能
- 文字列一致、数値範囲、地理的位置などを含む、様々なデータ型とクエリーをサポート
- 分散型で、水平スケーリングが可能
- Rustで実装されており、リソースを有効活用できる
Qdrantを使った検索をどのような分野で使うとよさそうなのかは、こちらのページを見るのがよいでしょう。
Vector Search Solutions - Qdrant
類似画像検索、セマンティックテキスト検索、レコメンデーション、チャットボット、マッチングエンジン、異常検知などが
挙げられています。
ドキュメントはこちらです。
最初に読むのはこちらのページでしょうか。
ざっくり見てみましょう。
- Qdrantは、ペイロードの追加と保存、検索、管理に便利なAPIを提供するプロダクションレディなベクトル類似性検索エンジン
- ベクトルデータベースは、高次元ベクトルを効率的に保存、クエリーできるように設計されたデータベースの一種
- ベクトルデータベースでは、ベクトルにIDとペイロードを加えたものを収集する
- ここでいう「ベクトル」とはオブジェクトまたはデータポイントの数学的表現を表す
- ベクトルデータベースでは、距離メトリクスに基づいてのクエリーベクトルに最も近いベクトルを見つけることで、高速な類似性検索とセマンティック検索が行えるようになる
- 一般的に使用される距離メトリクスは、ユークリッド距離、コサイン類似度、ドット積の3つであり、Qdrantではすべてサポートしている
- Qdrantの主な概念
- コレクション … 検索できる名前付きのポイント(ペイロードを含むベクトル)の集合
- 距離メトリクス … ベクトル間の類似性を測るのに使用され、コレクションの作成時に指定する
- ポイント … Qdrantが操作する中心的なエンティティであり、ベクトルとオプションのidおよびペイロードで構成される
- Points - Qdrant
- id … ベクトルの一意の識別子
- ベクトル … 画像、音、ドキュメント、ビデオなどのデータの高次元表現
- ペイロード … ベクトルに追加できるJSONオブジェクト
- ストレージ … Qdrantのデータの保存先で、メモリとディスクから選択
- クライアント … Qdrantへの接続に使用できるプログラミング言語
RDBMSでいうテーブルがコレクションで、レコードがポイント、という感じでしょうか。クエリーの戦略はコレクションの作成時に
決まるものみたいですね。
ベクトル検索の基本は、こちら。
ここは、次回また見たいと思います。
Qdrantのインストール方法は、Docker、Kubernetes、Qdrant Cloud、ソースコードのビルドが紹介されています。
また、記載はありませんが、ビルド済みのバイナリをダウンロードしても良さそうです。
Releases · qdrant/qdrant · GitHub
今回は、とりあえずビルド済みのバイナリをダウンロードして動かしてみたいと思います。
なぜQdrantか?
Qdrantを使う前に、ベクトルデータベースを使ってみるにあたり、どうしてQdrantを選んだのかを書いておきましょう。
ベクトルデータベースに興味を持ったのはLLMの文脈でよく登場するからと、最終的にはRAGなども試してみたいからです。
この時、ハイブリッド検索などもやってみたいので、最終的にはElasticsearchのような全文検索とベクトル検索の両方ができる
データストアを選ぶかもしれません。
という一方で、ベクトルデータベースに特化しているQdrantを選んだのは以下の理由です。
- ベクトルデータベースという知らない概念を学ぶのであれば、その分野に特化したプロダクトのドキュメントなどの方が情報が多く、深い
- 手元で動かしたいので、SaaSではなくセルフホスティング可能なものがよい
- 最終的にはPython以外からも使いたいので、組み込みモードのみをサポートしているプロダクトは避けたい(クライアント/サーバー形態が利用できて欲しい)
ハイブリッドなデータストアよりもベクトルデータベース専用のものを選んでいるのは、ドキュメントなどを見ることによる
ベクトルデータベースそのものに対する理解を期待しています。
このあたりの条件でベクトルデータベースを眺めていき、よさそうだったのがQdrantだったということです。
現時点ではまずはベクトルデータベースを1種学んでみたらいいかなと思っているので、他のベクトルデータベースに特化した
データストアを扱う予定はありませんが、実際どうなるかはQdrantを触ってみてどれくらい興味を持つかですね。
Qdrantは分散構成にできそうなところにも興味がありますが、そちらまで手を出すかはまだわかりません。
環境
今回の環境はこちら。Ubuntu Linux 22.04 LTSです。
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.3 LTS Release: 22.04 Codename: jammy $ uname -srvmpio Linux 5.15.0-92-generic #102-Ubuntu SMP Wed Jan 10 09:33:48 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
Qdrantにアクセスする際にしようするPython。
$ python3 --version Python 3.10.12 $ pip3 --version pip 22.0.2 from /usr/lib/python3/dist-packages/pip (python 3.10)
Qdrantをインストールする
それでは、Qdrantをインストールします。
こちらから最新のリリースをダウンロードします。
Releases · qdrant/qdrant · GitHub
$ curl -OL https://github.com/qdrant/qdrant/releases/download/v1.7.3/qdrant-x86_64-unknown-linux-gnu.tar.gz
展開。
$ tar xvf qdrant-x86_64-unknown-linux-gnu.tar.gz qdrant
シングルバイナリのようです。
バージョン。
$ ./qdrant --version qdrant 1.7.3
起動。
$ ./qdrant
_ _
__ _ __| |_ __ __ _ _ __ | |_
/ _` |/ _` | '__/ _` | '_ \| __|
| (_| | (_| | | | (_| | | | | |_
\__, |\__,_|_| \__,_|_| |_|\__|
|_|
Access web UI at http://0.0.0.0:6333/dashboard
2024-01-25T14:41:08.535870Z WARN qdrant::settings: Config file not found: config/config
2024-01-25T14:41:08.535964Z WARN qdrant::settings: Config file not found: config/development
2024-01-25T14:41:08.536773Z INFO storage::content_manager::consensus::persistent: Loading raft state from ./storage/raft_state.json
2024-01-25T14:41:08.614482Z INFO qdrant: Distributed mode disabled
2024-01-25T14:41:08.614537Z INFO qdrant: Telemetry reporting enabled, id: 515698cc-9357-487f-b0d2-c5732767837f
2024-01-25T14:41:08.619827Z INFO qdrant::tonic: Qdrant gRPC listening on 6334
2024-01-25T14:41:08.619867Z INFO qdrant::tonic: TLS disabled for gRPC API
2024-01-25T14:41:08.620446Z WARN qdrant::actix: Static content folder for Web UI './static' does not exist
2024-01-25T14:41:08.621420Z INFO qdrant::actix: TLS disabled for REST API
2024-01-25T14:41:08.621484Z INFO qdrant::actix: Qdrant HTTP listening on 6333
2024-01-25T14:41:08.621500Z INFO actix_server::builder: Starting 1 workers
2024-01-25T14:41:08.621517Z INFO actix_server::server: Actix runtime found; starting in Actix runtime
起動がとても速いです。
Qdrantが使用するポートはこちらに記載があります。
http://localhost:6333(REST API)、http://localhost:6333/dashboard(Web UI)、http://localhost:6334(GRPC API)の3つのようです。
なお、このバイナリにはWeb UIは含まれていないようです。Dockerイメージには含まれていました。
Dockerfileを見てみると、別リポジトリーにあるようですね。
qdrant/Dockerfile at v1.7.3 · qdrant/qdrant · GitHub
https://github.com/qdrant/qdrant/blob/v1.7.3/tools/sync-web-ui.sh
こちらです。
GitHub - qdrant/qdrant-web-ui: Self-hosted web UI for Qdrant
追加しましょう。
ダウンロードして展開。
$ curl -LO https://github.com/qdrant/qdrant-web-ui/releases/download/v0.1.20/dist-qdrant.zip $ unzip dist-qdrant.zip
展開後はdistディレクトリが現れます。こういうディレクトリ構造になっていて
$ tree dist -d dist └── assets 1 directory
assets配下にはJavaScriptやCSSが入っています。
dist直下はこんな感じですね。
$ ll dist total 1100 drwxr-xr-x 3 xxxxx xxxxx 4096 Dec 7 16:31 ./ drwxr-x--- 7 xxxxx xxxxx 4096 Jan 28 08:50 ../ drwxr-xr-x 2 xxxxx xxxxx 4096 Dec 7 16:31 assets/ -rw-r--r-- 1 xxxxx xxxxx 15086 Dec 7 16:31 favicon.ico -rw-r--r-- 1 xxxxx xxxxx 1781 Dec 7 16:31 index.html -rw-r--r-- 1 xxxxx xxxxx 8870 Dec 7 16:31 logo192.png -rw-r--r-- 1 xxxxx xxxxx 28384 Dec 7 16:31 logo512.png -rw-r--r-- 1 xxxxx xxxxx 9339 Dec 7 16:31 logo.png -rw-r--r-- 1 xxxxx xxxxx 484 Dec 7 16:31 manifest.json -rw-r--r-- 1 xxxxx xxxxx 239641 Dec 7 16:31 openapi.json -rw-r--r-- 1 xxxxx xxxxx 789035 Dec 7 16:31 qdrant-web-ui.spdx.json -rw-r--r-- 1 xxxxx xxxxx 67 Dec 7 16:31 robots.txt
Qdrantのスクリプトを見ると、このdistディレクトリの中身をstaticというディレクトリに入れればいいみたいです。
$ mkdir static $ mv dist/* static/
Qdrantを起動。
$ ./qdrant
http://[Qdrantが起動しているホスト]:6333/dashboardにアクセスすると、Web UIが現れます。

これで、Qdrantサーバーは起動できました。
QdrantのQuickstartを試す
では、Qdrantを使ってみましょう。
今回はQuickstartに沿って使ってみたいと思います。
Quickstartで使われているのはPython、TypeScript、Rust、Javaですが、今回はPythonを使います。
インストール方法は書かれていないので、PythonのQdrantクライアントのGitHubリポジトリを確認
GitHub - qdrant/qdrant-client: Python client for Qdrant vector search engine
QdrantのOpenAPI定義に従ってREST APIを呼び出すクライアントのようです。
インストール。
$ pip3 install qdrant-client
バージョン。
$ pip3 list Package Version ----------------- ---------- annotated-types 0.6.0 anyio 4.2.0 certifi 2023.11.17 exceptiongroup 1.2.0 grpcio 1.60.0 grpcio-tools 1.60.0 h11 0.14.0 h2 4.1.0 hpack 4.0.0 httpcore 1.0.2 httpx 0.26.0 hyperframe 6.0.1 idna 3.6 numpy 1.26.3 pip 22.0.2 portalocker 2.8.2 protobuf 4.25.2 pydantic 2.5.3 pydantic_core 2.14.6 qdrant-client 1.7.1 setuptools 59.6.0 sniffio 1.3.0 typing_extensions 4.9.0 urllib3 2.1.0
Python Qdrant Client(qdrant-client)は1.7.1です。
ちなみに、qdrant-clientにはQdrantサーバーなしで動作するローカルモードという機能もあるみたいです。
Python Qdrant Client / Local mode
QdrantクライアントからQdrantサーバーへの接続は、以下の記述で行うようです。
from qdrant_client import QdrantClient client = QdrantClient(host="localhost", port=6333)
まずはコレクションを作成してみます。コレクションはベクトルデータ(ポイント)を保存するものでした。
``create_collection.py
from qdrant_client import QdrantClient from qdrant_client.http.models import Distance, VectorParams client = QdrantClient(host="localhost", port=6333) client.create_collection( collection_name="test_collection", vectors_config=VectorParams(size=4, distance=Distance.DOT) )
コレクション名、ベクトルのサイズ(次元数)と距離メトリクスを指定しています。距離メトリクスはドット積ですね。
collections / Create collection
実行。
$ python3 create_collection.py
Web UIを見ると、コレクションが作成できたことが確認できます。

ちなみに、同じコードを2回実行するとエラーになりますね。
$ python3 create_collection.py
Traceback (most recent call last):
File "/path/to/create_collection.py", line 6, in <module>
client.create_collection(
File "/path/to/venv/lib/python3.10/site-packages/qdrant_client/qdrant_client.py", line 1669, in create_collection
return self._client.create_collection(
File "/path/to/venv/lib/python3.10/site-packages/qdrant_client/qdrant_remote.py", line 2161, in create_collection
result: Optional[bool] = self.http.collections_api.create_collection(
File "/path/to/venv/lib/python3.10/site-packages/qdrant_client/http/api/collections_api.py", line 1118, in create_collection
return self._build_for_create_collection(
File "/path/to/venv/lib/python3.10/site-packages/qdrant_client/http/api/collections_api.py", line 96, in _build_for_create_collection
return self.api_client.request(
File "/path/to/venv/lib/python3.10/site-packages/qdrant_client/http/api_client.py", line 74, in request
return self.send(request, type_)
File "/path/to/venv/lib/python3.10/site-packages/qdrant_client/http/api_client.py", line 97, in send
raise UnexpectedResponse.for_response(response)
qdrant_client.http.exceptions.UnexpectedResponse: Unexpected Response: 400 (Bad Request)
Raw response content:
b'{"status":{"error":"Wrong input: Collection `test_collection` already exists!"},"time":0.00003701}'
ベクトルを登録します。Quickstartで使っているのは、upsertというすでにポイントが存在していれば上書きするものです。
ポイントというのは、Qdrantが操作する中心的なエンティティであり、ベクトルとオプションのidおよびペイロードで構成される、
というものでした。
ソースコードはこちら。
add_vectors.py
from qdrant_client import QdrantClient from qdrant_client.http.models import PointStruct client = QdrantClient(host="localhost", port=6333) 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)
追加するベクトルは、コレクションに指定したように4次元ですね。最後に実行結果を出力しています。
waitにしているので、変更内容が実際に適用されるまで待つようになります。
実行。
$ python3 add_vectors.py operation_id=0 status=<UpdateStatus.COMPLETED: 'completed'>
Web UIでの確認。

次は検索してみます。
run_query.py
from qdrant_client import QdrantClient client = QdrantClient(host="localhost", port=6333) search_results = client.search( collection_name="test_collection", query_vector=[0.2, 0.1, 0.9, 0.7], limit=3 ) for result in search_results: print(result)
結果はScoredPointというクラスのリストとして返ってくるようです。
実行。
$ python3 run_query.py
id=4 version=0 score=1.362 payload={'city': 'New York'} vector=None shard_key=None
id=1 version=0 score=1.273 payload={'city': 'Berlin'} vector=None shard_key=None
id=3 version=0 score=1.208 payload={'city': 'Moscow'} vector=None shard_key=None
コレクションに指定している距離メトリクスがドット積なので、検索条件に指定したベクトルに「似ている」ものの上位3件(似ているものの
降順)が取得できた、ということになっているようです。
デフォルトではペイロードおよびベクトルは結果に含まれないようです(ペイロードは返ってきていますが…)。
最後に、検索時にペイロードにフィルターを加えて結果を絞り込みます。
add_filter.py
from qdrant_client import QdrantClient from qdrant_client.http.models import FieldCondition, Filter, MatchValue client = QdrantClient(host="localhost", port=6333) search_results = client.search( collection_name="test_collection", query_vector=[0.2, 0.1, 0.9, 0.7], query_filter=Filter( must=[FieldCondition(key="city", match=MatchValue(value="London"))] ), with_payload=True, limit=3 ) for result in search_results: print(result)
cityキーの値にLongonを含むものに絞り込んでいます。
実行。
$ python3 add_filter.py
id=2 version=0 score=0.871 payload={'city': 'London'} vector=None shard_key=None
実際にフィルターを使う時は、ペイロードにインデックスを作成することを推奨しているようです。
To make filtered search fast on real datasets, we highly recommend to create payload indexes!
ここまでで、Quickstartの内容は終わりです。
おわりに
まずはQuickstartをなぞっただけですが、少し雰囲気はわかった気がします。
ここから少しずつベクトルデータベースに慣れていければいいかなと思います。