Организация поиска в каталоге PHP
Различные подходы к поиску в каталоге
Как организовать быстрый полнотекстовый поиск по каталогу товаров в MySQL?
Наиболее эффективное решение для поиска по текстовым полям (название, описание) - это использование полнотекстового индекса MySQL (FULLTEXT). Оно обеспечивает высокую скорость, релевантность и поддержку различных режимов (естественный язык, булевый).
Для начала добавляем полнотекстовый индекс на нужные столбцы:
CREATE FULLTEXT INDEX ft_index ON products (name, description);
Search index php topic (поиск темы в индексе php)
Затем выполняем запрос с использованием MATCH ... AGAINST. Пример на PHP с подготовленным выражением:
<?php
$searchTerm = 'ноутбук'; // предположим, получено от пользователя
$stmt = $pdo->prepare(
"SELECT id, name, price,
MATCH(name, description) AGAINST(:term IN BOOLEAN MODE) AS relevance
FROM products
WHERE MATCH(name, description) AGAINST(:term IN BOOLEAN MODE)
ORDER BY relevance DESC"
);
$stmt->execute(['term' => $searchTerm]);
$results = $stmt->fetchAll();
?>
Search php images (поиск изображений в php)
Проблема: при использовании FULLTEXT минимальная длина слова (по умолчанию 4 символа) может игнорировать короткие запросы. Решение - изменить параметр ft_min_word_len в конфигурации MySQL и перестроить индекс.
Другая частая ошибка - забыть про экранирование специальных символов в булевом режиме (+, -, *, " и т.д.). Рекомендуется предварительно очищать запрос или использовать режим естественного языка.
Как выполнить простой поиск по каталогу с использованием SQL LIKE?
Если полнотекстовый индекс недоступен или база данных небольшая, можно использовать оператор LIKE. Однако этот вариант медленный на больших объёмах и не поддерживает ранжирование по релевантности.
<?php
$searchTerm = '%' . $search . '%';
$stmt = $pdo->prepare("SELECT * FROM products WHERE name LIKE :term");
$stmt->execute(['term' => $searchTerm]);
$results = $stmt->fetchAll();
?>
Posts php search (поиск постов в php)
Проблемы: оператор LIKE с ведущим '%' не использует индексы, что приводит к полному сканированию таблицы. Также возможна SQL-инъекция, если не использовать подготовленные выражения.
Как организовать поиск по каталогу без базы данных, используя массив?
Для статических данных или тестирования можно выполнять поиск прямо в PHP через array_filter, in_array или array_search. Этот вариант подходит только для очень маленьких наборов данных (десятки-сотни элементов).
<?php
$products = [
['id' => 1, 'name' => 'Ноутбук ASUS'],
['id' => 2, 'name' => 'Монитор Dell'],
['id' => 3, 'name' => 'Клавиатура']
];
$search = 'ноут';
$filtered = array_filter($products, function($item) use ($search) {
return stripos($item['name'], $search) !== false;
});
?>
Forum index php search (поиск на форуме (index) в php)
Проблема: потребление памяти и низкая производительность при больших массивах. Также нет возможности использовать полнотекстовый поиск, пагинация неэффективна.
Как подключить внешний поисковый движок Elasticsearch для каталога?
Для высоконагруженных проектов с большим объёмом данных и сложными поисковыми запросами (нечёткий поиск, автодополнение, фильтры) целесообразно использовать Elasticsearch. В PHP для работы с ним применяется официальный клиент elastic/elasticsearch.
<?php
require 'vendor/autoload.php';
use Elastic\Elasticsearch\ClientBuilder;
$client = ClientBuilder::create()->setHosts(['localhost:9200'])->build();
$params = [
'index' => 'products',
'body' => [
'query' => [
'multi_match' => [
'query' => 'ноутбук',
'fields' => ['name^3', 'description']
]
]
]
];
$response = $client->search($params);
$results = $response['hits']['hits'];
?>
Сложность: необходимо развернуть и поддерживать Elasticsearch отдельно, синхронизировать данные между MySQL и ES. Возможны проблемы с анализом русского языка, требуется установка соответствующего анализатора (например, elasticsearch-analysis-morphology).
Расширенные примеры и результаты
Пример 1: полнотекстовый поиск с булевыми операторами
Использование булевого режима для поиска точных фраз, исключений и обязательных слов.
<?php
$query = '+ноутбук -lenovo "игровой"';
$stmt = $pdo->prepare("SELECT name, MATCH(name, description) AGAINST(:q IN BOOLEAN MODE) AS rel
FROM products
WHERE MATCH(name, description) AGAINST(:q IN BOOLEAN MODE)");
$stmt->execute(['q' => $query]);
foreach ($stmt as $row) {
echo $row['name'] . ' (релевантность: ' . $row['rel'] . ')<br>';
}
?>
Результат (пример): Ноутбук ASUS TUF Gaming (релевантность: 5.2) Ноутбук MSI GF75 (релевантность: 3.8)
Пример 2: поиск по массиву с несколькими полями и сортировкой
<?php
$products = [
['id' => 1, 'name' => 'Ноутбук Apple MacBook Pro', 'price' => 150000],
['id' => 2, 'name' => 'Ноутбук Lenovo ThinkPad', 'price' => 80000],
['id' => 3, 'name' => 'Монитор LG 27"', 'price' => 30000]
];
$search = 'ноутбук';
$results = array_filter($products, function($p) use ($search) {
return stripos($p['name'], $search) !== false;
});
usort($results, function($a, $b) {
return $b['price'] <=> $a['price']; // сортировка по убыванию цены
});
print_r($results);
?>
Array
(
[0] => Array ([id] => 1, [name] => Ноутбук Apple MacBook Pro, [price] => 150000)
[1] => Array ([id] => 2, [name] => Ноутбук Lenovo ThinkPad, [price] => 80000)
)
Пример 3: поиск с использованием Elasticsearch и пагинацией
<?php
$page = 1;
$perPage = 10;
$from = ($page - 1) * $perPage;
$params = [
'index' => 'products',
'from' => $from,
'size' => $perPage,
'body' => [
'query' => ['match' => ['name' => 'ноутбук']],
'sort' => ['price' => ['order' => 'desc']]
]
];
$response = $client->search($params);
echo 'Найдено всего: ' . $response['hits']['total']['value'] . '<br>';
foreach ($response['hits']['hits'] as $hit) {
echo $hit['_source']['name'] . ' - ' . $hit['_source']['price'] . '<br>';
}
?>
Найдено всего: 23 Ноутбук ASUS ROG Strix - 120000 Ноутбук Dell XPS - 110000 ... (10 записей на странице)
Пример 4: обработка ошибок при использовании FULLTEXT
Типичная ошибка: пустой запрос или использование стоп-слов. Оборачиваем в try-catch.
<?php
try {
$stmt = $pdo->prepare($sql);
$stmt->execute(['q' => $query]);
} catch (PDOException $e) {
if (strpos($e->getMessage(), 'FTS') !== false) {
// логируем, показываем пользователю общее сообщение
echo 'Поиск временно недоступен';
}
}
?>