Módulo 5: Landscape de Vector Databases para AI Engineers

Cápsula 04: Comparación de Features para RAG

🎯 Objetivo de la cápsula

Comparar las features técnicas que más impactan un sistema RAG en producción — metadata filtering, hybrid search, multi-tenancy, batch operations, SDKs y scaling — y entender cómo varía el soporte entre proveedores.

En las cápsulas anteriores mapeaste el landscape de proveedores y entendiste el trade-off managed vs self-hosted. Ahora toca evaluar las features que afectan directamente la calidad, seguridad y eficiencia de tu pipeline RAG. Una base vectorial puede ser barata y fácil de operar, pero si no soporta metadata filtering avanzado o multi-tenancy, tu RAG tendrá problemas graves en producción.

Esta cápsula es deliberadamente table-heavy: las tablas comparativas son la herramienta más útil para una evaluación rápida. Cada tabla va acompañada de contexto sobre por qué esa feature importa para RAG y ejemplos de código que muestran diferencias de API entre proveedores.

Al finalizar esta cápsula:

  • ✅ Evaluarás 6 features críticas para RAG en 5 proveedores
  • ✅ Distinguirás entre soporte nominal y soporte real de cada feature
  • ✅ Identificarás qué features son "no negociables" para tu caso
  • ✅ Tendrás tablas de referencia para tu decision tree

Tiempo estimado: 25-30 minutos


📋 Las 6 Features Críticas para RAG

¿Qué hace una feature "crítica" para RAG?

Es crítica si afecta al menos uno de estos cuatro objetivos de un sistema RAG en producción:

ObjetivoPregunta clave
Precisión de retrieval¿Los documentos recuperados son relevantes?
Latencia p95¿El retrieval es suficientemente rápido para el UX?
Seguridad / aislamiento¿Los datos de un tenant pueden filtrarse a otro?
Costo operativo¿La operación escala sin explotar el presupuesto?

Mapa de features

Features Críticas para RAG
│
├── 1. Metadata Filtering     → Precisión
├── 2. Hybrid Search          → Precisión
├── 3. Multi-tenancy          → Seguridad
├── 4. Batch Operations       → Costo
├── 5. SDKs y DX              → Velocidad
└── 6. Scaling                → Costo + Latencia

1️⃣ Metadata Filtering

¿Por qué importa para RAG?

Metadata filtering reduce el espacio de búsqueda antes de calcular similitud. Sin filtrado, tu RAG busca en todos los vectores — con filtrado, busca solo en el subconjunto relevante.

Impacto real:

Sin filtrado:
  Query: "¿Cómo configuro autenticación?"
  Busca en: 500K vectores (todos los docs)
  Resultado: Mezcla docs de v1, v2, v3 del producto
  → LLM genera respuesta con info desactualizada

Con filtrado (version="v3"):
  Query: "¿Cómo configuro autenticación?"
  Busca en: 80K vectores (solo v3)
  Resultado: Docs relevantes y actualizados
  → LLM genera respuesta precisa

Comparación de metadata filtering

CapacidadChromaDBPineconeWeaviateQdrantMilvus
Filtros de igualdad (=)
Filtros de rango (>, <)
Filtros IN (lista)
AND / OR lógicos
NOT / negación
Nested filters (objetos)
Full-text search en metadata
Geo filtering
Payload index dedicadoN/AN/AAuto✅ Manual✅ Manual
Performance con filtros⚠️ Degrada✅ Buena✅ Buena✅ Excelente✅ Buena

Código: Filtrado en cada proveedor

# ChromaDB — where syntax
results = collection.query(
    query_texts=["autenticación"],
    n_results=5,
    where={
        "$and": [
            {"version": {"$eq": "v3"}},
            {"language": {"$eq": "es"}}
        ]
    }
)

# Pinecone — MongoDB-like syntax
results = index.query(
    vector=embedding,
    top_k=5,
    filter={
        "$and": [
            {"version": {"$eq": "v3"}},
            {"language": {"$eq": "es"}}
        ]
    }
)

# Weaviate — typed filter API
from weaviate.classes.query import Filter
response = collection.query.near_text(
    query="autenticación",
    limit=5,
    filters=(
        Filter.by_property("version").equal("v3") &
        Filter.by_property("language").equal("es")
    )
)

