Skip to main content

Quando NON usare vector DB: alternative ranking

Articolo della serie Vector DB Decision Tree 2027. Pubblicazione prevista 02 November 2027.

What do you think about this article?

Quando NON usare vector DB: alternative ranking
Vector DB DecisionPhotos:Google DeepMindPexels

At a Glance

Articolo della serie Vector DB Decision Tree 2027. Pubblicazione prevista 02 November 2027.

When NOT to use vector DB: alternative ranking

When NOT to use vector DB: alternative ranking

Vector databases like Pinecone and Weaviate are trending in 2026. But in 7 out of 10 real-world use cases, alternatives like BM25, Elasticsearch, and Redis outperform vector DBs on costs and relevance. A pragmatic guide to ranking choices when vector DBs are overkill.

Answer-First: when vector DBs are NOT needed

Vector databases shine for semantic similarity across billions of records. But if your use case falls into one of these scenarios, choose something else:

  • Keyword-focused search (90%+ exact query match): BM25 beats vector DBs on speed, cost, and relevance. A user searches for "budget tracker app"? BM25 finds exact titles in <5ms. Vector DB generates embeddings, adds latency, and often ranks keyword matches poorly.
  • Dataset <1M records with access <100 QPS: Postgres full-text search, open-source Elasticsearch, or Redis + Lua scripts cost 10x less and predict better than Pinecone for simple queries.
  • Multi-factor ranking (price, rating, freshness): Vector DBs lack the ability to read structured factors and re-rank based on business rules. Elasticsearch BM25 + scripting does this natively.
  • Retrieval on sparse/structured data (graph-like relations): If ranking depends on entity links ("show articles by reviewers who gave 5-star"), graph database or SQL wins. Vector similarity doesn't understand graphs.
  • Latency-critical (<10ms SLA) with batch embeddings: If you must compute embeddings at query-time, the computational cost is prohibitive. Pre-compute with BM25 instead.

Result: 70% of sites that "should" use vector DBs would perform better (and save money) with a BM25 + Elasticsearch + Redis stack for custom ranking. Vector DBs are optimal for the remaining 30%: multi-language similarity, dense retrieval on unstructured docs at scale, recommendation engines on user behavior embeddings.

1. What is a Vector DB and when do they REALLY shine

A vector database stores embeddings (N-dimensional vectors) and finds k-nearest-neighbors via approximate indexing (HNSW, IVF). Designed for: "give me the 10 articles semantically most similar to this query", where semantic means "conceptually related content regardless of exact wording".

Ideal use case: User writes in French "comment gagner de l'argent en ligne", your backend has 10M IT/EN/FR articles. Vector DB generates query embedding, searches 100 neighbors, re-ranks with LLM. Output: relevant articles even without exact keyword matches.

Worst use case: User searches "iPhone 15 Pro Max 256GB silver". Exact title match. Vector DB introduces latency (embedding + kNN search), and ranking often downscores this match in favor of "conceptually similar" articles (other smartphones) the user doesn't want. BM25 would solve this in 2ms.

2. The hidden cost of vector storage

A vector database doesn't just reduce query time: it increases operational costs in non-obvious ways.

  • Embedding computation: With embedding-on-write, every new document generates an embedding via LLM (cloud LLM API or Ollama on-prem). On 100K documents per month, cloud API costs €0.02 per 1K embedding tokens (€2K/month). With Ollama on-prem you save on API cost, but add GPU infrastructure cost.
  • Storage multiplied: An article is 5KB text; embedding is 3KB (384-dim float32). Total is 8KB. Multiply 10M articles = 80GB on-disk + 80GB in-memory HNSW index = 160GB RAM. Single Pinecone instance for this volume costs €500-1000/month. Redis with persistence costs €50-150 but adds maintenance overhead.
  • Latency not always improved: Embedding lookup (kNN) is O(1) but post-ranking among top-k is often serial. If vector DBs re-rank with LLM, p95 latency becomes 2-5 seconds (not 50ms as advertised).
  • Stale embeddings: If corpus changes daily (news, prices), old vectors degrade relevance. Must regenerate embeddings for modified docs. With keyword search, inverted index updates in milliseconds.

