examples/basics/rag/database-specific-config/README.md
This example demonstrates how to use database-specific configurations with vector query tools to optimize performance and leverage unique features of different vector stores.
Use different configurations for different environments:
import { createVectorQueryTool } from '@mastra/rag';
import { RequestContext } from '@mastra/core/request-context';
import { ModelRouterEmbeddingModel } from '@mastra/core/llm';
// Base configuration
const createSearchTool = (environment: 'dev' | 'staging' | 'prod') => {
return createVectorQueryTool({
vectorStoreName: 'pinecone',
indexName: 'documents',
model: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
databaseConfig: {
pinecone: {
namespace: environment,
},
},
});
};
// Create environment-specific tools
const devSearchTool = createSearchTool('dev');
const prodSearchTool = createSearchTool('prod');
// Or use runtime override
const dynamicSearchTool = createVectorQueryTool({
vectorStoreName: 'pinecone',
indexName: 'documents',
model: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
});
// Switch environment at runtime
const switchEnvironment = async (environment: string, query: string) => {
const requestContext = new RequestContext();
requestContext.set('databaseConfig', {
pinecone: {
namespace: environment,
},
});
return await dynamicSearchTool.execute({
context: { queryText: query },
mastra,
requestContext,
});
};
import { createVectorQueryTool } from '@mastra/rag';
import { RequestContext } from '@mastra/core/request-context';
// Base configuration
const createSearchTool = environment => {
return createVectorQueryTool({
vectorStoreName: 'pinecone',
indexName: 'documents',
model: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
databaseConfig: {
pinecone: {
namespace: environment,
},
},
});
};
// Create environment-specific tools
const devSearchTool = createSearchTool('dev');
const prodSearchTool = createSearchTool('prod');
// Or use runtime override
const dynamicSearchTool = createVectorQueryTool({
vectorStoreName: 'pinecone',
indexName: 'documents',
model: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
});
// Switch environment at runtime
const switchEnvironment = async (environment, query) => {
const requestContext = new RequestContext();
requestContext.set('databaseConfig', {
pinecone: {
namespace: environment,
},
});
return await dynamicSearchTool.execute({
context: { queryText: query },
mastra,
requestContext,
});
};
Optimize search performance for different use cases:
import { ModelRouterEmbeddingModel } from '@mastra/core/llm';
// High accuracy configuration - slower but more precise
const highAccuracyTool = createVectorQueryTool({
vectorStoreName: 'postgres',
indexName: 'embeddings',
model: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
databaseConfig: {
pgvector: {
ef: 400, // High accuracy for HNSW
probes: 20, // High recall for IVFFlat
minScore: 0.85, // High quality threshold
},
},
});
// Use for critical searches where accuracy is paramount
const criticalSearch = async (query: string) => {
return await highAccuracyTool.execute({
context: {
queryText: query,
topK: 5, // Fewer, higher quality results
},
mastra,
});
};
import { ModelRouterEmbeddingModel } from '@mastra/core/llm';
// High speed configuration - faster but less precise
const highSpeedTool = createVectorQueryTool({
vectorStoreName: 'postgres',
indexName: 'embeddings',
model: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
databaseConfig: {
pgvector: {
ef: 50, // Lower accuracy for speed
probes: 3, // Lower recall for speed
minScore: 0.6, // Lower quality threshold
},
},
});
// Use for real-time applications
const realtimeSearch = async (query: string) => {
return await highSpeedTool.execute({
context: {
queryText: query,
topK: 10, // More results to compensate for lower precision
},
mastra,
});
};
import { ModelRouterEmbeddingModel } from '@mastra/core/llm';
// Balanced configuration - good compromise
const balancedTool = createVectorQueryTool({
vectorStoreName: 'postgres',
indexName: 'embeddings',
model: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
databaseConfig: {
pgvector: {
ef: 150, // Moderate accuracy
probes: 8, // Moderate recall
minScore: 0.7, // Moderate quality threshold
},
},
});
// Adjust parameters based on load
const adaptiveSearch = async (query: string, isHighLoad: boolean) => {
const requestContext = new RequestContext();
if (isHighLoad) {
// Reduce quality for speed during high load
requestContext.set('databaseConfig', {
pgvector: {
ef: 75,
probes: 5,
minScore: 0.65,
},
});
}
return await balancedTool.execute({
context: { queryText: query },
mastra,
requestContext,
});
};
Implement tenant isolation using Pinecone namespaces:
import { ModelRouterEmbeddingModel } from '@mastra/core/llm';
interface Tenant {
id: string;
name: string;
namespace: string;
}
class MultiTenantSearchService {
private searchTool: RagTool;
constructor() {
this.searchTool = createVectorQueryTool({
vectorStoreName: 'pinecone',
indexName: 'shared-documents',
model: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
});
}
async searchForTenant(tenant: Tenant, query: string) {
const requestContext = new RequestContext();
// Isolate search to tenant's namespace
requestContext.set('databaseConfig', {
pinecone: {
namespace: tenant.namespace,
},
});
const results = await this.searchTool.execute({
context: {
queryText: query,
topK: 10,
},
mastra,
requestContext,
});
// Add tenant context to results
return {
tenant: tenant.name,
query,
results: results.relevantContext,
sources: results.sources,
};
}
async bulkSearchForTenants(tenants: Tenant[], query: string) {
const promises = tenants.map(tenant => this.searchForTenant(tenant, query));
return await Promise.all(promises);
}
}
// Usage
const searchService = new MultiTenantSearchService();
const tenants = [
{ id: '1', name: 'Company A', namespace: 'company-a' },
{ id: '2', name: 'Company B', namespace: 'company-b' },
];
const results = await searchService.searchForTenant(tenants[0], 'product documentation');
Combine semantic and keyword search:
import { ModelRouterEmbeddingModel } from '@mastra/core/llm';
const hybridSearchTool = createVectorQueryTool({
vectorStoreName: 'pinecone',
indexName: 'documents',
model: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
databaseConfig: {
pinecone: {
namespace: 'production',
sparseVector: {
// Example sparse vector for keyword "API"
indices: [1, 5, 10, 15],
values: [0.8, 0.6, 0.4, 0.2],
},
},
},
});
// Helper function to generate sparse vectors for keywords
const generateSparseVector = (keywords: string[]) => {
// This is a simplified example - in practice, you'd use
// a proper sparse encoding method like BM25
const indices: number[] = [];
const values: number[] = [];
keywords.forEach((keyword, i) => {
const hash = keyword.split('').reduce((a, b) => {
a = (a << 5) - a + b.charCodeAt(0);
return a & a;
}, 0);
indices.push(Math.abs(hash) % 1000);
values.push(1.0 / (i + 1)); // Decrease weight for later keywords
});
return { indices, values };
};
const hybridSearch = async (query: string, keywords: string[]) => {
const requestContext = new RequestContext();
if (keywords.length > 0) {
const sparseVector = generateSparseVector(keywords);
requestContext.set('databaseConfig', {
pinecone: {
namespace: 'production',
sparseVector,
},
});
}
return await hybridSearchTool.execute({
context: { queryText: query },
mastra,
requestContext,
});
};
// Usage
const results = await hybridSearch('How to use the REST API', ['API', 'REST', 'documentation']);
Implement progressive search quality:
const createQualityGatedSearch = () => {
const baseConfig = {
vectorStoreName: 'postgres',
indexName: 'embeddings',
model: new ModelRouterEmbeddingModel('openai/text-embedding-3-small'),
};
return {
// High quality search first
highQuality: createVectorQueryTool({
...baseConfig,
databaseConfig: {
pgvector: {
minScore: 0.85,
ef: 200,
probes: 15,
},
},
}),
// Medium quality fallback
mediumQuality: createVectorQueryTool({
...baseConfig,
databaseConfig: {
pgvector: {
minScore: 0.7,
ef: 150,
probes: 10,
},
},
}),
// Low quality last resort
lowQuality: createVectorQueryTool({
...baseConfig,
databaseConfig: {
pgvector: {
minScore: 0.5,
ef: 100,
probes: 5,
},
},
}),
};
};
const progressiveSearch = async (query: string, minResults: number = 3) => {
const tools = createQualityGatedSearch();
// Try high quality first
let results = await tools.highQuality.execute({
context: { queryText: query },
mastra,
});
if (results.sources.length >= minResults) {
return { quality: 'high', ...results };
}
// Fallback to medium quality
results = await tools.mediumQuality.execute({
context: { queryText: query },
mastra,
});
if (results.sources.length >= minResults) {
return { quality: 'medium', ...results };
}
// Last resort: low quality
results = await tools.lowQuality.execute({
context: { queryText: query },
mastra,
});
return { quality: 'low', ...results };
};
// Usage
const results = await progressiveSearch('complex technical query', 5);
console.log(`Found ${results.sources.length} results with ${results.quality} quality`);
This approach allows you to optimize vector search for your specific use case while maintaining flexibility and performance.