Comparaison de la recherche pleine texte PostgreSQL et d'Elasticsearch
Une seule base de données ou un véritable stack de recherche
Le véritable débat n’est pas de savoir si PostgreSQL peut effectuer des recherches de texte ou si Elasticsearch peut stocker des documents. Les deux peuvent le faire. La question intéressante est de savoir où la complexité de la recherche doit résider.
La recherche plein texte de PostgreSQL réside à l’intérieur d’une base de données relationnelle transactionnelle avec tsvector, tsquery, des dictionnaires, un classement et des index GIN. Elasticsearch est un moteur de recherche et d’analyse distribué construit sur Lucene, avec des analyseurs, un scoring BM25, une mise à l’échelle basée sur des fragments (shards), des agrégations et un indexage quasi temps réel.

Il s’agit de philosophies opérationnelles différentes avant même qu’il ne s’agisse de listes de fonctionnalités différentes.
Si vous mappez ce choix à des aspects stockage, pipelines et opérations, cet aperçu de l’infrastructure de données donne le contexte du système plus large.
De quoi cette comparaison est-elle vraiment
À un niveau basique, les deux systèmes s’appuient sur des concepts d’index inversé, mais ils les emballent très différemment. PostgreSQL recommande GIN comme type d’index de recherche de texte préféré et le décrit comme un index inversé sur les lexèmes dans les valeurs tsvector. Elasticsearch analyse les champs text et les indexe pour la recherche plein texte, puis distribue ces index sur des fragments et des nœuds pour la mise à l’échelle. En pratique, PostgreSQL donne l’impression d’une recherche embarquée dans votre base de données d’application, tandis qu’Elasticsearch donne l’impression d’une plateforme de recherche dédiée avec son propre runtime, son propre cycle de vie et son propre modèle de mise à l’échelle.
Cette comparaison porte principalement sur la recherche plein texte native de PostgreSQL et sur le très courant pg_trgm pour la correspondance approximative (fuzzy). Cette portée est importante car l’écosystème PostgreSQL plus large devient de plus en plus axé sur la recherche avec le temps. Des extensions comme RUM ajoutent un comportement d’index plus riche pour la recherche de phrases et des scans orientés classement, tandis que PGroonga étend PostgreSQL avec une autre voie d’indexation plein texte. Cela ne rend pas PostgreSQL natif équivalent à Elasticsearch, mais cela signifie que la frontière est moins statique que de nombreuses anciennes comparaisons ne le supposent.
Mon cadre d’opinion est simple. La recherche est généralement une fonctionnalité jusqu’à ce qu’elle devienne une surface produit. PostgreSQL a tendance à gagner tant que la recherche reste une fonctionnalité. Elasticsearch a tendance à gagner lorsque la recherche devient l’élément premier que les utilisateurs jugent. Cela dépend moins des noms de marque que de l’endroit où la logique de pertinence, la politique d’indexation et la douleur opérationnelle sont autorisées à vivre.
Comment fonctionne la recherche plein texte de PostgreSQL
La recherche plein texte de PostgreSQL commence par transformer le texte brut en lexèmes. to_tsvector découpe le texte en mots (tokenisation), le normalise via les dictionnaires configurés, supprime les mots vides (stop words) et stocke les lexèmes survivants avec leurs positions. setweight vous permet d’étiqueter les lexèmes provenant de différentes parties du document, tels que le titre, le résumé et le corps, afin que ces parties puissent influencer le classement différemment. PostgreSQL prend également en charge plusieurs configurations de langue prédéfinies et vous permet de créer des configurations personnalisées avec des analyseurs et des dictionnaires.
Si vous souhaitez une référence SQL concise lors de l’implémentation de ces modèles, cette fiche de référence PostgreSQL et cette fiche de référence SQL avec les commandes SQL les plus utiles sont des compagnons pratiques.
Un modèle de production typique est une colonne tsvector générée stockée plus un index GIN. La documentation de PostgreSQL est sans équivoque : la recherche de texte pratique nécessite généralement un index, et elle montre explicitement une colonne générée stockée alimentant un index GIN. Ce modèle évite de recalculer to_tsvector lors de la vérification et maintient la surface de requête propre.
alter table posts
add column search_vector tsvector
generated always as (
setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
setweight(to_tsvector('english', coalesce(summary, '')), 'B') ||
setweight(to_tsvector('english', coalesce(body, '')), 'D')
) stored;
create index posts_search_idx
on posts using gin (search_vector);
select id,
title,
ts_rank_cd(
search_vector,
websearch_to_tsquery('english', '"query planner" -mysql')
) as rank
from posts
where search_vector @@ websearch_to_tsquery('english', '"query planner" -mysql')
order by rank desc
limit 20;
Côté requête, PostgreSQL vous offre plusieurs analyseurs car les saisies utilisateur sont plus chaotiques que ce que les blogs d’ingénierie admettent. to_tsquery est explicite et puissant. phraseto_tsquery préserve l’ordre des mots avec l’opérateur <->. websearch_to_tsquery accepte des saisies similaires à celles d’un moteur de recherche, comprend les phrases entre guillemets, OR, et la négation -, et ne lève jamais d’erreurs de syntaxe sur les saisies utilisateur brutes. PostgreSQL prend également en charge la correspondance de préfixe en attachant * à un lexème dans to_tsquery.
Le classement est là où le PostgreSQL natif montre à la fois sa force et sa limite. ts_rank et ts_rank_cd peuvent utiliser la fréquence, la proximité et les poids structurels, et le modèle de pondération est étonnamment bon pour de nombreuses tâches de recherche d’application. En même temps, la propre documentation de PostgreSQL note que le classement peut être coûteux et que les fonctions de classement intégrées n’utilisent pas d’informations globales. C’est la limite silencieuse mais importante de la recherche plein texte native de PostgreSQL. Elle peut classer, mais la pertinence n’est pas le centre de gravité du moteur.
Quand PostgreSQL suffit pour la recherche plein texte
PostgreSQL suffit plus souvent que les fournisseurs de recherche dédiés ne le souhaiteraient. Il est particulièrement convaincant lorsque la recherche reste très proche des lignes transactionnelles, des jointures, des permissions et des écritures fraîches. Le modèle MVCC de PostgreSQL fournit une cohérence transactionnelle et des lectures basées sur des instantanés, de sorte que la même base de données qui accepte l’écriture peut répondre à la recherche sans fenêtre de rafraîchissement de style Elasticsearch. Lorsqu’une boîte de recherche est vraiment “trouvez les enregistrements à l’intérieur de l’application que je viens d’édition”, cette propriété est plus importante que des démos de pertinence luisantes.
C’est aussi suffisant lorsque le filtrage SQL constitue la moitié de la fonctionnalité. Les filtres de statut, l’isolation des locataires (tenant isolation), les états de publication, les horodatages et les jointures relationnelles sont souvent aussi importants que la pertinence des mots-clés dans les systèmes métier. Dans ces cas, la recherche plein texte de PostgreSQL se comporte comme un autre prédicat indexé dans un plan de requête relationnel, et non comme une plateforme séparée qui doit être alimentée et maintenue chaude. C’est une architecture ennuyeuse, et l’ennui est souvent le bon type de rapidité.
Comment fonctionne Elasticsearch en tant que moteur de recherche
Elasticsearch se présente très différemment. Sa propre documentation le définit comme un moteur de recherche et d’analyse distribué, un magasin de données évolutif et une base de données vectorielle construite sur Apache Lucene, optimisée pour la vitesse et la pertinence à l’échelle de production et fonctionnant en quasi temps réel. Elasticsearch divise chaque index en fragments (shards), réplique ces fragments et les distribue sur des nœuds pour augmenter la capacité d’indexation et de requête. C’est pourquoi Elasticsearch est rarement “juste un index”. C’est une architecture de cluster.
Sous le capot, les analyseurs font la majeure partie du travail. Un analyseur Elasticsearch est une composition de filtres de caractères, de découpeurs (tokenizers) et de filtres de tokens. Il existe des analyseurs intégrés, des analyseurs de langue et des analyseurs personnalisés, et la gestion des synonymes fait partie intégrante de l’analyse. Cela signifie que le comportement de la recherche ne dépend pas seulement de la requête. Cela dépend aussi de la manière dont les documents et les requêtes sont normalisés avant même que le scoring ne commence.
Pour une référence API pratique lors de l’implémentation de ces modèles, cette fiche de référence Elasticsearch rassemble les commandes essentielles et les raccourcis opérationnels.
PUT posts
{
"mappings": {
"properties": {
"title": { "type": "text" },
"summary": { "type": "text" },
"body": { "type": "text" },
"tags": { "type": "keyword" }
}
}
}
GET posts/_search
{
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "query planner",
"fields": ["title^3", "summary^2", "body"],
"type": "best_fields"
}
}
],
"must_not": [
{ "match": { "body": "mysql" } }
]
}
},
"aggs": {
"by_tag": {
"terms": {
"field": "tags"
}
}
},
"highlight": {
"fields": {
"body": {}
}
}
}
Le DSL de requête est là où Elasticsearch commence à se sentir “natif de la recherche” plutôt que “comme une base de données”. bool combine des clauses avec must, should, filter et must_not. multi_match peut chercher à travers de nombreux champs avec des boosts de champs et différents modes d’exécution tels que best_fields, most_fields, cross_fields, phrase et bool_prefix. Les agrégations, les surlignages (highlights) et les filtres peuvent tous coexister à côté de la requête principale dans la même demande. BM25 est le modèle de similarité par défaut.
Le modèle de fraîcheur est également explicite. Elasticsearch est en quasi temps réel, pas immédiatement cohérent pour la recherche. Les opérations récentes deviennent visibles pour la recherche lorsqu’un rafraîchissement ouvre un nouveau segment, et par défaut, ce rafraîchissement se produit toutes les secondes sur les index qui ont été consultés récemment. La documentation d’Elastic avertit également que les rafraîchissements sont gourmands en ressources et recommande d’attendre des rafraîchissements périodiques ou d’utiliser refresh=wait_for lorsqu’un flux de travail a besoin de visibilité de recherche “lecture après écriture”. C’est un contrat très différent de celui de PostgreSQL.
Pourquoi Elasticsearch classe généralement mieux la recherche complexe
C’est la raison technique la plus profonde pour laquelle de nombreuses équipes finissent par passer de la recherche plein texte de PostgreSQL à Elasticsearch. Les fonctions de classement intégrées de PostgreSQL n’utilisent pas d’informations globales, tandis qu’Elasticsearch utilise BM25 par défaut et expose des paramètres de similarité spécifiques aux champs, des analyseurs, des formes de requêtes multi-champs et un DSL de recherche conçu autour de l’ajustement de la pertinence. Une fois que la recherche devient moins “est-ce que ça correspond” et plus “pourquoi ces dix résultats ont gagné”, Elasticsearch a généralement plus de marge d’expression.
Elasticsearch a également un biais clair vers les documents dénormalisés. Sa documentation sur les jointures avertit explicitement contre la modélisation de plusieurs niveaux de relations pour répliquer un schéma relationnel et recommande la dénormalisation pour une meilleure performance de recherche. Ce choix de conception explique beaucoup des forces et des frustrations d’Elasticsearch. Il ne tente pas d’être PostgreSQL avec un LIKE plus rapide. Il tente d’être un moteur de recherche capable de noter et de récupérer rapidement de grandes collections de documents.
Recherche plein texte PostgreSQL vs Elasticsearch sur les fonctionnalités réelles
La tolérance aux fautes de frappe est là où les deux systèmes divergent radicalement. Elasticsearch fournit des requêtes floues (fuzzy queries) basées sur la distance d’édition de Levenshtein et propose également des types de champs dédiés pour les suggestions et la saisie progressive. La recherche plein texte native de PostgreSQL n’est pas tolérante aux fautes de frappe par elle-même. La réponse habituelle de PostgreSQL est pg_trgm, qui ajoute des opérateurs de similarité et une prise en charge d’index pour la similarité de trigrammes, LIKE et ILIKE. Cela fonctionne bien, mais c’est une stratégie de composition plutôt qu’un ensemble de fonctionnalités de moteur de recherche intégré unique.
Le surlignage (highlighting) existe dans les deux piles, mais les détails d’implementation racontent une histoire. PostgreSQL utilise ts_headline, qui peut retourner des extraits utiles, mais la documentation note qu’il utilise le document original, peut être lent, et n’est pas garanti sûr pour une insertion directe dans des pages web. Le surlignage d’Elasticsearch peut utiliser des décalages de postings ou des vecteurs de termes, ce qui est particulièrement précieux sur de grands champs car cela évite de réanalyser le texte complet pour chaque demande de surlignage. En bref, PostgreSQL peut surligner, tandis qu’Elasticsearch est conçu pour surligner à grande échelle.
Les facettes et l’analytique de recherche sont une autre ligne de fracture. Elasticsearch traite les agrégations comme une partie de premier ordre du modèle de recherche, avec des agrégations métriques, par buckets et par pipeline disponibles directement dans la réponse de recherche. PostgreSQL peut évidemment agréger car c’est du SQL, mais une fois que les buckets comptés, les histogrammes et l’analytique de recherche composite deviennent partie intégrante du produit de recherche lui-même, Elasticsearch se sent beaucoup plus natif. La différence n’est pas une question de capacité en principe. C’est la quantité d’ergonomie de requête et de politique de performance que le moteur consacre à cette charge de travail.
L’autocomplétion suit le même modèle. PostgreSQL peut faire de la correspondance de préfixe dans to_tsquery, ce qui est utile et souvent suffisant pour les outils internes. Elasticsearch va plus loin avec des champs search_as_you_type qui construisent automatiquement plusieurs sous-champs analysés pour la complétion de préfixe et d’infixe, plus des suggesteurs de complétion conçus spécifiquement pour des suggestions rapides. Cet écart est mineur sur un panneau d’administration et majeur sur une surface de découverte visible par l’utilisateur.
Le coût opérationnel compte plus que les captures d’écran de benchmark
La question tentante du moteur de recherche est “Elasticsearch est-il plus rapide que PostgreSQL pour la recherche ?” La réponse honnête est “pour quelle forme de recherche ?” Elasticsearch est conçu autour de fragments (shards), de réplicas, d’indexation par lots, de politique de rafraîchissement et de gestion de cycle de vie. Les propres documents de production d’Elastic vont en profondeur sur la stratégie de sharding, la taille des requêtes en lot, le débit d’indexation, les intervalles de rafraîchissement et l’ILM. PostgreSQL évite un deuxième cluster, mais la maintenance GIN n’est pas gratuite. La documentation de PostgreSQL avertit que les insertions GIN peuvent être lentes, que le nettoyage de la liste en attente peut provoquer des fluctuations du temps de réponse, et que la stratégie autovacuum est importante si l’index est mis à jour intensément.
Cela rend l’histoire de performance plus nuancée que la plupart des posts de comparaison n’admettent. Elasticsearch a généralement plus de marge pour la recherche lexicale top-N à grande échelle, les facettes, l’autocomplétion et le volume de lecture distribué car son architecture est dédiée à ces tâches. PostgreSQL se sent souvent plus rapide pour les requêtes d’application relationnelle avec des exigences de fraîcheur strictes car il n’y a pas de deuxième magasin de données, pas de frontière de rafraîchissement et pas de chemin de synchronisation à déboguer. Le gagnant est généralement la forme de la charge de travail, pas la capture d’écran de benchmark. C’est en partie une inférence, mais cela découle directement du modèle MVCC transactionnel de PostgreSQL et de la conception basée sur fragments en quasi temps réel d’Elasticsearch.
Les données transactionnelles et les index de recherche doivent-elles vivre dans le même système ? Lorsque la pertinence de la recherche est modérée mais que la fraîcheur, les permissions et la vérité transactionnelle sont critiques, la conception sur un seul système présente des avantages évidents. Lorsque la qualité de la recherche, les facettes, la politique de synonymes, la tolérance aux fautes de frappe et la mise à l’échelle horizontale de la recherche deviennent des préoccupations produit de premier ordre, un deuxième système commence à sembler justifié. Les propres conseils de dimensionnement des fragments d’Elasticsearch disent qu’il n’y a pas de stratégie universelle et recommande de benchmarker les données de production sur du matériel de production. Cette phrase capture parfaitement le compromis. Elasticsearch achète de la marge en vous demandant de faire fonctionner une architecture plus spécifique à la recherche.
Le verdict pratique
La recherche plein texte de PostgreSQL gagne les 80 premiers pourcentages de manière surprenante. Elle prend en charge la tokenisation, les mots vides, la racine (stemming), les requêtes de phrases, les poids, le classement, le surlignage, les vecteurs de recherche générés, les index GIN et les assistants de similarité basés sur les trigrammes. Combiné avec la sémantique transactionnelle de PostgreSQL, cela donne à de nombreuses applications une pile de recherche qui est simple, actuelle et proche des données. Pour les back offices SaaS, les outils internes, les sites de contenu modérés et la recherche native à l’application, cette combinaison est difficile à rejeter.
Elasticsearch devient persuasif lorsque la recherche n’est pas seulement un filtre mais une surface produit. BM25 par défaut, analyseurs personnalisés, filtres de synonymes, requêtes floues, classement multi-champs, agrégations, options d’autocomplétion dédiées, stratégies de surlignage pour grands champs et mise à l’échelle distribuée basée sur fragments ne sont pas des fonctionnalités secondaires. Ce sont les raisons pour lesquelles le moteur existe. C’est pourquoi les comparaisons d’Elasticsearch qui se concentrent uniquement sur la latence brute manquent généralement l’essentiel. La plus grande différence est la quantité de logique de produit de recherche que le moteur est prêt à assumer.
Le modèle mental le plus clair est le suivant. La recherche plein texte de PostgreSQL est excellente lorsque la recherche appartient à la base de données. Elasticsearch est excellent lorsque la base de données doit alimenter une plateforme de recherche. La plupart des équipes se concentrent trop sur la vitesse et pas assez sur les modes de défaillance. Le véritable compromis est l’endroit où l’ajustement de la pertinence, la fraîcheur des données et la complexité opérationnelle sont autorisés à résider.