Annual TCO estimate on 10M docs: Vector DB cloud (Pinecone) = €6K–€12K (storage + embedding API). Redis self-hosted + re-ranking = €1K–€3K (infrastructure + engineering).

3. The hidden complexity of retrieval ranking

Vector DBs promise "semantic ranking with one click". In reality, good hybrid ranking (keyword + semantic + business logic) requires the same engineering effort:


# NAIVE vector DB RAG (what guides tell you to do)
def rag_search(query):
    query_embedding = llm_api.embed(query)  # cloud LLM API
    results = pinecone_index.query(query_embedding, top_k=10)
    # HERE: results are often mediocre. Reason:
    # - "iPhone 15 Pro" embedding is similar to "Samsung Galaxy S25"
    # - LLM has seen millions products, confuses features
    # - Price is not a factor: VDB doesn't know user wants <€1000
    return results

# REALISTIC: hybrid ranking with BM25 fallback
def rag_search_realistic(query):
    # First: try keyword search (BM25)
    keyword_results = elasticsearch.search(query, size=50)

    # If few keyword matches (<5), integrate semantic
    if len(keyword_results) < 5:
        query_embedding = ollama.embed(query)  # on-prem, no cost
        semantic_results = redis_hnsw.query(query_embedding, top_k=10)
        results = merge_by_score(keyword_results, semantic_results)
    else:
        # Keyword match sufficient, skip expensive semantic
        results = keyword_results[:10]

    # Re-rank by business logic (price, rating, freshness)
    results = rerank_by_business_logic(results, query_context)
    return results

The problem: realistic code is more complex than simple vector DB query. You maintain both keyword and semantic index, merge logic, business re-ranking. In practice, you do 90% the work you'd do with Elasticsearch + BM25 + Lua scripting.

4. When alternatives categorically beat vector DBs

BM25 (Elasticsearch, Solr, Lucene): Choose this if ranking is summarizable as "document relevance to query terms".

  • Query: "best headphones under 200 euros"
  • BM25 term frequency matching vs. vector DB semantic similarity
  • Winner: BM25 (finds "200 euros" and "headphones" in title, relevance 0.95; vector DB picks "expensive audio equipment", 0.72)

Full-Text Search (PostgreSQL, SQLite FTS5): If corpus is <100GB and queries are symmetric (full document search, not embedding comparison).

  • Query: "how to deploy Angular SSR on Hetzner VPS"
  • Postgres FTS indexes keywords, searches fast. No embedding overhead. Relevance: 0.88
  • Vector DB embedding overhead for 30-40ms query latency, relevance: 0.85

Graph Database (Neo4j, ArangoDB): If ranking depends on relational data structure.

  • Query: "show articles by authors who reviewed products with 5-star rating"
  • Vector DB doesn't "understand" author→review→product→rating relations
  • Graph query: MATCH (a:Article)-[:AUTHORED_BY]-(u:User)-[:REVIEWED]-(p:Product) WHERE p.rating=5 - Five lines, exact result

Redis + Lua Scripting: If you must re-rank on-the-fly for business logic (price, availability, local delivery).


-- Redis Lua: custom re-ranking
local results = redis.call('ZRANGE', 'products_bm25', 0, 49)  -- top 50 BM25
local reranked = {}
for i, product_id in ipairs(results) do
    local price = tonumber(redis.call('HGET', product_id, 'price'))
    local rating = tonumber(redis.call('HGET', product_id, 'rating'))
    local freshness = tonumber(redis.call('HGET', product_id, 'days_since_updated'))

    -- Custom score: 50% BM25 relevance + 25% rating + 15% freshness - 10% price_penalty
    local custom_score =
        (50 * (50 - i) / 50) +  -- BM25 position decay
        (25 * rating / 5) +      -- rating 0-5 → 0-25 points
        (15 * math.max(0, (30 - freshness) / 30)) +  -- days freshness
        (-10 * math.min(1, price / 1000))  -- penalty if expensive

    table.insert(reranked, {product_id, custom_score})
