Организация поискового функционала товаров на PHP

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

В статье рассмотрены различные способы реализации поиска товаров на PHP. Цель - дать практические примеры и пояснения к каждому шагу, а также указать на типичные проблемы и способы их решения.

Основной эффективный метод: полнотекстовый поиск MySQL

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

Шаги реализации:

  1. Создание таблицы с 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)

Индекс создается по полям, в которых будет производиться поиск.

  1. Подготовка запроса 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 позволяет использовать операторы +, -, *, "фраза" для уточнения запроса.

  1. Вывод результатов:
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 ограничено.

- Search index php subaction (поддействие поиска в php)
- Php search form (форма поиска в php)
- Results php search (результаты поиска в php)

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

Расширенные примеры

Пример взвешенного поиска с 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]);
Товары, соответствующие категории и тексту запроса.

PHP скрипт для поиска товаров - comments

En
Search products php (php)