Milvus Free code examples in C#, Python, Java and JavaScript
Vector database built for billion-scale collections.
WHAT THIS PAGE COVERS
Partitioning and index choice, which is what changes at that scale.
text-embedding-3-small
pymilvus
Model ids change. If a sample returns a 404 on the model, that is the first thing to check against the official docs.
Milvus.Client is the .NET SDK. The client speaks gRPC, so it is expensive to construct and safe to share.
dotnet add package Milvus.Client
# host, port, TLS — the constructor takes them separately.
setx MILVUS_HOST "localhost"
The schema is an ARRAY OF FieldSchema, not a schema object with a Fields collection. FieldSchema.Create<long> marks the primary key; CreateFloatVector fixes the width.
using Milvus.Client;
var host = Environment.GetEnvironmentVariable("MILVUS_HOST") ?? "localhost";
MilvusClient client = new(host, 19530, false);
await client.CreateCollectionAsync(
"documents",
new[]
{
FieldSchema.Create<long>("id", isPrimaryKey: true),
FieldSchema.CreateVarchar("text", 8192),
// 1536 = text-embedding-3-small, fixed for the collection.
FieldSchema.CreateFloatVector("vector", 1536),
});
MilvusCollection collection = client.GetCollection("documents");
Insert is COLUMNAR: one FieldData per field, each holding the whole batch. That is the opposite of the row-per-object shape most databases take, and it is the thing to get right first.
await collection.InsertAsync(
new FieldData[]
{
FieldData.Create("id", new long[] { 1 }),
FieldData.Create("text", new[] { "The pump stalled." }),
FieldData.CreateFloatVector(
"vector", new ReadOnlyMemory<float>[] { embedding }),
});
SearchResults results = await collection.SearchAsync(
"vector",
new ReadOnlyMemory<float>[] { queryVector },
SimilarityMetricType.Cosine,
limit: 5);
Console.WriteLine($"{results.Scores.Count} hits");
The index and partition-key APIs this page is really about — prepare_index_params, add_index, is_partition_key — are documented against pymilvus and the v2 Java client. THE .NET EQUIVALENTS ARE NOT IN THE SDK'S OWN DOCS, so check its reference rather than adapting the Python shape by eye.
// Verified surface above; this is the boundary of it.
//
// Create the collection and its index from Python or Java — see the
// other tabs — and use the .NET client for reads and writes. That
// split is normal: schema and index are a deployment concern that
// usually lives in a migration, not in the application.
//
// pymilvus: client.prepare_index_params, then add_index
// Java v2: IndexParam.builder, then client.createIndex
//
// A collection must also be LOADED into memory before it is
// searchable. Doing that alongside the index, in the same migration,
// avoids an application that starts and then fails its first query.
Milvus Lite runs in-process from a file path and needs nothing else — good enough to learn the API. A URI pointing at a server is the same client and the same calls.
pip install -U pymilvus
# Local file, no server at all:
# MilvusClient("milvus_demo.db")
#
# Or a real deployment — same client, same methods:
# docker compose up -d
export MILVUS_URI="http://localhost:19530"
Passing `dimension` alone gives a collection with default everything — an AUTOINDEX and an id field. That is the fastest way to a working query and the wrong way to run anything large.
from pymilvus import MilvusClient
client = MilvusClient("milvus_demo.db") # or MilvusClient(uri=...)
client.create_collection(
collection_name="demo_collection",
dimension=1536, # exactly text-embedding-3-small
)
client.insert(collection_name="demo_collection", data=rows)
results = client.search(
collection_name="demo_collection",
data=[query_vector],
limit=2,
output_fields=["text", "source"],
)
prepare_index_params() then add_index() is the current API. HNSW for latency when the RAM is there; IVF_FLAT when it is not, because the graph plus the raw vectors is a lot of memory.
index_params = client.prepare_index_params()
# Graph-based: fastest queries, highest memory. M is the node degree,
# efConstruction trades build time for graph quality.
index_params.add_index(
field_name="vector",
index_type="HNSW",
index_name="vector_index",
metric_type="COSINE",
params={"M": 16, "efConstruction": 64},
)
# Cluster-based alternative: leaner, and recall is tunable at QUERY
# time via nprobe rather than fixed at build time.
# index_type="IVF_FLAT", params={"nlist": 1024}
# INVERTED is the current scalar index; Trie and STL_SORT are older.
index_params.add_index(field_name="source", index_type="INVERTED")
client.create_index(
collection_name="demo_collection",
index_params=index_params,
)
A partition key routes the search. Without it, a filter on tenant_id still touches every partition; with it, Milvus only reads the ones that can contain the answer.
from pymilvus import DataType
schema = client.create_schema(auto_id=False)
schema.add_field("id", DataType.INT64, is_primary=True)
schema.add_field("vector", DataType.FLOAT_VECTOR, dim=1536)
schema.add_field("text", DataType.VARCHAR, max_length=8192)
# THE PARTITION KEY. Milvus hashes it into partitions and prunes the
# search to the matching ones — this is routing, not filtering.
schema.add_field(
"tenant_id", DataType.VARCHAR, max_length=64, is_partition_key=True)
client.create_collection(
collection_name="documents",
schema=schema,
index_params=index_params,
num_partitions=64, # fixed at creation; pick for your tenancy
)
The filter expression names the partition key, and Milvus turns that into partition pruning. The same expression without a partition key is a full scan with a predicate on top.
results = client.search(
collection_name="documents",
data=[query_vector],
limit=5,
# Reads only the partitions that can hold tenant-acme.
filter='tenant_id == "tenant-acme"',
output_fields=["text", "source"],
# IVF_FLAT only: more partitions probed, better recall, slower.
search_params={"params": {"nprobe": 16}},
)
for hit in results[0]:
print(round(hit["distance"], 4), hit["entity"]["text"])
The Java SDK follows the same v2 client shape as pymilvus — MilvusClientV2, not the older MilvusServiceClient.
# Gradle
implementation("io.milvus:milvus-sdk-java:2.6.0")
export MILVUS_URI="http://localhost:19530"
MilvusClientV2 is the current entry point. If a snippet uses MilvusServiceClient it is the v1 API, which has a different method for everything below.
import io.milvus.v2.client.ConnectConfig;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.service.collection.request.CreateCollectionReq;
public class Setup {
public static void main(String[] args) {
String uri = System.getenv("MILVUS_URI");
if (uri == null) throw new IllegalStateException("MILVUS_URI is not set.");
MilvusClientV2 client = new MilvusClientV2(
ConnectConfig.builder().uri(uri).build());
client.createCollection(CreateCollectionReq.builder()
.collectionName("documents")
.dimension(1536) // exactly text-embedding-3-small
.build());
}
}
HNSW trades memory for latency; IVF_FLAT trades latency for memory and lets you raise recall at query time. Picking one deliberately is the whole reason to be on Milvus.
import io.milvus.v2.common.IndexParam;
import io.milvus.v2.service.index.request.CreateIndexReq;
import java.util.List;
import java.util.Map;
IndexParam index = IndexParam.builder()
.fieldName("vector")
.indexType(IndexParam.IndexType.HNSW)
.metricType(IndexParam.MetricType.COSINE)
.extraParams(Map.of("M", 16, "efConstruction", 64))
.build();
client.createIndex(CreateIndexReq.builder()
.collectionName("documents")
.indexParams(List.of(index))
.build());
The filter expression is a string, and when it names the partition key Milvus prunes to the matching partitions instead of scanning and then filtering.
import io.milvus.v2.service.vector.request.SearchReq;
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.response.SearchResp;
SearchResp response = client.search(SearchReq.builder()
.collectionName("documents")
.data(List.of(new FloatVec(queryVector)))
.filter("tenant_id == \"tenant-acme\"")
.outputFields(List.of("text"))
.topK(5)
.build());
response.getSearchResults().get(0).forEach(hit ->
System.out.printf("%.4f %s%n", hit.getScore(), hit.getEntity().get("text")));
The Node client speaks gRPC to a real Milvus server — there is no in-process Lite mode outside Python.
npm install @zilliz/milvus2-sdk-node
export MILVUS_URI="http://localhost:19530"
Declaring the index up front avoids the AUTOINDEX default, which is a reasonable guess and never the right one at scale.
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";
const client = new MilvusClient({ address: process.env.MILVUS_URI });
await client.createCollection({
collection_name: "documents",
fields: [
{ name: "id", data_type: DataType.Int64, is_primary_key: true },
// 1536 = text-embedding-3-small.
{ name: "vector", data_type: DataType.FloatVector, dim: 1536 },
{ name: "text", data_type: DataType.VarChar, max_length: 8192 },
{
name: "tenant_id",
data_type: DataType.VarChar,
max_length: 64,
is_partition_key: true, // routing, not filtering
},
],
num_partitions: 64,
});
HNSW for latency, IVF_FLAT for memory. The metric must match how the vectors were produced — COSINE for normalised embeddings, L2 for raw ones.
await client.createIndex({
collection_name: "documents",
field_name: "vector",
index_type: "HNSW",
metric_type: "COSINE",
params: { M: 16, efConstruction: 64 },
});
// A collection is not searchable until it is loaded into memory.
// Skipping this returns an error that does not mention loading.
await client.loadCollection({ collection_name: "documents" });
The filter names the partition key, so Milvus prunes to the partitions that can match. On a billion-row collection that is the difference between a query and an outage.
await client.insert({
collection_name: "documents",
data: [
{
id: 1,
vector: embedding, // 1536 floats
text: "The pump stalled.",
tenant_id: "tenant-acme",
},
],
});
const results = await client.search({
collection_name: "documents",
data: [queryVector],
limit: 5,
filter: 'tenant_id == "tenant-acme"',
output_fields: ["text"],
});
for (const hit of results.results) {
console.log(hit.score.toFixed(4), hit.text);
}
QUESTIONS
Which languages are the Milvus examples in?
C#, Python, Java and JavaScript. Every sample runs as pasted and reads its credentials from an environment variable — no key is ever written into the code.
Which package do I install for Milvus?
The samples use pymilvus. The install step on each language tab gives the exact command.
Which Milvus model do these examples use?
text-embedding-3-small, checked against the official documentation on 26 September 2026. Model ids change often; if a call returns a model-not-found error, check the current list in the official docs.
Where is the official Milvus documentation?
https://milvus.io/docs — it is linked at the bottom of this page, and it is the authority whenever it disagrees with a sample here.
ALSO IN EMBEDDINGS & SEARCH
The authority
Official Milvus documentation
Model names, pricing and rate limits change faster than any page here can. When this page and the official docs disagree, the official docs are right.
https://milvus.io/docs