Database-Specific Configurations
This example demonstrates how to use database-specific configurations with vector query tools to optimize performance and leverage unique features of different vector stores.
Multi-Environment Setup
Use different configurations for different environments:
TypeScript
import { openai } from "@ai-sdk/openai";
import { createVectorQueryTool } from "@mastra/rag";
import { RuntimeContext } from "@mastra/core/runtime-context";
// Base configuration
const createSearchTool = (environment: 'dev' | 'staging' | 'prod') => {
return createVectorQueryTool({
vectorStoreName: "pinecone",
indexName: "documents",
model: openai.embedding("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: openai.embedding("text-embedding-3-small")
});
// Switch environment at runtime
const switchEnvironment = async (environment: string, query: string) => {
const runtimeContext = new RuntimeContext();
runtimeContext.set('databaseConfig', {
pinecone: {
namespace: environment
}
});
return await dynamicSearchTool.execute({
context: { queryText: query },
mastra,
runtimeContext
});
};
Performance Optimization with pgVector
Optimize search performance for different use cases:
High Accuracy
// High accuracy configuration - slower but more precise
const highAccuracyTool = createVectorQueryTool({
vectorStoreName: "postgres",
indexName: "embeddings",
model: openai.embedding("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
});
};
Multi-Tenant Application with Pinecone
Implement tenant isolation using Pinecone namespaces:
interface Tenant {
id: string;
name: string;
namespace: string;
}
class MultiTenantSearchService {
private searchTool: RagTool
constructor() {
this.searchTool = createVectorQueryTool({
vectorStoreName: "pinecone",
indexName: "shared-documents",
model: openai.embedding("text-embedding-3-small")
});
}
async searchForTenant(tenant: Tenant, query: string) {
const runtimeContext = new RuntimeContext();
// Isolate search to tenant's namespace
runtimeContext.set('databaseConfig', {
pinecone: {
namespace: tenant.namespace
}
});
const results = await this.searchTool.execute({
context: {
queryText: query,
topK: 10
},
mastra,
runtimeContext
});
// 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"
);
Hybrid Search with Pinecone
Combine semantic and keyword search:
const hybridSearchTool = createVectorQueryTool({
vectorStoreName: "pinecone",
indexName: "documents",
model: openai.embedding("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 runtimeContext = new RuntimeContext();
if (keywords.length > 0) {
const sparseVector = generateSparseVector(keywords);
runtimeContext.set('databaseConfig', {
pinecone: {
namespace: "production",
sparseVector
}
});
}
return await hybridSearchTool.execute({
context: { queryText: query },
mastra,
runtimeContext
});
};
// Usage
const results = await hybridSearch(
"How to use the REST API",
["API", "REST", "documentation"]
);
Quality-Gated Search
Implement progressive search quality:
const createQualityGatedSearch = () => {
const baseConfig = {
vectorStoreName: "postgres",
indexName: "embeddings",
model: openai.embedding("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`);
Key Takeaways
- Environment Isolation: Use namespaces to separate data by environment or tenant
- Performance Tuning: Adjust ef/probes parameters based on your accuracy vs speed requirements
- Quality Control: Use minScore to filter out low-quality matches
- Runtime Flexibility: Override configurations dynamically based on context
- Progressive Quality: Implement fallback strategies for different quality levels
This approach allows you to optimize vector search for your specific use case while maintaining flexibility and performance.