{"id":47749393,"url":"https://github.com/terratensor/geoupdater","last_synced_at":"2026-04-03T02:43:23.588Z","repository":{"id":343474458,"uuid":"1177858258","full_name":"terratensor/geoupdater","owner":"terratensor","description":null,"archived":false,"fork":false,"pushed_at":"2026-03-10T15:09:23.000Z","size":40,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-10T19:59:55.271Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/terratensor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-10T12:52:56.000Z","updated_at":"2026-03-10T15:09:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/terratensor/geoupdater","commit_stats":null,"previous_names":["terratensor/geoupdater"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/terratensor/geoupdater","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/terratensor%2Fgeoupdater","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/terratensor%2Fgeoupdater/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/terratensor%2Fgeoupdater/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/terratensor%2Fgeoupdater/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/terratensor","download_url":"https://codeload.github.com/terratensor/geoupdater/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/terratensor%2Fgeoupdater/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31329161,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T02:17:30.558Z","status":"ssl_error","status_checked_at":"2026-04-03T02:17:30.071Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-03T02:43:18.626Z","updated_at":"2026-04-03T02:43:23.581Z","avatar_url":"https://github.com/terratensor.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GeoUpdater\n\nСервис для массового обновления геоданных в Manticore Search.\n\n## 🚀 Возможности\n\n- **Массовая обработка** NDJSON файлов с геоданными\n- **Два режима обновления геоданных**: REPLACE (полная замена) и MERGE (слияние)\n- **Режим NER** для обработки именованных сущностей в отдельную таблицу\n- **Пакетная обработка** с настраиваемым размером батча\n- **Параллельная обработка** нескольких файлов\n- **Graceful shutdown** с сохранением прогресса\n- **Детальные отчеты** о каждой обработке\n- **Хранение failed записей** для повторной обработки\n- **Поддержка больших чисел** (uint64) без потери точности\n\n## 📋 Требования\n\n- Go 1.21+\n- Manticore Search 5.0.0+\n- NDJSON файлы с геоданными\n\n## 🔧 Установка\n\n### Из исходников\n```bash\ngit clone https://github.com/terratensor/geoupdater.git\ncd geoupdater\nmake build\n```\n\n### Docker\n```bash\ndocker build -t geoupdater:latest .\n```\n\n## Архитектура\n\nПроект построен на гексагональной архитектуре (ports \u0026 adapters):\n\n- **core/domain** - бизнес-логика и модели данных\n- **core/ports** - интерфейсы для взаимодействия с внешним миром\n- **adapters/** - реализации портов (Manticore, NDJSON, логгер, failed storage)\n- **app/service** - координация всех компонентов\n\n## Особенности работы с ID\n\n**Критически важно:** В Manticore Search ID документов хранятся как 64-битные целые числа.\nВ нашем сервисе мы используем `uint64` для всех ID, чтобы избежать потери точности при парсинге JSON.\n\n```go\ntype Document struct {\n    ID uint64 `json:\"id\"` // ВАЖНО: всегда uint64, не string!\n}\n\ntype GeoUpdateData struct {\n    DocID uint64 `json:\"doc_id\"` // В JSON может приходить как строка или число\n}\n```\n\nПри парсинге NDJSON файлов мы используем `json.Number` для сохранения точности:\n```go\ndecoder := json.NewDecoder(bytes.NewReader(line))\ndecoder.UseNumber() // Критически важно для больших чисел!\n```\n\n## Режимы работы\n\n### 1. REPLACE (полная замена) - ПО УМОЛЧАНИЮ\n```bash\n./geoupdater -dir ./data -mode replace\n```\nСтарые геоданные полностью заменяются новыми.\n\n### 2. MERGE (слияние)\n```bash\n./geoupdater -dir ./data -mode merge\n```\nНовые геохеши добавляются к существующим, дубликаты удаляются.\n\n### 3. NER режим (обработка именованных сущностей)\n```bash\n./geoupdater -ner -dir ./data -pattern \"*.ndjson\"\n```\nОбрабатывает NER данные в отдельную таблицу `{основная_таблица}_ner` (например, `library2026_ner`).\n\nФормат входного файла:\n```json\n{\n  \"doc_id\": \"6056452479959192749\",\n  \"ner_loc\": [{\"value\": \"Египет\", \"start_pos\": 769, \"end_pos\": 775, \"geohash\": [\"1443f4\"], \"confidence\": 1}],\n  \"ner_per\": [{\"value\": \"Плутарх\", \"start_pos\": 839, \"end_pos\": 846, \"geohash\": [], \"confidence\": 0.6273}],\n  \"ner_org\": []\n}\n```\n\nСтруктура создаваемой таблицы:\n```sql\nCREATE TABLE library2026_ner (\n    doc_id bigint,\n    ner_loc json,\n    ner_per json,\n    ner_org json,\n    created_at timestamp,\n    updated_at timestamp\n) engine='rowwise'\n```\n\n## Конфигурация\n\n### Переменные окружения (.env)\n\n```env\n# Manticore connection\nMANTICORE_HOST=localhost\nMANTICORE_PORT=9308\nMANTICORE_TABLE=library2026\nMANTICORE_TIMEOUT=30s\nMANTICORE_MAX_CONNS=10\n\n# Processing\nUPDATE_MODE=merge              # replace или merge\nBATCH_SIZE=1000                # документов в батче\nWORKERS=5                      # параллельных воркеров\nMAX_RETRIES=3                   # попыток при ошибке\nRETRY_DELAY=1s                  # задержка между попытками\n\n# NER specific\nNER_BATCH_SIZE=1000             # размер батча для NER\nNER_WORKERS=5                   # воркеров для NER\n\n# Files\nINPUT_DIR=./data\nFILE_PATTERN=*.ndjson\nFAILED_DIR=./failed\nREPORTS_DIR=./reports\n\n# Logging\nLOG_LEVEL=info                  # debug, info, warn, error\nLOG_FILE=./logs/geoupdater.log\n```\n\n### Флаги командной строки\n\n| Флаг | Описание | Пример |\n|------|----------|--------|\n| `-dir` | Директория с файлами | `-dir ./data` |\n| `-files` | Конкретные файлы (через запятую) | `-files file1.ndjson,file2.ndjson` |\n| `-pattern` | Маска файлов | `-pattern \"*.ndjson\"` |\n| `-mode` | Режим обновления геоданных | `-mode merge` |\n| `-ner` | Режим обработки NER | `-ner` |\n| `-reprocess` | Повторная обработка failed записей | `-reprocess` |\n| `-reports` | Директория для отчетов | `-reports ./reports` |\n| `-version` | Версия | `-version` |\n\n## Детали реализации\n\n### Парсер NDJSON\n\n- Использует потоковое чтение (`bufio.Reader`) для работы с большими файлами\n- Поддерживает параллельную обработку нескольких файлов (`workers`)\n- Валидирует геохеши (длина 3-12 символов)\n- Сохраняет точность ID через `json.Number`\n\n### Manticore клиент\n\nДва способа взаимодействия:\n\n1. **JSON Search API** - для поиска документов\n   ```json\n   {\n     \"table\": \"library2026\",\n     \"query\": { \"in\": { \"id\": [123, 456, 789] } }\n   }\n   ```\n\n2. **Bulk API** - для массового обновления\n   ```json\n   { \"replace\": { \"index\": \"library2026\", \"id\": 123, \"doc\": {...} } }\n   { \"replace\": { \"index\": \"library2026\", \"id\": 456, \"doc\": {...} } }\n   ```\n\n**Важно:** В ответе на bulk запрос ключ называется `\"bulk\"`, а не `\"replace\"`:\n```json\n{\n  \"items\": [{\n    \"bulk\": {           // \u003c-- ВНИМАНИЕ: не \"replace\"!\n      \"_id\": 123,\n      \"result\": \"updated\"\n    }\n  }]\n}\n```\n\n## ⚠️ Важные особенности и грабли\n\n### 1. Работа с ID\n```go\n// ВАЖНО: Все ID должны быть uint64!\ntype Document struct {\n    ID uint64 `json:\"id\"`  // Никогда не используйте string!\n}\n```\n\n### 2. JSON парсинг\n```go\n// Всегда используйте decoder.UseNumber() для больших чисел\ndecoder := json.NewDecoder(bytes.NewReader(line))\ndecoder.UseNumber() // Критически важно!\n```\n\n### 3. Bulk ответ Manticore\n```json\n// В ответе на bulk запрос ключ называется \"bulk\", а не \"replace\"\n{\n  \"items\": [{\n    \"bulk\": {           // \u003c-- ВНИМАНИЕ!\n      \"_id\": 123,\n      \"result\": \"updated\"\n    }\n  }]\n}\n```\n\n### 4. 🚨 Особенность UPDATE для JSON полей в Manticore\n\n**Важное открытие!** Manticore некорректно обрабатывает прямые JSON массивы в UPDATE запросах, воспринимая их как MVA (multi-value attributes). \n\n**Неправильно (вызывает ошибку):**\n```json\n{\n  \"update\": {\n    \"doc\": {\n      \"ner_loc\": [{\"value\": \"Египет\", \"start_pos\": 769}]\n    }\n  }\n}\n// Ошибка: MVA elements should be integers\n```\n\n**Правильно (работает):**\n```json\n{\n  \"update\": {\n    \"doc\": {\n      \"ner_loc\": \"[{\\\"value\\\":\\\"Египет\\\",\\\"start_pos\\\":769}]\"\n    }\n  }\n}\n```\n\nПричина: Manticore путает JSON массивы с MVA. Решение - всегда передавать JSON поля как **строки** в UPDATE запросах, даже если в таблице они определены как `json`. При этом INSERT работает с обычными массивами без проблем.\n\n### 5. Merge режим для геоданных\n- Сохраняет уникальность геохешей\n- Автоматически сортирует для консистентности\n- Обновляет `updated_at` timestamp\n\n### 6. Graceful shutdown\n- При получении SIGINT/SIGTERM дает 2 секунды на завершение\n- Принудительное завершение через 5 секунд\n- Сохраняет статистику и отчеты\n\n### 7. Точность ID\nПри парсинге ответов от Manticore всегда используйте `json.Number` и `decoder.UseNumber()` для сохранения точности 64-битных ID.\n\n## Failed Records\n\nНеудачные записи сохраняются в `./failed/failed_YYYYMMDD_HHMMSS.ndjson`:\n```json\n{\n  \"data\": {\n    \"doc_id\": 6056452479959171091,\n    \"geohashes_string\": [\"test1\", \"test2\"],\n    \"geohashes_uint64\": [111111, 222222]\n  },\n  \"error\": \"document not found\",\n  \"attempts\": 1,\n  \"timestamp\": 1741712807,\n  \"filename\": \"data/results.ndjson\"\n}\n```\n\nАвтоматическая ротация:\n- По размеру (по умолчанию 100MB)\n- По времени (каждый день)\n- Очистка старых записей (по умолчанию 7 дней)\n\n## Примеры использования\n\n### 1. Обработка всех файлов с геоданными\n```bash\n./geoupdater -dir ./data -mode merge\n```\n\n### 2. Обработка NER файлов\n```bash\n./geoupdater -ner -dir ./data -pattern \"ner_*.ndjson\"\n```\n\n### 3. Обработка конкретных файлов\n```bash\n./geoupdater -files ./data/file1.ndjson,./data/file2.ndjson\n```\n\n### 4. Повторная обработка failed записей\n```bash\n./geoupdater -reprocess\n```\n\n## 🐳 Docker\n\n### Запуск с docker-compose\n```bash\n# Создаем структуру директорий\nmkdir -p data failed logs reports\n\n# Копируем файлы для обработки\ncp results.ndjson data/\ncp ner.ndjson data/\n\n# Запускаем\ndocker-compose up\n```\n\n### Запуск отдельного контейнера\n```bash\n# Для геоданных\ndocker run --rm \\\n  --network host \\\n  -v $(pwd)/data:/app/data \\\n  -v $(pwd)/failed:/app/failed \\\n  -v $(pwd)/logs:/app/logs \\\n  -v $(pwd)/reports:/app/reports \\\n  geoupdater:latest -dir /app/data -mode merge\n\n# Для NER\ndocker run --rm \\\n  --network host \\\n  -v $(pwd)/data:/app/data \\\n  -v $(pwd)/failed:/app/failed \\\n  -v $(pwd)/logs:/app/logs \\\n  -v $(pwd)/reports:/app/reports \\\n  geoupdater:latest -ner -dir /app/data -pattern \"ner_*.ndjson\"\n```\n\n## Отчеты\n\nПосле каждой обработки создается детальный отчет в `./reports/`:\n\n- `report_YYYYMMDD_HHMMSS.json` - для геоданных\n- `ner_report_YYYYMMDD_HHMMSS.json` - для NER\n\nПример отчета для NER:\n```json\n{\n  \"version\": \"v1.1.0\",\n  \"start_time\": \"2026-03-13T01:32:16.102+03:00\",\n  \"end_time\": \"2026-03-13T01:32:22.076+03:00\",\n  \"duration\": \"5.974244674s\",\n  \"mode\": \"ner\",\n  \"workers\": 5,\n  \"batch_size\": 1000,\n  \"files\": [\n    {\n      \"filename\": \"data/ner.ndjson\",\n      \"size_bytes\": 51300925,\n      \"lines\": 68672,\n      \"valid\": 68672,\n      \"errors\": 0,\n      \"duration\": \"5.974244674s\",\n      \"success\": 68672,\n      \"failed\": 0,\n      \"skipped\": 0,\n      \"first_id\": 6056452479959192576,\n      \"last_id\": 6056452479959171072\n    }\n  ],\n  \"total_files\": 1,\n  \"stats\": {\n    \"total_processed\": 68672,\n    \"total_success\": 68672,\n    \"total_failed\": 0,\n    \"total_skipped\": 0\n  }\n}\n```\n\n## Производительность\n\n### Геоданные (library2026)\n- **37,820 документов** обработано за **5.09 секунды**\n- Скорость: **~7,430 документов/сек**\n- Время на документ: ~0.13 мс\n\n### NER данные (library2026_ner)\n- **68,672 документа** обработано за **5.97 секунды**\n- Скорость: **~11,500 документов/сек**\n- Время на документ: ~0.087 мс\n\nПри увеличении `WORKERS` и `BATCH_SIZE` производительность растет линейно.\n\n## 🛠️ Команды Makefile\n\n```bash\nmake build          # Сборка проекта\nmake run ARGS=\"-h\"  # Запуск с аргументами\nmake test           # Запуск тестов\nmake clean          # Очистка\nmake docker-build   # Сборка Docker образа\nmake docker-run     # Запуск в Docker\nmake version        # Показать версию\n```\n\n## Версионирование\n\nПроект использует семантическое версионирование. Версия внедряется в бинарный файл при сборке:\n\n```bash\n./geoupdater -version\n# GeoUpdater v1.1.0 (commit: abc1234, built: 2026-03-13_01:30:45)\n```\n\nПодробнее в [VERSIONING.md](VERSIONING.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fterratensor%2Fgeoupdater","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fterratensor%2Fgeoupdater","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fterratensor%2Fgeoupdater/lists"}