Тип: in-memory inverted index + простой скоринг (TF * IDF-вариант с нормализованным tf).
search-engine– чистое ядро (TypeScript): токенизация, инвертированный индекс, поискserver– HTTP API (Node.js, без фреймворков) поверх ядраweb– UI (React + Vite + TypeScript)data– вспомогательные данные (fallback seed)docs– документация (архитектура и т.п.)
- Индексация произвольных документов (строковые поля)
- Поиск по нескольким термам + fallback подстрочного совпадения (expand)
- Импорт товаров из DummyJSON (fallback на локальный seed
data/products-seed.json) - Подсветка терминов (клиент)
- Удаление / реиндексация документов
Node.js 18+ (глобальный fetch).
# Установка (соберёт search-engine через prepare)
npm install
# Запуск сервера (порт 4000)
npm run dev:server
# Запуск фронтенда (порт 5173)
npm run dev:web
# Параллельно (Unix шелл)
npm run devОткрой http://localhost:5173 — кнопка «Импортировать товары» подтянет данные и создаст индекс.
Создайте .env.local при необходимости:
VITE_API_BASE=http://localhost:4000
По умолчанию используется http://localhost:4000.
search-engine/
src/
tokenizer.ts
invertedIndex.ts
index.ts
types.ts
server/
src/index.ts
web/
src/api.ts
src/App.tsx
src/components/ProductCard.tsx
src/styles.css
data/products-seed.json
docs/ARCHITECTURE.md
| Метод | Путь | Параметры | Описание |
|---|---|---|---|
| GET | /search | q, limit, expand=0/1 | Поиск |
| GET | /products | limit, offset, category | Листинг |
| POST | /index | body JSON | Индексация |
| POST | /delete | { id } | Удаление |
| GET | /stats | — | Количество документов |
| POST | /import/products | — | Импорт (DummyJSON/seed) |
| POST | /reset | — | Сброс индекса |
| GET | /health | — | Health-check |
curl -X POST http://localhost:4000/import/products
curl 'http://localhost:4000/search?q=phone'
curl -X POST http://localhost:4000/index -H 'Content-Type: application/json' -d '{"title":"Пример","body":"Тестовый документ"}'import { InvertedIndex } from '@search/engine';
const idx = new InvertedIndex();
idx.addDocument({ id: '1', title: 'Поисковый движок', body: 'Учебный пример инвертированного индекса' });
idx.addDocument({ id: '2', title: 'Алгоритмы', body: 'Структуры данных и индекс' });
const hits = idx.search('индекс движок');
console.log(hits.map(h => ({ id: h.id, score: h.score })));- Инвертированный индекс:
Map(term -> Map(docId -> freq)) - Нормализация TF:
tf / docLength - IDF:
log(1 + N / (df + 1)) - Подстрочное расширение: если терм не найден и длина >= 3 — поиск включает термы содержащие подстроку (лимитируется)
- Токенизация: Unicode lower-case + фильтрация русских стоп-слов
- Всё в памяти (нет persist)
- Нет позиционных списков (нет поиска фраз)
- Удаление O(T) по количеству термов
- Скоринг упрощённый (не BM25)
- BM25
- Позиции и фразовый поиск
- Snapshot / WAL
- Иммутабельные сегменты + merge
- Фильтры (числовые/диапазоны)
- Bulk индексация
- Кэш статистик термов
- Репликация (WAL + restore)
- Server-side snippets
- Unit / интеграционные тесты + бенчмарки
npm run build
# Результат:
# server/dist (Node JS)
# web/dist (статические файлы)Далее отдавайте web/dist любым статик-сервером и запускайте node server/dist/index.js.
Старые файлы (vanilla JS сервер/клиент) удалены / заглушены. Актуальный код — только в пакетах search-engine, server, web.
npm run dev:servernpm run dev:web- Импорт → поиск → проверка
/stats
MIT (учебный пример).