# Qdrant — model-based filters
from qdrant_client.models import Filter, FieldCondition, MatchValue
results = client.query_points(
    collection_name="docs",
    query=embedding,
    limit=5,
    query_filter=Filter(
        must=[
            FieldCondition(key="version", match=MatchValue(value="v3")),
            FieldCondition(key="language", match=MatchValue(value="es"))
        ]
    )
)

# Milvus — SQL-like string
results = client.search(
    collection_name="docs",
    data=[embedding],
    limit=5,
    filter='version == "v3" and language == "es"'
)

Insight para RAG: Qdrant destaca en performance de filtrado porque soporta payload indexes dedicados que evitan full-scan del metadata. Weaviate tiene los filtros más expresivos (geo, nested objects). ChromaDB cubre lo básico pero puede degradar con filtros complejos sobre colecciones grandes.


2️⃣ Hybrid Search

¿Por qué importa para RAG?

Hybrid search combina keyword match (BM25) con semantic search (vector) para capturar tanto coincidencias exactas ("GPT-4", "INV-2024-001") como conceptuales ("cómo mejorar rendimiento"). En RAG para documentación técnica, hybrid search mejora accuracy entre 15-25%.

Comparación de hybrid search

CapacidadChromaDBPineconeWeaviateQdrantMilvus
Hybrid search nativo⚠️ Sparse✅ Built-in✅ Sparse
Keyword index (BM25)✅ Inverted
Sparse vectors
Fusion algorithmN/AManualRRF (default)ManualRRF/Weighted
Alpha tuningN/AN/A✅ (0-1)Manual
Custom rankingN/A

Niveles de soporte:

  • Nativo (Weaviate): Un solo endpoint, fusión automática, alpha ajustable.
  • Sparse vectors (Pinecone, Qdrant): Envías dense + sparse vectors, fusión manual o semi-auto.
  • No soportado (ChromaDB): Necesitas implementar BM25 externamente (rank_bm25 library).

Código: Hybrid search por proveedor

# Weaviate — hybrid nativo con alpha
response = collection.query.hybrid(
    query="GPT-4 API rate limits",
    alpha=0.5,  # 0 = pure keyword, 1 = pure vector
    limit=10
)

# Pinecone — sparse + dense vectors
results = index.query(
    vector=dense_embedding,        # semantic
    sparse_vector={                 # keyword
        "indices": [102, 5483, 9201],
        "values": [0.8, 0.5, 0.3]
    },
    top_k=10
)

# Qdrant — sparse vectors con named vectors
results = client.query_points(
    collection_name="docs",
    query=embedding,
    using="dense",
    limit=10
)
# + query separada con sparse vector, fusión manual

# ChromaDB — NO soporta hybrid, workaround con BM25 externo
from rank_bm25 import BM25Okapi

tokenized = [doc.split() for doc in all_documents]
bm25 = BM25Okapi(tokenized)
keyword_scores = bm25.get_scores(query.split())

semantic_results = collection.query(
    query_texts=[query], n_results=20
)
# Fusión manual (RRF o weighted)

Insight para RAG: Si tu RAG maneja documentación técnica con IDs, versiones o nombres de producto, hybrid search no es opcional — es esencial. Weaviate es la opción más madura para hybrid. Pinecone y Qdrant lo soportan via sparse vectors pero requieren más trabajo.


3️⃣ Multi-tenancy

¿Por qué importa para RAG?

Multi-tenancy determina cómo aislas datos entre usuarios, organizaciones o clientes. En un RAG SaaS, si el Tenant A puede ver documentos del Tenant B, tienes un data leakage — un problema de seguridad grave.

Estrategias de multi-tenancy

Estrategia 1: Collection per tenant
┌──────────┐ ┌──────────┐ ┌──────────┐
│Tenant A  │ │Tenant B  │ │Tenant C  │
│Collection│ │Collection│ │Collection│
└──────────┘ └──────────┘ └──────────┘
Pro: Aislamiento total
Con: Overhead de gestión, no escala a 1000+ tenants

Estrategia 2: Metadata filtering
┌─────────────────────────────────┐
│       Shared Collection          │
│  [tenant_id=A] [tenant_id=B]   │
│  [tenant_id=A] [tenant_id=C]   │
└─────────────────────────────────┘
Pro: Simple, escala a muchos tenants
Con: Filter overhead, riesgo de olvidar filtro (leakage)

