Mythosia.VectorDb.Qdrant
4.2.0
dotnet add package Mythosia.VectorDb.Qdrant --version 4.2.0
NuGet\Install-Package Mythosia.VectorDb.Qdrant -Version 4.2.0
<PackageReference Include="Mythosia.VectorDb.Qdrant" Version="4.2.0" />
<PackageVersion Include="Mythosia.VectorDb.Qdrant" Version="4.2.0" />
<PackageReference Include="Mythosia.VectorDb.Qdrant" />
paket add Mythosia.VectorDb.Qdrant --version 4.2.0
#r "nuget: Mythosia.VectorDb.Qdrant, 4.2.0"
#:package Mythosia.VectorDb.Qdrant@4.2.0
#addin nuget:?package=Mythosia.VectorDb.Qdrant&version=4.2.0
#tool nuget:?package=Mythosia.VectorDb.Qdrant&version=4.2.0
Mythosia.VectorDb.Qdrant
v4.2.0: Includes text-only and configurable hybrid search. See the release notes for compatibility and fixes.
Qdrant vector store implementation for the Mythosia VectorDb abstraction layer.
Uses a single Qdrant collection (physical container) with payload-based metadata filtering for logical isolation.
All isolation keys (e.g. namespace, scope, category) are standard metadata entries — there are no framework-reserved keys.
Migration from v1.0.0
v1.0.0 collections are dense-only. v2.0.0 uses hybrid-capable collections and writes a schema marker so the tooling can detect whether migration is needed.
Install the migration tool first:
Install-Package Mythosia.VectorDb.Tools
If docs is the collection you want to migrate, run:
mythosia-vectordb migrate qdrant --endpoint localhost:6334 --source docs --replace
This migrates through a staging collection, then recreates docs with the new schema and copies the migrated data back into docs.
If your Qdrant server is remote or authenticated, add --api-key your-api-key and use your remote endpoint URL.
Stop application writes before migration if consistency matters.
Installation
dotnet add package Mythosia.VectorDb.Qdrant
Current package version:
dotnet add package Mythosia.VectorDb.Qdrant --version 4.2.0
Quick Start
using Mythosia.VectorDb;
using Mythosia.VectorDb.Qdrant;
// 1. Configure — CollectionName is the physical Qdrant collection
var options = new QdrantOptions
{
Host = "localhost",
Port = 6334,
CollectionName = "my_vectors", // physical collection
Dimension = 1536, // must match your embedding model
DistanceStrategy = QdrantDistanceStrategy.Cosine
};
// 2. Create the store
using var store = new QdrantStore(options);
// 3. Upsert records
var record = new VectorRecord("doc-1", embedding, "Hello world");
record.Metadata["namespace"] = "documents"; // optional logical isolation via metadata
await store.UpsertAsync(record);
// 4. Search — use VectorFilter.Where() for logical isolation
var filter = new VectorFilter().Where("namespace", "documents");
var results = await store.SearchAsync(queryVector, topK: 5, filter: filter);
Options
| Property | Default | Description |
|---|---|---|
Host |
"localhost" |
Qdrant server host |
Port |
6334 |
Qdrant gRPC port |
UseTls |
false |
Enable TLS for gRPC |
ApiKey |
null |
Optional API key |
CollectionName |
(required) | Qdrant collection name (physical container) |
Dimension |
(required) | Embedding vector dimension |
DistanceStrategy |
Cosine |
Cosine, Euclidean, or DotProduct |
AutoCreateCollection |
true |
Auto-create the collection on first use |
HybridFusionStrategy |
Rrf |
Server-side fusion for the overload without HybridSearchOptions — Rrf (Reciprocal Rank Fusion) or Dbsf (Distribution-Based Score Fusion) |
AdditionalPayloadIndexes |
[] |
Extra payload fields to index on collection creation (e.g. meta.author). |
Hybrid Search (v2.0.0)
QdrantStore always provisions/uses hybrid-capable storage (dense + sparse) and supports native IVectorStore.HybridSearchAsync.
Choose retrieval mode at query time (SearchAsync for vector-only, HybridSearchAsync for native hybrid):
var options = new QdrantOptions
{
Host = "localhost",
Port = 6334,
CollectionName = "my_vectors",
Dimension = 1536
};
var store = new QdrantStore(options);
On upsert, BM25 sparse vectors are automatically computed from the record's Content and stored alongside the dense embedding. Direct legacy HybridSearchAsync uses Qdrant’s prefetch + fusion (RRF/DBSF). The new HybridSearchOptions overload instead retrieves active candidate legs and merges them with normalized weighted RRF, preserving its explicit weight, candidate multiplier and smoothing options. TextSearchAsync searches the named sparse vector without sending a dense query vector.
Via the RAG pipeline, UseHybridSearch(...) now selects configurable weighted RRF. QdrantOptions.HybridFusionStrategy continues to control direct legacy calls only:
var store = await RagStore.BuildAsync(config => config
.AddDocument("docs.txt")
.UseOpenAIEmbedding(apiKey)
.UseVectorStore(new QdrantStore(new QdrantOptions
{
Host = "localhost",
Dimension = 1536
}))
.UseHybridSearch()
);
Metadata Filtering
// Logical isolation via metadata
var filter = new VectorFilter().Where("namespace", "docs");
var results = await store.SearchAsync(queryVector, topK: 10, filter: filter);
// Multiple metadata conditions
var filter = new VectorFilter()
.Where("namespace", "docs")
.Where("category", "science");
var results = await store.SearchAsync(queryVector, topK: 5, filter: filter);
// Minimum score threshold
var filter = new VectorFilter { MinScore = 0.7 };
var results = await store.SearchAsync(queryVector, topK: 5, filter: filter);
Filter operators for vector and legacy hybrid search
| Operator | Qdrant translation |
|---|---|
Eq |
Must → FieldCondition keyword match |
Ne |
MustNot → FieldCondition |
In |
Must → nested Should (keyword per value) |
NotIn |
MustNot → nested Should |
And / Or groups |
Nested Condition{Filter} in Must / Should |
Gt / Gte / Lt / Lte / Like / Exists / NotExists |
Silently ignored for SearchAsync and the HybridSearchAsync overload without HybridSearchOptions (no client-side fallback). Evaluated client-side via MatchesFilter for GetAsync / GetBatchAsync. |
The v4.2.0 TextSearchAsync and configurable HybridSearchAsync paths support Eq, Ne, In, NotIn, Exists, NotExists, and nested And / Or groups. Unsupported range and Like filters throw NotSupportedException before a request is sent. Ne / NotIn exclude records missing the metadata key.
VectorFilter
For the full operator reference and fluent API examples (Where, WhereNot, WhereIn, WhereLike, WhereExists, Or, And, WithMinScore, etc.), see the Mythosia.VectorDb.Abstractions README.
Qdrant-specific note: Check the operator support for the search overload you use. The text/configurable-hybrid paths reject unsupported filters; vector and legacy hybrid search keep the behavior in the table above.
Resource Disposal
QdrantStore implements IDisposable. When created via the standard constructor, it owns the internal QdrantClient and disposes it on Dispose():
using var store = new QdrantStore(options);
// QdrantClient disposed automatically
When injecting a pre-configured client, the caller retains ownership and is responsible for disposal:
using Qdrant.Client;
var client = new QdrantClient("my-qdrant-cloud.example.com", 6334, https: true, apiKey: "my-key");
var store = new QdrantStore(options, client);
// store.Dispose() will NOT dispose client — caller must dispose it separately
Payload Layout
Records are stored as Qdrant points with the following payload keys:
| Key | Description |
|---|---|
_id |
Original string record ID |
_content |
Text content |
meta.<key> |
User metadata entries (e.g. meta.namespace, meta.category) |
ID Mapping
Point IDs are deterministic UUIDs derived from record Id via MD5 hash. The original string ID is preserved in the _id payload field.
Batch Get & Count
// Fetch multiple records by ID — single Qdrant gRPC RetrieveAsync call
var records = await store.GetBatchAsync(new[] { "id-1", "id-2", "id-3" });
// Count total vectors (excludes internal schema marker)
long count = await store.CountAsync();
// Count with metadata filter
long filtered = await store.CountAsync(
new VectorFilter().Where("category", "finance"));
GetBatchAsync maps string IDs to deterministic Qdrant point UUIDs, calls the Qdrant batch RetrieveAsync API, and applies metadata conditions client-side. CountAsync uses the Qdrant server-side CountAsync API and always excludes the internal schema marker point from the result.
Vector Replacement
ReplaceByFilterAsync is available via the IVectorStore default interface method. It performs sequential DeleteByFilterAsync → UpsertBatchAsync. Qdrant does not support server-side transactions, so sequential execution is the best available behavior:
var filter = new VectorFilter().Where("full_path", "/docs/file.md");
await store.ReplaceByFilterAsync(filter, newRecords);
Connection Verification
Call VerifyConnectionAsync to test gRPC connectivity before running queries:
var store = new QdrantStore(new QdrantOptions
{
Host = "localhost",
Port = 6334,
CollectionName = "my_vectors",
Dimension = 1536
});
try
{
await store.VerifyConnectionAsync();
Console.WriteLine("Connected!");
}
catch (Exception ex)
{
Console.WriteLine($"Connection failed: {ex.Message}");
}
License
See repository root for license information.
Filters on the new retrieval paths
TextSearchAsync and configurable hybrid search apply equality, membership, existence and nested AND/OR filters before candidate truncation. String range and LIKE conditions fail explicitly rather than disappearing. Ne/NotIn exclude records missing the key; empty In matches no records. Flat meta.* payload keys are addressed literally, with no stored-data migration. Keys containing quotes or backslashes cannot be represented by the supported path grammar and are rejected. The legacy filter limitations listed above do not describe these new methods.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- Mythosia.VectorDb.Abstractions (>= 4.1.0)
- Qdrant.Client (>= 1.17.0)
- System.IO.Hashing (>= 10.0.7)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
v4.2.0: Adds sparse-only text queries and configurable normalized weighted RRF while retaining configured server RRF/DBSF for direct legacy hybrid calls. Treats flat metadata keys literally and excludes missing fields from Ne/NotIn. New text/configurable-hybrid paths support equality, membership, existence and nested groups; unsupported string range/LIKE filters and unrepresentable keys fail explicitly. No stored payload migration is required. Uses System.IO.Hashing 10.0.7. Full notes: https://github.com/AJ-comp/Mythosia.AI/blob/main/src/vectordb/Mythosia.VectorDb.Qdrant/RELEASE_NOTES.md#v420