end
table.sort(reranked, function(a,b) return a[2] > b[2] end)
return reranked

Vector DBs lack this capability. If ranking logic is "semantic similarity only", OK; if it's "semantic + business rules", choose something more flexible.

5. BM25 and Full-Text Search: the workhorse

BM25 is the optimized tf-idf ranking algorithm used by Google, Elasticsearch, Solr since 2009. Simple, proven, fast.


// Elasticsearch BM25 query
GET /articles/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "vector database alternatives",
            "fields": ["title^3", "content^1", "tags^2"],
            "type": "best_fields",
            "fuzziness": "AUTO"
          }
        }
      ],
      "filter": [
        { "range": { "publishedAt": { "gte": "2026-01-01" } } }
      ]
    }
  },
  "size": 10
}

Why BM25 beats vector DBs for keywords:

  • Speed: <5ms per query (inverted index O(1) lookup). Vector DB embedding + kNN = 50-200ms.
  • Precision: If user searches "Angular 21 TypeScript", BM25 matches titles with those words. Vector DB matches "frontend framework" and "programming language" regardless.
  • Interpretability: You know exactly why "Angular 21" ranks high (term frequent in results, rare in corpus). Vector DB embedding is black-box.
  • Cost: Open-source Elasticsearch costs €0 (self-hosted). Cloud Pinecone costs €500/month minimum.

6. Graph Database: for queries with relations

If ranking requires traversal of relations (author→article, product→category, user→past_purchases), graph database is mandatory.


// Neo4j Cypher query: find articles from trusted authors
MATCH (a:Article)-[:AUTHORED_BY]-(u:User)-[:FOLLOWS]-(follower:User)
WHERE follower.id = $current_user_id AND a.publishedAt > datetime.now() - duration('P30D')
RETURN a ORDER BY a.engagement DESC LIMIT 10

This query is impossible in vector DB. With vector DB you'd need:

  1. Fetch all articles for author→article relations
  2. Filter by trusted author (JOIN in query, not possible in vector DB)
  3. Re-rank by engagement (2nd pass)

With Neo4j, single step. With vector DB, 2-3 database round-trips.

7. Elasticsearch: the hybrid middle ground

Elasticsearch 8.0+ supports dense vector search (HNSW) PLUS BM25. Best-of-both-worlds if you want to avoid multi-system complexity.


// Elasticsearch hybrid query: BM25 + dense vector
GET /articles/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "multi_match": {
            "query": "machine learning algorithms",
            "fields": ["title^3", "content"],
            "boost": 0.5
          }
        },
        {
          "knn": {
            "embeddings": {
              "vector": [...query_embedding_384_dim...],
              "k": 10,
              "boost": 0.5
            }
          }
        }
      ]
    }
  }
}

Advantages:

  • Single system, single index, single deployment
  • Re-ranking script-based (Elasticsearch Painless scripting language)
  • Cost: €500-2000/month self-hosted; 5x cheaper than Pinecone cloud
  • Scalability: proven with Netflix, Uber, Shopify

Disadvantages:

  • Learning curve (more complex than Pinecone UI)
  • Maintenance overhead (cluster tuning, shard allocation)
  • Still need to generate embeddings (no native LLM inside)

8. Redis HNSW + Lua scripting: custom ranking on a budget

Redis Stack 7.0+ adds HNSW vector indexing. Combine with Lua scripting for custom re-ranking. Cost: €50-200/month.


