Technologie et infrastructure → Elasticsearch et recherche en texte intégral
Elasticsearch et recherche en texte intégral
1) Le rôle d'Elasticsearch
Elasticsearch (ES) est un système de recherche et d'analyse distribué basé sur des index inversés et des structures de colonne pour les agrégations. Elle donne :- Texte intégral : pertinence (BM25), morphologie, fuzzy/typo tolérant.
- Facettes et agrégations : tranches rapides par attributs.
- Recherche hybride : BM25 + vecteurs kNN (sémantique).
- Vitesse de développement : Query DSL, ingest pipelines, un écosystème riche.
Pour iGaming/fintech : recherche de jeux/fournisseurs, promos et règles, facettes réactives (fournisseur, volatilité, RTP, langage), recherche par KYC/AML, analyse des logs et alertes.
2) Modèle de données et mappings
2. 1 Index et types de champs
'text' (champ analysé) - pour le texte intégral.
'keyword 'est la valeur exacte/agrégation/tri.
`date`, `long/double`, `boolean`, `ip`, `geo_point`.
'nested'est un tableau d'objets avec une corrélation correcte des champs.
« dense _ vector » est une représentation vectorielle (embeddings).
2. 2 Stratégie multisectorielle
Stockez le champ dans plusieurs vues : 'name. text' (analysé), 'name. raw` (keyword), `name. ngram '(pour le remplissage automatique).
json
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ru_morph",
"fields": {
"raw": { "type": "keyword", "ignore_above": 256 },
"ngram": { "type": "text", "analyzer": "edge_ngram_2_20" }
}
},
"provider": { "type": "keyword" },
"tags": { "type": "keyword" },
"rtp": { "type": "float" },
"released_at": { "type": "date" },
"lang": { "type": "keyword" },
"embedding": { "type": "dense_vector", "dims": 384, "index": true, "similarity": "cosine" }
}
},
"settings": {
"analysis": {
"filter": {
"ru_stop": { "type": "stop", "stopwords": "_russian_" },
"ru_stemmer": { "type": "stemmer", "language": "russian" },
"syn_ru": { "type": "synonym", "lenient": true, "synonyms": [
"слот,игровой автомат => слот",
"джекпот,суперприз => джекпот"
] }
},
"analyzer": {
"ru_morph": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "ru_stop", "ru_stemmer", "syn_ru"]
},
"edge_ngram_2_20": {
"type": "custom",
"tokenizer": "edge_ngram",
"filter": ["lowercase"],
"char_filter": [],
"tokenizer": "edge_ngram"
}
},
"tokenizer": {
"edge_ngram": { "type": "edge_ngram", "min_gram": 2, "max_gram": 20 }
}
}
}
}
2. 3 Nestées pour facettes
Attributs de la vue 'features : [{name, value}]' formatez 'nested', sinon les facettes donneront de fausses correspondances.
3) Pertinence : BM25, brousse et hybride
3. 1 Classique (BM25)
Combiner les champs avec les échelles (titre ^ 4, tags ^ 2, description).
Utilisez 'minimum _ should _ match' pour contrôler les correspondances « bruyantes ».
3. 2 Vecteurs (kNN) + BM25 (rerank)
Embeddings (par exemple 384-768) dans « dense _ vector ».
D'abord kNN par vecteur (top 200-500), puis rescore BM25 + boosts d'affaires (nouveauté, RTP, licence de la région).
json
{
"knn": {
"field": "embedding",
"query_vector": [/... /],
"k": 400, "num_candidates": 2000
},
"query": {
"bool": {
"should": [
{ "multi_match": {
"query": "египетские слоты джекпот",
"fields": ["title^4","tags^2","description"],
"type": "best_fields",
"minimum_should_match": "60%"
}}
],
"filter": [
{ "term": { "region": "TR" }},
{ "range": { "rtp": { "gte": 94.0 }}}
]
}
},
"rescore": {
"window_size": 400,
"query": {
"rescore_query": {
"function_score": {
"query": { "match_all": {} },
"boost_mode": "sum",
"functions": [
{ "gauss": { "released_at": { "scale": "180d", "offset": "30d", "decay": 0.5 } } },
{ "field_value_factor": { "field": "popularity", "factor": 0.2, "modifier": "log1p" } }
]
}
}
}
},
"highlight": { "fields": { "title": {}, "description": {} } }
}
4) Remplissage automatique et conseils
Approches :- Edge N-gram sur le sous-champ 'title. ngram '(rapide, simple).
- Completion suggesters (champ 'completion') - indices rapides, mais un chemin d'indexation distinct.
- Search-as-you-type - combine la tokenisation pour commencer le mot et les mots.
json
{ "suggest": { "game-suggest": { "prefix": "book o", "completion": { "field": "title_suggest", "fuzzy": { "fuzziness": 1 }}}}}
5) Synonymes, erreurs typographiques et multilinguisme
Synonymes : téléchargez le fichier/la liste via le filtre 'synonym' ; divisez les domaines (casino/sport).
Erreurs typographiques : 'fuzz....: AUTO' dans 'multi _ match', limiter par la longueur et les champs. Pour les indices - 'fuzzy' mode complet.
- Index-per-local (ru/en/tr/pt-BR) ou schéma multidépandeur : « title _ ru », « title _ en ».
- Разные analyzers: `russian`, `english`, `turkish`, `portuguese`.
- Transférez la langue dans la clé de routage (routing) pour garder les localités « chaudes » plus proches de l'utilisateur.
6) Filtres, facettes et agrégations
Pour les facettes, utilisez 'keyword' et 'nested' agrégation.
Évitez les champs cardinaux (identifiants uniques) dans les agrégations - sortez dans 'runtime fields' ou les vitrines préliminaires.
json
{
"size": 20,
"aggs": {
"by_provider": { "terms": { "field": "provider", "size": 20 } },
"by_volatility": { "terms": { "field": "volatility" } },
"rtp_hist": { "histogram": { "field": "rtp", "interval": 1 } }
}
}
7) Saisie de données et nettoyage de texte
Ingest pipelines : normalisation, extraction de champs, géo-encodage, suppression de HTML.
Attachment/ingest-ocr (si nécessaire) : indexation PDF/images (avec attention à PII).
Lemmatisation : via des analyseurs ou des piplines externes (tokens de pré-compute).
8) Chardes, répliques et ILM
8. 1 Dimensions et sharding
Moins de chardes, c'est mieux. Objectif : 10-50 Go par shard pour les charges mixtes.
Commencez par 'number _ of _ shards : 1-3', mettez à l'échelle en fait. Les répliques sont au moins 1 en vente.
8. 2 ILM (Lifecycle)
hot → warm → cold → delete pour les logs/historique promo.
Compression (force merge) pour les segments « froids ».
Pour les catalogues et la recherche par produit - « indéfiniment » hot avec optimisation périodique.
8. 3 Algorithme de migration sans downtime
Le nouvel index 'games _ v2' → alias 'games' passe après 'reindex' et backfill. Champs deprecated - supprimer progressivement.
9) Snapshots, DR et mises à jour
Snapshots vers le stockage objet (S3/GCS), la planification et la vérification de la restauration.
Mise à jour des noeuds, vérification de shard allocation awareness (par zone).
Plans RD : Réplication croisée des régions (RCC) pour les indices critiques (annuaires, catalogues).
10) Sécurité et PII
TLS/mTLS entre le client et le cluster.
RBAC : rôles sur l'indice/les opérations ; Dev/Stage/Prod - séparé.
PII/PCI : ne pas indexer inutilement les champs avec les données personnelles ; utilisez le masquage ingest.
Right to be forgotten : conservez les liens vers les documents à supprimer par user_id ; soft-delete + reindex/annonce.
11) Observation et recherche SLO
Métriques :- P50/P95/P99 latency sur query, erreurs 4xx/5xx.
- Cache hit (query cache / shard request cache).
- Heap usage, GC паузы, segment merges, threadpools (search/write).
- Hot shards/hot nodes, rejections.
- KNN: `graph_hits`, `search_k`, latency, recall@k.
- Recherche de jeux : P95 ≤ 200 ms, erreurs <0. 5 % dans la fenêtre de 30 min.
- Indices : P95 ≤ 80 ms.
- KNN hybride : P95 ≤ 350 ms pour les résultats top-20.
12) FinOps : coût et performance
Taille de l'index : économisez par tokenization, désactivez les 'fielddata' inutiles, utilisez 'doc _ values' seulement là où vous voulez.
Segments : planifiez une politique merge, ne laissez pas le « broyage ».
KNN est plus cher par RAM/CPU : limitez les dims, 'num _ candidates', pré-filtre par BM25.
Champs chauds dans le RAM : surveiller le champ data/heap ; emmener les agrégations « lourdes » dans des indices distincts.
13) Exemples de demandes
13. 1 Plein texte multi-champ avec boost
json
{
"query": {
"multi_match": {
"query": "book of",
"fields": ["title^4","title.ngram^2","tags^2","description"]
}
},
"sort": ["_score", { "released_at": "desc" }]
}
13. 2 Filtres + facettes
json
{
"query": {
"bool": {
"must": [{ "match": { "title": "египет" }}],
"filter": [
{ "terms": { "provider": ["Novomatic","PragmaticPlay"]}},
{ "range": { "rtp": { "gte": 95 }}}
]
}
},
"aggs": {
"by_provider": { "terms": { "field": "provider" } },
"by_year": { "date_histogram": { "field": "released_at", "calendar_interval": "year" } }
}
}
13. 3 Filtrage des attributs Nested
json
{
"query": {
"nested": {
"path": "features",
"query": { "bool": {
"must": [
{ "term": { "features.name": "volatility" }},
{ "term": { "features.value": "high" }}
]
}}
}
}
}
13. 4 Recherche par logs (ECS) avec highlight
json
{
"query": {
"bool": {
"must": [{ "match_phrase": { "message": "payment declined" }}],
"filter": [
{ "term": { "service.name": "payments" }},
{ "range": { "@timestamp": { "gte": "now-1h" }}}
]
}
},
"highlight": { "fields": { "message": { "number_of_fragments": 0 } } }
}
14) Multi-tenant et isolant
Index par tenant (mieux) ou champ 'tenant _ id' + filtre ACL (plus cher sur les agrégations).
Routing par 'tenant _ id'pour localiser les chardes.
Limitez les requêtes tenantes aux limites/temporisations, 'query. phase` guard-rails.
15) Chèque de mise en œuvre
1. Schéma : 'text/keyword/nested' + multipole,' dense _ vector 'si nécessaire.
2. Analyseurs per-language, synonymes, edge-ngram pour l'auto-remplissage.
3. Pertinence : Boost BM25 + hybride kNN→rescore.
4. Facettes : keyword/nested, agrégations uniquement par champs « sains ».
5. Indexation : ingest pipelines (normalisation), téléchargement.
6. Chardonnages : commencez petit, alias pour les déménagements, ILM pour les loges « longues ».
7. DR : planning snapshots, vérification de récupération, CCR pour les indices critiques.
8. Sécurité : TLS, RBAC, masque PII, politique de suppression.
9. Observabilité : latinité, heap/GC, cache hit, hot shards, réactions.
10. FinOps : taille d'index, paramétrage kNN, désactivation des superflus 'doc _ values/fielddata'.
16) Anti-modèles
Un index « par tous » : différents domaines (catalogue, logs, transactions) nécessitent des paramètres différents.
Irréfléchie 'fuzz....: AUTO' dans tous les champs → lent et bruyant.
Les synonymes « mangent le sens » : ne pas séparer les domaines des dictionnaires.
Sans nested là où il faut des ligaments de champs → de fausses facettes.
Trop de chardons (un par document) - frais généraux pour l'état du cluster.
Ne pas utiliser alias lors des migrations - Temps d'arrêt et liens « battus ».
L'indexation du PII « tel quel » - les risques réglementaires et les réindex coûteux.
17) Contexte iGaming/fintech : recettes rapides
Recherche de jeux : 'multi _ match'avec le boost 'title ^ 4', 'tags ^ 2', facettes par fournisseur/volatilité, filtres par région/devise, hybride avec des vecteurs pour « thème » (par exemple « egypte », « fruit classic »).
Promo/bonus : synonymes (« frispins », « free spins »), filtres de données 'active _ from/active _ to', indices via completion.
KYC/AML journaux : schéma ECS, texte intégral par 'message', agrégation par 'rule _ name', 'country', anomalie par '@ timestamp'histogramme.
Référence des fournisseurs : keyword champs pour facettes et triage ; les descriptions textuelles sont 'text' avec la morphologie.
Pages de régulation : champs multi-langues, 'search _ as _ you _ type' pour les indices souples.
Total
Une recherche efficace sur Elasticsearch n'est pas seulement « allumer BM25 » : ce sont les bons analyseurs et mappings, multipole et nested, vecteurs VM25 + hybride, facettes et agrégations soignées, discipline de chardage et ILM, SLO et observabilité claires, et sécurité et FinOps. Avec ces principes, votre recherche sera rapide, pertinente et prévisible - et résistera aux pics de trafic de la plate-forme alimentaire.