This is the multi-page printable view of this section. Click here to print.
Vector
1 - Vector overview
Alpha
The vector API is currently in alpha.Dapr’s vector API provides a single, portable way to store, retrieve, and run similarity queries over dense vectors (embeddings) in vector databases such as Meilisearch, Pinecone, Qdrant, Milvus, and pgvector. It is the storage and retrieval layer for AI patterns like retrieval-augmented generation (RAG), semantic search, and recommendations: your application writes embeddings with some metadata, then asks for the records closest to a query vector. You swap the underlying vector database by changing the vector component.
The vector API stores vectors that are already embedded. Generating embeddings is out of scope; your application generates them, for example with an embedding model, before calling the API. For keyword and full-text search over JSON documents, use the search API instead.
You can pair the vector API with Dapr functionality, like:
- The conversation API to send the retrieved context to a large language model (LLM)
- Resiliency policies including circuit breakers, timeouts, and retries for calls to the vector database
- Observability with metrics (
dapr_component_vector_countanddapr_component_vector_latencies, tagged byoperation) and distributed tracing using OpenTelemetry and Zipkin - Secret store references for vector database credentials
Concepts
| Concept | Description |
|---|---|
| Vector store | A Dapr vector component that connects to a vector database. |
| Collection | A named container of records. Every collection has fixed dimensions and a distance metric. |
| Record | A caller-supplied id, a dense vector of float values, structured metadata that can be filtered on, and an opaque payload that is stored and returned unchanged but never filterable. |
| Match | A record returned by a query, with its similarity score. |
Features
Collection management
Create, get, list, and delete collections. When you create a collection, you set:
dimensions(required): the length of every vector stored in the collection. A request withoutdimensionsis rejected withINVALID_ARGUMENT.metric(optional): the distance metric used to compare vectors. When unspecified, the component’s default metric is used. Requesting a metric the vector database can’t provide returnsINVALID_ARGUMENT.
Creating a collection that already exists returns ALREADY_EXISTS. Getting a collection returns its effective dimensions and metric, an approximate record count, and component-specific properties.
Distance metrics and scores
| Metric | Score |
|---|---|
DISTANCE_METRIC_COSINE | Cosine similarity in [-1, 1]. Higher is closer. |
DISTANCE_METRIC_DOT_PRODUCT | Dot product. Higher is closer. |
DISTANCE_METRIC_EUCLIDEAN | Euclidean distance. Lower is closer. |
Scores are the unnormalised value of the metric, so you can reason about them the same way across vector databases. A query can set a scoreThreshold to drop weak matches. The threshold is inclusive and uses the metric’s own scale: matches with a score greater than or equal to the threshold are kept for cosine and dot product, and matches with a score less than or equal to it are kept for Euclidean.
Keyed upserts
Records are written with a keyed upsert. Every record carries a non-empty id that must be unique within the request, so retrying the same request is idempotent. Record values must have the collection’s dimensions. Records that fail individually are reported in failedItems, while the rest of the batch is still written.
Records can be read back by ID in request order, optionally with their vector values, and deleted by ID. Deleting an ID that does not exist is not an error.
Write acknowledgements
Vector writes share the acknowledgement model of the search API. Upserts and deletes accept the same options to either return as soon as the vector database accepts the write (INDEXING_MODE_RETURN_ON_ACCEPTANCE, the default), or wait for the final result (INDEXING_MODE_WAIT_FOR_COMPLETION) with a timeout and a timeout action. The wait timeout follows the same rules, including the headroom it needs below the vector store’s resiliency timeout. See write acknowledgements in the search overview.
Querying
A query request carries a required query that finds the topK records closest to exactly one of:
- A query vector (
vector.values), which must not be empty. Only thevaluesofvectorare read; its other fields are ignored. - The stored vector of an existing record (
byId).
When topK is omitted or 0, the component’s default applies. A query can also set a metric that determines how scores and the score threshold are interpreted; when unspecified, the collection’s metric is used. Queries can be combined with:
- Metadata filters written in the portable filter language
- Score thresholds to keep only sufficiently close matches
- Projection with
includeValuesandincludePayloadto control whether vector values and payloads are returned
Batch queries
A batch query runs several queries against the same collection in one call. The store, collection, and request metadata are set once on the batch request, and each entry of queries has the same shape as the query of a single query request. Each query succeeds or fails on its own, and the response contains one result per query in request order: either the query’s matches or its error. Only request-wide failures, such as a missing collection or invalid credentials, fail the whole call.
Filter language
Filters address the structured metadata of a record, using dotted notation for nested fields, for example dealer.city. The filter language is the same as the search API’s:
| Type | Operators |
|---|---|
| Comparison | $eq, $ne, $gt, $gte, $lt, $lte |
| Set | $in, $nin |
| Existence | $exists (boolean) |
| Logical | $and, $or, $not |
For example, the following filter matches records for Tesla cars sold by a dealer in Seattle or Portland:
{
"make": "tesla",
"dealer.city": { "$in": ["Seattle", "Portland"] }
}
Error reporting and resiliency
Vector errors are reported like search errors: errors that the vector database returns keep their canonical gRPC status code and details, also over HTTP, while errors that Dapr detects before calling the vector database use ERR_VECTOR_STORE_NOT_FOUND, ERR_VECTOR_STORE_NOT_CONFIGURED, or ERR_VECTOR_INVALID_REQUEST. Calls run under the vector store’s resiliency policies with the same retry and timeout behaviour as the search API, where a retried collection delete that returns NOT_FOUND is reported as successful.
Try out the vector API
How-to guide
Now that you’ve learned what the vector API building block provides, learn how it can work in your application with the following how-to guide:
Next steps
2 - How-To: Store and query vectors
Alpha
The vector API is currently in alpha.Let’s get started using the vector API. In this guide, you’ll learn how to:
- Set up a Meilisearch vector component.
- Create a collection.
- Upsert records with vectors, metadata, and payloads.
- Run similarity queries with metadata filters and score thresholds.
- Run several queries in one batch.
- Retrieve and delete records by ID.
The examples in this guide call the Dapr HTTP API with curl and use 4-dimensional vectors to keep them readable. In a real application, vectors come from an embedding model and typically have hundreds or thousands of dimensions. The HTTP API uses the JSON mapping of the protobuf messages: field names are camelCase, 64-bit integers are strings, and bytes are base64-encoded. Responses include fields set to their zero value and omit optional fields that aren’t set. For .NET examples using typed payloads, metadata filters, and batch queries, see the Vector .NET SDK guide, targeting SDK 1.19.0 or later.
Pre-requisites
Start Meilisearch
Run a local Meilisearch instance with Docker:
docker run -d --name meilisearch -p 7700:7700 \
-e MEILI_MASTER_KEY=masterKey \
getmeili/meilisearch:v1.43
Set up the vector component
Create a file called vector.yaml in your components directory (for example, ./components):
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: myvectors
spec:
type: vector.meilisearch
version: v1
metadata:
- name: host
value: "http://localhost:7700"
- name: apiKey
value: "masterKey"
Warning
The above example uses secrets as plain strings. Using a secret store is recommended to store the secrets, as described here.See the vector component specs for all supported vector components.
Run the Dapr sidecar
Start a Dapr sidecar that loads the component:
dapr run --app-id vector-app --dapr-http-port 3500 --resources-path ./components
Create a collection
Create a collection called cars for 4-dimensional vectors compared with cosine similarity:
curl -X POST http://localhost:3500/v1.0-alpha1/vector/myvectors/collections/cars \
-H "Content-Type: application/json" \
-d '{
"dimensions": 4,
"metric": "DISTANCE_METRIC_COSINE"
}'
Confirm the collection was created:
curl http://localhost:3500/v1.0-alpha1/vector/myvectors/collections/cars
{
"collection": "cars",
"recordCount": "0",
"properties": {
"filterableAttributes": "daprMetadata",
"primaryKey": "id"
},
"dimensions": 4,
"metric": "DISTANCE_METRIC_COSINE"
}
Upsert records
Each record has an id, the vector values, structured metadata that you can filter on, and an optional opaque payload. Because payload is defined as bytes, the HTTP API expects it base64-encoded. In this example, the payload of car-1 is the base64 encoding of {"name":"Model 3"}.
The following request upserts three records and waits up to 10 seconds for them to be written:
curl -X POST http://localhost:3500/v1.0-alpha1/vector/myvectors/collections/cars/vectors \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"id": "car-1",
"values": [1, 0, 0, 0],
"metadata": { "make": "tesla", "year": 2023, "dealer": { "city": "Seattle" } },
"payload": "eyJuYW1lIjoiTW9kZWwgMyJ9"
},
{
"id": "car-2",
"values": [0.9, 0.1, 0, 0],
"metadata": { "make": "tesla", "year": 2025, "dealer": { "city": "Portland" } },
"payload": "eyJuYW1lIjoiTW9kZWwgWSJ9"
},
{
"id": "car-3",
"values": [0, 1, 0, 0],
"metadata": { "make": "ford", "year": 2024, "dealer": { "city": "Seattle" } }
}
],
"options": {
"mode": "INDEXING_MODE_WAIT_FOR_COMPLETION",
"waitTimeout": "10s",
"onWaitTimeout": "INDEXING_WAIT_TIMEOUT_ACTION_FAIL_REQUEST"
}
}'
{
"failedItems": [],
"ack": "INDEX_ACK_COMPLETED"
}
Records that can’t be written are listed in failedItems with their error, while the rest of the batch is still written. For example, with Meilisearch a record whose values don’t have the collection’s dimensions is reported with INVALID_ARGUMENT.
Note
If you omitoptions, Dapr returns as soon as Meilisearch queues the write (INDEX_ACK_QUEUED). The records become queryable shortly afterwards, once Meilisearch has processed the task.Query the collection
Find the two records closest to a query vector, only for Teslas sold in Seattle or Portland, and return their payloads. The query is set in the required query field of the request:
curl -X POST http://localhost:3500/v1.0-alpha1/vector/myvectors/collections/cars/query \
-H "Content-Type: application/json" \
-d '{
"query": {
"vector": { "values": [1, 0, 0, 0] },
"topK": 2,
"filter": {
"make": "tesla",
"dealer.city": { "$in": ["Seattle", "Portland"] }
},
"includePayload": true
}
}'
{
"matches": [
{
"record": {
"id": "car-1",
"values": [],
"payload": "eyJuYW1lIjoiTW9kZWwgMyJ9",
"metadata": { "dealer": { "city": "Seattle" }, "make": "tesla", "year": 2023 }
},
"score": 1
},
{
"record": {
"id": "car-2",
"values": [],
"payload": "eyJuYW1lIjoiTW9kZWwgWSJ9",
"metadata": { "dealer": { "city": "Portland" }, "make": "tesla", "year": 2025 }
},
"score": 0.9938837
}
],
"metric": "DISTANCE_METRIC_COSINE"
}
Scores are the cosine similarity between the query vector and each record, where 1 is identical. values is empty because the query didn’t set includeValues.
Filter by score
Use scoreThreshold to keep only matches that are close enough. For cosine similarity, matches with a score greater than or equal to the threshold are kept:
curl -X POST http://localhost:3500/v1.0-alpha1/vector/myvectors/collections/cars/query \
-H "Content-Type: application/json" \
-d '{
"query": {
"vector": { "values": [1, 0, 0, 0] },
"topK": 10,
"scoreThreshold": 0.5
}
}'
Query by record ID
To find records similar to one you’ve already stored, use byId instead of a query vector:
curl -X POST http://localhost:3500/v1.0-alpha1/vector/myvectors/collections/cars/query \
-H "Content-Type: application/json" \
-d '{
"query": {
"byId": "car-3",
"topK": 3
}
}'
Run a batch of queries
Send several queries to the same collection in one call. Each entry of queries has the same shape as the query of a single query request, and runs against the collection in the URL. Each query succeeds or fails independently, and results are returned in request order. In this example, the second query is invalid because it sets neither a vector nor a record ID, so its slot contains an error while the first query still returns matches (error details are omitted from the response below for brevity):
curl -X POST http://localhost:3500/v1.0-alpha1/vector/myvectors/collections/cars/batch-query \
-H "Content-Type: application/json" \
-d '{
"queries": [
{ "vector": { "values": [0, 1, 0, 0] }, "topK": 1 },
{ "topK": 1 }
]
}'
{
"results": [
{
"response": {
"matches": [
{ "record": { "id": "car-3", "values": [], "metadata": { "dealer": { "city": "Seattle" }, "make": "ford", "year": 2024 } }, "score": 1 }
],
"metric": "DISTANCE_METRIC_COSINE"
}
},
{
"error": {
"code": 3,
"message": "exactly one of vector or by_id must be set"
}
}
]
}
Get records by ID
Fetch records by ID, including their vector values. Found records are returned in request order; IDs that don’t exist are omitted:
curl -X POST http://localhost:3500/v1.0-alpha1/vector/myvectors/collections/cars/vectors/get \
-H "Content-Type: application/json" \
-d '{
"ids": [ "car-1", "car-42" ],
"includeValues": true
}'
Delete records
Deleting records is a write and takes the same options as upserting. IDs that don’t exist are not an error:
curl -X POST http://localhost:3500/v1.0-alpha1/vector/myvectors/collections/cars/vectors/delete \
-H "Content-Type: application/json" \
-d '{
"ids": [ "car-2" ]
}'
Delete the collection
curl -X DELETE http://localhost:3500/v1.0-alpha1/vector/myvectors/collections/cars
Requests without a body (get, list, and delete collection) take request metadata as metadata.<key>=<value> query parameters.