Estrategia 3: Namespace / Partition
┌─────────────────────────────────┐
│           Index                  │
│  ┌─────────┐  ┌─────────┐      │
│  │Namespace│  │Namespace│      │
│  │   "A"   │  │   "B"   │      │
│  └─────────┘  └─────────┘      │
└─────────────────────────────────┘
Pro: Aislamiento lógico nativo
Con: Límites por proveedor

Comparación de multi-tenancy

CapacidadChromaDBPineconeWeaviateQdrantMilvus
Estrategia principalCollectionsNamespacesTenants nativosPayload filterPartitions
Aislamiento de datosMedioAltoAltoMedio-AltoAlto
Límite de tenants~100 collections10K namespaces100K+ tenantsSin límite soft4096 partitions
Tenant inactivo offload✅ (activity-based)
Tenant-level metrics
Riesgo de data leakageMedioBajoBajoMedio*Bajo

*Qdrant con payload filter requiere que el developer siempre incluya el filtro de tenant — si se olvida, hay leakage.

Código: Multi-tenancy por proveedor

# Pinecone — namespaces nativos
index.upsert(
    vectors=[{"id": "doc_1", "values": emb, "metadata": {"content": "..."}}],
    namespace="tenant_acme"  # aislamiento por namespace
)
results = index.query(
    vector=query_emb, top_k=5,
    namespace="tenant_acme"  # solo busca en este tenant
)

# Weaviate — multi-tenancy nativa (v1.20+)
import weaviate.classes as wvc

collection = client.collections.create(
    name="Documents",
    multi_tenancy_config=wvc.config.Configure.multi_tenancy(enabled=True),
    vectorizer_config=wvc.config.Configure.Vectorizer.text2vec_openai()
)
collection.tenants.create([
    wvc.tenants.Tenant(name="tenant_acme"),
    wvc.tenants.Tenant(name="tenant_globex"),
])
tenant_collection = collection.with_tenant("tenant_acme")
tenant_collection.data.insert(properties={"content": "..."})

# Qdrant — payload filter (manual, sin namespace nativo)
client.upsert(
    collection_name="docs",
    points=[PointStruct(
        id=1, vector=emb,
        payload={"tenant_id": "acme", "content": "..."}
    )]
)
results = client.query_points(
    collection_name="docs",
    query=query_emb, limit=5,
    query_filter=Filter(must=[
        FieldCondition(key="tenant_id", match=MatchValue(value="acme"))
    ])
)

# ChromaDB — collection per tenant
tenant_collection = client.get_or_create_collection(name="tenant_acme")
tenant_collection.add(documents=["..."], ids=["doc_1"])

Insight para RAG: Si construyes RAG para SaaS (múltiples clientes), multi-tenancy es un requisito de seguridad, no una feature. Weaviate tiene la implementación más madura con offload de tenants inactivos. Pinecone con namespaces es simple y seguro. Qdrant requiere disciplina del developer para nunca olvidar el filtro.


4️⃣ Batch Operations

¿Por qué importa para RAG?

La ingesta de documentos en RAG es inherentemente un problema de batch: chunkeaste 10,000 documentos en 50,000 chunks → necesitas insertar 50,000 vectores. La velocidad y eficiencia de batch operations afecta directamente el tiempo de setup y el costo de re-indexación.

Comparación de batch operations

CapacidadChromaDBPineconeWeaviateQdrantMilvus
Batch upsert
Batch size máximo~41K*100 vectorsSin límite**Sin límite**Sin límite**
Upsert paraleloManual✅ Async✅ Batch API✅ Async
Streaming ingestion
Progress tracking
Rate de ingesta (1536-dim)~5K/s local~1K/s API~10K/s local~15K/s local~20K/s local

*ChromaDB tiene límite por request size de SQLite. **Limitado por memoria del servidor.

Código: Batch ingestion eficiente

# ChromaDB — batch con chunks
def batch_insert_chromadb(collection, documents, batch_size=1000):
    for i in range(0, len(documents), batch_size):
        batch = documents[i:i + batch_size]
        collection.add(
            documents=[d["text"] for d in batch],
            metadatas=[d["metadata"] for d in batch],
            ids=[d["id"] for d in batch]
        )
        print(f"Insertados {min(i + batch_size, len(documents))}/{len(documents)}")