# Python: Redis HNSW + Lua re-ranking
from redis import Redis
from sentence_transformers import SentenceTransformer

model = SentenceTransformer('all-MiniLM-L6-v2')  # 384-dim, local
redis_client = Redis(host='localhost', port=6379)

def hybrid_search(query, user_context):
    # Step 1: get query embedding locally
    query_embedding = model.encode(query).tolist()

    # Step 2: Redis HNSW kNN search
    results = redis_client.execute_command(
        'FT.SEARCH',
        'articles_idx',
        f'*=>[KNN 10 @embeddings $query_embedding EF_RUNTIME 200]',
        'PARAMS', 2, 'query_embedding', query_embedding
    )

    # Step 3: Lua re-ranking with business logic
    lua_script = '''
    local results = cjson.decode(ARGV[1])
    local user_preferences = cjson.decode(ARGV[2])

    for i, doc in ipairs(results) do
        local article_id = doc.id
        local knn_score = doc.similarity
        local author_id = redis.call('HGET', article_id, 'author_id')
        local is_trusted = redis.call('SISMEMBER', 'trusted_authors', author_id)

        -- Re-rank: 70% kNN + 30% trust bonus
        doc.final_score = (0.7 * knn_score) + (0.3 * (is_trusted and 1 or 0))
    end

    table.sort(results, function(a,b) return a.final_score > b.final_score end)
    return cjson.encode(results)
    '''

    final_results = redis_client.eval(
        lua_script,
        0,
        json.dumps(results),
        json.dumps(user_context)
    )
    return final_results

Why this wins: Single Redis instance, embeddings computed locally (Ollama), zero cloud API cost, re-ranking logic fully controlled.

9. Case study: Ollama on-prem + Redis HNSW vs Pinecone cloud

Setup: 500K Italian tech articles, RAG chatbot, 100 QPS.

Metric Pinecone cloud Ollama + Redis Winner
Monthly cost €800 (100GB index, 100 QPS) €150 (Hetzner CCX23, 32GB RAM) Redis (5.3x cheaper)
Query latency p50 45ms 35ms Redis
Embedding cost per M tokens €15 (cloud LLM API) €0 (local Ollama llama3.1:8b) Redis (free)
Re-ranking flexibility Limited (Pinecone SDK only) Full (Lua + Python logic) Redis
Data residency Pinecone cloud (privacy risk for EU) On-prem Hetzner VPS (GDPR compliant) Redis
Hallucination mitigation VDB + cloud LLM (prone to drift) VDB + local LLM (deterministic Ollama) Redis (local LLM)

Result: Ollama + Redis costs 5.3x less, faster, more private, better re-ranking. Pinecone wins only if you need 10M+ docs and specialized teams.

10. Cost analysis: realistic TCO

Scenario: 2M articles, 100 QPS peak, 12-month horizon.


# TCO calculator

scenarios = {
    'vector_db_cloud': {
        'infra': 800 * 12,  # €800/month Pinecone
        'embedding_api': 0.015 * 2000000 / 1000,  # €30 cloud LLM API embedding
        'llm_inference': 0.003 * 1000000,  # €3k cloud LLM API inference usage
        'engineering': 150 * 12,  # €150/month DevOps
        'total': 9600 + 30 + 3000 + 1800,
    },
    'vector_db_self_hosted_elasticsearch': {
        'infra': 300 * 12,  # €300/month Hetzner VM
        'embedding_api': 0,  # Ollama local
        'llm_inference': 0,  # Ollama local
        'engineering': 300 * 12,  # €300/month maintenance
        'total': 3600 + 3600,
    },
    'redis_hnsw_bm25_hybrid': {
        'infra': 150 * 12,  # €150/month Hetzner+Redis Stack
        'embedding_api': 0,  # Ollama
        'llm_inference': 0,  # Ollama
        'engineering': 200 * 12,  # €200/month (less overhead)
        'total': 1800 + 2400,
    },
}

