🗄️ Bases de données

database-elasticsearch-guide

Recherche et analyse avec Elasticsearch et la stack ELK.

⚡ Installation & lancement en 1 commande

Copiez-collez dans votre terminal : le skill s'installe dans ~/.claude/skills et Claude Code se lance directement dessus.

macOS / Linux
curl -fsSL https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.sh | sh -s -- database-elasticsearch-guide --launch
Windows (PowerShell)
iex "& { $(iwr -useb https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.ps1) } database-elasticsearch-guide -Launch"

🚀 Déjà installé ?

claude "/database-elasticsearch-guide"

Ou tapez /database-elasticsearch-guide dans une session Claude Code, ou décrivez simplement votre besoin — le skill se déclenche automatiquement via le skill-router.

🔑 Déclencheurs automatiques

Le skill s'active automatiquement quand votre demande contient :

ElasticsearchElasticKibanafull-text searchrecherche plein texte

📦 Installation manuelle

git clone https://github.com/khalilbenaz/claude-skills-collection.git cp -r claude-skills-collection/skills/database-elasticsearch-guide ~/.claude/skills/

Payload du plugin : skills/database-elasticsearch-guide · source éditable : database-skills/elasticsearch-guide

đź“– Manuel

Elasticsearch Guide

Workflow

1. Analyser le besoin

Avant tout mapping ou requête, répondre à ces questions :

QuestionImpact
Full-text ou filtrage exact ?text vs keyword
Données temporelles (logs, métriques) ?ILM + data streams
Volume par jour / rétention ?Nombre de shards, rollover
Latence cible (<100ms / <1s) ?Replicas, routing, cache
Agrégations nécessaires ?doc_values: true, fielddata à éviter

2. Concevoir le mapping

Toujours définir un mapping explicite — ne jamais laisser Elasticsearch inférer en production.

PUT /produits
{
  "mappings": {
    "properties": {
      "titre":        { "type": "text", "analyzer": "french",
                        "fields": { "raw": { "type": "keyword" } } },
      "categorie":    { "type": "keyword" },
      "prix":         { "type": "scaled_float", "scaling_factor": 100 },
      "created_at":   { "type": "date", "format": "strict_date_optional_time" },
      "tags":         { "type": "keyword" },
      "description":  { "type": "text", "index": false, "doc_values": false }
    }
  }
}

Critères de décision des types :


3. Configurer l'index et l'ILM

PUT _ilm/policy/logs-policy
{
  "policy": {
    "phases": {
      "hot":    { "actions": { "rollover": { "max_size": "50gb", "max_age": "7d" } } },
      "warm":   { "min_age": "7d",  "actions": { "shrink": { "number_of_shards": 1 }, "forcemerge": { "max_num_segments": 1 } } },
      "cold":   { "min_age": "30d", "actions": { "freeze": {} } },
      "delete": { "min_age": "90d", "actions": { "delete": {} } }
    }
  }
}

Règle de sizing des shards :

Alias obligatoire pour zero-downtime reindex :

POST _aliases
{
  "actions": [
    { "add": { "index": "produits-v2", "alias": "produits", "is_write_index": true } },
    { "remove": { "index": "produits-v1", "alias": "produits" } }
  ]
}

4. Construire les requĂŞtes

Cas courants :

// Full-text avec boost et filtre
GET /produits/_search
{
  "query": {
    "bool": {
      "must":   [{ "multi_match": { "query": "chaussure running", "fields": ["titre^3", "description"] } }],
      "filter": [{ "term": { "categorie": "sport" } }, { "range": { "prix": { "lte": 150 } } }]
    }
  },
  "sort": [{ "_score": "desc" }, { "created_at": "desc" }]
}
// Auto-complétion avec search_as_you_type
GET /produits/_search
{
  "query": {
    "multi_match": {
      "query": "chaus",
      "type": "bool_prefix",
      "fields": ["titre", "titre._2gram", "titre._3gram"]
    }
  }
}
// Agrégation : top catégories + prix moyen
GET /produits/_search
{
  "size": 0,
  "aggs": {
    "par_categorie": {
      "terms": { "field": "categorie", "size": 10 },
      "aggs": {
        "prix_moyen": { "avg": { "field": "prix" } }
      }
    }
  }
}

Règle filter vs query :


5. Réindexation sans downtime

# 1. Créer le nouvel index avec le mapping corrigé
PUT /produits-v2 { ... }

# 2. Réindexer
POST _reindex?wait_for_completion=false
{
  "source": { "index": "produits-v1" },
  "dest":   { "index": "produits-v2" }
}

# 3. Suivre la tâche
GET _tasks/<task_id>

# 4. Basculer l'alias (voir étape 3)

6. Optimiser les performances

Index settings pour la production :

PUT /produits/_settings
{
  "index": {
    "refresh_interval": "30s",        // augmenter si ingestion intensive
    "number_of_replicas": 1,
    "translog.durability": "async",   // OK si perte de quelques secondes acceptable
    "translog.sync_interval": "5s"
  }
}

Désactiver ce qui n'est pas utilisé :

// Dans le mapping
"description": { "type": "text", "norms": false }          // si pas besoin de scoring par longueur
"identifiant": { "type": "keyword", "doc_values": false }   // si jamais agrégé/trié

Bulk indexing :

curl -X POST "localhost:9200/_bulk" -H 'Content-Type: application/json' --data-binary @data.ndjson
# Taille recommandée : 5–15 Mo par batch, 500–1000 docs

7. Monitorer le cluster

# Santé globale
GET _cluster/health?pretty

# Shards non alloués (rouge/jaune)
GET _cluster/allocation/explain

# Noeuds et utilisation disque
GET _cat/nodes?v&h=name,heap.percent,disk.used_percent,load_1m

# RequĂŞtes lentes (slow log)
PUT /produits/_settings
{
  "index.search.slowlog.threshold.query.warn": "2s",
  "index.search.slowlog.threshold.fetch.warn": "1s"
}

# Thread pools (rejects = problème de charge)
GET _cat/thread_pool?v&h=name,active,queue,rejected

Anti-patterns et pièges

PiègeConséquenceCorrection
Mapping dynamique en prodTypes inférés incorrectement, impossible à changerMapping explicite toujours
fielddata: true sur textExplosion mémoire heapUtiliser keyword pour les agrégations
Trop de shards (over-sharding)Surcharge CPU/mémoire, latence accrue1 shard/40 Go, shrink en warm
_source: false sans réflexionImpossible de réindexer, pas de highlightNe désactiver que si cas extrême
nested partoutRequêtes très lentes, explosion du doc countPréférer flattened ou dénormaliser
size: 10000 sans paginationOOM, timeoutUtiliser search_after + pit
Pas d'aliasRéindexation avec coupure de serviceAlias dès le départ, toujours
refresh_interval: 1s en ingestion bulkTrop de segments, I/O élevéPasser à 30s ou -1 pendant le bulk

Bonnes pratiques 2026