Организация поиска по содержанию сайта на PHP
Методы поиска записей в PHP
При разработке веб-приложений часто требуется реализовать поиск постов (записей) в базе данных. Рассмотрим несколько подходов с примерами кода на PHP и MySQL, а также обсудим их эффективность и типичные проблемы.
Как организовать эффективный полнотекстовый поиск постов в MySQL?
Самый производительный способ для больших объёмов данных — использование полнотекстового индекса (FULLTEXT) и оператора MATCH ... AGAINST. Этот метод поддерживает стоп-слова, ранжирование по релевантности и работает значительно быстрее LIKE на больших таблицах.
Создание таблицы с FULLTEXT индексом:
CREATE TABLE posts (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FULLTEXT (title, content)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Search forum php (поиск по форуму на php)
Пример PHP-кода для выполнения поискового запроса:
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass');
$keyword = $_GET['q'] ?? '';
// Подготовка запроса с полнотекстовым поиском
$stmt = $pdo->prepare("SELECT id, title, MATCH(title,content) AGAINST(:keyword IN NATURAL LANGUAGE MODE) AS relevance FROM posts WHERE MATCH(title,content) AGAINST(:keyword IN NATURAL LANGUAGE MODE) ORDER BY relevance DESC");
$stmt->execute(['keyword' => $keyword]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($results as $row) {
echo '<h3>' . htmlspecialchars($row['title']) . '</h3>';
echo '<p>Релевантность: ' . $row['relevance'] . '</p>';
}
?>
Search php browse (просмотр поиска php)
Типичные ошибки и их решения:
- Пустые результаты при очевидном совпадении. Проверьте, что длина искомого слова не меньше, чем параметр ft_min_word_len (по умолчанию 4). Для коротких слов измените настройки MySQL или используйте IN BOOLEAN MODE с подстановочным знаком *.
- SQL-инъекция. Всегда используйте подготовленные выражения (PDO), как в примере выше.
- Несоответствие кодировок. Убедитесь, что таблица и соединение используют utf8mb4, иначе поиск может работать некорректно.
- Стоп-слова. По умолчанию MySQL игнорирует частые слова (например, «и», «в»). Для их включения измените параметр ft_stopword_file.
Как выполнить простой поиск по заголовку через LIKE?
Для небольших таблиц (до десятков тысяч записей) допустимо использовать оператор LIKE. Он прост в написании, но не использует индексы, если шаблон начинается с %.
<?php
$keyword = $_GET['q'] ?? '';
$stmt = $pdo->prepare("SELECT id, title FROM posts WHERE title LIKE :pattern");
$stmt->execute(['pattern' => '%' . $keyword . '%']);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
Search php mode search (режим поиска php)
Проблемы: LIKE неэффективен при больших объёмах данных, не поддерживает ранжирование, подвержен SQL-инъекциям при неправильном экранировании. Используйте addcslashes для экранирования спецсимволов %, _.
Как найти посты с помощью регулярных выражений в MySQL?
Оператор REGEXP (или RLIKE) позволяет задать сложные шаблоны. Однако он не использует индексы и работает медленнее LIKE.
<?php
$keyword = $_GET['q'] ?? '';
// Экранирование специальных символов regex
$safeKeyword = preg_quote($keyword, '/');
$stmt = $pdo->prepare("SELECT id, title FROM posts WHERE title REGEXP :pattern");
$stmt->execute(['pattern' => $safeKeyword]);
?>
Ошибки: При неэкранировании символов (например, точка) шаблон может интерпретироваться неверно. Всегда используйте preg_quote. Производительность REGEXP резко падает на таблицах более 10–20 тысяч строк.
Каждый вариант имеет свою область применения: LIKE подходит для прототипов и малых проектов, REGEXP — для гибких условий, а полнотекстовый поиск — основное решение для продакшена. Для крайне высоких нагрузок рассмотрите специализированные системы, описанные в разделе расширенных примеров.
Расширенные примеры и нестандартные подходы
Как реализовать подсветку найденных слов в результатах?
После получения результатов можно заменить искомое слово на выделенный вариант с помощью функции PHP.
<?php
$keyword = $_GET['q'] ?? '';
$results = $pdo->query("SELECT id, title, content FROM posts WHERE MATCH(title,content) AGAINST('$keyword' IN BOOLEAN MODE)")->fetchAll();
foreach ($results as $row) {
$highlightedTitle = preg_replace('/(' . preg_quote($keyword, '/') . ')/iu', '<mark>$1</mark>', $row['title']);
echo '<h3>' . $highlightedTitle . '</h3>';
}
?>
Результат: заголовок «Как настроить PHP» при поиске «php» превратится в «Как настроить <mark>PHP</mark>».
Обратите внимание на модификатор iu (регистронезависимость и Unicode).
Как использовать булевый режим FULLTEXT для точного поиска?
В режиме IN BOOLEAN MODE можно использовать операторы + (слово обязательно), - (слово запрещено) и * (подстановка).
<?php
// Поиск постов, содержащих обязательно «php» и не содержащих «java»
$stmt = $pdo->prepare("SELECT id, title FROM posts WHERE MATCH(title,content) AGAINST(:query IN BOOLEAN MODE)");
$stmt->execute(['query' => '+php -java']);
?>
Найдутся записи, где есть слово «php», но нет «java».
Как интегрировать Elasticsearch для высоконагруженного поиска?
Elasticsearch — внешний поисковый сервер, обеспечивающий почти мгновенный поиск даже по миллионам записей.
Установка (через Docker):
docker run -d --name elasticsearch -p 9200:9200 -e "discovery.type=single-node" elasticsearch:8.10.0
Создание индекса и добавление данных (PHP через Guzzle):
<?php
use GuzzleHttp\Client;
$client = new Client(['base_uri' => 'http://localhost:9200']);
// Создаём индекс
$client->put('posts', [
'json' => [
'mappings' => [
'properties' => [
'title' => ['type' => 'text'],
'content' => ['type' => 'text']
]
]
]
]);
// Индексируем документ
$client->post('posts/_doc', [
'json' => ['title' => 'Основы PHP', 'content' => 'PHP – популярный язык...']
]);
?>
Выполнение поискового запроса:
<?php
$response = $client->get('posts/_search', [
'json' => [
'query' => [
'match' => ['title' => 'PHP основы']
]
]
]);
$data = json_decode($response->getBody(), true);
foreach ($data['hits']['hits'] as $hit) {
echo $hit['_source']['title'] . "\n";
}
?>
Вывод: Основы PHP
Сложности: Требуется установка и поддержка отдельного сервера, увеличение сложности архитектуры. Для небольших проектов избыточно.
Как организовать поиск с учётом морфологии (стемминг) средствами MySQL?
В MySQL нет встроенного стемминга, но можно использовать пользовательскую функцию или перед поиском применять PHP-библиотеку (например, stemmer).
<?php
require_once 'vendor/autoload.php';
use Wamania\Snowball\StemmerFactory;
// Получаем стеммер для русского языка
$stemmer = StemmerFactory::create('russian');
$keyword = 'бегали';
$stem = $stemmer->stem($keyword); // 'бегал'
// Затем выполняем поиск по стемам
$stmt = $pdo->prepare("SELECT * FROM posts WHERE MATCH(content) AGAINST(:stem IN BOOLEAN MODE)");
$stmt->execute(['stem' => $stem . '*']);
?>
Будут найдены посты, содержащие слова с корнем «бегал» (бегает, бегали, бегать).
Такой подход повышает качество поиска, но требует предварительной обработки и индексации.