# Pinecone — batch con límite de 100
def batch_insert_pinecone(index, vectors, batch_size=100):
    for i in range(0, len(vectors), batch_size):
        batch = vectors[i:i + batch_size]
        index.upsert(vectors=batch)


# Qdrant — batch con upload_points
def batch_insert_qdrant(client, collection_name, points, batch_size=1000):
    client.upload_points(
        collection_name=collection_name,
        points=points,
        batch_size=batch_size,
        parallel=4  # hilos concurrentes
    )


# Milvus — insert masivo
def batch_insert_milvus(client, collection_name, data, batch_size=5000):
    for i in range(0, len(data), batch_size):
        batch = data[i:i + batch_size]
        client.insert(collection_name=collection_name, data=batch)

Insight para RAG: Pinecone tiene el batch size más restrictivo (100 vectors por request), lo que hace la ingesta inicial más lenta via API. Para cargas masivas (> 100K docs), Qdrant y Milvus self-hosted son significativamente más rápidos. ChromaDB es eficiente para tamaños moderados.


5️⃣ SDKs y Developer Experience

¿Por qué importa para RAG?

La calidad del SDK determina cuánto tiempo invierte tu equipo integrando la vector DB con tu pipeline RAG. Un SDK mal diseñado genera bugs, frustración y tiempo perdido en debugging.

Comparación de SDKs

CapacidadChromaDBPineconeWeaviateQdrantMilvus
Python SDK✅ Excelente✅ Excelente✅ Bueno (v4)✅ Excelente✅ Bueno
TypeScript SDK
Go SDK
Rust SDK✅ (oficial)
Java SDK
Async support
Type hints (Python)Parcial✅ CompletoParcial
Auto-embedding✅ (modules)
LangChain integration
LlamaIndex integration
Documentación SDK⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

Insight para RAG: ChromaDB gana en simplicidad absoluta (auto-embedding, 5 líneas para funcionar). Qdrant tiene la mejor documentación y type hints. Weaviate requiere más boilerplate pero ofrece más poder (schema, modules). Para equipos que usan LangChain/LlamaIndex, la diferencia de SDK importa menos porque el framework abstrae la API. La comparación detallada de patrones de código por proveedor está en la cápsula 02.


6️⃣ Scaling

¿Por qué importa para RAG?

Tu RAG empieza con 10K documentos en MVP. En 6 meses tiene 500K. En un año, 5M. La capacidad de escalar la vector DB sin re-arquitectar todo el sistema es crítica.

Comparación de scaling

CapacidadChromaDBPineconeWeaviateQdrantMilvus
Escala máxima recomendada~1M100M+100M+100M+Billones
Sharding automáticoManual
Replicación✅ (multi-AZ)✅ (Raft)
Horizontal scaling✅ (auto)
QuantizationPQScalar/PQ/BinarySQ8/PQ
On-disk indexN/A (cloud)✅ (mmap)✅ (DiskANN)
GPU acceleration
Segment managementAutoAutoManual/AutoAuto

Impacto de quantization en costos

TipoEjemplo (10M × 1536-dim)MemoriaAhorroAccuracy loss
Sin quantization (float32)57.3 GB
Scalar (Qdrant)14.3 GB75%< 1%Recomendado
Product (Weaviate/Milvus)~1.8 GB96%2-5%Aceptable
Binary (Qdrant)~1.8 GB96%3-7%Requiere rescoring

Insight para RAG: Si tu RAG crece a > 5M vectores, quantization se convierte en el factor más importante de costo. Qdrant lidera con 3 tipos de quantization. Milvus escala más con GPU y DiskANN. ChromaDB no es opción para esta escala.


📊 Tabla Maestra: Feature Comparison para RAG

