Інтеграція Sphinx для пошуку на сайті
Sphinx Search та його активно розвивається форк Manticore Search — перевірені пошукові рушії з багаторічною історією в highload-проектах. Використовуються там, де важлива сумісність із MySQL-протоколом, зрілість рішення та прямий контроль над індексами. Manticore Search додав підтримку JSON-документів, колоночного сховища, HTTP API та RT-індексів реального часу.
Коли вибирають Sphinx / Manticore
- Легаси-проекти з існуючою Sphinx-інфраструктурою
- Потрібна MySQL-сумісність — підключення через стандартні MySQL-клієнти
- Корпус в сотні мільйонів документів: новинні агрегатори, архіви
- Повний контроль над токенізатором та морфологією
- Інтеграція з існуючою MySQL/MariaDB-реплікацією
Manticore Search vs Sphinx
| Характеристика | Sphinx 3.x | Manticore Search 6.x |
|---|---|---|
| Розробка | Замалена | Активна |
| RT-індекси | Базові | Повнофункціональні |
| JSON-документи | Ні | Так |
| HTTP API | Ні | Так (Elasticsearch-сумісний) |
| Колоночне сховище | Ні | Так |
| MySQL-протокол | Так | Так |
Для нових проектів — краще Manticore Search.
Установка
# docker-compose.yml
services:
manticore:
image: manticoresearch/manticore:6.2.12
environment:
- EXTRA=1
ports:
- "9306:9306" # MySQL-сумісний порт
- "9308:9308" # HTTP API
volumes:
- manticore_data:/var/lib/manticore
- ./manticore.conf:/etc/manticoresearch/manticore.conf
Конфігурація індексу
# manticore.conf
index articles {
type = rt
path = /var/lib/manticore/articles
rt_field = title
rt_field = body
rt_field = author
rt_attr_uint = category_id
rt_attr_bigint = created_at
rt_attr_float = rating
rt_attr_string = slug
morphology = stem_ru, stem_en
min_word_len = 2
expand_keywords = 1
min_infix_len = 3
stopwords = /etc/manticoresearch/stopwords_ru.txt
}
searchd {
listen = 0.0.0.0:9306:mysql41
listen = 0.0.0.0:9308:http
log = /var/log/manticore/searchd.log
query_log = /var/log/manticore/query.log
max_matches = 10000
}
Підключення через MySQL-клієнт (PHP)
Manticore приймає SQL на порту 9306 по MySQL-протоколу:
$pdo = new PDO('mysql:host=localhost;port=9306;charset=utf8', '', '');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Вставка документа:
$stmt = $pdo->prepare("
INSERT INTO articles (id, title, body, author, category_id, created_at, rating)
VALUES (:id, :title, :body, :author, :category_id, :created_at, :rating)
");
$stmt->execute([
'id' => $article->id,
'title' => $article->title,
'body' => strip_tags($article->content),
'author' => $article->user->name,
'category_id' => $article->category_id,
'created_at' => $article->created_at->timestamp,
'rating' => $article->rating,
]);
Повнотекстовий пошук з вагами полів:
$stmt = $pdo->prepare("
SELECT id, title, author, rating,
WEIGHT() AS relevance
FROM articles
WHERE MATCH(:query)
AND category_id = :category_id
ORDER BY relevance DESC, rating DESC
LIMIT :offset, :limit
OPTION ranker=bm25, field_weights=(title=10, body=1, author=2)
");
HTTP API (Elasticsearch-сумісний)
# Індексація
curl -X POST 'http://localhost:9308/articles/_doc/1' \
-H 'Content-Type: application/json' \
-d '{
"title": "Заголовок статті",
"body": "Текст статті",
"category_id": 5
}'
# Повнотекстовий пошук
curl -X POST 'http://localhost:9308/articles/_search' \
-H 'Content-Type: application/json' \
-d '{
"query": { "match": { "title": "пошук по сайту" } },
"sort": [{ "_score": "desc" }],
"size": 20
}'
Синхронізація з основною БД
Batch-індексація через chunk:
public function handle(): void
{
$pdo = $this->getManticoreConnection();
Article::with('user', 'category')
->where('published', true)
->chunk(1000, function ($articles) use ($pdo) {
$pdo->beginTransaction();
foreach ($articles as $article) {
$pdo->prepare("REPLACE INTO articles ...")->execute(
$this->toDocument($article)
);
}
$pdo->commit();
});
}
Event-driven оновлення через Observer:
class ArticleObserver
{
public function saved(Article $article): void
{
ManticoreIndexJob::dispatch($article->id);
}
public function deleted(Article $article): void
{
ManticoreDeleteJob::dispatch($article->id);
}
}
Морфологія російської мови
Sphinx/Manticore включає stem_ru. Для точнішої морфології використовуйте morphology = lemmatize_ru_all — вимагає окремих словників lemmatize. Це дозволяє знаходити «бігун» по запиту «біг».
Стоп-слова підключаються файлом — список прийменників, сполучників, частиць, які виключаються з індексу.
Підсвітка результатів
SELECT id, title,
SNIPPET(body, :query,
'limit=200, around=5, html_strip_mode=strip') AS excerpt
FROM articles
WHERE MATCH(:query)
LIMIT 20
SNIPPET() повертає фрагмент тексту із виділенням знайдених слів — готово для відображення в результатах пошуку.
Графіки робіт
| Етап | Час |
|---|---|
| Установка, конфігурація, схема індексу | 1 день |
| Первинна індексація + синхронізатор | 2 дні |
| API пошуку в додатку + тести | 2 дні |
| UI: результати, сніппети, пагінація | 1–2 дні |
Усього: 6–7 робочих днів.







