Поисковый сервис для текстов документов с интеграцией Elasticsearch и PostgreSQL.
Юнгблюд Артур
- API-слой: Реализован через паттерн Router Factory с ленивыми импортами. Эндпоинты выполняют роль тонких контроллеров, отвечая исключительно за маршрутизацию, конфигурацию OpenAPI и HTTP-контракты.
- Бизнес-логика: Выделена в независимые сценарии использования через паттерн Command.
- Слой данных: Взаимодействие с базами данных и внешними сервисами полностью изолировано с помощью паттерна Repository.
- Связанность компонентов: Благодаря строгому соблюдению принципа инверсии зависимостей удалось органично изолировать части приложения друг от друга. Слои получились независимыми, расширяемыми и легко тестируемыми.
- Жизненный цикл: Все основные переиспользуемые сущности и клиенты подключений инициализируются в рамках Lifespan и хранятся непосредственно в состоянии приложения (
app.stateв FastAPI). Это обеспечивает единую точку конфигурации и эффективное управление системными ресурсами. - Асинхронность: Внутри процесса инициализации реализовано конкурентное перестроение индексов с использованием современного механизма
asyncio.TaskGroup. - Контроль конкурентности: В коде продемонстрировано владение инструментами синхронизации через
asyncio.Semaphore, который жестко ограничивает пул одновременных запросов к Elasticsearch для предотвращения сетевого оверхеда.💡 Примечание: В текущих масштабах инфраструктуры данное решение является избыточным, однако оно намеренно заложено для демонстрации навыков проектирования высоконагруженных сценариев.
- Сидинг данных: Реализовано динамическое наполнение базы данных из CSV-файла непосредственно в процессе выполнения миграций Alembic (процесс опционален и управляется флагом из конфигурации).
- Кастомизация миграций: Продемонстрировано умение модифицировать автосгенерированные файлы Alembic и выполнять чистые SQL-запросы.
- Оптимизация запросов: Данные вставляются пачкой путем передачи списка словарей в метод выполнения. Это полностью исключает проблему N+1 запросов и избыточные сетевые задержки при развертывании приложения.
Высокая изолированность архитектурных слоев позволила выстроить эффективную и быструю модель тестирования:
- Smoke-тесты: Выступают в роли первой линии проверки. Они валидируют успешный запуск базы данных и проверяют доступность приложения через эндпоинт Healthcheck.
- Функциональные тесты: Проверяют целиком изолированный объект резолвера, подменяя внешний мир и базы данных быстрыми заглушками репозиториев.
- Интеграционные тесты: Покрывают исключительно слой репозиториев Repository, проверяя корректность запросов в реальной, изолированной тестовой среде (БД и Elasticsearch).
- E2E-тесты: Запускают приложение целиком в тестовом окружении и проверяют по одному ключевому положительному сценарию на каждый сквозной процесс.
Сервис предоставляет API для полнотекстового поиска по документам с использованием Elasticsearch в качестве поискового движка. Данные документов хранятся в PostgreSQL, а поисковый индекс — в Elasticsearch.
- 🔍 Полнотекстовый поиск по документам с ранжированием по дате создания
- 📄 Возврат первых 20 документов с полными данными из БД
- 🗑️ Удаление документов из БД и индекса по ID
- Python 3.14
- FastAPI — веб-фреймворк
- PostgreSQL — хранение документов
- Elasticsearch — поисковый индекс
- SQLAlchemy — ORM
- Alembic — миграции
- Docker / Docker Compose — контейнеризация
- Pytest — тестирование
- Docker и Docker Compose
- Python 3.14 (для локальной разработки)
# Клонировать репозиторий
git clone <repository-url>
cd search_docs
# Запустить все сервисы
docker-compose up -dСервис будет доступен по адресу: http://localhost:8000
Документация API (OpenAPI): http://localhost:8000/search_docs/docs
# Установить uv (если не установлен)
# Создать виртуальное окружение и установить зависимости
uv venv
source .venv/bin/activate
uv pip install -e .
# Запустить PostgreSQL и Elasticsearch через docker-compose
docker-compose up -d db elasticsearch
# Применить миграции
alembic upgrade head
# Загрузить тестовые данные
python -m app.startup
# Запустить сервер
python main.py.
├── app/ # Основной код приложения
│ ├── api/ # API слои
│ │ ├── health.py # Healthcheck эндпоинт
│ │ └── v1/ # API v1
│ │ ├── resolvers.py # Обработчики запросов
│ │ ├── routers.py # Роутеры
│ │ └── schemas.py # Pydantic схемы
│ ├── storage/ # Работа с хранилищами
│ │ ├── database.py # Подключение к БД
│ │ └── elastic.py # Подключение к Elasticsearch
│ ├── config.py # Конфигурация приложения
│ ├── logger.py # Настройка логирования
│ ├── models.py # SQLAlchemy модели
│ ├── repositories.py # Репозитории для работы с БД и индексом
│ ├── startapp.py # lifespan эвенты при старте приложения
│ └── server.py # Настройка FastAPI приложения
├── alembic/ # Миграции БД
│ └── versions/ # Файлы миграций
├── tests/ # Тесты
│ ├── test_01_smoke/ # Смок тесты (Ожидание: долгое поднятие всей инфраструктуры)
│ ├── test_01_functional/ # Функциональные тесты (Ожидание: быстрое, не требуется поднятие инфраструктуры)
│ ├── test_02_integration/ # Интеграционные тесты (Ожидание: долгое поднятие части инфраструктуры, зависит от тестов)
│ └── test_03_e2e/ # End-to-end тесты (Ожидание: долгое поднятие всей инфраструктуры)
├── docker-compose.yaml # Docker Compose конфигурация
├── Dockerfile # Docker образ
├── docs.json # OpenAPI документация
├── main.py # Точка входа
└── pyproject.toml # Зависимости проекта
- Быстрота выполнения: ⚡ (требуют времени на инициализацию)
- Цель: Проверка базовой работоспособности и доступности сервисов
- Особенности: Ожидают долгое поднятие всей инфраструктуры, валидируют старт базы данных и проверяют эндпоинт Healthcheck
- Запуск: pytest tests/test_01_smoke/
- Быстрота выполнения: ⚡⚡⚡ (самые быстрые)
- Цель: Тестирование бизнес-логики и сервисного слоя
- Особенности: Используют моки для внешних зависимостей (БД, Elasticsearch)
- Запуск: pytest tests/test_01_functional/
- Быстрота выполнения: ⚡⚡ (средние)
- Цель: Проверка взаимодействия с реальными хранилищами
- Особенности: Поднимают тестовые контейнеры с БД и Elasticsearch
- Запуск: pytest tests/test_02_integration/
- Быстрота выполнения: ⚡ (самые долгие)
- Цель: Полная проверка API эндпоинтов в реальной среде
- Особенности: Тестируют полный цикл: HTTP запрос → обработка → ответ
- Запуск: pytest tests/test_03_e2e/
pytestPOST /search_docs/v1/documents/search?selected_size=20
Тело запроса:
{
"search_text": "Грибное смузи"
}Ответ (200):
{
"documents": [
{
"id": 999,
"text": "Здесь могла бы быть ваша реклама",
"rubrics": ["VK-191919191"],
"created_date": "2026-06-30T22:17:14.561073"
}
]
}DELETE /search_docs/v1/documents/{id}
Ответ: 204 No Content
GET /search_docs/health/
Ответ (200):
{
"status": "ok"
}Создайте файл .env в корне проекта, пример файла .env.example в корне