Aller au contenu

MongoDB : dumper et réimporter les indexes seuls

Pas de commande native mongodump pour les indexes seuls. On passe par mongosh avec un script JS pour les extraire en JSON, puis on les réinjecte sur la cible.

On s'en sert pour cloner les indexes de prod vers staging, pour migrer entre clusters ou pour garder une copie avant une opération destructive.

Dump des indexes

Collection unique

getIndexes() rend la définition complète, c'est déjà le format que prend createIndexes() en face :

// mongosh "mongodb://host:27017/mydb"
const indexes = db.myCollection.getIndexes();
print(JSON.stringify(indexes, null, 2));

Toutes les collections d'une base

On boucle sur getCollectionNames() et on indexe le résultat par collection :

// mongosh "mongodb://host:27017/mydb"
const dump = {};
db.getCollectionNames().forEach(collName => {
  dump[collName] = db.getCollection(collName).getIndexes();
});
print(JSON.stringify(dump, null, 2));

Sauvegarder dans un fichier

Le flag --quiet supprime la bannière de connexion - sans lui le JSON est invalide.

mongosh "mongodb://host:27017/mydb" --quiet --eval '
  const dump = {};
  db.getCollectionNames().forEach(collName => {
    dump[collName] = db.getCollection(collName).getIndexes();
  });
  print(JSON.stringify(dump, null, 2));
' > indexes_dump.json

Réimport des indexes

mongosh ne supporte pas le module fs de Node - on injecte le JSON via une variable shell et --eval :

INDEXES=$(cat /tmp/indexes_dump.json)

mongosh "mongodb://target-host:27017/mydb" --quiet --eval "
  const dump = $INDEXES;
  Object.entries(dump).forEach(([collName, indexes]) => {
    indexes.forEach(idx => {
      if (idx.name === '_id_') return;
      const key = idx.key;
      const opts = { name: idx.name };
      if (idx.unique)                     opts.unique                  = idx.unique;
      if (idx.sparse)                     opts.sparse                  = idx.sparse;
      if (idx.expireAfterSeconds != null) opts.expireAfterSeconds      = idx.expireAfterSeconds;
      if (idx.partialFilterExpression)    opts.partialFilterExpression = idx.partialFilterExpression;
      if (idx.collation)                  opts.collation               = idx.collation;
      if (idx.weights)                    opts.weights                 = idx.weights;
      if (idx['default_language'])        opts['default_language']     = idx['default_language'];
      try {
        db.getCollection(collName).createIndex(key, opts);
        print('[OK] ' + collName + '.' + idx.name);
      } catch(e) {
        print('[SKIP] ' + collName + '.' + idx.name + ' - ' + e.message);
      }
    });
  });
"

Ce qui est préservé (et ce qui est ignoré)

Propriété Comportement
_id index Toujours ignoré - MongoDB le crée automatiquement
unique, sparse Préservés
expireAfterSeconds (TTL) Préservé
partialFilterExpression Préservé
collation Préservée
weights, default_language (texte) Préservés
Index wildcard ($**) Supporté tel quel
Index hashé Supporté tel quel
Conflit d'index existant Affiche [SKIP] et continue sans écraser

Pour MongoDB Atlas ou une instance avec auth, ajouter --username --password --authenticationDatabase admin à la commande mongosh.

Exemple complet

Le dump et le réimport enchaînés, en passant par un fichier et une variable shell :

# 1. Dump depuis la source
mongosh "mongodb://source-host:27017/mydb" --quiet --eval '
  const dump = {};
  db.getCollectionNames().forEach(c => { dump[c] = db.getCollection(c).getIndexes(); });
  print(JSON.stringify(dump));
' > /tmp/indexes_dump.json

# 2. Réimport sur la cible
INDEXES=$(cat /tmp/indexes_dump.json)
mongosh "mongodb://target-host:27017/mydb" --quiet --eval "
  const dump = $INDEXES;
  Object.entries(dump).forEach(([collName, indexes]) => {
    indexes.forEach(idx => {
      if (idx.name === '_id_') return;
      const key = idx.key;
      const opts = { name: idx.name };
      if (idx.unique)                     opts.unique                  = idx.unique;
      if (idx.sparse)                     opts.sparse                  = idx.sparse;
      if (idx.expireAfterSeconds != null) opts.expireAfterSeconds      = idx.expireAfterSeconds;
      if (idx.partialFilterExpression)    opts.partialFilterExpression = idx.partialFilterExpression;
      if (idx.collation)                  opts.collation               = idx.collation;
      if (idx.weights)                    opts.weights                 = idx.weights;
      if (idx['default_language'])        opts['default_language']     = idx['default_language'];
      try {
        db.getCollection(collName).createIndex(key, opts);
        print('[OK] ' + collName + '.' + idx.name);
      } catch(e) {
        print('[SKIP] ' + collName + '.' + idx.name + ' - ' + e.message);
      }
    });
  });
"

Voir aussi