Dan Levy

Recherche vectorielle sémantique et autres sujets pour se faire des amis et séduire

Le paysage complet de la recherche : exacte, floue, sémantique, hybride — et quand les combiner toutes.

La recherche n’est pas une seule chose, et la recherche sémantique ne remplace pas tout le reste.

Panorama de la recherche vectorielle : comparaison de 16 options

Comparez le déploiement, la licence, les capacités de recherche et l’adéquation aux charges de travail. Les explications des colonnes et les exemples SQL suivent ci-dessous.

Comment lire cette comparaison

Base de donnéesDéploiementLicenceRecherche hybrideVecteurs creuxInterface de requêteEmbedding multimodal intégréIndex sur disqueLimites de dimension des vecteursIdéal pour
pgvectorAuto-hébergé / managé (Supabase, Neon, RDS)OSS (PostgreSQL)Manuelle (RRF via SQL)❌✅ SQL complet❌✅ HNSW sur disque16 000 en stockage ; 2 000 indexés vectorVous êtes déjà sur Postgres ; volumes de vecteurs modérés
QdrantAuto-hébergé / CloudApache 2.0✅ BM25 natif✅ Support mature❌ (REST/gRPC)❌✅65 535Requêtes filtrées à grande échelle ; métadonnées complexes
WeaviateAuto-hébergé / CloudBSD 3✅ BM25 natif + RRF✅❌ (GraphQL / gRPC)✅ via des modules✅65 535Modes d’accès GraphQL ; vectorisation intégrée
PineconeCloud uniquementPropriétaire✅ (ajouté en 2024)✅❌❌✅ (serverless)20 000Simplicité du managé ; aucune équipe ops
Milvus / ZillizAuto-hébergé / Cloud (Zilliz)Apache 2.0✅ Native✅✅ SQL-like (Milvus Query Language)✅✅ DiskANN32 768Échelle du milliard de vecteurs ; on-premise d’entreprise
ChromaEmbarqué / auto-hébergéApache 2.0❌❌❌❌❌65 535Développement local et prototypage uniquement
LanceDBEmbarqué / CloudApache 2.0✅❌✅ SQL via DataFusion✅ Natif✅ (format Lance)IllimitéeEdge / serverless ; lakehouse multimodal
OramaEmbarqué / CloudApache 2.0✅ Texte intégral + vecteur❌❌❌❌VariableApplications JS/edge ; recherche légère pour sites et applications
TurbopufferCloud uniquement (serverless)Propriétaire✅ BM25 + vecteur❌❌❌✅ (stockage objet)16 000SaaS multitenant ; millions de namespaces
ElasticsearchAuto-hébergé / Elastic CloudSSPL / AGPLv3✅ RRF + ELSER creux✅ (ELSER)✅ Query DSL❌✅ DiskBBQ4 096Vous êtes déjà sur la stack Elastic ; recherche hybride d’entreprise
OpenSearchAuto-hébergé / managé par AWSApache 2.0✅ RRF + Neural Search✅✅ Query DSL❌✅ FAISS + HNSW16 000Environnement AWS ; alternative open source à Elastic
VespaAuto-hébergé / CloudApache 2.0✅ Native✅ Tenseurs / classement lexical✅ YQL✅ Tenseurs✅Effectivement illimitéeSystèmes de recherche, de classement et de recommandation
ClickHouseAuto-hébergé / CloudApache 2.0Manuelle❌✅ SQL complet❌✅ Colonnaire + HNSWVariableAnalytics/logs avec recherche vectorielle à côté de l’OLAP
MongoDB AtlasCloud / auto-hébergéSSPL✅ Intégrée❌✅ MQL + agrégation❌✅ HNSW8 192Vous êtes déjà sur MongoDB ; documents et vecteurs au même endroit
Redis (VSS)Auto-hébergé / Redis CloudRSALv2 / SSPL✅ (RediSearch)✅❌❌❌ En RAM uniquement32 768Latence extrêmement faible ; recherche vectorielle dans la couche de cache
MarqoCloud / auto-hébergéApache 2.0✅❌❌✅ Orientation native✅VariableMultimodal de bout en bout : image + texte + vidéo

