Поиск в PHP: вывод результатов и варианты реализации

Раздел: Веб-разработка -> Реализация поиска

Реализация поиска: результаты и их отображение

Наиболее эффективным способом вывода результатов поиска в веб-приложениях является использование полнотекстового индекса MySQL с оператором MATCH ... AGAINST. Этот подход обеспечивает высокую скорость, ранжирование по релевантности и поддержку морфологии (через встроенные или сторонние парсеры).


// Пример: поиск по таблице articles с полнотекстовым индексом
$search = $_GET['q'] ?? '';
if (mb_strlen($search) < 3) {
    echo 'Введите минимум 3 символа';
    exit;
}

$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass');
$stmt = $pdo->prepare(
    "SELECT id, title, content,
            MATCH(title, content) AGAINST(:query IN BOOLEAN MODE) AS relevance
     FROM articles
     WHERE MATCH(title, content) AGAINST(:query2 IN BOOLEAN MODE)
     ORDER BY relevance DESC
     LIMIT 10"
);
// Экранирование спецсимволов для boolean mode
$escaped = preg_replace('/[+\-&|!(){}~^"\\*@]/', ' ', $search);
$stmt->execute([':query' => $escaped, ':query2' => $escaped]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

foreach ($results as $row) {
    echo "<h3>{$row['title']}</h3>";
    echo "<p>{$row['content']}</p>";
    echo "<small>Релевантность: {$row['relevance']}</small>";
}
  

Search tags php tag (поиск по тегам в php)

Типичные проблемы:

  • Минимальная длина слова для индексации (по умолчанию 4 символа). Изменяется через параметр ft_min_word_len в my.cnf, после чего требуется перестроение индекса.
  • Стоп-слова (например, "и", "в", "на") игнорируются индексатором. Отключаются настройкой ft_stopword_file=''.
  • Спецсимволы в запросе ломают BOOLEAN MODE. Необходимо очищать строку, как показано в примере.
  • Для морфологии требуется установка плагина ngram или использование внешних анализаторов.

Как осуществить поиск по массиву данных на PHP?

Когда данные хранятся в оперативной памяти (например, кэш, конфигурация), можно использовать array_filter и stripos для регистронезависимого поиска.


$items = [
    ['title' => 'PHP руководство', 'desc' => 'Изучаем PHP'];
    ['title' => 'JavaScript учебник', 'desc' => 'Основы JS'];
];

$search = 'php';
$filtered = array_filter($items, function($item) use ($search) {
    return stripos($item['title'], $search) !== false
        || stripos($item['desc'], $search) !== false;
});

print_r($filtered);
  

Search topic php (поиск по теме в php)

Проблемы: на больших массивах (десятки тысяч элементов) скорость падает. Подход не поддерживает ранжирование. Рекомендуется только для небольших статических данных.

Как выполнить поиск с использованием оператора LIKE в MySQL?

Для простых запросов, когда полнотекстовый индекс недоступен, применяют LIKE с подстановочными знаками. Важно использовать подготовленные выражения для предотвращения SQL-инъекций.


$search = $_GET['q'] ?? '';
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'user', 'pass');
$stmt = $pdo->prepare(
    "SELECT id, title, content
     FROM articles
     WHERE title LIKE :like OR content LIKE :like2
     ORDER BY id"
);
$like = '%' . $search . '%';
$stmt->execute([':like' => $like, ':like2' => $like]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
  

Search type php id type (тип поиска по id в php)

Недостатки: полное сканирование таблицы (индекс не используется из-за ведущего %), низкая производительность на больших объёмах, отсутствие ранжирования. Подходит только для таблиц с несколькими сотнями записей или когда скорость не критична.

Как интегрировать Elasticsearch для полнотекстового поиска?

Elasticsearch предоставляет распределённый поисковый движок с морфологией, фасетами и высокой скоростью. Для PHP используется официальный клиент elasticsearch/elasticsearch.


require 'vendor/autoload.php';

$client = Elasticsearch\ClientBuilder::create()->build();

// Индексация документа
$params = [
    'index' => 'articles',
    'id' => 1,
    'body' => [
        'title' => 'Основы PHP',
        'content' => 'PHP - скриптовый язык программирования.'
    ]
];
$client->index($params);

// Поиск
$searchParams = [
    'index' => 'articles',
    'body' => [
        'query' => [
            'match' => [
                'content' => 'скриптовый язык'
            ]
        ]
    ]
];
$response = $client->search($searchParams);
$hits = $response['hits']['hits'];
foreach ($hits as $hit) {
    echo $hit['_source']['title'] . "\n";
}
  

Search php view (вид поиска в php)

Сложности: требуется установка и настройка отдельного сервера Elasticsearch, увеличение потребления памяти. Для небольших проектов избыточно.

Как организовать поиск по содержимому файлов на сервере?

Для поиска в текстовых файлах (логи, документы) можно комбинировать scandir с file_get_contents и strpos. Эффективно для статичных файлов небольшого размера.


$search = 'ошибка';
$dir = '/var/log';
$files = scandir($dir);
$results = [];

foreach ($files as $file) {
    if (pathinfo($file, PATHINFO_EXTENSION) === 'log') {
        $content = file_get_contents($dir . '/' . $file);
        if (strpos($content, $search) !== false) {
            $results[] = $file;
        }
    }
}
print_r($results);
  

Ограничения: загрузка больших файлов в память. Для больших объёмов лучше использовать SplFileObject и построчное чтение, либо grep через exec.

- Search php cid (поиск по cid в php)
- Search php keyword (поиск по ключевому слову в php)
- Search php search name (поиск по имени в php)

Расширенные примеры реализации поиска

Полнотекстовый поиск с подсветкой и пагинацией

Классический запрос с MATCH AGAINST дополняется вычислением страниц и подсветкой искомого слова в результатах.

Пример

$search = $_GET['q'] ?? '';
$page = max(1, (int)($_GET['page'] ?? 1));
$perPage = 5;
$offset = ($page - 1) * $perPage;

$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass');

// Подсчёт общего числа результатов
$countStmt = $pdo->prepare(
    "SELECT COUNT(*) FROM articles
     WHERE MATCH(title, content) AGAINST(:query IN BOOLEAN MODE)"
);
$escaped = preg_replace('/[+\-&|!(){}~^"\\*@]/', ' ', $search);
$countStmt->execute([':query' => $escaped]);
$total = $countStmt->fetchColumn();
$pages = ceil($total / $perPage);

// Выборка с пагинацией
$stmt = $pdo->prepare(
    "SELECT id, title, content,
            MATCH(title, content) AGAINST(:query2 IN BOOLEAN MODE) AS relevance
     FROM articles
     WHERE MATCH(title, content) AGAINST(:query3 IN BOOLEAN MODE)
     ORDER BY relevance DESC
     LIMIT :limit OFFSET :offset"
);
$stmt->bindValue(':query2', $escaped, PDO::PARAM_STR);
$stmt->bindValue(':query3', $escaped, PDO::PARAM_STR);
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

// Функция подсветки
function highlight($text, $query) {
    $words = explode(' ', $query);
    foreach ($words as $word) {
        $text = preg_replace(
            '/(' . preg_quote($word, '/') . ')/iu',
            '<mark>$1</mark>',
            $text
        );
    }
    return $text;
}

echo "<p>Найдено: $total | Страница $page из $pages</p>";
foreach ($results as $row) {
    echo "<div>";
    echo "<h3>" . highlight($row['title'], $search) . "</h3>";
    echo "<p>" . highlight(mb_substr($row['content'], 0, 200), $search) . "...</p>";
    echo "<small>Релевантность: {$row['relevance']}</small>";
    echo "</div>";
}

// Постраничная навигация
for ($i = 1; $i <= $pages; $i++) {
    echo "<a href='?q=$search&page=$i'>$i</a> ";
}

Результат: отображается нумерованный список результатов с подсветкой совпадений и ссылками на страницы.

Найдено: 23 | Страница 1 из 5
[1] Основы PHP - php скриптовый язык...
[2] PHP фреймворки: Laravel, Symfony...
...

Комбинированный поиск по нескольким полям с UNION

Если требуется ранжировать результаты по разным полям с разными весами, можно использовать UNION и сортировку по общему баллу.

Пример

$search = 'php';
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'user', 'pass');