FeatureChromaDBPineconeWeaviateQdrantMilvus
Metadata filtering✅ Básico✅ Bueno✅ Avanzado✅ Excelente✅ Bueno
Hybrid search⚠️ Sparse✅ Nativo✅ Sparse
Multi-tenancy⚠️ Collections✅ Namespaces✅ Nativo⚠️ Payload✅ Partitions
Batch operations⚠️ Lento✅ Rápido✅ Rápido
SDK quality✅ Simple✅ Bueno✅ Bueno✅ Excelente✅ Bueno
Scaling❌ Limitado✅ Auto✅ Enterprise
Quantization✅ PQ✅ 3 tipos✅ SQ8/PQ
Observabilidad✅ Console✅ Metrics✅ Dashboard✅ Attu

Lectura rápida de la tabla

  • ChromaDB: Excelente para empezar, limitado para producción.
  • Pinecone: Sólido en todo excepto hybrid search y quantization.
  • Weaviate: El más completo en features (hybrid nativo, multi-tenancy, PQ).
  • Qdrant: Mejor en filtering y quantization, hybrid search mejorando.
  • Milvus: El más escalable, pero complejidad operativa mayor.

🔍 Cómo Usar Esta Comparación Sin Sesgo

Proceso de 3 pasos

Paso 1: Marca 2-3 features "no negociables" para tu RAG.

no_negociables = {
    "saas_multitenant": ["multi_tenancy"],
    "documentation_rag": ["hybrid_search", "metadata_filtering"],
    "internal_search": ["scaling", "batch_operations"],
    "prototype": []  # para prototipos, ninguna feature es blocker
}

mi_caso = "documentation_rag"
print(f"Features no negociables: {no_negociables[mi_caso]}")
# → Features no negociables: ['hybrid_search', 'metadata_filtering']

Paso 2: Elimina opciones que no cubran mínimos.

Caso: documentation_rag (hybrid search + metadata filtering)

ChromaDB: ❌ No tiene hybrid search → ELIMINADA
Pinecone: ⚠️ Hybrid via sparse vectors (requiere trabajo extra)
Weaviate: ✅ Hybrid nativo + filtering avanzado
Qdrant:   ✅ Sparse vectors + filtering excelente
Milvus:   ✅ Hybrid + filtering bueno

Candidatas: Weaviate, Qdrant, Milvus (Pinecone con reservas)

Paso 3: Recién después compara costo y operación entre las candidatas restantes.


🔧 Troubleshooting de evaluación

1. "Todos dicen que soportan todo"

Causa: Marketing vs realidad. "Soporta metadata filtering" puede significar filtros básicos de igualdad o un sistema completo con geo-filtering y nested objects.

Solución: Para cada feature "no negociable", busca:

  • Documentación oficial con ejemplos de código
  • Límites documentados (max filters por query, max tenants, etc.)
  • Issues abiertos en GitHub relacionados con esa feature
  • Benchmarks independientes (no del propio proveedor)

2. "No sé cuánto pesa hybrid search en mi caso"

Causa: No has medido la distribución de tipos de query.

Solución: Analiza 100 queries reales de tu sistema (o proyectadas):

  • Cuenta cuántas contienen nombres propios, IDs o términos exactos
  • Si > 20% son "específicas", hybrid search mejorará tu accuracy significativamente
  • Si < 5% son específicas, pure semantic es suficiente

3. "Multi-tenancy me parece overkill"

Causa: Solo tienes un tenant hoy — pero ¿en 6 meses?

Solución: Si tu producto es B2B o SaaS, multi-tenancy no es opcional. Implementarla después es una migración costosa. Si tu producto es B2C (un solo tenant), metadata filtering con user_id es suficiente y multi-tenancy formal es innecesaria.

4. "¿Cuánta accuracy pierdo con quantization?"

Causa: Miedo a degradar la calidad del RAG por comprimir vectores.

Solución: La pérdida típica con Scalar Quantization es < 1% en recall@10. Con Product Quantization es 2-5%. Para RAG, esta pérdida es generalmente aceptable. Haz una prueba A/B: compara recall de 100 queries con y sin quantization antes de decidir.


✏️ Ejercicios

Ejercicio 1: Features no negociables

Para tu proyecto RAG (real o hipotético), define:

  1. Las 3 features más importantes de la lista de 6.
  2. El nivel mínimo aceptable para cada una.
  3. Qué proveedores sobreviven el filtro.
