Организация поискового функционала товаров на PHP
В статье рассмотрены различные способы реализации поиска товаров на PHP. Цель - дать практические примеры и пояснения к каждому шагу, а также указать на типичные проблемы и способы их решения.
Основной эффективный метод: полнотекстовый поиск MySQL
Наиболее производительным решением для средних каталогов является использование полнотекстового индекса MySQL и оператора MATCH ... AGAINST. Этот метод поддерживает ранжирование результатов по релевантности, учитывает стоп-слова и минимальную длину слова.
Шаги реализации:
- Создание таблицы с FULLTEXT индексом:
CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
FULLTEXT(name, description)
) ENGINE=InnoDB;Search tags php tag (поиск по тегам в php)
Индекс создается по полям, в которых будет производиться поиск.
- Подготовка запроса PHP:
$pdo = new PDO('mysql:host=localhost;dbname=shop', 'user', 'pass');
$searchTerm = $_GET['q'] ?? '';
$query = "SELECT *, MATCH(name, description) AGAINST(:search IN BOOLEAN MODE) AS relevance
FROM products
WHERE MATCH(name, description) AGAINST(:search IN BOOLEAN MODE)
ORDER BY relevance DESC";
$stmt = $pdo->prepare($query);
$stmt->execute(['search' => $searchTerm]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);Search topic php (поиск по теме в php)
Режим BOOLEAN MODE позволяет использовать операторы +, -, *, "фраза" для уточнения запроса.
- Вывод результатов:
foreach ($results as $product) {
echo '' . htmlspecialchars($product['name']) . ' (релевантность: ' . $product['relevance'] . ')';
}Search type php id type (тип поиска по id в php)
Типичные проблемы:
- Минимальная длина слова по умолчанию 4 символа (для InnoDB). Короткие запросы могут быть проигнорированы. Решение: изменить параметр innodb_ft_min_token_size.
- Стоп-слова (предлоги, союзы) исключаются из поиска. Решение: отключить стоп-слова через параметр ft_stopword_file.
- Для больших таблиц скорость может снижаться. Рекомендуется использовать внешние движки (Elasticsearch, Sphinx).
Как сделать простой поиск по названию товара с помощью LIKE?
Простейший вариант - оператор LIKE с подстановочными символами %.
$query = "SELECT * FROM products WHERE name LIKE :search";
$stmt = $pdo->prepare($query);
$stmt->execute(['search' => '%' . $searchTerm . '%']);
$results = $stmt->fetchAll();Search php view (вид поиска в php)
Этот подход легко реализовать, но он не использует индексы (кроме как для префиксного поиска), медленный на больших объёмах и не ранжирует результаты.
Проблемы: полное сканирование таблицы при поиске в середине слова; уязвимость к SQL-инъекциям (обязательно использовать подготовленные запросы).
Как искать товары по сложным шаблонам с помощью REGEXP?
MySQL поддерживает регулярные выражения через REGEXP. Подходит для сложных масок, но неэффективен.
$query = "SELECT * FROM products WHERE name REGEXP :pattern";
$stmt = $pdo->prepare($query);
$stmt->execute(['pattern' => $searchTerm]); // например, '^[A-Z].*phone$'Catalog php search (поиск в каталоге php)
Примечание: REGEXP не использует индексы, скорость низкая.
Регулярные выражения могут быть дорогими; ошибки в шаблоне приводят к пустым результатам.
Как искать в нескольких столбцах одновременно?
Комбинация LIKE через OR:
$query = "SELECT * FROM products WHERE name LIKE :search OR description LIKE :search2";
$stmt = $pdo->prepare($query);
$stmt->execute(['search' => '%' . $searchTerm . '%', 'search2' => '%' . $searchTerm . '%']);Index php act search (действие поиска в php)
Проблемы: дублирование параметров, отсутствие сортировки по релевантности.
При большом количестве полей запрос становится громоздким; невозможно ранжировать результаты по значимости полей.
Как реализовать масштабируемый поиск с Elasticsearch?
Для больших каталогов (сотни тысяч товаров) рекомендуется Elasticsearch. Пример отправки запроса через HTTP:
$client = new Elasticsearch\Client(['hosts' => ['localhost:9200']]);
$params = [
'index' => 'products',
'body' => [
'query' => [
'match' => ['name' => $searchTerm]
]
]
];
$response = $client->search($params);
$results = array_column($response['hits']['hits'], '_source');Search php cid (поиск по cid в php)
Elasticsearch обеспечивает высокую скорость, fuzzy-поиск, автодополнение, фасетные фильтры.
Требует отдельного сервиса, более сложная настройка, дополнительное потребление памяти.
Как искать товары, хранящиеся в формате JSON?
В MySQL 5.7+ можно использовать функции JSON. Например, если товары хранятся в столбце JSON с полями 'title' и 'description':
$query = "SELECT * FROM products WHERE JSON_EXTRACT(data, '$.title') LIKE :search OR JSON_EXTRACT(data, '$.description') LIKE :search2";
Альтернатива: использовать FULLTEXT индекс на JSON столбец, приведя его к VARCHAR.
JSON функции медленнее, чем прямое обращение к столбцам. Индексирование JSON ограничено.
Расширенные примеры с нестандартными подходами.
Расширенные примеры
Пример взвешенного поиска с FULLTEXT
$query = "SELECT *,
MATCH(name) AGAINST(:search IN BOOLEAN MODE) * 10 +
MATCH(description) AGAINST(:search IN BOOLEAN MODE) AS relevance
FROM products
WHERE MATCH(name, description) AGAINST(:search IN BOOLEAN MODE)
ORDER BY relevance DESC";
Результат: товары с наибольшим весом названия будут выше.
Поиск с пагинацией и сохранением общего количества
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$perPage = 10;
$offset = ($page - 1) * $perPage;
$searchTerm = $_GET['q'] ?? '';
$countQuery = "SELECT COUNT(*) FROM products WHERE MATCH(name, description) AGAINST(:search IN BOOLEAN MODE)";
$countStmt = $pdo->prepare($countQuery);
$countStmt->execute(['search' => $searchTerm]);
$total = $countStmt->fetchColumn();
$dataQuery = "SELECT * FROM products
WHERE MATCH(name, description) AGAINST(:search IN BOOLEAN MODE)
ORDER BY MATCH(name, description) AGAINST(:search IN BOOLEAN MODE) DESC
LIMIT :limit OFFSET :offset";
$stmt = $pdo->prepare($dataQuery);
$stmt->bindValue(':search', $searchTerm, PDO::PARAM_STR);
$stmt->bindValue(':limit', $perPage, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$items = $stmt->fetchAll();
Результат: массив пагинированных товаров и переменная $total для постраничной навигации.
Поиск с автокоррекцией опечаток (левенштейн)
function similarWords($word, $dictionary, $maxDistance = 2) {
$matches = [];
foreach ($dictionary as $dictWord) {
$dist = levenshtein($word, $dictWord);
if ($dist <= $maxDistance) {
$matches[$dictWord] = $dist;
}
}
asort($matches);
return array_keys($matches);
}
$searchTerm = 'iphon'; // опечатка
$allNames = $pdo->query("SELECT name FROM products")->fetchAll(PDO::FETCH_COLUMN);
$suggestions = similarWords($searchTerm, $allNames);
if (!empty($suggestions)) {
$terms = array_slice($suggestions, 0, 3);
$placeholders = implode(',', array_fill(0, count($terms), '?'));
$stmt = $pdo->prepare("SELECT * FROM products WHERE name IN ($placeholders)");
$stmt->execute($terms);
$results = $stmt->fetchAll();
}
Поиск найдет товары с названием, близким к 'iphon' (например, 'iPhone').
Поиск с использованием SphinxQL
$pdo = new PDO('mysql:host=127.0.0.1;port=9306;charset=utf8', '', '');
$query = "SELECT id, weight() FROM products_index WHERE MATCH(:search) LIMIT 20";
$stmt = $pdo->prepare($query);
$stmt->execute(['search' => $searchTerm]);
$ids = $stmt->fetchAll(PDO::FETCH_COLUMN, 0);
// затем загрузить данные из основной MySQL по id
Возвращает идентификаторы товаров с весом релевантности.
Поиск с фильтрацией по категории
$categoryId = (int)$_GET['cat'];
$searchTerm = $_GET['q'];
$query = "SELECT * FROM products
WHERE category_id = :category
AND MATCH(name, description) AGAINST(:search IN BOOLEAN MODE)
ORDER BY MATCH(name, description) AGAINST(:search IN BOOLEAN MODE) DESC";
$stmt = $pdo->prepare($query);
$stmt->execute(['category' => $categoryId, 'search' => $searchTerm]);
Товары, соответствующие категории и тексту запроса.