helgesverre/milvus

PHP Client for the Milvus Rest API

Maintainers

Package info

github.com/HelgeSverre/milvus

Homepage

pkg:composer/helgesverre/milvus

Transparency log

Statistics

Installs: 7 298

Dependents: 0

Suggesters: 0

Stars: 33

Open Issues: 0

v0.3.1 2026-08-02 12:22 UTC

This package is auto-updated.

Last update: 2026-08-02 12:24:36 UTC


README

Milvus.io PHP API Client

Latest Version on Packagist Total Downloads CI Code Coverage

Milvus is an open-source vector database that is highly flexible, reliable, and blazing fast. It supports adding, deleting, updating, and near real-time search of vectors on a trillion-byte scale.

This package is a PHP client for the stable Milvus REST v2 endpoints shared by Milvus 2.5 through 3.0. It is tested against Milvus 2.5.21, 2.6.21, and 3.0.0, and built on Saloon.

See the Milvus REST API documentation and the official database, collection, and vector OpenAPI definitions.

See the changelog for the complete release history and upgrade notes.

Compatibility

The supported runtime matrix is PHP 8.3–8.5 and Laravel 12–13. Laravel 10 and 11 remain covered by compatibility tests for existing applications, but both framework versions are end-of-life and should not be used for new installations. Their CI jobs use Composer's command-scoped --no-blocking option because current Composer versions block their affected upstream framework releases.

Laravel Version Tested PHP Status
13.x 8.5 Supported
12.x 8.4 Supported
11.x 8.3 Legacy compatibility
10.x 8.3 Legacy compatibility

Versions

Milvus Version PHP Client Version
v3.0.x v0.2.0+
v2.6.x v0.2.0+
v2.5.x v0.2.0+
v2.3.x v0.0.x-v0.1.x

PHP 8.3–8.5 is supported. Laravel is optional; the client can also be used as a standalone Saloon connector.

Installation

You can install the package via composer:

composer require helgesverre/milvus

You can publish the config file with:

php artisan vendor:publish --tag="milvus-config"

This is the contents of the published config/milvus.php file:

return [
    'token' => env('MILVUS_TOKEN'),
    'username' => env('MILVUS_USERNAME'),
    'password' => env('MILVUS_PASSWORD'),
    'host' => env('MILVUS_HOST', 'localhost'),
    'port' => env('MILVUS_PORT', '19530'),
];

MILVUS_TOKEN takes precedence. When it is absent, the Laravel service provider builds the token from a complete MILVUS_USERNAME and MILVUS_PASSWORD pair. Milvus expects that credential token in raw username:password format.

Usage

With Laravel

For Laravel users, you can use the Milvus facade to interact with the Milvus API:

use HelgeSverre\Milvus\Facades\Milvus;

// NOTE: dbName is optional and defaults to 'default', this is only relevant if you have multiple databases.
// List all collections in the 'default' database
Milvus::collections()->list(
    dbName: 'default'
);

// Create a new collection named 'documents' in the 'default' database with a specified dimension
Milvus::collections()->create(
    collectionName: 'documents',
    dimension: 128,
    dbName: 'default',
    autoID: false,
);

// Describe the structure and properties of the 'documents' collection in the 'default' database
Milvus::collections()->describe(
    collectionName: 'documents',
    dbName: 'default',
);

// Drop or delete the 'documents' collection from the 'default' database
Milvus::collections()->drop(
    collectionName: 'documents',
    dbName: 'default',
);

// Insert a new vector into the 'documents' collection with additional fields like title and link
// Note "vector" is a reserved field name and must be used for the vector data
Milvus::vector()->insert(
    collectionName: 'documents',
    data: [
        [
            'id' => 123129471497,
            'vector' => [0.1, 0.2, 0.3 /* etc... */],
            'title' => 'Document name here',
            'link' => 'https://example.com/document-name-here',
        ],
    ]
);

// Search for similar vectors in the 'documents' collection using a provided vector
Milvus::vector()->search(
    collectionName: 'documents',
    data: [[0.1, 0.2, 0.3 /* etc... */]],
    annsField: 'vector',
);

// Delete a vector from the 'documents' collection using its ID
Milvus::vector()->delete(
    collectionName: 'documents',
    filter: 'id == 123129471497',
);

// Query the 'documents' collection for specific documents using a filter condition and select specific output fields
Milvus::vector()->query(
    collectionName: 'documents',
    filter: 'id in [443300716234671427, 443300716234671426]',
    outputFields: ['id', 'title', 'link'],
);

// Retrieve a specific vector from the 'documents' collection using its ID
Milvus::vector()->get(
    id: '123129471497',
    collectionName: 'documents'
);

// Update or insert a vector in the 'documents' collection. If the ID exists, it's updated; if not, a new entry is created
Milvus::vector()->upsert(
    collectionName: 'documents',
    data: [
        [
            'id' => 123129471497,
            'vector' => [0.1, 0.2, 0.3 /* etc... */],
            'title' => 'Document name here',
            'link' => 'https://example.com/document-name-here',
        ],
    ]
);

