Поиск в 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.
Расширенные примеры реализации поиска
Полнотекстовый поиск с подсветкой и пагинацией
Классический запрос с 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-поле встречается искомое значение.