« Trouver l’utilisateur dont l’adresse e-mail est dan@example.com » et « trouve-moi des articles sur le débogage quand on est un nouvel ingénieur » sont tous deux décrits comme de la recherche, mais ils n’ont pratiquement rien en commun en tant que problèmes d’ingénierie. Le premier a une réponse correcte et un accès indexé en O(log n). Le second n’a pas de réponse correcte — seulement une notion de pertinence — et nécessite de comprendre le langage, l’intention et le sens.

Les ingénieurs les plus convaincants lorsqu’il s’agit de décisions liées à la recherche — ceux qui gagnent les débats et livrent le bon système — comprennent l’ensemble du paysage. Ils savent quel outil utiliser et pourquoi, et savent l’expliquer clairement.

Cet article couvre la couche sémantique : ce que fait réellement la recherche vectorielle, dans quels cas elle est efficace et dans quelles situations elle doit rester en retrait. La version utile n’est pas « vectoriser tout ce qui bouge ». Il s’agit de savoir quand les vecteurs ont leur place aux côtés de la recherche lexicale, floue et par correspondance exacte, dans une architecture hybride.

La moitié lexicale et floue du sujet — tsvector, pg_trgm, pg_search — est traitée dans Guide de la recherche textuelle dans Postgres 2026.


Termes utilisés dans ce guide

Embedding — Une liste dense de nombres à virgule flottante produite par un modèle, qui représente un morceau de texte (ou une image, un fichier audio, etc.) comme un point dans un espace de grande dimension. Les contenus sémantiquement proches se retrouvent à proximité ; les contenus sans rapport se retrouvent éloignés.

Recherche lexicale — Recherche fondée sur la correspondance exacte des mots et des tokens. Rapide, déterministe et fiable pour les termes connus. Elle ne comprend ni les synonymes, ni les paraphrases, ni les équivalents dans d’autres langues.

Recherche sémantique — Recherche fondée sur le sens plutôt que sur les tokens. Une requête comme « comment gérer les timeouts » peut correspondre à un document intitulé « configurer des politiques de nouvelle tentative » sans aucun mot en commun, parce que leurs embeddings sont géométriquement proches.

Vecteur — Une liste de nombres. Dans le contexte de la recherche, c’est le résultat produit par un modèle d’embedding. La « recherche vectorielle » trouve les vecteurs les plus proches d’un vecteur de requête selon une distance géométrique.

FTS (Full-Text Search) — La recherche lexicale intégrée à Postgres, basée sur tsvector / tsquery. Elle segmente, lemmatise et indexe le texte pour les requêtes par mots-clés. Elle est efficace pour la prose et la recherche de termes exacts ; le sens lui échappe complètement.

BM25 — Un algorithme de classement pour la recherche lexicale (utilisé par Elasticsearch, Qdrant et d’autres). Il attribue un score aux résultats selon la fréquence d’un terme, pondérée par la rareté de ce terme dans le corpus. C’est mieux qu’une simple correspondance de mots-clés, mais cela reste lexical.

HNSW (Hierarchical Navigable Small World) — L’index standard de recherche approximative des plus proches voisins pour la recherche vectorielle. Il construit un graphe de proximité en couches afin d’effectuer rapidement des requêtes de similarité avec un fort rappel. pgvector, Qdrant, Weaviate et la plupart des autres solutions l’utilisent.

RRF (Reciprocal Rank Fusion) — Algorithme permettant de fusionner des listes de résultats classées provenant de plusieurs systèmes de recherche. Il utilise uniquement la position dans le classement — aucune normalisation des scores n’est nécessaire. Un résultat bien classé à la fois dans les listes FTS et vectorielles obtient un score combiné supérieur à celui qui domine une seule des deux listes.


Comment les embeddings trouvent du contenu connexe

Les embeddings vectoriels convertissent du texte (ou des images, de l’audio, etc.) en une liste de nombres — un point dans un espace à grande dimension. Un modèle d’embeddings est entraîné de manière à ce que les textes sémantiquement proches se retrouvent à proximité dans cet espace. « Chien » et « canidé » finissent proches. « Courir un marathon » et « exécuter un script Python » finissent très éloignés malgré un mot en commun.