for scenario, costs in scenarios.items():
    print(f"{scenario}: €{costs['total']}")

# Output:
# vector_db_cloud: €14,430
# vector_db_self_hosted_elasticsearch: €7,200
# redis_hnsw_bm25_hybrid: €4,200

Insight: Redis hybrid is 3.4x cheaper than Pinecone cloud, 1.7x cheaper than Elasticsearch self-hosted.

11. Practical decision tree: which to choose

Q1: Is your dataset structured or unstructured?

  • Structured (SQL tables, known JSON keys): → PostgreSQL FTS or Elasticsearch BM25
  • Unstructured (long-form articles, PDFs): → Elasticsearch hybrid or Redis HNSW

Q2: Do queries include relational business logic (author, category, recency)?

  • Yes: → Neo4j (graph) or Elasticsearch + Painless scripts
  • No, pure semantic similarity: → Redis HNSW or Pinecone

Q3: How many queries per second and what latency SLA?

  • <10 QPS, <500ms SLA: → Postgres FTS (simplest)
  • 10-100 QPS, <100ms: → Open-source Elasticsearch
  • >100 QPS, <50ms: → Redis HNSW (on-prem) or Pinecone (cloud)

Q4: Privacy + data residency (GDPR)?

  • Must stay in EU: → Self-hosted (Elasticsearch, Redis, PostgreSQL). Avoid cloud LLM providers for GDPR compliance.
  • Cloud allowed: → Pinecone EU region (if available)

Q5: Annual budget?

  • <€5K: → Redis HNSW + Ollama self-hosted
  • €5K-€15K: → Elasticsearch self-hosted
  • >€15K: → Pinecone cloud (convenience, managed service)

12. When to migrate from vector DB to hybrid solution

If you already use Pinecone/Weaviate, is migration worthwhile? Only if:

  • Monthly cost > €500 and latency p99 > 200ms: Your access pattern is inefficient. Hybrid approach often reduces both latency and cost.
  • Relevance metrics degrading: If ranking declined in the last 3 months, problem isn't the vector DB but re-ranking logic. Switch to Elasticsearch with custom scoring.
  • Data privacy mandate: If board requires GDPR-only on-prem, migrate from cloud vector DB to self-hosted Redis/Elasticsearch.
  • Query pattern shifting to keyword-heavy: If 70%+ queries are keyword matches (not semantic), BM25 is more efficient.

Don't migrate if: Embedding quality is the main value (the 30% of edge cases where vector DB truly excels), your team loves Pinecone's managed service, compliance isn't a concern.

13. FastAPI implementation: smart routing between BM25 and vector DB

Code that automatically chooses the ranking algorithm:


from fastapi import FastAPI, Query
from elasticsearch import Elasticsearch
from redis import Redis
from sentence_transformers import SentenceTransformer

app = FastAPI()
es = Elasticsearch([{'host': 'localhost', 'port': 9200}])
redis_client = Redis(host='localhost', port=6379, decode_responses=True)
model = SentenceTransformer('all-MiniLM-L6-v2')

