Qdrant
rig::qdrant (feature qdrant) implements Rig’s InsertDocuments and VectorStoreIndex traits on top of Qdrant, a dedicated vector database written in Rust. Use it when you want a persistent, filterable store that scales beyond the in-memory one.
[dependencies]rig = { version = "0.44.0", features = ["qdrant"] }qdrant-client = "1"tokio = { version = "1", features = ["full"] }qdrant-client is Qdrant’s own Rust client, which you use to connect and create collections. Start a local Qdrant with Docker:
docker run -p 6333:6333 -p 6334:6334 qdrant/qdrantMinimal example
Section titled “Minimal example”Create a collection, embed some documents, insert them, then search. The collection’s vector size must match your embedding model (1536 for text-embedding-3-small).
use qdrant_client::{ Qdrant, qdrant::{CreateCollectionBuilder, Distance, QueryPointsBuilder, VectorParamsBuilder},};use rig::prelude::*;use serde::{Deserialize, Serialize};use rig::providers::openai::{self, OpenAI};use rig::qdrant::QdrantVectorStore;use rig::vector_store::InsertDocuments;
const COLLECTION_NAME: &str = "rig-collection";
#[derive(Embed, Serialize, Deserialize, Debug)]struct Word { id: String, #[embed] definition: String,}
#[tokio::main]async fn main() -> Result<(), anyhow::Error> { let client = Qdrant::from_url("http://localhost:6334").build()?;
if !client.collection_exists(COLLECTION_NAME).await? { client .create_collection( CreateCollectionBuilder::new(COLLECTION_NAME) .vectors_config(VectorParamsBuilder::new(1536, Distance::Cosine)), ) .await?; }
let model = OpenAI::from_env()?.embedding(openai::TEXT_EMBEDDING_3_SMALL, None);
let documents = EmbeddingsBuilder::new(model.clone()) .document(Word { id: "doc0".to_string(), definition: "A flurbo is a green alien that lives on cold planets.".to_string(), })? .document(Word { id: "doc1".to_string(), definition: "A linglingdong is a term used to describe humans.".to_string(), })? .build() .await?;
// The store queries the collection named in these parameters. let query_params = QueryPointsBuilder::new(COLLECTION_NAME).with_payload(true); let vector_store = QdrantVectorStore::new(client, model, query_params.build());
vector_store.insert_documents(documents).await?;
let req = VectorSearchRequest::builder() .query("What is a linglingdong?") .samples(1) .build();
for result in vector_store.top_n::<Word>(req).await? { println!("{:.3} {} {}", result.score, result.id, result.document.definition); }
Ok(())}How it works
Section titled “How it works”QdrantVectorStore::new(client, model, query_params)takes a Qdrant client, the embedding model, and aQueryPointsbuilt withQueryPointsBuilder. The query parameters name the collection; each search reuses them and sets the query vector, limit, threshold and filter. Use.with_payload(true)so the stored documents come back with each hit.insert_documentsupserts one point per embedding, with the serialized document as its payload. Point ids are fresh UUIDs, so filter on your own payload fields (such asidbelow) to find a document.top_n::<T>(req)embeds the query, runs the search, and deserializes each payload intoT. The request’sthresholdbecomes Qdrant’s score threshold.client()returns the underlyingQdrantclient for anything else.
Filtering
Section titled “Filtering”Qdrant filters run on the server. Build the request with QdrantFilter as its filter type:
use rig::qdrant::QdrantFilter;use rig::vector_store::request::SearchFilter;
let req = VectorSearchRequest::<QdrantFilter>::builder() .query("What is a linglingdong?") .samples(1) .filter(QdrantFilter::eq("id", json!("doc1"))) .build();Beyond eq, gt, lt, and and or, QdrantFilter has not, exists, is_null, is_empty and range conditions (range_inclusive, range_exclusive, …).
See also
Section titled “See also”- Vector Stores overview: the shared traits,
VectorSearchRequest, filters - RAG: retrieval in an agent
rig-qdrantexample and the Qdrant collections docs