Managing databases

Database operations use the Milvus REST v2 database endpoints. The REST API is stateless, so pass dbName to each collection or vector operation that should run outside the default database.

Milvus::databases()->create(
    dbName: 'analytics',
    properties: ['database.replica.number' => 1],
)->dto()->throwIfFailed();

$databases = Milvus::databases()->list()->dto()->throwIfFailed();
$analytics = Milvus::databases()->describe('analytics')->dto()->throwIfFailed();

Milvus::collections()->list(dbName: 'analytics');

Milvus::databases()->drop('analytics')->dto()->throwIfFailed();

The default database cannot be dropped, and a database must have no collections before it can be dropped. Database list and describe responses expose databases and database; database IDs remain strings if they exceed PHP's integer range.

Filtering vector searches

Use filter to restrict similarity search by scalar fields. data is an array of query vectors and annsField is the collection's vector field. Scalar fields must be part of the collection schema or accepted by its dynamic field.

$results = Milvus::vector()->search(
    collectionName: 'documents',
    data: [[0.1, 0.2, 0.3 /* etc... */]],
    annsField: 'vector',
    filter: 'project_id == 10',
    limit: 10,
    outputFields: ['title', 'project_id'],
)->dto()->throwIfFailed();

The same Milvus expression syntax works for query() and delete(), including expressions such as id in [1, 2, 3] and status == "published".

Custom schemas and AutoID

For explicit field control, pass a custom schema. Field options follow the Milvus REST schema unchanged:

Milvus::collections()->create(
    collectionName: 'documents',
    schema: [
        'autoID' => false,
        'enableDynamicField' => false,
        'fields' => [
            ['fieldName' => 'id', 'dataType' => 'Int64', 'isPrimary' => true],
            [
                'fieldName' => 'vector',
                'dataType' => 'FloatVector',
                'elementTypeParams' => ['dim' => '1536'],
            ],
            ['fieldName' => 'project_id', 'dataType' => 'Int64'],
        ],
    ],
);

With quick setup, set autoID: true and omit the primary key from inserted rows. The mutation DTO returns generated IDs through $response->dto()->result->insertIds.

Milvus 3 features

Milvus 3 can search using existing entity IDs instead of providing a vector directly:

Milvus::vector()->search(
    collectionName: 'documents',
    data: null,
    annsField: 'vector',
    ids: [123129471497],
    outputFields: ['title', 'link'],
);

Partial upserts let you update scalar fields without resending the vector:

Milvus::vector()->upsert(
    collectionName: 'documents',
    data: [
        ['id' => 123129471497, 'title' => 'Updated document name'],
    ],
    partialUpdate: true,
);

Without Laravel

Without Laravel, create a Milvus instance with a token, host, and port. For username/password authentication, pass the raw username:password value as the token.

use HelgeSverre\Milvus\Milvus;

$milvus = new Milvus(
    token: 'root:Milvus',
    host: 'localhost',
    port: '19530'
);

The connector exposes the same methods shown above; replace Milvus:: with $milvus->.

Typed responses

Every request still returns a Saloon response, so existing json() and collect() calls continue to work. Call dto() when you want a validated response object:

$search = Milvus::vector()->search(
    collectionName: 'documents',
    data: [[0.1, 0.2, 0.3]],
    annsField: 'vector',
    limit: 3,
    outputFields: ['title'],
)->dto()->throwIfFailed();

foreach ($search->entities as $entity) {
    echo $entity->id.' '.$entity->field('title').PHP_EOL;
}

Milvus can report API failures with HTTP status 200, so use throwIfFailed() when handling a DTO. It throws a MilvusApiException containing the Milvus error code. Malformed success payloads throw InvalidResponseException instead of silently returning partial data.

The response types are EmptyResponse for create/drop, DatabaseListResponse, DatabaseDescriptionResponse, CollectionListResponse, CollectionDescriptionResponse, MutationResponse for insert/upsert/delete, EntityResponse for get/query, and SearchResponse. Dynamic entity fields and unknown future response fields remain available through raw.

Using with Zilliz Cloud

Milvus v0.2 and newer automatically uses the /v2/vectordb/... endpoints; do not include an API version or operation path in the host. For Zilliz Cloud, pass the HTTPS cluster endpoint, port 443, and your API key as the token:

use HelgeSverre\Milvus\Milvus;

$milvus = new Milvus(
    token: 'your-api-key',
    host: 'https://in03-example.serverless.gcp-us-west1.cloud.zilliz.com',
    port: '443'
);

Existing applications that still send requests to /v1/vector/... should upgrade with composer require helgesverre/milvus:^0.2.

Example: Semantic Search with Milvus and OpenAI Embeddings

This example demonstrates how to perform a semantic search in Milvus using embeddings generated from OpenAI.

Prepare Your Data

First, create an array of data you wish to index. In this example, we'll use blog posts with titles, summaries, and tags.