La recherche par similarité dans cet espace trouve les documents dont le sens est le plus proche de celui de la requête, indépendamment du recouvrement exact des mots.

Cela signifie que :

La recherche lexicale (tsvector, pg_trgm) ne sait rien faire de tout cela. Elle opère sur les mots et les caractères, pas sur le sens. Ces outils ne sont pas interchangeables : ils résolvent des problèmes différents.


Quand pgvector prend l’avantage

Construire un système RAG. La génération augmentée par récupération (Retrieval-Augmented Generation) récupère les fragments de documents dont le sens est le plus proche de la question de l’utilisateur, puis les transmet à un modèle de langage comme contexte. Cette étape de récupération est une opération vectorielle. La FTS ne détectera pas les paraphrases, les synonymes ni les correspondances conceptuelles qu’un fragment pertinent peut exprimer différemment. L’avantage de pgvector par rapport à un magasin vectoriel autonome : il s’exécute dans votre instance Postgres existante — aucun service distinct à déployer, exploiter ou synchroniser.

Les utilisateurs décrivent ce qu’ils veulent, pas ce qu’il faut rechercher. « Des articles sur la prise de confiance lorsqu’on devient manager » ne contient aucun mot-clé qui apparaisse de façon fiable dans les articles pertinents. « Un framework léger pour gérer les effets de bord » peut ne pas employer exactement ces termes dans la documentation. La recherche vectorielle fait correspondre l’intention, pas l’orthographe.

Trouver des éléments similaires. Produits associés, tickets de support similaires, rapports de bugs en double, articles susceptibles de vous intéresser. « Trouver les problèmes similaires à celui-ci » est une recherche des plus proches voisins — on vectorise l’élément, puis on trouve ses voisins géométriques. Une réserve importante : la recherche vectorielle renvoie toujours des résultats, même lorsque rien n’est réellement similaire. Pour les cas d’usage de dédoublonnage et de recommandation, appliquez un filtre sur un seuil minimal de similarité (par exemple, similarité cosinus ≥ 0,80) afin d’éviter de présenter des correspondances peu fiables comme si elles étaient pertinentes.

Dédoublonnage sémantique. Avant d’indexer du contenu pour un système RAG ou une recherche, il faut souvent identifier les quasi-doublons du corpus — articles révisés plusieurs fois, tickets de support soumis deux fois, entrées de base de connaissances qui se recouvrent largement. Vectorisez les documents, puis appliquez un filtre de seuil sur la similarité cosinus afin de signaler ou de fusionner les quasi-doublons avant qu’ils ne polluent l’index. Cela évite que la récupération renvoie plusieurs fragments presque identiques et dilue la fenêtre de contexte.

Recherche multilingue. Les modèles d’embeddings multilingues projettent dans des vecteurs proches les contenus sémantiquement équivalents entre différentes langues. Une requête en espagnol pour « perder peso » peut correspondre à un article anglais sur les « sustainable weight loss habits » — aucun token commun, mais le même sens sous-jacent. La FTS nécessite une configuration de dictionnaire propre à chaque langue et gère mal les requêtes interlingues. pg_trgm ne dépend pas de la langue, mais reste orthographique, pas sémantique.

Configurer pgvector

De l’installation de l’extension à la requête de similarité, la configuration tient en quelques instructions SQL :

CREATE EXTENSION IF NOT EXISTS vector;
ALTER TABLE documents ADD COLUMN embedding vector(1536);
-- HNSW is usually the first index to try for moderate-size datasets
CREATE INDEX documents_embedding_idx
ON documents USING hnsw (embedding vector_cosine_ops);
-- Semantic search query
SELECT id, title, 1 - (embedding <=> $1::vector) AS similarity
FROM documents
ORDER BY embedding <=> $1::vector
LIMIT 10;

<=> représente la distance cosinus. 1 - cosine_distance donne la similarité cosinus (1,0 = identique, 0,0 = orthogonal). Pour ivfflat (l’alternative plus ancienne, mais plus rapide à construire), utilisez lists = sqrt(row_count) comme point de départ.