Solución de referencia (caso: RAG para soporte técnico SaaS)
  1. Features no negociables:

    • Multi-tenancy (aislamiento de datos por cliente)
    • Hybrid search (queries con IDs de ticket + conceptuales)
    • Metadata filtering (filtrar por producto, versión, idioma)
  2. Nivel mínimo:

    • Multi-tenancy: aislamiento nativo (no solo metadata filter)
    • Hybrid search: built-in o sparse vectors
    • Metadata filtering: AND/OR + rangos
  3. Proveedores que sobreviven:

    • ✅ Weaviate (multi-tenancy nativa, hybrid nativo, filtering avanzado)
    • ⚠️ Pinecone (namespaces buenos, hybrid via sparse, filtering bueno)
    • ⚠️ Milvus (partitions, hybrid, filtering bueno)
    • ❌ ChromaDB (multi-tenancy limitada, sin hybrid)
    • ⚠️ Qdrant (multi-tenancy via payload filter, sparse vectors)

Ejercicio 2: Tabla de scoring personalizada

Asigna puntuación (1-5) a cada proveedor para las 6 features, ponderando por importancia para tu caso:

FeaturePesoChromaDBPineconeWeaviateQdrantMilvus
Metadata filtering
Hybrid search
Multi-tenancy
Batch operations
SDK quality
Scaling
Total ponderado
Solución de referencia (caso: RAG documentation interno, equipo pequeño)
FeaturePesoChromaDBPineconeWeaviateQdrantMilvus
Metadata filtering43 (12)4 (16)5 (20)5 (20)4 (16)
Hybrid search51 (5)3 (15)5 (25)4 (20)4 (20)
Multi-tenancy12 (2)4 (4)5 (5)3 (3)4 (4)
Batch operations33 (9)2 (6)4 (12)5 (15)5 (15)
SDK quality35 (15)4 (12)3 (9)5 (15)3 (9)
Scaling21 (2)5 (10)4 (8)4 (8)5 (10)
Total ponderado4563798174

Resultado: Qdrant y Weaviate lideran. Para un equipo pequeño sin necesidad de multi-tenancy, Qdrant gana por filtering + SDK + batch.

Ejercicio 3: Implementar filtrado avanzado

Usando ChromaDB, implementa una query que:

  1. Busque documentos sobre "optimización de rendimiento"
  2. Filtre por version >= 3 AND language = "es"
  3. Retorne los 3 resultados más relevantes con scores
Solución
import chromadb

client = chromadb.PersistentClient(path="./ejercicio_filter")
collection = client.get_or_create_collection(
    name="docs",
    metadata={"hnsw:space": "cosine"}
)

collection.add(
    documents=[
        "Optimización de queries en bases de datos",
        "Mejora de rendimiento en APIs REST",
        "Performance tuning para sistemas distribuidos",
        "Guía de CSS para diseñadores",
        "Cómo optimizar rendimiento en Python v2",
    ],
    metadatas=[
        {"version": 3, "language": "es"},
        {"version": 4, "language": "es"},
        {"version": 3, "language": "en"},
        {"version": 2, "language": "es"},
        {"version": 2, "language": "es"},
    ],
    ids=["d1", "d2", "d3", "d4", "d5"]
)

results = collection.query(
    query_texts=["optimización de rendimiento"],
    n_results=3,
    where={
        "$and": [
            {"version": {"$gte": 3}},
            {"language": {"$eq": "es"}}
        ]
    }
)

for doc, dist, meta in zip(
    results["documents"][0],
    results["distances"][0],
    results["metadatas"][0]
):
    score = 1 - dist
    print(f"Score: {score:.4f} | v{meta['version']} | {doc}")

Resultado esperado: Solo d1 y d2 pasan el filtro (version >= 3 AND language = "es"). d3 falla por idioma, d4 y d5 por versión.

Ejercicio 4: Diseñar estrategia multi-tenant

Tu empresa tiene 3 clientes (Acme, Globex, Initech) con documentos separados. Diseña la estrategia de multi-tenancy para:

  • Escenario A: ChromaDB (desarrollo)
  • Escenario B: Pinecone (producción)

Para cada uno, escribe el código de inserción y query que garantice aislamiento.

Solución
# Escenario A: ChromaDB — collection per tenant
import chromadb

client = chromadb.PersistentClient(path="./multi_tenant")