$sql = "
(SELECT id, title, 'title' as field, 10 AS weight
 FROM articles WHERE title LIKE :like)
UNION ALL
(SELECT id, content, 'content' as field, 5 AS weight
 FROM articles WHERE content LIKE :like2)
ORDER BY weight DESC, id
";

$like = '%' . $search . '%';
$stmt = $pdo->prepare($sql);
$stmt->execute([':like' => $like, ':like2' => $like]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

// Группировка по id (если требуется уникальность)
$grouped = [];
foreach ($results as $row) {
    $id = $row['id'];
    if (!isset($grouped[$id])) {
        $grouped[$id] = ['title' => '', 'content' => ''];
    }
    if ($row['field'] === 'title') $grouped[$id]['title'] = $row['title'];
    else $grouped[$id]['content'] = $row['content'];
}

foreach ($grouped as $item) {
    echo "<h4>{$item['title']}</h4>";
    echo "<p>{$item['content']}</p>";
}

Результат: сначала выводятся заголовки (с весом 10), затем содержимое (вес 5), при этом один и тот же документ может появиться дважды, но группировка решает эту проблему.

[Заголовок] Основы PHP
[Содержимое] PHP - это язык программирования, широко используемый...

Поиск с использованием регулярных выражений в PHP

Для извлечения сложных паттернов (email, телефон) из строки используется preg_match_all.

Пример

$text = "Свяжитесь с нами: info@example.com или support@test.org.";
$pattern = '/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/';
if (preg_match_all($pattern, $text, $matches)) {
    echo "Найденные email:";
    print_r($matches[0]);
}
Найденные email:
Array
(
    [0] => info@example.com
    [1] => support@test.org
)

Поиск по JSON-полям в MySQL (MySQL 8+)

Если данные хранятся в формате JSON, используется оператор JSON_SEARCH или JSON_CONTAINS.

Пример

$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass');
$search = 'активный';
$stmt = $pdo->prepare(
    "SELECT id, metadata
     FROM users
     WHERE JSON_SEARCH(metadata, 'one', :query) IS NOT NULL"
);
$stmt->execute([':query' => $search]);
while ($row = $stmt->fetch()) {
    echo "Пользователь #{$row['id']} имеет статус: {$row['metadata']}";
}

Результат: строки, где в JSON-поле встречается искомое значение.

Результаты поиска в PHP - comments

En
Results php search (php)