Quand la recherche vectorielle renvoie une mauvaise réponse


Combinez mots-clés et vecteurs pour les requêtes mixtes

La documentation technique est l’exemple le plus clair de cas où aucun des deux outils ne suffit à lui seul.

Les utilisateurs qui recherchent « comment configurer les délais d’expiration » ont besoin d’une correspondance conceptuelle : un article intitulé « Définir des stratégies de nouvelle tentative et des limites de connexion » ne partage aucun mot-clé, mais correspond exactement à leur besoin.

Ces mêmes utilisateurs recherchent aussi withRetry(), ECONNRESET et ERR_SOCKET_TIMEOUT. Ces chaînes exactes doivent apparaître — la recherche sémantique risque de ne pas les trouver de manière fiable, et un faux positif (conceptuellement similaire, mais correspondant à une API différente) est franchement trompeur.

La recherche vectorielle traite les requêtes conceptuelles. La recherche plein texte traite les termes exacts. Aucune des deux ne gère correctement les deux cas à elle seule.

La solution est une recherche hybride : exécuter les deux recherches, puis fusionner les résultats.

Fusionnez les résultats classés avec RRF

La Reciprocal Rank Fusion (RRF) est l’algorithme standard pour combiner les listes classées de différents systèmes de recherche. Elle ne nécessite pas de normaliser les scores entre les systèmes : elle utilise uniquement les positions dans le classement. Un résultat qui apparaît haut dans les deux listes obtient un score combiné supérieur à celui qui ne domine que dans une seule.

WITH fts_results AS (
SELECT id,
ROW_NUMBER() OVER (ORDER BY ts_rank(search_vector, query) DESC) AS rank
FROM documents, to_tsquery('english', $1) query
WHERE search_vector @@ query
LIMIT 50
),
vector_results AS (
SELECT id,
ROW_NUMBER() OVER (ORDER BY embedding <=> $2::vector) AS rank
FROM documents
ORDER BY embedding <=> $2::vector
LIMIT 50
),
rrf AS (
SELECT
COALESCE(f.id, v.id) AS id,
COALESCE(1.0 / (60 + f.rank), 0) +
COALESCE(1.0 / (60 + v.rank), 0) AS rrf_score
FROM fts_results f
FULL OUTER JOIN vector_results v ON f.id = v.id
)
SELECT d.id, d.title, rrf.rrf_score
FROM rrf
JOIN documents d ON d.id = rrf.id
ORDER BY rrf_score DESC
LIMIT 10;

Le 60 au dénominateur est la constante RRF. Des valeurs plus élevées atténuent les différences de position dans le classement ; des valeurs plus faibles les accentuent. La valeur par défaut de 60 fonctionne bien pour la plupart des types de contenu.

RRF évite le problème plus délicat qui consiste à normaliser ts_rank (un score de fréquence logarithmique) par rapport à la distance cosinus (une mesure géométrique). Ces valeurs ne sont pas comparables. RRF pose simplement la question suivante : « À quelle position ce résultat est-il apparu dans chaque liste ? »

Ajoutez les trigrammes pour les fautes et les noms

Pour une recherche destinée aux utilisateurs et portant sur des contenus hétérogènes — lorsque les utilisateurs peuvent rechercher le nom d’une personne, un concept ou un terme exact au cours d’une même session — une fusion à trois voies couvre tous ces cas :

WITH trgm_results AS (
SELECT id,
ROW_NUMBER() OVER (ORDER BY similarity(title, $1) DESC) AS rank
FROM documents
WHERE title % $1
LIMIT 50
),
fts_results AS (
SELECT id,
ROW_NUMBER() OVER (ORDER BY ts_rank(search_vector, to_tsquery('english', $1)) DESC) AS rank
FROM documents
WHERE search_vector @@ to_tsquery('english', $1)
LIMIT 50
),
vector_results AS (
SELECT id,
ROW_NUMBER() OVER (ORDER BY embedding <=> $2::vector) AS rank
FROM documents
ORDER BY embedding <=> $2::vector
LIMIT 50
),
rrf AS (
SELECT
COALESCE(t.id, f.id, v.id) AS id,
COALESCE(1.0 / (60 + t.rank), 0) +
COALESCE(1.0 / (60 + f.rank), 0) +
COALESCE(1.0 / (60 + v.rank), 0) AS rrf_score
FROM trgm_results t
FULL OUTER JOIN fts_results f ON t.id = f.id
FULL OUTER JOIN vector_results v ON COALESCE(t.id, f.id) = v.id
)
SELECT d.id, d.title, rrf.rrf_score
FROM rrf
JOIN documents d ON d.id = rrf.id
ORDER BY rrf_score DESC
LIMIT 10;

