Milvus 3.0 brought full-text retrieval, sorting, aggregation and faceting into one engine, and we put it head to head with Elasticsearch on live queries.
lab Run the comparison yourself
Docker Compose, one dataset, side-by-side queries against Elasticsearch and Milvus.
recording Watch the previous webinar
The full walkthrough of what Milvus 3.0 added and how the two engines compared.
TodayFrom comparison to migration: tooling, deployment choices and lessons from real migrations.
The migration path
The major stages and decisions in an Elasticsearch / OpenSearch migration to Milvus or Zilliz Cloud
Tooling + real walkthrough
VTS (open source) and Zilliz Migration Service (managed), then a real migration workflow end to end
Licensing & commercial tradeoffs
Licensing models and the commercial considerations behind Elastic Cloud (ESS), OpenSearch and Zilliz Cloud
Deployment choices
Open-source Milvus, Zilliz Cloud and Zilliz BYOC across operating model, control and security
Customer migration stories
Why real teams moved, how they approached it, and what they learned along the way
Live AMA
Around 20 minutes for your migration, architecture and evaluation questions
Vector search is a plugin
Seen at Orfium, Rexera, OpusSearch
Read & write fight for resource
Seen at 123RF, Jobright, HelloBike
Cost scales with data, not value
Seen at 123RF, Rexera, UNIwise
None of these are tuning problems. They are architecture problems.
Every Elasticsearch / OpenSearch migration passes through the same stages:
open source
VTS
Open-source migration tool: streams data from Elasticsearch / OpenSearch into Milvus. Run it yourself, inspect every step
managed
Zilliz Migration Service
Managed service built on VTS: source into Milvus or Zilliz Cloud with the operational heavy lifting handled for you
documentation
Same ~100k Amazon product documents, loaded from one Parquet: 2×1024-d COSINE FP32 vectors, no re-embedding, same BM25 analyzer chain.
Elasticsearch 9.5.2→Milvus 3.0
VTSlocal
Docker to Docker on the laptop. Open-source VTS, every step inspectable
Elastic Cloud 9.5.2→Zilliz Cloud
VTScloud
Managed to managed, both in AWS eu-west-1. The same VTS job pointed at two cloud endpoints
OpenSearch 3.7.0→Zilliz Cloud
ZMSmanaged
AWS OpenSearch Service into a second Zilliz Cloud cluster via Zilliz Migration Service, from the console
local · docker
Elasticsearch 9.5.2
Single node, 8 GB / 4 CPU, 4 GB JVM heap. 1 primary / 0 replica
local · docker
Milvus 3.0
Standalone with etcd + MinIO, 8 GB / 4 CPU, 1 replica
aws eu-west-1
Elastic Cloud 9.5.2
2 × c6gd data nodes, 4 GB each. 1 primary / 1 replica
aws eu-west-1
AWS OpenSearch 3.7.0
2 × c8g.large.search data nodes, 4GB each with 120 GB gp3. 5 primary / 1 replica
aws eu-west-1
Zilliz CloudVTS target
1 CU, performance-optimized
aws eu-west-1
Zilliz CloudZMS target
1 CU, performance-optimized. A second, separate cluster
Every cloud service in AWS eu-west-1 to control for network latency. The client for every figure is one laptop: Apple M5, Docker Desktop with all 10 cores, on home fibre internet in London (1 Gbps down / 100 Mbps up).
Cloud Standard: $9.93 for two c6gd data nodes, $2.48 Kibana node, $1.24 mandatory master, ~$1 data transfer, $0.09 storage. No auto scale-down, no suspend option.
Source: cloud.elastic.co/pricing$6.82 for two c8g.large.search nodes, $1.06 for 120 GB gp3 each, no dedicated master. No auto scale-down, no suspend option.
Dedicated Standard: $4.68 compute, $0.01 storage including backups. Auto-scales storage and throughput both ways, can suspend to zero compute when not in use.
Source: zilliz.com/pricing/pricing-guideAll AWS eu-west-1, equivalent hardware specifications, lowest commercial tier. List price per 24h of runtime.
Elasticsearch / OpenSearch to Milvus migration workflows, end to end:
Schema first. The target collection exists before VTS is asked to move a row, so every field and index is explicit up-front.
1 · before the job
Decide the schema
Build the empty collection first: fields, analyzer, BM25 function and indexes, then load it. Indexes are enabled on vector fields and common filter fields.
2 · one JSON document
Submit the job
Source Elasticsearch scrolls the index 500 docs at a time, sink Milvus inserts in batches of 500. Posted to the VTS REST API, polled until SinkWriteCount reaches 97,894, then flush. The cloud leg is the same job with two endpoints swapped
3 · by name, then by target type
How VTS maps fields
Every projected source column needs a Milvus field of the same name; the sink reads the collection schema first and converts each value to the declared type. Two explicit mappings: array_column (ES has no array type) and pk = parent_asin. Computed fields like text_sparse are not sent
ZMS simplifies: the wizard reads the existing index and creates the target collection. Its analyzer step takes our filter chain and the text_sparse name, so BM25 is still declared before rows land. Indexes come afterwards (you can always add indexes to existing fields).
Every field type an index needs has a home in Milvus. Most of the mapping is mechanical: the analyzer chain and the vector index are the two decisions that actually matter.
| Need | Elasticsearch / OpenSearch | Milvus | Notes |
|---|---|---|---|
| Primary key | _id, implicit string |
VARCHAR or INT64 with is_primary, auto_id optional |
Keep the source _id: it is the join key for validation |
| Numbers, booleans | long integer double float boolean |
INT64 INT32 INT16 INT8 DOUBLE FLOAT BOOL |
nullable and default_value per field |
| Exact-match strings | keyword |
VARCHAR + INVERTED or BITMAP index |
Filters, sort, group by |
| Full-text, BM25 | text + analyzer |
VARCHAR enable_analyzer → BM25 Function → SPARSE_FLOAT_VECTOR |
Same tokenizer and filter chain, declared per field |
| Term, phrase match | term match_phrase |
enable_match → TEXT_MATCH, PHRASE_MATCH in the filter |
A boolean pre-filter, not a scorer |
| Dense vectors | dense_vector / knn_vector; HNSW with bbq, int8, faiss compression_level |
FLOAT_VECTOR (FLOAT16 BFLOAT16 INT8 BINARY); HNSW IVF_RABITQ DISKANN AUTOINDEX |
Several vector fields per collection, hybrid search across them |
| Learned sparse | sparse_vector / rank_features (ELSER, neural-sparse) |
SPARSE_FLOAT_VECTOR + SPARSE_INVERTED_INDEX, metric IP |
The same field type BM25 writes into |
| Arrays | any field, multi-valued | ARRAY<type> with max_capacity |
array_contains, array_length in filters |
| Objects, nested | object nested flattened |
JSON + JSON path index; dynamic fields for unmapped keys |
Nested docs flatten, or become a second collection |
| Dates | date |
TIMESTAMPTZ, or INT64 epoch |
Range filter and sort either way |
| Geo | geo_point geo_shape |
GEOMETRY, WKT in, ST_* predicates in filters |
Supports distance, bounding polygons etc. |
Ground truth
Exact, not sampled: brute-force top-100 over all 97,894 normalized vectors for 200 fixed queries drawn from the corpus. Text and image spaces scored separately, each against its own truth
Filter specificity
Filtered benches use 20 ≤ price ≤ 60: 27,300 rows, 27.9% of the corpus. Ground truth is recomputed over only those rows, and the bench filter is the same constant by construction
Latency & throughput
One discarded warm-up, then every query timed sequentially: no queueing noise in p50 / p95. QPS is a separate phase with a bounded pool of 8. All client-observed
Recall & BM25 parity
recall@10 is the overlap with the exact top-10. BM25 has no ground truth, so a fixed 20-query set is scored as overlap@10 against the ES-local baseline
Honesty rulesBenches never run concurrently across systems. Latency and QPS are client-observed from my laptop and are illustrative; recall, filtered recall and BM25 overlap are the architectural numbers. For hardware-scale claims, see published VDBBench studies.
open source
OpenSearch
source available
Elasticsearch
open source
Milvus
aws managed
Amazon OpenSearch Service
elastic managed
Elastic Cloud (ESS)
zilliz managed
Zilliz Cloud
Self-managed software
Milvus
The most popular open source vectorDB
Milvus Lite · Docker · Kubernetes
Fully managed service
Zilliz Cloud
Milvus as a service, tuned and scaled for you
AWS · Google Cloud · Azure
Bring your own cloud
Zilliz Cloud BYOC
Managed control plane, deployed in your VPC
AWS · Google Cloud · Azure
Migrate once: one Milvus API & SDK across all three with seamless migration
OpenSearch→Zilliz Cloudtext-to-image and reverse image searchSource: zilliz.com/customers/123rf
Average, down from ~100ms
vs. unstable OpenSearch environment
Image vectors, growing daily
10M+ per job, no query impact
why they moved
what they built
what they learned
“Moving to Zilliz Cloud didn't just cut our infrastructure costs dramatically; it gave our engineering team the confidence that search will scale with our business instead of holding it back.” Su-Meng Yong, Engineering Team Lead, 123RF
OpenSearch→Zilliz Cloudagentic RAG and memory for 2M+ devices in 170+ countriesSource: zilliz.com/customers/plaud
Embeddings per region, and growing
Average, <800ms at P99
Billion-scale with no query impact
Incl. new fields & indexes and upgrades
why they moved
what they built
what they learned
“Zilliz Cloud gives us a solid foundation we can trust for agentic memory retrieval at a massive scale, so our team can put its energy into product innovation and user experience, not the plumbing beneath it.” Charles Liu, Co-founder & CTO, Plaud