@app.post("/search")
async def search(query: str = Query(..., min_length=3)):
    # Heuristic: if query has exact keywords, try BM25 first
    keyword_tokens = query.lower().split()

    # Step 1: BM25 search (fast)
    bm25_results = es.search(
        index='articles',
        body={
            'query': {'multi_match': {
                'query': query,
                'fields': ['title^3', 'content', 'tags^2']
            }},
            'size': 50
        }
    )

    bm25_hits = [
        {
            'id': hit['_id'],
            'score': hit['_score'],
            'source': hit['_source']
        }
        for hit in bm25_results['hits']['hits']
    ]

    # Decision: if top BM25 score > 5.0, return BM25 (high confidence keyword match)
    if bm25_hits and bm25_hits[0]['score'] > 5.0:
        return {'method': 'bm25', 'results': bm25_hits[:10]}

    # Otherwise: integrate semantic search via Redis HNSW
    query_embedding = model.encode(query).tolist()
    hnsw_results = redis_client.execute_command(
        'FT.SEARCH', 'articles_idx',
        f'*=>[KNN 10 @embeddings $query_embedding]',
        'PARAMS', 2, 'query_embedding', query_embedding
    )

    # Merge and deduplicate
    merged = {}
    for hit in bm25_hits:
        merged[hit['id']] = {'bm25_score': hit['score']}
    for hit in hnsw_results:
        if hit['id'] in merged:
            merged[hit['id']]['hnsw_score'] = hit.get('score', 0)
        else:
            merged[hit['id']] = {'hnsw_score': hit.get('score', 0)}

    # Hybrid re-ranking: 60% BM25 + 40% semantic
    final_results = sorted(
        [
            {
                'id': doc_id,
                'hybrid_score': (
                    0.6 * doc.get('bm25_score', 0) +
                    0.4 * doc.get('hnsw_score', 0)
                ),
                'source': bm25_hits[[h['id'] for h in bm25_hits].index(doc_id)]['source']
            }
            for doc_id, doc in merged.items()
        ],
        key=lambda x: x['hybrid_score'],
        reverse=True
    )[:10]

    return {'method': 'hybrid', 'results': final_results}

14. FAQ: Common questions on ranking alternatives

Q: "If I use Ollama for embeddings, don't I create the same problem as Pinecone (non-local embeddings)?"

A: No. Ollama runs on-prem, owned by you. Zero embedding API cost, zero data exfiltration, zero latency for embedding inference. Massive advantage compared to cloud LLM providers that require network round-trips and monthly fees.

Q: "Isn't BM25 'old tech'? Shouldn't we always use semantic search?"

A: BM25 is not obsolete, it's specialized. Gold standard for keyword search. Semantic is complementary, not substitutive. If you love novelty, hybrid (BM25 + semantic) is the true modern approach, not full-vector DB.

Q: "What if I change ranking algorithm? Do I need to re-index?"

A: No. Index (inverted for BM25, HNSW for vector DB) stays the same. Change ranking formula using Lua scripts or Elasticsearch Painless, and result is immediate. Zero down-time, zero re-indexing.

Q: "Is Elasticsearch hybrid (BM25 + HNSW) really 'hybrid' or just two separate searches?"

A: Single query with `bool.should` clause (OR logic). Elasticsearch executes both BM25 and kNN, then merges with combined scoring. More efficient than two separate queries.

Q: "If relevance degrades over time, is it the algorithm's fault or the dataset's?"

A: Often dataset (stale embeddings, outdated content, model drift). Monitor: token frequency distribution, embedding drift (paragraph similarity over time), user feedback (CTR). If feedback is good, algorithm is fine. If audience preference shifts, adapt ranking formula (re-weight BM25 vs semantic, adjust freshness factor).

Conclusions: choose the right tool

Vector databases are powerful for semantic search at scale. But 70% of real-world use cases achieve better results and save money with BM25, Elasticsearch, Redis, PostgreSQL. Decision must be data-driven:

  • If query is "iPhone 15 Silver 256GB", use BM25.
  • If query is "articles on machine learning by trusted authors", use graph database.
  • If query is "conceptually similar articles in multiple languages", use vector DB.
  • If query is "TOP 10 products for user preferences + trending + in-stock", use hybrid (BM25 + Lua re-ranking).

Takeaway: Don't auto-default to vector DBs. Benchmarking (relevance metric + TCO cost) always beats trend-chasing. Your corpus, your SLA, your budget deserve the optimal solution, not the trendiest one.

Commenti

Caricamento commenti...

Sign in to comment

Discuss this article in the community

Join the Telegram group to discuss with other developers, ask questions and share your experiences.

Did you like this article?

Explore other content on the blog or discover my projects