qdrant

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 5, 2025 License: MIT Imports: 10 Imported by: 0

Documentation

Overview

Package qdrant provides a modular, dependency-injected client for the Qdrant vector database.

The qdrant package is designed to simplify interaction with Qdrant in Go applications, offering a clean, testable abstraction layer for common vector database operations such as collection management, embedding insertion, similarity search, and deletion. It integrates seamlessly with the fx dependency injection framework and supports builder-style configuration.

Core Features:

  • Managed Qdrant client lifecycle with Fx integration
  • Config struct supporting environment and YAML loading
  • Automatic health checks on client initialization
  • Safe, batched insertion of embeddings with configurable batch size
  • Vector similarity search with abstracted SearchResult interface
  • Type-safe collection creation and existence checks
  • Support for payload metadata and optional vector retrieval
  • Extensible abstraction layer for alternate vector stores (e.g., Pinecone, Postgres)

Basic Usage:

import "github.com/Aleph-Alpha/std/v1/qdrant"

// Create a new client
client, err := qdrant.NewQdrantClient(qdrant.QdrantParams{
	Config: &qdrant.Config{
		Endpoint: "localhost:6334",
		ApiKey:   "",
	},
})
if err != nil {
	log.Fatal(err)
}

collectionName := "documents"

// Insert single embedding
input := qdrant.EmbeddingInput{
	ID:     "doc_1",
	Vector: []float32{0.12, 0.43, 0.85},
	Meta:   map[string]any{"title": "My Document"},
}
if err := client.Insert(ctx, collectionName, input); err != nil {
	log.Fatal(err)
}

// Batch insert embeddings
batch := []qdrant.EmbeddingInput{input1, input2, input3}
if err := client.BatchInsert(ctx, collectionName, batch); err != nil {
	log.Fatal(err)
}

// Perform similarity search
results, err := client.Search(ctx, qdrant.SearchRequest{
	CollectionName: collectionName,
	Vector:         queryVector,
	TopK:           5,
})
for _, res := range results[0] {
	fmt.Printf("ID=%s Score=%.4f\n", res.GetID(), res.GetScore())
}

FX Module Integration:

The package exposes an Fx module for automatic dependency injection:

app := fx.New(
	qdrant.FXModule,
	// other modules...
)
app.Run()

Abstractions:

The package defines a lightweight SearchResultInterface that encapsulates search results via methods such as GetID(), GetScore(), GetMeta(), and GetCollectionName(). The underlying concrete type remains SearchResult, allowing both strong typing internally and loose coupling externally.

Example:

type SearchResultInterface interface {
	GetID() string
	GetScore() float32
	GetMeta() map[string]*qdrant.Value
	GetCollectionName() string
}

type SearchResult struct { /* implements SearchResultInterface */ }

// Function signature:
func (c *QdrantClient) Search(ctx context.Context, vector []float32, topK int) ([]SearchResultInterface, error)

Configuration:

Qdrant can be configured via environment variables or YAML:

QDRANT_ENDPOINT=http://localhost:6334
QDRANT_API_KEY=your-api-key

Performance Considerations:

The BatchInsert method automatically splits large embedding inserts into smaller upserts (default batch size = 500). This minimizes memory usage and avoids timeouts when ingesting large datasets.

Thread Safety:

All exported methods on QdrantClient are safe for concurrent use by multiple goroutines.

Testing:

For testing and mocking, application code should depend on the public interface types (e.g., SearchResultInterface, EmbeddingInput) instead of concrete Qdrant structs. This allows replacing the QdrantClient with in-memory or mock implementations in tests.

Example Mock:

type MockResult struct {
	id    string
	score float32
	meta  map[string]any
}
func (m MockResult) GetID() string           { return m.id }
func (m MockResult) GetScore() float32       { return m.score }
func (m MockResult) GetMeta() map[string]any { return m.meta }

Package Layout:

qdrant/
├── setup.go         // Qdrant client implementation
├── utils.go         // Shared types and interfaces
└── config.go        // Configuration and Fx module definitions

Index

Constants

This section is empty.

Variables

FXModule defines the Fx module for the Qdrant client.

This module integrates the Qdrant client into an Fx-based application by providing the client factory and registering its lifecycle hooks.

The module:

  1. Provides the NewQdrantClient factory function to the dependency injection container, making the client available to other components.
  2. Provides the NewEmbeddingsStore function, which wraps the client into a higher-level abstraction.
  3. Invokes RegisterQdrantLifecycle to handle startup/shutdown of the client.

Usage:

app := fx.New(
    qdrant.FXModule,
    // other modules...
)

Dependencies required by this module: - A qdrant.Config instance must be available in the dependency injection container.

Functions

func RegisterQdrantLifecycle

func RegisterQdrantLifecycle(lc fx.Lifecycle, client *QdrantClient)

RegisterQdrantLifecycle handles startup/shutdown of the Qdrant client. It ensures proper resource cleanup and logging.

OnStart:

  • Performs a Qdrant health check to verify connectivity.
  • Logs a success message once the client is ready.

