Поиск на PHP: эффективные стратегии и код
Методы реализации поиска на PHP
Как найти записи по части слова с помощью SQL оператора LIKE?
Цель: быстрый старт без дополнительных настроек базы данных. Подходит для малых объёмов данных и простых запросов.
$search = '%' . $query . '%';
$stmt = $pdo->prepare('SELECT * FROM articles WHERE title LIKE :search OR content LIKE :search');
$stmt->execute(['search' => $search]);
$results = $stmt->fetchAll();
Search php (реализация поиска на php)
Проблемы: оператор LIKE не использует индексы при подстановочном знаке в начале, что замедляет поиск на больших таблицах. Также нет учёта морфологии и релевантности.
Как задать сложный шаблон поиска через регулярные выражения в MySQL?
Цель: точный поиск с гибкими шаблонами, например, поиск слов с определённым окончанием или исключение символов.
$pattern = '[[:<:]]' . preg_quote($query, '/') . '[[:>:]]'; // целое слово
$stmt = $pdo->prepare("SELECT * FROM articles WHERE title REGEXP :pattern");
$stmt->execute(['pattern' => $pattern]);
Results php (результаты php)
Ошибки: REGEXP не использует индексы, производительность падает с ростом таблицы. Некорректное экранирование специальных символов в пользовательском запросе.
Как организовать быстрый поиск по тексту с учётом морфологии и релевантности?
Цель: эффективный поиск в текстовых полях с ранжированием результатов, поддержка стоп-слов и минимальной длины слова. Используется полнотекстовый индекс MySQL (FULLTEXT).
Для работы необходим индекс FULLTEXT на столбцах, по которым ведётся поиск. Индекс может быть создан как на таблицах MyISAM, так и InnoDB (начиная с MySQL 5.6).
-- Создание индекса FULLTEXT
ALTER TABLE articles ADD FULLTEXT INDEX ft_search (title, content);
// Поиск в естественном режиме
$query = 'PHP поиск';
$stmt = $pdo->prepare("SELECT *, MATCH(title, content) AGAINST(:query) AS relevance FROM articles WHERE MATCH(title, content) AGAINST(:query) ORDER BY relevance DESC");
$stmt->execute(['query' => $query]);
Типичные ошибки:
- Отсутствие индекса FULLTEXT приводит к ошибке SQL.
- Стоп-слова (например, "и", "в", "на") игнорируются по умолчанию.
- Минимальная длина слова (по умолчанию 4 символа для InnoDB) может отсекать короткие запросы. Изменяется через параметры ft_min_word_len (MyISAM) или innodb_ft_min_token_size (InnoDB).
- Булев режим (BOOLEAN MODE) требует экранирования специальных символов (+, -, *, ~).
Как обрабатывать большие объёмы данных с высокой скоростью и настраиваемой логикой поиска?
Цель: полноценная поисковая система с кластеризацией, фасетами, исправлением опечаток и многопоточной индексацией. Используются специализированные инструменты: Elasticsearch, Sphinx, Apache Solr.
Пример интеграции с Elasticsearch через HTTP API (без библиотек).
// Индексация документа
$data = [
'title' => 'Пример реализации поиска',
'content' => 'Текст статьи о PHP и Elasticsearch'
];
$json = json_encode($data);
$ch = curl_init('http://localhost:9200/articles/_doc');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
// Поиск
$searchQuery = ['query' => ['match' => ['content' => 'PHP']]];
$json = json_encode($searchQuery);
$ch = curl_init('http://localhost:9200/articles/_search');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true);
Сложности: требуется установка и настройка отдельного сервера (Elasticsearch, Sphinx). Необходимо поддерживать синхронизацию индекса с базой данных. Повышенные требования к памяти и диску.
Расширенные примеры реализации
Ниже приведены подробные примеры кода с пояснением каждого шага и выводом результатов.
Пример 1: Полнотекстовый поиск в MySQL с булевым режимом и настройкой минимальной длины слова
Для демонстрации создана таблица articles с индексом FULLTEXT и вставлены несколько записей.
-- Настройка минимальной длины токена (InnoDB) перед созданием индекса
SET GLOBAL innodb_ft_min_token_size = 2;
-- Создание таблицы и индекса
CREATE TABLE articles (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255),
content TEXT,
FULLTEXT ft_index (title, content)
) ENGINE=InnoDB;
-- Вставка данных
INSERT INTO articles (title, content) VALUES
('Основы PHP', 'PHP это скриптовый язык для веб-разработки.'),
('Поиск данных в MySQL', 'MySQL поддерживает полнотекстовый поиск с 5.6.'),
('Elasticsearch для новичков', 'Elasticsearch распределенная поисковая система.');
Запрос в булевом режиме с принудительным включением слова (+), исключением (-) и подстановкой (*).
$query = '+PHP -Elasticsearch *'; // слово 'PHP' обязательно, 'Elasticsearch' исключено, любое слово по маске
$stmt = $pdo->prepare("SELECT *, MATCH(title, content) AGAINST(:query IN BOOLEAN MODE) AS relevance FROM articles WHERE MATCH(title, content) AGAINST(:query IN BOOLEAN MODE) ORDER BY relevance DESC");
$stmt->execute(['query' => $query]);
$results = $stmt->fetchAll();
print_r($results);
Array
(
[0] => Array
(
[id] => 1
[title] => Основы PHP
[content] => PHP это скриптовый язык для веб-разработки.
[relevance] => 1.234567
)
)
Обратите внимание: стоп-слово это не участвует в поиске, слово для тоже игнорируется. Если нужно изменить список стоп-слов, используется системная таблица INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD.
Пример 2: Поиск с использованием библиотеки Elasticsearch-PHP (официальная)
Установка через Composer: composer require elasticsearch/elasticsearch.
require 'vendor/autoload.php';
use Elastic\Elasticsearch\ClientBuilder;
$client = ClientBuilder::create()->build();
// Параметры индекса (создание, если не существует)
$params = [
'index' => 'articles',
'body' => [
'settings' => [
'number_of_shards' => 1,
'number_of_replicas' => 0
],
'mappings' => [
'properties' => [
'title' => ['type' => 'text'],
'content' => ['type' => 'text']
]
]
]
];
$client->indices()->create($params);
// Индексация двух документов
$documents = [
['title' => 'PHP фреймворки', 'content' => 'Laravel, Symfony, Yii'],
['title' => 'Поиск в PHP', 'content' => 'Реализация поиска через Elasticsearch']
];
foreach ($documents as $doc) {
$client->index([
'index' => 'articles',
'body' => $doc
]);
}
$client->indices()->refresh(['index' => 'articles']);
// Поиск с выделением фрагментов (highlighting)
$searchResult = $client->search([
'index' => 'articles',
'body' => [
'query' => [
'multi_match' => [
'query' => 'PHP',
'fields' => ['title', 'content']
]
],
'highlight' => [
'fields' => [
'title' => ['number_of_fragments' => 1],
'content' => ['number_of_fragments' => 2]
]
]
]
]);
// Вывод результатов
echo '';
foreach ($searchResult['hits']['hits'] as $hit) {
echo "ID: {$hit['_id']}\n";
echo "Score: {$hit['_score']}\n";
echo "Title: {$hit['_source']['title']}\n";
if (isset($hit['highlight'])) {
echo "Highlight: ";
print_r($hit['highlight']);
}
echo "\n---\n";
}
echo '';
ID: 1
Score: 0.2876821
Title: PHP фреймворки
Highlight: Array
(
[title] => Array
(
[0] => PHP фреймворки
)
[content] => Array
(
[0] => Laravel, Symfony, Yii
)
)
---
ID: 2
Score: 0.2876821
Title: Поиск в PHP
Highlight: Array
(
[title] => Array
(
[0] => Поиск в PHP
)
[content] => Array
(
[0] => Реализация поиска через Elasticsearch
)
)
Пример 3: Поиск с использованием Sphinx через PDO-подобный API (sphinxapi)
Необходимо скачать sphinxapi.php из дистрибутива Sphinx и подключить.
require 'sphinxapi.php';
$sphinx = new SphinxClient();
$sphinx->setServer('localhost', 9312);
$sphinx->setMatchMode(SPH_MATCH_EXTENDED2);
$sphinx->setSortMode(SPH_SORT_RELEVANCE);
// Поиск с морфологией (морфологический словарь должен быть настроен)
$sphinx->setLimits(0, 10);
$result = $sphinx->query('@(title,content) PHP поиск', 'articles_index');
if ($result === false) {
echo "Ошибка: {$sphinx->GetLastError()}\n";
} else {
echo "Найдено совпадений: {$result['total']}\n";
foreach ($result['matches'] as $id => $match) {
echo "ID: $id, Вес: {$match['weight']}\n";
}
}
Найдено совпадений: 2 ID: 1, Вес: 1523 ID: 2, Вес: 1487
Типичные проблемы: для корректной работы Sphinx требуется настроить источник данных (часто XML или SQL), а также демон searchd. Ошибки соединения или отсутствие индекса приводят к пустому результату или исключению.