Cette approche gère les correspondances approximatives de noms (trigrammes), les correspondances exactes de mots-clés (FTS) et les requêtes conceptuelles (vecteurs). Un champ de recherche unique peut ainsi répondre aux trois intentions utilisateur.


Faites correspondre chaque surface de recherche à ses types de requêtes

Les applications réelles ont rarement une seule surface de recherche. Elles en ont plusieurs, chacune répondant à un besoin différent :

SurfaceRequêtes des utilisateursCouches recommandées
Recherche dans un blog / une documentationMots-clés + conceptsFTS + pgvector (RRF)
Recherche de noms d’utilisateurs / clientsNoms avec fautes de frappepg_trgm
Recherche de produitsNoms, descriptions, « similaire à »pg_trgm + FTS + pgvector
Déduplication de tickets de support« Problèmes similaires à celui-ci »pgvector uniquement
Recherche interne de SKU / commandesIdentifiants exactsIndex B-tree
RAG sur une base de connaissances volumineuseQuestions en langage naturelpgvector (documents découpés en chunks)
« Vous aimerez peut-être aussi » dans un site e-commerceSimilarité comportementale + sémantiquepgvector
AutocomplétionPréfixe, tolérance aux fautes d’orthographepg_trgm

Ce ne sont pas des cas théoriques. La plupart des applications riches en contenu ont besoin d’au moins deux surfaces de recherche distinctes, avec des formes de requête différentes. La tentation consiste à choisir une approche et à l’utiliser partout — généralement la recherche vectorielle aujourd’hui, puisqu’elle a le vent en poupe. On finit alors par calculer des embeddings coûteux pour des problèmes auxquels un index trigramme aurait répondu plus vite, à moindre coût et plus correctement.

Ajoutez une couche de recherche lorsqu’un type de requête échoue

Ajoutez une couche lorsqu’un mode d’échec apparaît et que la couche actuelle ne peut pas le corriger :


Quand aller au-delà de pgvector

pgvector couvre une grande partie des besoins de recherche applicative avant qu’une autre base de données ne devienne nécessaire. Le seuil approximatif dépend du nombre de vecteurs, des paramètres d’index, du débit d’écriture, des filtres, du matériel et de la concurrence. Considérez donc toute règle du type « moins de 10 millions de vecteurs » comme une hypothèse de départ à mesurer, pas comme une limite produit. Lorsque vous le dépassez réellement — très forte concurrence, exigences de latence p99 très basse, milliards de vecteurs ou besoins sérieux d’isolation multi-tenant — l’écosystème des bases vectorielles dédiées est vaste et mérite d’être compris.

Comment lire la comparaison

La recherche hybride signifie que la recherche par mots-clés BM25 et la similarité vectorielle s’exécutent dans une seule requête, puis sont fusionnées via RRF. Sans cette capacité, vous devez choisir un mode de recherche ou fusionner vous-même deux requêtes.

Les vecteurs creux vont plus loin que BM25. Un vecteur creux SPLADE possède environ 30 000 dimensions (une par terme du vocabulaire), dont environ 98 % sont nulles. Les positions non nulles indiquent quels termes comptent, et dans quelle mesure. Une requête sur « chiens » pondère également « canin » et « animal de compagnie » : une précision de niveau BM25, avec une expansion des termes intégrée à un index vectoriel. Si cette colonne vaut false, il vous faut une couche FTS distincte pour les requêtes portant sur des termes exacts.