tenants = ["acme", "globex", "initech"]
for tenant in tenants:
    col = client.get_or_create_collection(name=f"docs_{tenant}")
    col.add(
        documents=[f"Documento interno de {tenant}"],
        ids=[f"{tenant}_doc_1"]
    )

acme_results = client.get_collection("docs_acme").query(
    query_texts=["documento interno"], n_results=5
)
# Solo retorna docs de Acme — aislamiento por collection


# Escenario B: Pinecone — namespace per tenant
from pinecone import Pinecone

pc = Pinecone(api_key="...")
index = pc.Index("production-rag")

for tenant in ["acme", "globex", "initech"]:
    index.upsert(
        vectors=[{"id": f"{tenant}_1", "values": embedding, "metadata": {"text": "..."}}],
        namespace=tenant
    )

results = index.query(
    vector=query_embedding,
    top_k=5,
    namespace="acme"  # aislamiento por namespace
)
# Solo retorna docs del namespace "acme"

Diferencia clave: ChromaDB crea colecciones separadas (más overhead de gestión). Pinecone usa namespaces dentro del mismo index (más eficiente, con aislamiento nativo).

Ejercicio 5: Feature decision matrix

Dado el siguiente caso, recomienda un proveedor con justificación:

Caso: Empresa de e-commerce. 5M productos. Queries con nombre de producto exacto + conceptuales ("zapatos cómodos para correr"). 200 marcas (multi-tenant por marca). Equipo de 10 con 2 DevOps. Presupuesto medio.

Solución

Análisis de features no negociables:

  1. Hybrid search — Obligatorio (nombre exacto + conceptual) → Elimina ChromaDB
  2. Multi-tenancy — 200 marcas → Necesita soporte robusto → Elimina Qdrant (payload filter a 200 tenants es viable pero no ideal)
  3. Scaling — 5M vectores → Todas excepto ChromaDB

Candidatas finales: Weaviate, Pinecone, Milvus

Scoring:

FeatureWeaviatePineconeMilvus
Hybrid search✅ Nativo (alpha)⚠️ Sparse vectors
Multi-tenancy 200 marcas✅ Nativo (offload)✅ Namespaces✅ Partitions
Scaling 5M✅ (overkill)
Equipo DevOps✅ Self-hosted viable✅ Zero ops⚠️ Complejo

Recomendación: Weaviate — hybrid search nativo es el diferenciador principal para e-commerce. Multi-tenancy con offload de marcas inactivas reduce costos. El equipo tiene 2 DevOps que pueden operar self-hosted.

Alternativa: Pinecone si el equipo prefiere zero ops, aceptando que hybrid search requiere más trabajo con sparse vectors.


🔗 Conexión con proyecto (Decision Tree)

Esta cápsula te da los criterios técnicos para los nodos de decisión de tu decision tree:

¿Necesitas hybrid search?
├── SÍ → Weaviate (nativo) o Qdrant/Milvus (sparse)
└── NO → Cualquiera sirve
         │
         ¿Necesitas multi-tenancy nativa?
         ├── SÍ → Weaviate o Pinecone
         └── NO → Qdrant o ChromaDB (si escala < 1M)

Combina estos criterios con los de managed vs self-hosted (cápsula anterior) y costos (siguiente cápsula) para completar tu árbol de decisión.


📝 Resumen

  • Metadata filtering es universal, pero la profundidad varía: Qdrant y Weaviate lideran con payload indexes y geo-filtering.
  • Hybrid search es esencial para RAG con queries específicas — Weaviate es la opción más madura con fusión RRF nativa.
  • Multi-tenancy es requisito de seguridad en SaaS — Weaviate tiene la implementación más completa con tenant offload.
  • Batch operations importan en ingesta masiva — Pinecone es el más lento (100 vectors/request), Qdrant y Milvus los más rápidos.
  • SDK quality afecta velocidad de desarrollo — ChromaDB gana en simplicidad, Qdrant en type safety y documentación.
  • Scaling y quantization determinan el costo a largo plazo — Qdrant lidera en quantization, Milvus en escala absoluta.
  • No hay proveedor que gane en todas las categorías; define tus "no negociables" primero y compara después.

📚 Recursos adicionales


Tiempo de lectura: 25-30 minutos Siguiente: 05-costos-tradeoffs.md