DEV Community

Machine coding Master
Machine coding Master

Posted on

Stop Spinning Up Separate Vector DBs: Multi-Tenant Spring AI with Pgvector Metadata Filtering

Stop Spinning Up Separate Vector DBs: Multi-Tenant Spring AI with Pgvector Metadata Filtering

Shipping RAG to production in 2026 means solving the multi-tenancy problem without blowing up your cloud budget on isolated vector database instances. If you aren't enforcing strict tenant isolation at the metadata layer, you're one bad prompt away from leaking proprietary enterprise data across tenant boundaries.

Heads up: if you want to see these patterns applied to real interview problems, javalld.com has full machine coding solutions with traces.

Why Most Developers Get This Wrong

  • Spinning up one database per tenant: This is an operational nightmare that kills connection pools, wastes memory on duplicate indexes, and makes schema migrations a living hell.
  • Post-filtering in application memory: Fetching K-nearest neighbors first and then filtering by tenant_id in Java is a massive security compliance failure and a guaranteed performance bottleneck.
  • Bypassing the framework abstraction: Writing native PostgreSQL/Pgvector SQL queries directly to bypass Spring AI destroys your ability to swap models or scale your pipeline cleanly.

The Right Way

Leverage Spring Security's context to dynamically inject tenant metadata into Spring AI's FilterExpression AST before querying a shared Pgvector store.

  • ThreadLocal/Reactive Context propagation: Automatically extract the secure tenantId from the JWT/Security context during the request lifecycle.
  • AST-based Filter Generation: Use Spring AI's Filter.ExpressionBuilder to programmatically build the AST (e.g., tenant_id == 'tenant_A') so the framework handles SQL translation.
  • Metadata-driven indexing: Ensure your PostgreSQL instance has a functional B-Tree index on the metadata->>'tenant_id' JSONB field alongside your HNSW vector index.

Show Me The Code

// Dynamically inject tenant context into Spring AI SearchRequest
String tenantId = TenantContextHolder.getTenantId(); // Resolved from JWT
Filter.Expression tenantFilter = new Filter.ExpressionBuilder()
    .eq("tenant_id", tenantId)
    .build();

List<Document> results = vectorStore.similaritySearch(
    SearchRequest.query(userPrompt)
        .withTopK(5)
        .withSimilarityThreshold(0.75)
        .withFilterExpression(tenantFilter) // Enforced isolation
);
Enter fullscreen mode Exit fullscreen mode

Key Takeaways

  • Logical isolation wins: Stop paying for idle vector databases; use Pgvector's JSONB metadata filtering combined with Spring AI's AST parser to keep tenants securely segregated.
  • Index your metadata: A vector search with metadata filtering is only fast if you have a composite index on your vector column and your JSONB metadata fields.
  • Automate the filter injection: Never trust developers to manually append the tenant filter; wrap the VectorStore bean or use an AOP aspect to inject the security context globally.

Top comments (0)