# SPLADE: ~30,000 dims, ~60 non-zero — only relevant vocabulary positions fire
def encode_splade(text: str) -> dict:
tokens = tokenizer(text, return_tensors="pt", truncation=True, max_length=512)
with torch.no_grad():
output = model(**tokens)
vec = torch.log1p(torch.relu(output.logits)).max(dim=1).values.squeeze()
return {"indices": vec.nonzero().squeeze().tolist(), "values": vec[vec != 0].tolist()}

SQL / SQL-like concerne en réalité le filtrage. Une recherche vectorielle sans filtrage est une démonstration. Vous avez toujours besoin de filtrer par tenant, par plage de dates, par permissions et par catégorie. SQL complet (pgvector, LanceDB) exprime ces contraintes à côté de vos jointures existantes. Les bases spécialisées utilisent des objets de filtres JSON (Qdrant, Pinecone), un DSL de requête (Elasticsearch, Milvus) ou GraphQL (Weaviate). Ces approches fonctionnent ; SQL devient simplement plus intéressant lorsque la logique de filtrage se complexifie.

-- pgvector: vector similarity is just another expression
SELECT id, title, 1 - (embedding <=> $1) AS score
FROM documents
WHERE tenant_id = $2
AND category = ANY($3::text[])
AND created_at > NOW() - INTERVAL '90 days'
ORDER BY embedding <=> $1
LIMIT 10;
# Qdrant: equivalent filter as a Python object — same result, more ceremony
results = client.query_points(
collection_name="documents", query=query_embedding,
query_filter=models.Filter(must=[
models.FieldCondition(key="tenant_id", match=models.MatchValue(value=tenant_id)),
models.FieldCondition(key="category", match=models.MatchAny(any=categories)),
models.FieldCondition(key="created_at", range=models.DatetimeRange(gte=cutoff)),
]),
limit=10,
)

Multimodal natif signifie que la base de données fournit des modèles d’embedding pour les contenus autres que textuels. Vous lui transmettez l’URL brute d’une image ; elle se charge de la vectorisation. La plupart des bases de données sont agnostiques vis-à-vis des embeddings : c’est à vous de gérer le pipeline d’embedding. Marqo et Weaviate (via les modules CLIP/ImageBind) ferment cette boucle.

# Marqo: POST raw images, query with text — no external embedding step
mq.index("products").add_documents(
[{"id": "shoe-001", "image": "https://cdn.example.com/shoes/001.jpg"}],
tensor_fields=["image"]
)
results = mq.index("products").search(q="lightweight shoes for summer")
# Returns shoe-001 despite zero keyword overlap — CLIP handles the cross-modal match

Un index sur disque est un levier de coût. Les index HNSW résidents en RAM peuvent nécessiter plusieurs Go de RAM par million de vecteurs de 1536 dimensions, une fois pris en compte les vecteurs bruts, la surcharge du graphe et les métadonnées. Les alternatives natives sur disque (DiskANN de Milvus, DiskBBQ d’Elasticsearch, format Lance de LanceDB, niveau de stockage objet de Turbopuffer) échangent souvent une partie de la latence des requêtes contre une baisse du coût d’infrastructure. Pour les charges RAG où la latence du modèle domine déjà, ce compromis mérite fréquemment d’être mesuré.

Le nombre maximal de dimensions dissimule une migration dans votre architecture. text-embedding-3-large utilise 3072 dimensions, Jina v3 peut produire des embeddings plus grands, et les modèles de recherche continuent de pousser cette limite vers le haut. Certains services managés publient des plafonds stricts ; d’autres annoncent des plafonds élevés, voire aucune limite pratique pour les modèles d’embedding courants. Consultez la documentation actuelle avant de vous engager. Choisissez une solution qui laisse de la marge ; migrer un index vectoriel parce que vous avez atteint un plafond de dimensions est un sprint pénible.

Les compromis opérationnels que le tableau ne peut pas montrer

Le multi-tenancy de Turbopuffer est conçu autour d’un très grand nombre d’espaces de noms. Son positionnement public et les témoignages clients mettent en avant des charges comme le corpus volumineux et fortement segmenté en espaces de noms de Notion. Si chaque utilisateur ou organisation a besoin d’une recherche vectorielle isolée, cette architecture peut modifier l’économie du système ; mesurez malgré tout votre propre répartition des tenants.

