Пошук
- Вступ
- Повнотекстовий пошук
- Семантичний / Векторний пошук
- Пересортування результатів
- Laravel Scout
- Комбінування технік
Вступ
Майже кожен застосунок потребує пошуку. Чи ваші користувачі шукають у базі знань відповідні статті, переглядають каталог продуктів, чи ставлять питання природною мовою до корпусу документів, Laravel надає вбудовані інструменти для обробки кожного з цих сценаріїв — і часто вам не потрібні жодні зовнішні сервіси для цього.
Більшість застосунків виявлять, що вбудовані опції на основі бази даних, які надає Laravel, більш ніж достатні — зовнішні пошукові сервіси потрібні лише тоді, коли вам потрібні такі функції, як толерантність до помилок, фасетна фільтрація або геопошук у великому масштабі.
Повнотекстовий пошук
Коли вам потрібне ранжування за релевантністю ключових слів — коли база даних оцінює та сортує результати на основі того, наскільки добре вони відповідають пошуковим термінам — метод конструктора запитів Laravel whereFullText використовує вбудовані повнотекстові індекси в MariaDB, MySQL та PostgreSQL. Повнотекстовий пошук розуміє межі слів і стемінг, тому пошук за "running" може знаходити записи, що містять "run". Жоден зовнішній сервіс не потрібен.
Семантичний / Векторний пошук
Для семантичного пошуку на основі ШІ, який підбирає результати за значенням, а не за точними ключовими словами, метод конструктора запитів whereVectorSimilarTo використовує векторні вбудовування, збережені в PostgreSQL з розширенням pgvector. Наприклад, пошук за запитом "кращі виноробні в Долині Напа" може знайти статтю з назвою "Топ виноградників для відвідування" — навіть якщо слова не збігаються. Векторний пошук вимагає PostgreSQL з розширенням pgvector та Laravel AI SDK.
Пересортування
Laravel AI SDK надає можливості повторного ранжування, які використовують AI-моделі для перестановки будь-якого набору результатів за семантичною релевантністю до запиту. Повторне ранжування особливо ефективне як другий етап після швидкого початкового отримання результатів, наприклад, повнотекстового пошуку — забезпечуючи як швидкість, так і семантичну точність.
Пошук Laravel Scout
Для застосунків, які потребують трейту Searchable, що автоматично підтримує синхронізацію пошукових індексів з моделями Eloquent, Laravel Scout пропонує як вбудований рушій бази даних, так і драйвери для сторонніх сервісів, таких як Algolia, Meilisearch та Typesense.
Повнотекстовий пошук
Хоча запити LIKE добре підходять для простого пошуку підрядків, вони не розуміють мову. Пошук LIKE за "running" не знайде запис, що містить "run", а результати не впорядковуються за релевантністю — вони просто повертаються в тому порядку, в якому база даних їх знаходить. Повнотекстовий пошук вирішує обидві ці проблеми, використовуючи спеціалізовані індекси, які розуміють межі слів, стемінг і оцінку релевантності, дозволяючи базі даних повертати найрелевантніші результати першими.
Швидкий повнотекстовий пошук вбудований у MariaDB, MySQL та PostgreSQL — зовнішній сервіс пошуку не потрібен. Вам потрібно лише додати повнотекстовий індекс до стовпців, у яких ви хочете здійснювати пошук, а потім використовувати метод конструктора запитів whereFullText для пошуку по них.
Повнотекстовий пошук наразі підтримується MariaDB, MySQL та PostgreSQL.
Додавання повнотекстових індексів
Щоб використовувати повнотекстовий пошук, спочатку додайте повнотекстовий індекс до стовпців, у яких ви хочете здійснювати пошук. Ви можете додати індекс до одного стовпця або передати масив стовпців, щоб створити складений індекс, який дозволяє шукати одночасно по декількох полях:
Schema::create('articles', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('body');
$table->timestamps();
$table->fullText(['title', 'body']);
});
У PostgreSQL ви можете вказати мовну конфігурацію для індексу, яка визначає, як слова будуть стемовані:
$table->fullText('body')->language('english');
Для отримання додаткової інформації про створення індексів зверніться до документації з міграцій.
Виконання повнотекстових запитів
Після створення індексу використовуйте метод конструктора запитів whereFullText для пошуку по ньому. Laravel згенерує відповідний SQL для вашого драйвера бази даних — наприклад, MATCH(...) AGAINST(...) для MariaDB та MySQL, а також to_tsvector(...) @@ plainto_tsquery(...) для PostgreSQL:
$articles = Article::whereFullText('body', 'web developer')->get();
Під час використання MariaDB та MySQL результати автоматично впорядковуються за релевантністю. У PostgreSQL whereFullText фільтрує відповідні записи, але не впорядковує їх за релевантністю — якщо вам потрібне автоматичне впорядкування за релевантністю у PostgreSQL, розгляньте можливість використання базового рушія Scout, який забезпечує це для вас.
Якщо ви створили складений повнотекстовий індекс для кількох стовпців, ви можете виконати пошук по всіх них, передавши той самий масив стовпців у whereFullText:
$articles = Article::whereFullText(
['title', 'body'], 'web developer'
)->get();
Метод orWhereFullText може бути використаний для додавання виразу повнотекстового пошуку як умови "or". Для повної інформації зверніться до документації по конструктору запитів.
Семантичний / Векторний пошук
Повнотекстовий пошук ґрунтується на співставленні ключових слів — слова у запиті мають з'являтися (у певній формі) у даних. Семантичний пошук використовує принципово інший підхід: він застосовує AI-генеровані векторні вбудовування для представлення значення тексту у вигляді масивів чисел, а потім знаходить результати, значення яких найбільш схожі до запиту. Наприклад, пошук за запитом "кращі виноробні в Долині Напа" може знайти статтю з назвою "Топ виноградників для відвідування" — навіть якщо слова зовсім не збігаються.
Основний робочий процес для векторного пошуку такий: згенерувати embedding (числовий масив) для кожного фрагмента контенту та зберегти його разом із вашими даними, потім під час пошуку згенерувати embedding для запиту користувача і знайти збережені embedding, які знаходяться найближче до нього у векторному просторі.
Векторний пошук вимагає базу даних PostgreSQL з розширенням pgvector та Laravel AI SDK. Усі серверлес-бази даних Postgres від Laravel Cloud вже містять pgvector.
Генерування векторних представлень
Вбудовування — це високовимірний числовий масив (зазвичай сотні або тисячі чисел), який представляє семантичне значення фрагмента тексту. Ви можете згенерувати вбудовування для рядка за допомогою методу toEmbeddings, доступного в класі Laravel Stringable:
use Illuminate\Support\Str;
$embedding = Str::of('Napa Valley has great wine.')->toEmbeddings();
Щоб згенерувати векторні представлення для кількох вхідних даних одночасно — що є ефективнішим, ніж генерувати їх по одному, оскільки це вимагає лише одного виклику API до провайдера векторних представлень — використовуйте клас Embeddings:
use Laravel\Ai\Embeddings;
$response = Embeddings::for([
'Napa Valley has great wine.',
'Laravel is a PHP framework.',
])->generate();
$response->embeddings; // [[0.123, 0.456, ...], [0.789, 0.012, ...]]
Для отримання додаткової інформації про налаштування embedding-провайдерів, налаштування розмірів і кешування зверніться до документації AI SDK.
Зберігання та індексування векторів
Щоб зберігати векторні вбудовування, визначте стовпець vector у вашій міграції, вказавши кількість вимірів, яка відповідає вихідним даним вашого провайдера вбудовувань (наприклад, 1536 для моделі OpenAI text-embedding-3-small). Також слід викликати index для цього стовпця, щоб створити індекс HNSW (Hierarchical Navigable Small World), який значно пришвидшує пошук за схожістю у великих наборах даних:
Schema::ensureVectorExtensionExists();
Schema::create('documents', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->vector('embedding', dimensions: 1536)->index();
$table->timestamps();
});
Метод Schema::ensureVectorExtensionExists гарантує, що розширення pgvector увімкнено у вашій базі даних PostgreSQL перед створенням таблиці.
У вашій Eloquent моделі приведіть стовпець vector до типу array, щоб Laravel автоматично обробляв перетворення між масивами PHP та векторним форматом бази даних:
protected function casts(): array
{
return [
'embedding' => 'array',
];
}
Для детальнішої інформації про векторні стовпці та індекси зверніться до документації з міграцій.
Запити за схожістю
Після того як ви зберегли вектори для вашого контенту, ви можете шукати схожі записи за допомогою методу whereVectorSimilarTo. Цей метод порівнює заданий вектор збереженими векторами за допомогою косинусної схожості, відфільтровує результати, що нижче порогу minSimilarity, і автоматично впорядковує результати за релевантністю — з найсхожішими записами першими. Поріг повинен бути значенням між 0.0 та 1.0, де 1.0 означає, що вектори ідентичні:
$documents = Document::query()
->whereVectorSimilarTo('embedding', $queryEmbedding, minSimilarity: 0.4)
->limit(10)
->get();
Для зручності, коли замість масиву embedding передається звичайний рядок, Laravel автоматично згенерує embedding за допомогою налаштованого провайдера embedding. Це означає, що ви можете передати пошуковий запит користувача без необхідності попередньо вручну конвертувати його в embedding:
$documents = Document::query()
->whereVectorSimilarTo('embedding', 'best wineries in Napa Valley')
->limit(10)
->get();
Для більш низькорівневого контролю над векторними запитами також доступні методи whereVectorDistanceLessThan, selectVectorDistance та orderByVectorDistance. Ці методи дозволяють працювати безпосередньо зі значеннями відстані замість оцінок схожості, вибирати обчислену відстань як стовпець у ваших результатах або вручну контролювати порядок сортування. Для повної інформації зверніться до документації по конструктору запитів та документації AI SDK.
Пересортування результатів
Переранжування — це техніка, при якій модель ШІ переупорядковує набір результатів відповідно до того, наскільки семантично релевантний кожен результат заданому запиту. На відміну від векторного пошуку, який вимагає попереднього обчислення та зберігання ембеддінгів, переранжування працює з будь-якою колекцією тексту — воно приймає як вхідні дані необроблений вміст і запит та повертає елементи, відсортовані за релевантністю.
Переранжування є особливо потужним як другий етап після швидкого початкового пошуку. Наприклад, ви можете використати повнотекстовий пошук, щоб швидко звузити тисячі записів до топ-50 кандидатів, а потім застосувати переранжування, щоб найрелевантніші результати були на початку списку. Такий підхід "спочатку знайти, потім переранжувати" забезпечує і швидкість, і семантичну точність.
Ви можете перевпорядкувати масив рядків за допомогою класу Reranking:
use Laravel\Ai\Reranking;
$response = Reranking::of([
'Django is a Python web framework.',
'Laravel is a PHP web application framework.',
'React is a JavaScript library for building user interfaces.',
])->rerank('PHP frameworks');
$response->first()->document; // "Laravel is a PHP web application framework."
Колекції Laravel також мають макрос rerank, який приймає назву поля (або closure) та запит, що дозволяє легко змінювати порядок результатів Eloquent:
$articles = Article::all()
->rerank('body', 'Laravel tutorials');
Для повної інформації про налаштування провайдерів переупорядкування та доступні параметри зверніться до документації AI SDK.
Laravel Scout
Описані вище техніки пошуку є методами конструктора запитів, які ви викликаєте безпосередньо у своєму коді. Laravel Scout використовує інший підхід: він надає трейд Searchable, який ви додаєте до своїх Eloquent-моделей, і Scout автоматично підтримує ваші пошукові індекси в актуальному стані під час створення, оновлення та видалення записів. Це особливо зручно, коли ви хочете, щоб ваші моделі завжди були доступні для пошуку без ручного керування оновленнями індексу.
Двигун бази даних
Вбудований рушій бази даних Scout виконує повнотекстовий та LIKE пошук у вашій існуючій базі даних — жодних зовнішніх сервісів або додаткової інфраструктури не потрібно. Просто додайте трейт Searchable до вашої моделі та визначте метод toSearchableArray, який повертає стовпці, які ви хочете зробити доступними для пошуку.
Ви можете використовувати атрибути PHP для керування стратегією пошуку для кожного стовпця. SearchUsingFullText використовуватиме повнотекстовий індекс вашої бази даних, SearchUsingPrefix буде знаходити лише ті значення, які починаються з вказаного рядка (example%), а для стовпців без атрибутів використовується стандартна стратегія LIKE з підстановочними знаками з обох боків (%example%):
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Attributes\SearchUsingFullText;
use Laravel\Scout\Attributes\SearchUsingPrefix;
use Laravel\Scout\Searchable;
class Article extends Model
{
use Searchable;
#[SearchUsingPrefix(['id'])]
#[SearchUsingFullText(['title', 'body'])]
public function toSearchableArray(): array
{
return [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body,
];
}
}
Перш ніж вказати, що стовпець повинен використовувати обмеження повнотекстового запиту, переконайтеся, що цьому стовпцю призначено повнотекстовий індекс.
Після додавання трейта ви можете здійснювати пошук у вашій моделі за допомогою методу search Scout. Двигун бази даних Scout автоматично впорядкує результати за релевантністю, навіть у PostgreSQL:
$articles = Article::search('Laravel')->get();
Двигун бази даних є чудовим вибором, коли ваші потреби у пошуку помірні і ви бажаєте скористатися зручністю автоматичної синхронізації індексу Scout без розгортання зовнішнього сервісу. Він добре справляється з найпоширенішими випадками використання пошуку, включаючи фільтрацію, пагінацію та обробку м'яко видалених записів. Для повної інформації зверніться до документації Scout.
Сторонні рушії
Scout також підтримує сторонні пошукові системи, такі як Algolia, Meilisearch та Typesense. Ці спеціалізовані пошукові сервіси пропонують розширені можливості, такі як толерантність до помилок, фасетна фільтрація, геопошук та власні правила ранжування — функції, які стають важливими при дуже великому масштабі або коли вам потрібен високоякісний пошук у режимі "пошук під час введення".
Оскільки Scout надає уніфікований API для всіх своїх драйверів, перехід від рушія бази даних до стороннього рушія пізніше вимагає мінімальних змін у коді. Ви можете почати з рушія бази даних і перейти до стороннього сервісу лише у випадку, якщо потреби вашого застосунку перевищують можливості бази даних.
Для отримання повної інформації про налаштування сторонніх рушіїв зверніться до документації Scout.
Багатьом застосункам ніколи не потрібен зовнішній пошуковий рушій. Вбудовані методи, описані на цій сторінці, охоплюють переважну більшість випадків використання.
Поєднання технік
Описані на цій сторінці методи пошуку не є взаємовиключними — їх поєднання часто дає найкращі результати. Ось два поширені шаблони, які демонструють, як ці інструменти працюють разом.
Повнотекстовий пошук + повторне ранжування
Використовуйте повнотекстовий пошук, щоб швидко звузити великий набір даних до набору кандидатів, а потім застосуйте повторне ранжування для сортування цих кандидатів за семантичною релевантністю. Це забезпечує швидкість повнотекстового пошуку на рівні бази даних з точністю оцінювання релевантності на основі ШІ.
$articles = Article::query()
->whereFullText('body', $request->input('query'))
->limit(50)
->get()
->rerank('body', $request->input('query'), limit: 10);
Векторний пошук + традиційні фільтри
Поєднайте пошук за схожістю векторів зі стандартними виразами where, щоб обмежити семантичний пошук підмножиною записів. Це корисно, коли ви хочете здійснювати пошук за змістом, але потрібно обмежити результати за власником, категорією або будь-якою іншою ознакою:
$documents = Document::query()
->where('team_id', $user->team_id)
->whereVectorSimilarTo('embedding', $request->input('query'))
->limit(10)
->get();