$blogPosts = [
    [
        'title' => 'Exploring Laravel',
        'summary' => 'A deep dive into Laravel frameworks...',
        'tags' => ['PHP', 'Laravel', 'Web Development']
    ],
       [
        'title' => 'Exploring Laravel',
        'summary' => 'A deep dive into Laravel frameworks, exploring its features and benefits for modern web development.',
        'tags' => ['PHP', 'Laravel', 'Web Development']
    ],
    [
        'title' => 'Introduction to React',
        'summary' => 'Understanding the basics of React and how it revolutionizes frontend development.',
        'tags' => ['JavaScript', 'React', 'Frontend']
    ],
    [
        'title' => 'Getting Started with Vue.js',
        'summary' => 'A beginner’s guide to building interactive web interfaces with Vue.js.',
        'tags' => ['JavaScript', 'Vue.js', 'Frontend']
    ],
];

Generate Embeddings

Use OpenAI's embeddings API to convert the summaries of your blog posts into vector embeddings.

$summaries = array_column($blogPosts, 'summary');
$embeddingsResponse = OpenAI::client('sk-your-openai-api-key')
    ->embeddings()
    ->create([
        'model' => 'text-embedding-ada-002',
        'input' => $summaries,
    ]);

foreach ($embeddingsResponse->embeddings as $embedding) {
    $blogPosts[$embedding->index]['vector'] = $embedding->embedding;
}

Create Milvus collection

Create a collection in Milvus to store your blog post embeddings, note that the dimension of the embeddings must match the dimension of the embeddings generated by OpenAI (1536 if you are using the text-embedding-ada-002 model).

$milvus = new Milvus(
    token: 'your-token',
    host: 'localhost',
    port: '19530'
);


$milvus->collections()->create(
    collectionName: 'blog_posts',
    dimension: 1536,
);

Insert into Milvus

Insert these embeddings, along with other blog post data, into your Milvus collection.

$insertResponse = $milvus->vector()->insert('blog_posts', $blogPosts);

Creating a Search Vector with OpenAI

Generate a search vector for your query, akin to how you processed the blog posts.

$searchVectorResponse = OpenAI::client('sk-your-openai-api-key')
    ->embeddings()
    ->create([
        'model' => 'text-embedding-ada-002',
        'input' => 'laravel framework',
    ]);

$searchEmbedding = $searchVectorResponse->embeddings[0]->embedding;

Searching using the Embedding in Milvus

Use the Milvus client to perform a search with the generated embedding.

$searchResponse = $milvus->vector()->search(
    collectionName: 'blog_posts',
    data: [$searchEmbedding],
    annsField: 'vector',
    limit: 3,
    outputFields: ['title', 'summary', 'tags']
)->dto()->throwIfFailed();

// Output the search results
foreach ($searchResponse->entities as $result) {
    echo "Title: " . $result->field('title') . "\n";
    echo "Summary: " . $result->field('summary') . "\n";
    echo "Tags: " . implode(', ', $result->field('tags', [])) . "\n\n";
}

Running Milvus in Docker

To quickly get started with Milvus, you can run it in Docker, by using the following command

# Download the docker-compose.yml file
wget https://github.com/milvus-io/milvus/releases/download/v3.0.0/milvus-standalone-docker-compose.yml -O docker-compose.yml

# Start Milvus
docker compose up --wait --wait-timeout 180

A healthcheck endpoint will now be available on http://localhost:9091/healthz, and the Milvus API will be available on http://localhost:19530.

To stop Milvus, run docker compose down. Data is stored in the local volumes/ directory.

For more details Installing Milvus Standalone with Docker Compose

For production workloads, consider checking out Zilliz.com, which are the developers behind Milvus and provides a hosted version of Milvus in the Cloud ☁️.

Testing

The fast suite verifies request serialization, response decoding and edge cases, authentication, Laravel service-provider resolution, and architecture rules without contacting Milvus:

just unit

The full test command starts Docker and runs both the unit suite and live full-client smoke, database, collection, custom-schema, AutoID, filtered-search, error-envelope, and response-DTO scenarios:

just test

To run only the integration suite against a specific supported Milvus version:

cp .env.example .env
just integration 2.5.21

Run the remaining release checks with:

composer analyse src
composer format:test
composer validate --strict
composer audit

CI repeats the integration test against Milvus 2.5.21, 2.6.21, and 3.0.0, and runs the package suite across PHP 8.3–8.5 and Laravel 10–13.

License

The MIT License (MIT). Please see License File for more information.

Disclaimer

"Milvus®" and the Milvus logo are registered trademarks of the Linux Foundation (LF Projects, LLC). This package is not affiliated with, endorsed by, or sponsored by the Linux Foundation. It's developed independently and uses the "Milvus" name under fair use, solely for identification. All trademarks and registered trademarks, including "Milvus®", are the property of their respective owners. "Milvus®" is a registered trademark of the Linux Foundation.