OnStop:

  • Ensures the Qdrant client is closed exactly once.
  • Logs a shutdown message.

Types

type Collection

type Collection struct {
	Name       string
	Status     string
	VectorSize int
	Distance   string
	Vectors    uint64
	Points     uint64
}

type Config

type Config struct {
	// Hostname of the Qdrant server, e.g. "localhost".
	Endpoint string `yaml:"endpoint" env:"QDRANT_ENDPOINT"`

	// gRPC port of the Qdrant server. Defaults to 6334.
	Port int `yaml:"port" env:"QDRANT_PORT"`

	// Optional authentication token for secured deployments.
	ApiKey string `yaml:"api_key" env:"QDRANT_API_KEY"`

	// Maximum request duration before timing out.
	Timeout time.Duration `yaml:"timeout" env:"QDRANT_TIMEOUT"`

	// Connection establishment timeout.
	ConnectTimeout time.Duration `yaml:"connect_timeout" env:"QDRANT_CONNECT_TIMEOUT"`

	// Whether to keep idle connections open for reuse.
	KeepAlive bool `yaml:"keep_alive" env:"QDRANT_KEEP_ALIVE"`

	// Enable gzip compression for requests.
	Compression bool `yaml:"compression" env:"QDRANT_COMPRESSION"`

	// Whether to perform version compatibility checks between client and server.
	CheckCompatibility bool `yaml:"check_compatibility" env:"QDRANT_CHECK_COMPATIBILITY"`
}

Config holds connection and behavior settings for the Qdrant client.

It is intentionally minimal, readable, and easy to override from environment variables, YAML, or programmatically via helper methods.

Example (programmatic):

cfg := qdrant.DefaultConfig()
cfg.Endpoint = "http://localhost:6334"
cfg.ApiKey = os.Getenv("QDRANT_API_KEY")
cfg.Timeout = 10 * time.Second

Example (builder style):

cfg := qdrant.FromEndpoint("http://localhost:6334").
    WithApiKey(os.Getenv("QDRANT_API_KEY")).
    WithTimeout(10 * time.Second)

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig provides sensible defaults for most use cases.

func FromEndpoint

func FromEndpoint(url string) *Config

FromEndpoint returns a default config pre-filled with a specific endpoint.

func (*Config) WithApiKey

func (c *Config) WithApiKey(key string) *Config

Builder-style helpers (optional, ergonomic)

func (*Config) WithCompatibilityCheck

func (c *Config) WithCompatibilityCheck(enabled bool) *Config

func (*Config) WithCompression

func (c *Config) WithCompression(enabled bool) *Config

func (*Config) WithConnectTimeout

func (c *Config) WithConnectTimeout(d time.Duration) *Config

func (*Config) WithKeepAlive

func (c *Config) WithKeepAlive(enabled bool) *Config

func (*Config) WithTimeout

func (c *Config) WithTimeout(d time.Duration) *Config

type Embedding

type Embedding struct {
	ID     string         // Unique identifier (same as Qdrant point ID)
	Vector []float32      // Vector representation of the embedding
	Meta   map[string]any // Optional metadata associated with the embedding
}

Embedding represents a dense embedding vector.

func NewEmbedding

func NewEmbedding(input EmbeddingInput) Embedding

NewEmbedding converts a high-level EmbeddingInput into the internal Embedding type. Having this builder allows for future validation or normalization logic. For now, it performs a shallow copy.

type EmbeddingInput

type EmbeddingInput struct {
	ID     string         // Unique identifier for the embedding (e.g., document ID)
	Vector []float32      // Dense vector representation of the embedding
	Meta   map[string]any // Optional metadata associated with the embedding
}

EmbeddingInput is the type the application provides to insert embeddings. Keeps the app decoupled from internal Qdrant SDK structs.

type QdrantClient

type QdrantClient struct {
	// contains filtered or unexported fields
}

func NewQdrantClient

func NewQdrantClient(p QdrantParams) (*QdrantClient, error)

NewQdrantClient ────────────────────────────────────────────────────────────── NewQdrantClient ──────────────────────────────────────────────────────────────

NewQdrantClient constructs a new instance of QdrantClient and validates connectivity via a health check.

The Qdrant Go SDK creates lightweight gRPC connections, so this method performs an immediate health check to fail fast if the service is unreachable.

Example:

client, _ := qdrant.NewQdrantClient(qdrant.QdrantParams{Config: cfg})

func (*QdrantClient) BatchInsert

func (c *QdrantClient) BatchInsert(ctx context.Context, collectionName string, inputs []EmbeddingInput) error

BatchInsert ────────────────────────────────────────────────────────────── BatchInsert ──────────────────────────────────────────────────────────────

BatchInsert efficiently inserts multiple embeddings in batches to reduce network overhead.

This method is safe to call for large datasets — it will automatically split inserts into smaller chunks (`defaultBatchSize`) and perform multiple upserts sequentially.

Logs batch indices and collection name for debugging.

func (*QdrantClient) Close

func (c *QdrantClient) Close() error

