← Code Recipes
Embeddings & Search

Milvus Free code examples in C#, Python, Java and JavaScript

Vector database built for billion-scale collections.

C# Python Java JavaScript

WHAT THIS PAGE COVERS

Partitioning and index choice, which is what changes at that scale.

Model text-embedding-3-small
Package pymilvus
Checked

Model ids change. If a sample returns a 404 on the model, that is the first thing to check against the official docs.

Install

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"
Create a collection

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 and search

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");
Index choice and partition keys are a Python-first surface

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.
Install

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"
A collection you can query immediately

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"],
)
Choosing an index instead of accepting one

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,
)
The partition key, which is what changes at a billion rows

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
)
Searching one tenant

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"])
Install

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"
Create a collection

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());
    }
}
Choosing the index

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());
Searching one tenant

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")));
Install

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"
Create a collection with a chosen index

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,
});
Building the index

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" });
Insert and search one tenant

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.

Read the docs ↗

https://milvus.io/docs

New drops, every week

Free tools & Claude templates, landing regularly.

We keep shipping new free tools, code recipes and Claude Code templates. Follow @freeyantra on Instagram or join the newsletter so you never miss one.

Follow @freeyantra

or get them by email

Double opt-in. Unsubscribe any time. Privacy