ocaml-qdrant

Pure OCaml client for Qdrant vector database.

The first OCaml Qdrant client! Combines best practices from official Python, Rust, Go, and TypeScript clients.

Features

Install

opam install qdrant

Quick Start

let () = Lwt_main.run begin
  let open Lwt.Syntax in
  let open Qdrant in

  (* Create collection *)
  let* _ = create_collection
    ~name:"my_collection"
    ~vector_config:{ size = 1024; distance = Cosine }
    () in

  (* Insert points *)
  let* _ = upsert ~collection:"my_collection" ~points:[
    { id = "1"; vector = my_embedding; payload = [("text", `String "hello")] }
  ] () in

  (* Search *)
  let* results = search
    ~collection:"my_collection"
    ~vector:query_embedding
    ~limit:10
    () in

  Lwt.return ()
end

Filter API

Two styles are available - choose based on your preference:

Style 1: Sum Types (Explicit, Pattern-matchable)

Direct use of algebraic data types. Best for:

let open Qdrant.Filter in

(* Simple filter *)
let filter = Must [
  MatchKeyword ("category", "tech");
  Range ("price", { gt = None; gte = Some 10.0; lt = None; lte = Some 100.0 });
]

(* Complex filter with nesting *)
let filter = And [
  Must [MatchKeyword ("status", "active")];
  Should [
    MatchAny ("tags", ["ai"; "ml"; "data"]);
    GeoRadius ("location", { lat = 37.5; lon = 127.0 }, 1000.0);
  ];
  MustNot [IsNull "deleted_at"];
]

Style 2: Fluent Helpers (Ergonomic, Go/Rust-inspired)

Convenience functions wrapping sum types. Best for:

let open Qdrant.Filter in

(* Simple filter *)
let filter = must [
  match_keyword "category" "tech";
  range "price" ~gte:10.0 ~lte:100.0 ();
]

(* Complex filter *)
let filter = combine [
  must [match_keyword "status" "active"];
  should [
    match_any "tags" ["ai"; "ml"; "data"];
    geo_radius "location" ~lat:37.5 ~lon:127.0 ~radius_m:1000.0;
  ];
  must_not [is_null "deleted_at"];
]

(* Full-text search *)
let filter = must [full_text "content" "machine learning"]

(* Null checks *)
let filter = must [is_not_null "required_field"]

Both styles produce identical JSON and can be used with search_with_filter:

let* results = search_with_filter
  ~collection:"items"
  ~vector:query_vec
  ~limit:10
  ~filter:(Filter.to_json filter)
  ()

Batch Operations

Python-inspired batch upsert with automatic chunking:

(* Insert 10,000 points in chunks of 100 *)
let* count = batch_upsert
  ~collection:"embeddings"
  ~points:large_point_list
  ~chunk_size:100
  () in
Printf.printf "Inserted %d points\n" count

Recommend API

Find similar items based on examples:

let* recommendations = recommend
  ~collection:"products"
  ~positive:["liked-item-1"; "liked-item-2"]
  ~negative:["disliked-item"]
  ~limit:10
  ()

Config

From Environment Variables (Recommended)

export QDRANT_URL="http://localhost:6333"
export QDRANT_API_KEY="your-api-key"  # optional
(* Explicit is better than implicit - use config_from_env *)
let config = Qdrant.config_from_env () in
let* result = Qdrant.health ~config () in
...

Programmatic Configuration

let config = Qdrant.{
  base_url = "https://your-qdrant.cloud";
  api_key = Some "your-key";
  timeout_s = 30.0;
}

Default Config

(* default_config = localhost:6333, no API key, 30s timeout *)
let* result = Qdrant.health () in  (* uses default_config *)

API Reference

Configuration

Health & Info

Collections

Points

Scroll & Count

Filter Module (Sum Types)

Filter Module (Fluent Helpers)

Inspiration

This client combines the best from official Qdrant clients:

Testing

Unit Tests (No Qdrant Required)

dune runtest

27 tests covering types, filters, config, and error handling.

Integration Tests (Qdrant Required)

# Set up environment
export QDRANT_URL="http://localhost:6333"
# or for cloud: export QDRANT_URL="https://your-cluster.qdrant.io"
# export QDRANT_API_KEY="your-key"

# Run integration example
dune exec examples/basic.exe

Security

License

MIT