Организация поиска по содержанию сайта на PHP

Раздел: Разработка веб-приложений -> Поиск в 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 — для гибких условий, а полнотекстовый поиск — основное решение для продакшена. Для крайне высоких нагрузок рассмотрите специализированные системы, описанные в разделе расширенных примеров.

- Search php keywords (поиск по ключевым словам в php)
- Search php user (поиск пользователя в php)
- Posts php search (поиск постов в php)

Расширенные примеры и нестандартные подходы

Как реализовать подсветку найденных слов в результатах?

После получения результатов можно заменить искомое слово на выделенный вариант с помощью функции 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 . '*']);
?>
Будут найдены посты, содержащие слова с корнем «бегал» (бегает, бегали, бегать).

Такой подход повышает качество поиска, но требует предварительной обработки и индексации.

Поиск постов в PHP - comments

En
Posts php search (php)