Le mode embarqué de LanceDB est ce qui se rapproche le plus de « SQLite pour la recherche vectorielle ». Il s’exécute dans le processus, ne nécessite aucun serveur et fonctionne dans Lambda, Cloudflare Workers et les environnements edge. Le format colonnaire Lance rend l’exécution embarquée viable à une échelle réelle.

Chroma est surtout adapté au développement, aux tests et aux petits déploiements applicatifs. Si vous ciblez des corpus très volumineux, la haute disponibilité, un fonctionnement fortement fondé sur le disque ou une recherche hybride de premier ordre, évaluez un magasin orienté production avant de transformer le prototype en infrastructure.

Vespa est le choix qui s’impose lorsque la recherche ne représente que la moitié du produit. Il combine recherche lexicale, recherche par plus proches voisins, tenseurs, expressions de classement, regroupement et service en ligne. Cette puissance est bien réelle, mais la complexité opérationnelle et de modélisation l’est aussi. Il convient davantage aux équipes qui construisent des plateformes de recherche ou de recommandation qu’à celles qui veulent simplement « ajouter la recherche sémantique à leur application CRUD ».

ClickHouse a sa place dans la discussion lorsque la recherche est liée à l’analytique. Si votre source de vérité est constituée d’événements, de journaux, de traces ou de métriques, ClickHouse regroupe dans un même moteur SQL la distance vectorielle, le filtrage, l’agrégation et un véritable index de recherche plein texte. Ce n’est pas une base vectorielle spécialisée, mais c’est souvent la réponse raisonnable — et peu spectaculaire — pour la recherche analytique.

Les vecteurs creux permettent d’obtenir une correspondance par mots-clés de qualité BM25 dans un index vectoriel, sans exécuter de moteur plein texte séparé. Qdrant et Elasticsearch proposent ici des implémentations particulièrement matures. Si la recherche hybride est essentielle et qu’une architecture à deux systèmes est rédhibitoire, c’est le support des vecteurs creux qu’il faut rechercher.

Choisir un magasin en fonction de la charge


N’intégrez pas les identifiants en espérant obtenir des correspondances exactes

N’utilisez pas la recherche vectorielle comme une recherche textuelle floue pour des éléments qui ont une réponse correcte.

« Trouve-moi l’utilisateur dont l’adresse e-mail est dan@example.com » n’est pas un problème de recherche vectorielle. « Trouve la commande dont l’identifiant est ORD-12345 » non plus. Intégrer ORD-12345 et effectuer une recherche par similarité cosinus renverra quelque chose — mais ce quelque chose peut être faux. Un identifiant a une réponse correcte. Une correspondance approximative sur un identifiant est un bug.

La recherche vectorielle renvoie l’élément le plus similaire de votre jeu de données, même lorsqu’aucun élément n’est réellement pertinent. Elle ne sait pas détecter l’absence de bonne réponse. Ce n’est pas un problème pour les documents connexes. En revanche, c’est un problème sérieux pour la recherche exacte d’un enregistrement, où une réponse erronée donnée avec assurance est pire qu’un résultat vide.

L’inverse est également vrai : n’utilisez pas la recherche plein texte pour les requêtes dans lesquelles l’utilisateur décrit un concept. « des articles sur la prise de décisions difficiles en situation d’incertitude » ne contient aucun mot-clé fiable. La recherche plein texte renverra soit du bruit, soit rien. Utilisez l’outil adapté à la forme de la requête.


Construire la recherche autour de la forme des requêtes

La plupart des systèmes de recherche en production ont besoin de plusieurs couches :

Ces outils ne sont pas concurrents. Ils sont complémentaires. Un système de recherche bien conçu sélectionne la couche adaptée à chaque forme de requête — et lorsque plusieurs formes se recouvrent, il exécute plusieurs couches puis fusionne les résultats.

Les équipes qui livrent de bonnes fonctionnalités de recherche comprennent l’ensemble de la pile. Les autres adoptent une base vectorielle, intègrent tout et s’étonnent que les recherches exactes renvoient parfois le mauvais enregistrement.