You've already forked mongo-index-helper
Initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.idea
|
||||||
38
CLAUDE.md
Normal file
38
CLAUDE.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# mongo-index-helper
|
||||||
|
|
||||||
|
Два bash-скрипта для экспорта и импорта схемы индексов MongoDB. Предназначены для запуска внутри контейнера mongo.
|
||||||
|
|
||||||
|
## Скрипты
|
||||||
|
|
||||||
|
- `export-indexes.sh` — экспортирует индексы всех коллекций указанной БД в JSON-файл
|
||||||
|
- `import-indexes.sh` — импортирует индексы из JSON-файла в указанную БД
|
||||||
|
|
||||||
|
## Технические детали
|
||||||
|
|
||||||
|
- Требуют `mongosh` (доступен в официальном образе mongo 5+)
|
||||||
|
- Встроенный JavaScript передаётся через временный `.js`-файл в `/tmp`, который удаляется после выполнения
|
||||||
|
- Переменные среды `MONGO_DB`, `DRY_RUN`, `INDEXES_JSON` используются для передачи параметров в JS-код внутри mongosh
|
||||||
|
- Индекс `_id_` пропускается при экспорте и импорте
|
||||||
|
- Поля `v` и `ns` удаляются из экспортируемых индексов (служебные, не нужны при создании)
|
||||||
|
|
||||||
|
## Формат JSON
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"collectionName": [
|
||||||
|
{
|
||||||
|
"key": { "field": 1 },
|
||||||
|
"name": "index_name",
|
||||||
|
"unique": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Имя БД в файл не сохраняется — передаётся параметром `--db` при каждом запуске.
|
||||||
|
|
||||||
|
## Поведение импорта
|
||||||
|
|
||||||
|
- Если коллекция не существует — создаётся автоматически
|
||||||
|
- Если в коллекции уже есть индексы (кроме `_id_`) — они дропаются перед созданием новых
|
||||||
|
- `--dry-run` показывает все планируемые действия без их выполнения
|
||||||
91
README.md
Normal file
91
README.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# mongo-index-helper
|
||||||
|
|
||||||
|
Bash-скрипты для экспорта и импорта схемы индексов MongoDB. Работают внутри контейнера mongo через `mongosh`.
|
||||||
|
|
||||||
|
## Требования
|
||||||
|
|
||||||
|
- `mongosh` (входит в официальный образ `mongo:5+`)
|
||||||
|
|
||||||
|
## Использование
|
||||||
|
|
||||||
|
### Экспорт
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./export-indexes.sh --uri <uri> --db <dbname> --output <file>
|
||||||
|
```
|
||||||
|
|
||||||
|
| Параметр | Описание | Обязательный |
|
||||||
|
|------------|---------------------------------|:------------:|
|
||||||
|
| `--uri` | MongoDB connection URI | да |
|
||||||
|
| `--db` | Имя базы данных | да |
|
||||||
|
| `--output` | Путь к выходному JSON-файлу | да |
|
||||||
|
|
||||||
|
**Пример:**
|
||||||
|
```bash
|
||||||
|
./export-indexes.sh \
|
||||||
|
--uri "mongodb://admin:secret@localhost:27017/admin" \
|
||||||
|
--db mydb \
|
||||||
|
--output indexes.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Импорт
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./import-indexes.sh --uri <uri> --db <dbname> --input <file> [--dry-run]
|
||||||
|
```
|
||||||
|
|
||||||
|
| Параметр | Описание | Обязательный |
|
||||||
|
|-------------|-------------------------------------------------------|:------------:|
|
||||||
|
| `--uri` | MongoDB connection URI | да |
|
||||||
|
| `--db` | Имя базы данных | да |
|
||||||
|
| `--input` | Путь к входному JSON-файлу | да |
|
||||||
|
| `--dry-run` | Показать планируемые действия без реального выполнения | нет |
|
||||||
|
|
||||||
|
**Примеры:**
|
||||||
|
```bash
|
||||||
|
# Предварительный просмотр
|
||||||
|
./import-indexes.sh \
|
||||||
|
--uri "mongodb://admin:secret@localhost:27017/admin" \
|
||||||
|
--db mydb \
|
||||||
|
--input indexes.json \
|
||||||
|
--dry-run
|
||||||
|
|
||||||
|
# Реальный импорт
|
||||||
|
./import-indexes.sh \
|
||||||
|
--uri "mongodb://admin:secret@localhost:27017/admin" \
|
||||||
|
--db mydb \
|
||||||
|
--input indexes.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Формат файла
|
||||||
|
|
||||||
|
Индексы сохраняются в JSON без указания имени БД:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"key": { "email": 1 },
|
||||||
|
"name": "email_1",
|
||||||
|
"unique": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": { "createdAt": 1 },
|
||||||
|
"name": "createdAt_1",
|
||||||
|
"expireAfterSeconds": 2592000
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"orders": [
|
||||||
|
{
|
||||||
|
"key": { "userId": 1, "status": 1 },
|
||||||
|
"name": "userId_1_status_1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Поведение импорта
|
||||||
|
|
||||||
|
- Коллекция не существует → создаётся автоматически
|
||||||
|
- В коллекции есть индексы → дропаются перед созданием новых
|
||||||
|
- Индекс `_id_` игнорируется при экспорте и импорте
|
||||||
57
export-indexes.sh
Executable file
57
export-indexes.sh
Executable file
@@ -0,0 +1,57 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: $0 --uri <uri> --db <dbname> --output <file>"
|
||||||
|
echo ""
|
||||||
|
echo " --uri MongoDB connection URI (e.g. mongodb://user:pass@host:27017/admin)"
|
||||||
|
echo " --db Database name"
|
||||||
|
echo " --output Path to output JSON file"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
URI=""
|
||||||
|
DB=""
|
||||||
|
OUTPUT=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--uri) URI="$2"; shift 2 ;;
|
||||||
|
--db) DB="$2"; shift 2 ;;
|
||||||
|
--output) OUTPUT="$2"; shift 2 ;;
|
||||||
|
*) echo "Unknown argument: $1"; usage ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -z "$URI" ]] && echo "Error: --uri is required" && usage
|
||||||
|
[[ -z "$DB" ]] && echo "Error: --db is required" && usage
|
||||||
|
[[ -z "$OUTPUT" ]] && echo "Error: --output is required" && usage
|
||||||
|
|
||||||
|
TMP_JS=$(mktemp /tmp/mongo-export-XXXXXX.js)
|
||||||
|
trap 'rm -f "$TMP_JS"' EXIT
|
||||||
|
|
||||||
|
cat > "$TMP_JS" <<'EOF'
|
||||||
|
const dbName = process.env.MONGO_DB;
|
||||||
|
const targetDb = db.getSiblingDB(dbName);
|
||||||
|
|
||||||
|
const result = {};
|
||||||
|
const collections = targetDb.getCollectionNames();
|
||||||
|
|
||||||
|
for (const collName of collections) {
|
||||||
|
const indexes = targetDb.getCollection(collName).getIndexes();
|
||||||
|
const filtered = indexes.filter(idx => idx.name !== "_id_");
|
||||||
|
|
||||||
|
if (filtered.length > 0) {
|
||||||
|
result[collName] = filtered.map(idx => {
|
||||||
|
const { v, ns, ...rest } = idx;
|
||||||
|
return rest;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print(JSON.stringify(result, null, 2));
|
||||||
|
EOF
|
||||||
|
|
||||||
|
MONGO_DB="$DB" mongosh --quiet "$URI" --file "$TMP_JS" > "$OUTPUT"
|
||||||
|
|
||||||
|
echo "Exported indexes to: $OUTPUT"
|
||||||
96
import-indexes.sh
Executable file
96
import-indexes.sh
Executable file
@@ -0,0 +1,96 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage: $0 --uri <uri> --db <dbname> --input <file> [--dry-run]"
|
||||||
|
echo ""
|
||||||
|
echo " --uri MongoDB connection URI (e.g. mongodb://user:pass@host:27017/admin)"
|
||||||
|
echo " --db Database name"
|
||||||
|
echo " --input Path to input JSON file"
|
||||||
|
echo " --dry-run Show planned actions without executing them"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
URI=""
|
||||||
|
DB=""
|
||||||
|
INPUT=""
|
||||||
|
DRY_RUN="false"
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--uri) URI="$2"; shift 2 ;;
|
||||||
|
--db) DB="$2"; shift 2 ;;
|
||||||
|
--input) INPUT="$2"; shift 2 ;;
|
||||||
|
--dry-run) DRY_RUN="true"; shift ;;
|
||||||
|
*) echo "Unknown argument: $1"; usage ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -z "$URI" ]] && echo "Error: --uri is required" && usage
|
||||||
|
[[ -z "$DB" ]] && echo "Error: --db is required" && usage
|
||||||
|
[[ -z "$INPUT" ]] && echo "Error: --input is required" && usage
|
||||||
|
|
||||||
|
[[ ! -f "$INPUT" ]] && echo "Error: input file not found: $INPUT" && exit 1
|
||||||
|
|
||||||
|
TMP_JS=$(mktemp /tmp/mongo-import-XXXXXX.js)
|
||||||
|
trap 'rm -f "$TMP_JS"' EXIT
|
||||||
|
|
||||||
|
cat > "$TMP_JS" <<'EOF'
|
||||||
|
const dbName = process.env.MONGO_DB;
|
||||||
|
const dryRun = process.env.DRY_RUN === "true";
|
||||||
|
const data = JSON.parse(process.env.INDEXES_JSON);
|
||||||
|
|
||||||
|
const targetDb = db.getSiblingDB(dbName);
|
||||||
|
const existingCollections = new Set(targetDb.getCollectionNames());
|
||||||
|
|
||||||
|
if (dryRun) {
|
||||||
|
print("[dry-run] No changes will be made.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [collName, indexes] of Object.entries(data)) {
|
||||||
|
if (!existingCollections.has(collName)) {
|
||||||
|
if (dryRun) {
|
||||||
|
print(`[dry-run] Would create collection: ${collName}`);
|
||||||
|
} else {
|
||||||
|
targetDb.createCollection(collName);
|
||||||
|
print(`Created collection: ${collName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingIndexes = targetDb.getCollection(collName).getIndexes()
|
||||||
|
.filter(idx => idx.name !== "_id_");
|
||||||
|
|
||||||
|
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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const idx of indexes) {
|
||||||
|
if (idx.name === "_id_") continue;
|
||||||
|
|
||||||
|
const { key, name, ...rest } = idx;
|
||||||
|
const options = { name, ...rest };
|
||||||
|
|
||||||
|
if (dryRun) {
|
||||||
|
print(`[dry-run] Would create index on ${collName}: ${JSON.stringify({ key, options })}`);
|
||||||
|
} else {
|
||||||
|
targetDb.getCollection(collName).createIndex(key, options);
|
||||||
|
print(`Created index "${name}" on: ${collName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dryRun) {
|
||||||
|
print("\nImport complete.");
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
MONGO_DB="$DB" \
|
||||||
|
DRY_RUN="$DRY_RUN" \
|
||||||
|
INDEXES_JSON="$(cat "$INPUT")" \
|
||||||
|
mongosh --quiet "$URI" --file "$TMP_JS"
|
||||||
Reference in New Issue
Block a user