Dense vectors are the most common way to represent embeddings (numbers produced by an ML model) for semantic search. Each record stores a fixed-length array of numbers, and VBase can quickly find the nearest vectors to a query vector.
VBase is our managed vector database layer (backed by Milvus) exposed through the
dodilSDK.
When to use dense vectors
Use dense vectors when you want similarity search over embeddings, for example:
- Semantic search over documents, PDFs, support tickets, or code.
- Recommendation (“show me items similar to this one”).
- Deduplication / near-duplicate detection.
- Clustering and analytics (e.g., grouping similar items).
In practice, you store an embedding vector alongside your metadata (title, source, tags, timestamps, etc.). Later, you embed a query and search for the closest vectors.
Dense vector field types
A dense vector field stores a fixed-length array. The vector type impacts precision, storage, and performance.
Supported dense vector element types:
- FLOAT_VECTOR (float32): highest precision, most common.
- FLOAT16_VECTOR (float16): smaller + faster in some GPU-heavy flows, lower precision.
- BFLOAT16_VECTOR (bfloat16): keeps float32-like range with lower precision, good for large-scale workloads.
- INT8_VECTOR (int8): quantized vectors (–128..127). Great for size/speed when produced by a quantized model; typically supported only with HNSW indexing.
Recommendation: Start with FLOAT_VECTOR unless you have a clear reason to use a lower-precision / quantized format.
Create a collection with a dense vector field
A collection is like a table. You define a schema containing:
- A primary key field (
pk) that uniquely identifies each row. - A dense vector field (
dense_vector) with a fixed dimension (dim). - Optional metadata fields (strings, numbers, booleans) you want to filter on or return in results.
1) Define the schema
from dodil.vbase import FieldSchema, CollectionSchema, DataType
# Primary key: typically VarChar or Int64
pk = FieldSchema(
name="pk",
dtype=DataType.VARCHAR,
is_primary=True,
max_length=100,
)
# Dense vector field: choose dtype + dim
dense_vector = FieldSchema(
name="dense_vector",
dtype=DataType.FLOAT_VECTOR,
dim=768, # must match your embedding model output dimension
)
# Optional metadata fields (examples)
source = FieldSchema(
name="source",
dtype=DataType.VARCHAR,
max_length=512,
)
schema = CollectionSchema(
fields=[pk, dense_vector, source],
description="Documents with dense embeddings",
)2) Set index parameters
Indexes are what make vector search fast. Without an index, search can be slow or restricted depending on deployment settings.
from dodil.vbase import IndexParams
index_params = IndexParams()
index_params.add_index(
field_name="dense_vector",
index_name="dense_vector_index",
index_type="AUTOINDEX",
metric_type="COSINE", # common for normalized embeddings
)Which metric should I use?
- COSINE: common when embeddings are normalized.
- IP (inner product): common for many embedding models; with normalized vectors, IP and COSINE behave similarly.
- L2: Euclidean distance.
Pick what your model and evaluation results prefer.
3) Create the collection
vbase.create_collection(
collection_name="my_collection",
schema=schema,
index_params=index_params,
)Insert data
When inserting, each row must include:
- The primary key (
pk) value. - The dense vector with the same dimension you declared (
dim). - Any metadata fields you defined (optional but recommended).
rows = [
{
"pk": "doc_001",
"dense_vector": [0.1] * 768,
"source": "support",
},
{
"pk": "doc_002",
"dense_vector": [0.2] * 768,
"source": "docs",
},
]
vbase.insert(collection_name="my_collection", data=rows)Search (nearest neighbors)
To search, you provide a query vector and retrieve the most similar rows.
query_vec = [0.15] * 768
results = vbase.search(
collection_name="my_collection",
data=[query_vec],
limit=5,
anns_field="dense_vector",
output_fields=["pk", "source"],
)
print(results)Filter + search
You can combine vector search with metadata filtering.
results = vbase.search(
collection_name="my_collection",
data=[query_vec],
limit=5,
anns_field="dense_vector",
filter='source == "docs"',
output_fields=["pk", "source"],
)Common pitfalls
- Dimension mismatch: the inserted/search vectors must match the schema
dimexactly. - Indexing matters: for large datasets, create an index before expecting fast search.
- Load state: some deployments require collections to be loaded into memory before serving search.
- Precision trade-offs: FLOAT16/BFLOAT16/INT8 reduce size but can reduce recall depending on your workload.
Next steps
- Learn how to design fields and schemas: Schema and Primary field
- Understand performance controls: Indexing, Load & release
- Explore what’s next: Vector Intelligence (coming soon)