Close ────────────────────────────────────────────────────────────── Close ──────────────────────────────────────────────────────────────

Close gracefully shuts down the Qdrant client.

Since the official Qdrant Go SDK doesn’t maintain persistent connections, this is currently a no-op. It exists for lifecycle symmetry and future safety.

func (*QdrantClient) Delete

func (c *QdrantClient) Delete(ctx context.Context, collectionName string, ids []string) error

Delete ────────────────────────────────────────────────────────────── Delete ──────────────────────────────────────────────────────────────

Delete removes embeddings from a collection by their IDs.

It constructs a `DeletePoints` request containing a list of `PointId`s, waits synchronously for completion, and logs the operation status.

func (*QdrantClient) EnsureCollection

func (c *QdrantClient) EnsureCollection(ctx context.Context, name string) error

EnsureCollection ────────────────────────────────────────────────────────────── EnsureCollection ──────────────────────────────────────────────────────────────

EnsureCollection verifies if a given collection exists, and creates it if missing.

It’s safe to call this multiple times — if the collection already exists, the function exits early. This pattern simplifies startup logic for embedding services that may bootstrap their own Qdrant collections.

func (*QdrantClient) GetCollection

func (c *QdrantClient) GetCollection(ctx context.Context, name string) (*Collection, error)

func (*QdrantClient) Insert

func (c *QdrantClient) Insert(ctx context.Context, collectionName string, input EmbeddingInput) error

Insert ────────────────────────────────────────────────────────────── Insert ──────────────────────────────────────────────────────────────

Insert adds a single embedding to Qdrant.

Internally, it reuses the BatchInsert logic to ensure consistent handling of payload serialization and error management.

func (*QdrantClient) ListCollections

func (c *QdrantClient) ListCollections(ctx context.Context) ([]string, error)

ListCollections ────────────────────────────────────────────────────────────── ListCollections ──────────────────────────────────────────────────────────────

ListCollections retrieves all existing collections from Qdrant and returns their names as a string slice. This can be extended to preload metadata using GetCollection for each name if needed.

Example:

names, err := client.ListCollections(ctx)
if err != nil {
    log.Fatalf("failed to list collections: %v", err)
}
log.Printf("Found collections: %v", names)

func (*QdrantClient) Search

func (c *QdrantClient) Search(ctx context.Context, requests ...SearchRequest) ([][]SearchResultInterface, error)

Search ────────────────────────────────────────────────────────────── Search ──────────────────────────────────────────────────────────────

Search performs multiple searches and returns results for each request. Each request can optionally include filters.

Parameters:

  • requests — variadic SearchRequest structs, each containing:
  • CollectionName: the collection to search in
  • Vector: the query embedding
  • TopK: maximum number of results per request
  • Filters: optional key-value filters (AND logic)

Returns:

A slice of result slices — one []SearchResultInterface per request.

Example:

results, err := client.Search(ctx,
    SearchRequest{CollectionName: "docs", Vector: vec1, TopK: 10, Filters: map[string]string{"partition_id": "store-A"}},
    SearchRequest{CollectionName: "docs", Vector: vec2, TopK: 5},
)
// results[0] = results for first request
// results[1] = results for second request

type QdrantParams

type QdrantParams struct {
	fx.In
	Config *Config
}

QdrantParams defines dependencies needed to construct the Qdrant client.

type SearchRequest added in v0.3.0

type SearchRequest struct {
	CollectionName string
	Vector         []float32
	TopK           int
	Filters        map[string]string //Optional: key-value filters
}

SearchRequest represents a single search request for batch operations

type SearchResult

type SearchResult struct {
	ID         string
	Score      float32
	Meta       map[string]*qdrant.Value
	Vector     []float32
	Collection string
}

SearchResult holds results from a similarity search.

func (SearchResult) GetCollectionName

func (r SearchResult) GetCollectionName() string

GetCollectionName returns the name of the collection from which the result originated.

func (SearchResult) GetID

func (r SearchResult) GetID() string

GetID returns the result's unique identifier.

func (SearchResult) GetMeta

func (r SearchResult) GetMeta() map[string]*qdrant.Value

GetMeta returns the metadata stored with the vector.

func (SearchResult) GetScore

func (r SearchResult) GetScore() float32

GetScore returns the similarity score associated with the result.

func (SearchResult) GetVector

func (r SearchResult) GetVector() []float32

GetVector returns the dense embedding vector if available.

func (SearchResult) HasVector

func (r SearchResult) HasVector() bool

HasVector reports whether the result contains a non-empty vector payload.

type SearchResultInterface

type SearchResultInterface interface {
	GetID() string                     // Unique result identifier
	GetScore() float32                 // Similarity score
	GetMeta() map[string]*qdrant.Value // Metadata associated with the result
	GetVector() []float32              // Optional embedding vector
	HasVector() bool                   // Whether a vector payload is present
	GetCollectionName() string         // Name of the Qdrant collection
}

SearchResultInterface is the public interface for search results. It provides a consistent way to access search results regardless of the underlying implementation.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL