Репозиторий содержит решение задач 1–3: загрузку JSON и CSV в PostgreSQL, SQL-аналитику, отчёт об аномалиях и Next.js-страницу /companies с серверной фильтрацией.
- Next.js App Router + TypeScript;
- PostgreSQL 16 в Docker;
- Node.js-скрипты импорта с
pgиcsv-parse.
Требуются Docker и Node.js 20.12+.
cp .env.example .env
docker compose up -d
npm install
psql "$DATABASE_URL" -f schema.sql
npm run import:json
npm run import:review
npm run devОткройте http://localhost:3000/companies.
Для PowerShell вместо cp используйте Copy-Item .env.example .env. Если psql отсутствует локально, примените схему через контейнер:
Get-Content schema.sql | docker compose exec -T postgres psql -U postgres -d polzaДанные из задания находятся в data/. Можно указать альтернативную папку: DATA_DIR=/path/to/data npm run import:json.
npm run import:json читает все page_*.json, дедуплицирует ID в пределах запуска и делает upsert. npm run import:review валидирует CSV, нормализует только формат рейтинга с запятой, пропускает дубликаты и записывает непригодные строки в import_rejects. Повторный запуск безопасен.
В критериях задания упомянуты email, но в исходных JSON/CSV-полях email отсутствует: в выгрузке есть сайт и телефон. Поэтому email не добавлялся искусственно и не выводится в схему.
Подробности — в ANOMALIES.md. Три требуемых аналитических SQL-запроса находятся в queries.sql.
npm test
npm run buildПроверено вручную:
- На
/companiesотображается таблица компаний, полученных из PostgreSQL. - Поиск по части названия сужает результат и сохраняется в URL (
?q=...). - Фильтр города работает отдельно и вместе с поиском; ссылка «Сбросить» возвращает полный список.
- Пустой результат показывает понятное сообщение.
- Повторный импорт JSON не создаёт дубликаты; невалидные CSV-строки не нарушают ограничения таблицы.
Скриншот поиска «Сфера» с фильтром «Москва»: screenshots/companies-search-moscow.png.
DATABASE_URL хранится только в .env; в репозитории остаётся .env.example без секретов. Все пользовательские фильтры передаются в параметризованный SQL-запрос.