You've already forked mongo-index-helper
feat: smart index diffing on import
Compare desired indexes against existing ones before applying changes: - skip indexes that are already up to date - drop and recreate if definition changed - handle key conflicts (same key, different name) - drop obsolete indexes not present in the JSON Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -34,5 +34,9 @@
|
||||
## Поведение импорта
|
||||
|
||||
- Если коллекция не существует — создаётся автоматически
|
||||
- Если в коллекции уже есть индексы (кроме `_id_`) — они дропаются перед созданием новых
|
||||
- Индексы сравниваются с существующими перед применением:
|
||||
- Совпадает по имени и определению — пропускается
|
||||
- Совпадает по имени, но определение изменилось — дропается и пересоздаётся
|
||||
- Совпадает по ключу, но имя другое — старый дропается, создаётся новый с нужным именем
|
||||
- Есть в БД, но отсутствует в JSON — дропается как устаревший
|
||||
- `--dry-run` показывает все планируемые действия без их выполнения
|
||||
|
||||
@@ -87,5 +87,9 @@ Bash-скрипты для экспорта и импорта схемы инд
|
||||
## Поведение импорта
|
||||
|
||||
- Коллекция не существует → создаётся автоматически
|
||||
- В коллекции есть индексы → дропаются перед созданием новых
|
||||
- Индексы сравниваются с существующими перед применением:
|
||||
- Совпадает по имени и определению → пропускается
|
||||
- Совпадает по имени, но определение изменилось → дропается и пересоздаётся
|
||||
- Совпадает по ключу, но имя другое → старый дропается, создаётся с новым именем
|
||||
- Есть в БД, но отсутствует в JSON → дропается как устаревший
|
||||
- Индекс `_id_` игнорируется при экспорте и импорте
|
||||
|
||||
@@ -43,34 +43,49 @@ const data = JSON.parse(process.env.INDEXES_JSON);
|
||||
const targetDb = db.getSiblingDB(dbName);
|
||||
const existingCollections = new Set(targetDb.getCollectionNames());
|
||||
|
||||
function normalizeKey(key) {
|
||||
return JSON.stringify(key, Object.keys(key).sort());
|
||||
}
|
||||
|
||||
function normalizeIndex(idx) {
|
||||
const { v, ns, background, ...rest } = idx;
|
||||
return JSON.stringify(rest, Object.keys(rest).sort());
|
||||
}
|
||||
|
||||
function indexesEqual(a, b) {
|
||||
return normalizeIndex(a) === normalizeIndex(b);
|
||||
}
|
||||
|
||||
if (dryRun) {
|
||||
print("[dry-run] No changes will be made.\n");
|
||||
}
|
||||
|
||||
for (const [collName, indexes] of Object.entries(data)) {
|
||||
print(`Collection: ${collName}`);
|
||||
|
||||
const coll = targetDb.getCollection(collName);
|
||||
|
||||
if (!existingCollections.has(collName)) {
|
||||
if (dryRun) {
|
||||
print(` [dry-run] Would create collection: ${collName}`);
|
||||
} else {
|
||||
targetDb.createCollection(collName);
|
||||
print(`Created collection: ${collName}`);
|
||||
print(` Created collection`);
|
||||
}
|
||||
}
|
||||
|
||||
const collectionExists = existingCollections.has(collName);
|
||||
const existingIndexes = collectionExists
|
||||
? targetDb.getCollection(collName).getIndexes().filter(idx => idx.name !== "_id_")
|
||||
: [];
|
||||
const existingByName = {};
|
||||
const existingByKey = {};
|
||||
if (existingCollections.has(collName)) {
|
||||
for (const idx of coll.getIndexes()) {
|
||||
if (idx.name !== "_id_") {
|
||||
existingByName[idx.name] = idx;
|
||||
existingByKey[normalizeKey(idx.key)] = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (existingIndexes.length > 0) {
|
||||
if (dryRun) {
|
||||
const names = existingIndexes.map(i => i.name).join(", ");
|
||||
print(`[dry-run] Would drop indexes on ${collName}: ${names}`);
|
||||
} else {
|
||||
targetDb.getCollection(collName).dropIndexes();
|
||||
print(`Dropped existing indexes on: ${collName}`);
|
||||
}
|
||||
}
|
||||
const consumedExistingNames = new Set();
|
||||
|
||||
for (const idx of indexes) {
|
||||
if (idx.name === "_id_") continue;
|
||||
@@ -78,11 +93,49 @@ for (const [collName, indexes] of Object.entries(data)) {
|
||||
const { key, name, ...rest } = idx;
|
||||
const options = { name, ...rest };
|
||||
|
||||
if (dryRun) {
|
||||
print(`[dry-run] Would create index on ${collName}: ${JSON.stringify({ key, options })}`);
|
||||
if (existingByName[name]) {
|
||||
consumedExistingNames.add(name);
|
||||
if (indexesEqual(existingByName[name], idx)) {
|
||||
print(` Index "${name}" is up to date, skipping`);
|
||||
} else {
|
||||
targetDb.getCollection(collName).createIndex(key, options);
|
||||
print(`Created index "${name}" on: ${collName}`);
|
||||
if (dryRun) {
|
||||
print(` [dry-run] Would drop and recreate index "${name}" (definition changed)`);
|
||||
} else {
|
||||
coll.dropIndex(name);
|
||||
coll.createIndex(key, options);
|
||||
print(` Dropped and recreated index "${name}" (definition changed)`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const conflicting = existingByKey[normalizeKey(key)];
|
||||
if (conflicting) {
|
||||
consumedExistingNames.add(conflicting.name);
|
||||
if (dryRun) {
|
||||
print(` [dry-run] Would drop index "${conflicting.name}" and create "${name}" (same key, name changed)`);
|
||||
} else {
|
||||
coll.dropIndex(conflicting.name);
|
||||
coll.createIndex(key, options);
|
||||
print(` Dropped "${conflicting.name}" and created "${name}" (same key, name changed)`);
|
||||
}
|
||||
} else {
|
||||
if (dryRun) {
|
||||
print(` [dry-run] Would create index "${name}"`);
|
||||
} else {
|
||||
coll.createIndex(key, options);
|
||||
print(` Created index "${name}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const name of Object.keys(existingByName)) {
|
||||
if (!consumedExistingNames.has(name)) {
|
||||
if (dryRun) {
|
||||
print(` [dry-run] Would drop obsolete index "${name}"`);
|
||||
} else {
|
||||
coll.dropIndex(name);
|
||||
print(` Dropped obsolete index "${name}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user