Skip to main content

Convex vector store

The ConvexVector class provides vector storage and similarity search using Convex. It stores embeddings inside Convex and performs cosine similarity search in the Mastra adapter.

Development-scale search

ConvexVector reads matching vectors through the Mastra storage handler, filters in JavaScript, computes cosine similarity, sorts results, and returns the top matches. Use it for local development, tests, and small datasets.

For production vector search on Convex, use ConvexNativeVector. It uses the Convex native vectorSearch API, which requires a deployed Convex vector index and a Convex action.

Installation
Direct link to Installation

npm install @mastra/convex@latest

Convex setup
Direct link to Convex setup

Before using ConvexVector, you need to set up the Convex schema and storage handler. See Convex Storage Setup for setup instructions.

Constructor options
Direct link to Constructor options

deploymentUrl:

string
Convex deployment URL (e.g., https://your-project.convex.cloud)

adminAuthToken:

string
Convex admin authentication token

storageFunction?:

string
= mastra/storage:handle
Path to the storage mutation function

Constructor examples
Direct link to Constructor examples

Basic configuration
Direct link to Basic configuration

import { ConvexVector } from '@mastra/convex'

const vectorStore = new ConvexVector({
id: 'convex-vectors',
deploymentUrl: 'https://your-project.convex.cloud',
adminAuthToken: 'your-admin-token',
})

Use ConvexNativeVector for production vector workloads. It stores vectors in a dedicated Convex table and queries a schema-defined Convex vector index.

In convex/schema.ts, define a dedicated table for each Mastra vector index:

convex/schema.ts
import { defineSchema } from 'convex/server'
import { defineMastraNativeVectorTable } from '@mastra/convex/schema'

export default defineSchema({
docs_vectors: defineMastraNativeVectorTable({
dimensions: 1536,
}),
})

In convex/mastra/nativeVector.ts, export the native vector handlers:

convex/mastra/nativeVector.ts
import {
mastraNativeVectorAction,
mastraNativeVectorMutation,
mastraNativeVectorQuery,
} from '@mastra/convex/server'

export const query = mastraNativeVectorAction
export const read = mastraNativeVectorQuery
export const write = mastraNativeVectorMutation

In your Mastra app, configure ConvexNativeVector with the deployed table and vector index:

src/mastra/index.ts
import { ConvexNativeVector } from '@mastra/convex'

const vectorStore = new ConvexNativeVector({
id: 'convex-native-vectors',
deploymentUrl: process.env.CONVEX_URL!,
adminAuthToken: process.env.CONVEX_ADMIN_KEY!,
indexes: {
docs: {
tableName: 'docs_vectors',
vectorIndexName: 'by_embedding',
dimension: 1536,
},
},
})

const results = await vectorStore.query({
indexName: 'docs',
queryVector: embedding,
topK: 10,
})

For native filter support, declare filter fields in your Convex schema. The native vector handlers copy matching metadata fields to top-level document fields when vectors are written.

convex/schema.ts
import { defineSchema, defineTable } from 'convex/server'
import { v } from 'convex/values'

export default defineSchema({
docs_vectors: defineTable({
id: v.string(),
embedding: v.array(v.float64()),
metadata: v.optional(v.any()),
tenantId: v.string(),
})
.index('by_record_id', ['id'])
.vectorIndex('by_embedding', {
vectorField: 'embedding',
dimensions: 1536,
filterFields: ['tenantId'],
}),
})
src/mastra/index.ts
const vectorStore = new ConvexNativeVector({
id: 'convex-native-vectors',
deploymentUrl: process.env.CONVEX_URL!,
adminAuthToken: process.env.CONVEX_ADMIN_KEY!,
indexes: {
docs: {
tableName: 'docs_vectors',
dimension: 1536,
filterFields: ['tenantId'],
},
},
})

await vectorStore.upsert({
indexName: 'docs',
ids: ['chunk-1'],
vectors: [embedding],
metadata: [{ tenantId: 'acme', text: 'Account setup guide' }],
})

const results = await vectorStore.query({
indexName: 'docs',
queryVector: embedding,
filter: { tenantId: 'acme' },
})

ConvexNativeVector supports Convex native vector filter shapes: one equality field, or $or of equality fields. It doesn't support metadata-only queries, filter-based updates, or filter-based deletes. Use vector IDs for updates and deletes.

Custom storage function
Direct link to Custom storage function

const vectorStore = new ConvexVector({
id: 'convex-vectors',
deploymentUrl: 'https://your-project.convex.cloud',
adminAuthToken: 'your-admin-token',
storageFunction: 'custom/path:handler',
})

Methods
Direct link to Methods

createIndex()
Direct link to createindex

indexName:

string
Name of the index to create

dimension:

number
Vector dimension (must match your embedding model)

metric?:

'cosine' | 'euclidean' | 'dotproduct'
= cosine
Distance metric for similarity search (only cosine is currently supported)
await vectorStore.createIndex({
indexName: 'my_vectors',
dimension: 1536,
})

upsert()
Direct link to upsert

indexName:

string
Name of the index to upsert vectors into

vectors:

number[][]
Array of embedding vectors

metadata?:

Record<string, any>[]
Metadata for each vector

ids?:

string[]
Optional vector IDs (auto-generated if not provided)
await vectorStore.upsert({
indexName: "my_vectors",
vectors: [[0.1, 0.2, 0.3, ...]],
metadata: [{ label: "example" }],
ids: ["vec-1"],
});

query()
Direct link to query

indexName:

string
Name of the index to query

queryVector:

number[]
Query vector

topK?:

number
= 10
Number of results to return

filter?:

Record<string, any>
Metadata filters

includeVector?:

boolean
= false
Whether to include the vector in the result
const results = await vectorStore.query({
indexName: "my_vectors",
queryVector: [0.1, 0.2, 0.3, ...],
topK: 5,
filter: { category: "documents" },
});

listIndexes()
Direct link to listindexes

Returns an array of index names as strings.

const indexes = await vectorStore.listIndexes()
// ["my_vectors", "embeddings", ...]

describeIndex()
Direct link to describeindex

indexName:

string
Name of the index to describe

Returns:

interface IndexStats {
dimension: number
count: number
metric: 'cosine' | 'euclidean' | 'dotproduct'
}

deleteIndex()
Direct link to deleteindex

indexName:

string
Name of the index to delete

Deletes the index and all its vectors.

await vectorStore.deleteIndex({ indexName: 'my_vectors' })

updateVector()
Direct link to updatevector

Update a single vector by ID or by metadata filter. Either id or filter must be provided, but not both.

indexName:

string
Name of the index containing the vector

id?:

string
ID of the vector to update (mutually exclusive with filter)

filter?:

Record<string, any>
Metadata filter to identify vector(s) to update (mutually exclusive with id)

update:

{ vector?: number[]; metadata?: Record<string, any>; }
Object containing the vector and/or metadata to update
// Update by ID
await vectorStore.updateVector({
indexName: 'my_vectors',
id: 'vector123',
update: {
vector: [0.1, 0.2, 0.3],
metadata: { label: 'updated' },
},
})

// Update by filter
await vectorStore.updateVector({
indexName: 'my_vectors',
filter: { category: 'product' },
update: {
metadata: { status: 'reviewed' },
},
})

deleteVector()
Direct link to deletevector

indexName:

string
Name of the index containing the vector

id:

string
ID of the vector to delete
await vectorStore.deleteVector({ indexName: 'my_vectors', id: 'vector123' })

deleteVectors()
Direct link to deletevectors

Delete multiple vectors by IDs or by metadata filter. Either ids or filter must be provided, but not both.

indexName:

string
Name of the index containing the vectors to delete

ids?:

string[]
Array of vector IDs to delete (mutually exclusive with filter)

filter?:

Record<string, any>
Metadata filter to identify vectors to delete (mutually exclusive with ids)
// Delete by IDs
await vectorStore.deleteVectors({
indexName: 'my_vectors',
ids: ['vec1', 'vec2', 'vec3'],
})

// Delete by filter
await vectorStore.deleteVectors({
indexName: 'my_vectors',
filter: { status: 'archived' },
})

Response types
Direct link to Response types

Query results are returned in this format:

interface QueryResult {
id: string
score: number
metadata: Record<string, any>
vector?: number[] // Only included if includeVector is true
}

Metadata filtering
Direct link to Metadata filtering

ConvexVector supports metadata filtering with various operators. These filters are applied by the adapter after vectors are loaded from Convex.

// Simple equality
const results = await vectorStore.query({
indexName: 'my_vectors',
queryVector: embedding,
filter: { category: 'documents' },
})

// Comparison operators
const results = await vectorStore.query({
indexName: 'my_vectors',
queryVector: embedding,
filter: {
price: { $gt: 100 },
status: { $in: ['active', 'pending'] },
},
})

// Logical operators
const results = await vectorStore.query({
indexName: 'my_vectors',
queryVector: embedding,
filter: {
$and: [{ category: 'electronics' }, { price: { $lte: 500 } }],
},
})

Supported filter operators
Direct link to Supported filter operators

OperatorDescription
$eqEqual to
$neNot equal to
$gtGreater than
$gteGreater than or equal
$ltLess than
$lteLess than or equal
$inIn array
$ninNot in array
$andLogical AND
$orLogical OR

Architecture
Direct link to Architecture

ConvexVector stores vectors in the mastra_vectors table with the following structure:

  • id: Unique vector identifier
  • indexName: Name of the index
  • embedding: The vector data (array of floats)
  • metadata: Optional JSON metadata

Vector similarity search is performed with cosine similarity in the Mastra adapter. This keeps setup flexible, but it's not designed for large production vector collections.

ConvexNativeVector stores each Mastra vector index in a dedicated Convex table. Its queries call a Convex action that uses ctx.vectorSearch, then load the matched documents through a Convex query. This follows the Convex native vector search model:

  • Vector indexes are declared in convex/schema.ts.
  • Vector search runs from a Convex action.
  • topK must be between 1 and 256.
  • Filters must target fields listed in the Convex vector index filterFields.
  • Use one dedicated table per Mastra vector index to avoid cross-index results.

Use an external vector database when you need dynamic index creation at runtime, metadata-only queries, complex filter operators, filter-based bulk updates or deletes, or result limits above Convex's native vector search cap.