---
title: "Простий векторний пошук подібності для Laravel: драйвер Pgvector для Scout"
url: https://laravelukraine.com/blog/prostii-vektornii-posuk-za-sxozistiu-dlia-laravel
date: 2026-10-06
source: https://benbjurstrom.com/pgvector-for-laravel-scout
---

# Простий векторний пошук подібності для Laravel: драйвер Pgvector для Scout

Розробник Бен Бьюрстром (Ben Bjurstrom) у [статті на своєму блозі](https://benbjurstrom.com/pgvector-for-laravel-scout) розповів про драйвер Pgvector для Laravel Scout. Він спрощує пошук за векторними ембедингами та їх підтримку в актуальному стані. Scout добре поєднується з pgvector, оскільки його model observers автоматично синхронізують ембединги зі змінами в даних. Репозиторій пакета: [benbjurstrom/pgvector-scout](https://github.com/benbjurstrom/pgvector-scout).

## Навіщо потрібен пошук за поняттям

Автор ставить питання: що, якщо шукати дані за концепцією, а не за ключовим словом, наприклад за абстрактною категорією? Для демонстрації він використовує набір даних [Amazon Fine Food Reviews](https://www.kaggle.com/datasets/snap/amazon-fine-food-reviews): перші 500 відгуків завантажено в таблицю демонстраційного застосунку.

Запит за ключовим словом на кшталт «pancakes» виконати нескладно, можна навіть додати нечіткий пошук, щоб знаходилась і форма однини «pancake». Але як знайти всі відгуки про сніданкову їжу: млинці, вафлі, яйця, вівсянку тощо? Людина розуміє, що всі ці страви належать до сніданків, а от як змусити пошук це «зрозуміти»?

## Векторні ембединги

Відповідь - векторні ембединги. Це числові представлення даних, які передають семантичний зміст. За допомогою великої мовної моделі фрагменти тексту відображаються у багатовимірний простір, де схожі за змістом поняття групуються поруч.

У прикладі зі сніданками відгуки про різні сніданкові страви опиняться приблизно в одній ділянці цього простору. Тоді можна обчислити відстань між вектором загального пошукового терміна, наприклад «Breakfast», і векторами відгуків. Відгуки з найменшою відстанню, імовірно, стосуються сніданків (за умови, що такі є в наборі даних).

### А як щодо великих документів?

Відгуки - це короткі тексти. Якщо ж йдеться про довші матеріали (дописи блогу, документацію, PDF), не можна просто віддати весь документ в один ембединг і чекати хороших результатів. Спершу його потрібно розбити на семантичні частини. Для цього автор радить свою статтю про [PHP Markdown Chunker](https://benbjurstrom.com/php-markdown-chunker), де описано розумне розбиття документів зі збереженням структури та змісту.

Раніше для зберігання ембедингів і обчислення відстаней між векторами потрібна була окрема векторна база даних. Проте розширення [Pgvector](https://github.com/pgvector/pgvector) для PostgreSQL дає змогу зберігати ембединги та виконувати пошук за подібністю в тій самій реляційній базі, де вже лежать ваші дані.

## Pgvector і Laravel Scout: ідеальна пара

У Laravel-застосунку потрібно вирішити кілька завдань: перетворити текст відгуків на ембединги через API (наприклад, OpenAI), зробити те саме з пошуковим терміном «Breakfast» і підтримувати синхронізацію ембедингів, коли відгуки редагуються, оновлюються чи видаляються.

[Laravel Scout](https://laravel.com/docs/11.x/scout) - офіційна бібліотека на основі драйверів, що додає пошук до Eloquent-моделей. Вона містить model observers, які автоматично синхронізують дані з пошуковим індексом під час створення, оновлення чи видалення моделі. Драйвер автора використовує ці observers, тож векторні ембединги завжди відповідають актуальному стану даних.

## Демонстраційний застосунок

Щоб показати підхід на практиці, автор підготував демо на основі згаданого датасету. Репозиторій демо: [benbjurstrom/pgvector-scout-demo](https://github.com/benbjurstrom/pgvector-scout-demo).

### Налаштування бази даних і моделі

Застосунок містить міграцію для таблиці reviews і сідери з першими 500 відгуками Amazon Fine Food та відповідними ембедингами, отриманими з моделі OpenAI `text-embedding-3-small`.

Також є Eloquent-модель для таблиці reviews із двома необхідними трейтами: `HasEmbeddings` і `Searchable`.

```php
// App\Models\Review

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
use BenBjurstrom\PgvectorScout\Models\Concerns\HasEmbeddings;

class Review extends Model
{
    use HasEmbeddings, Searchable;
```

### Пошук в індексі

На цьому все налаштування завершено. Контролер індексу приймає запит і передає його в метод `search` моделі через Laravel Scout. Запит автоматично перетворюється на вектор за допомогою OpenAI, після чого в базі виконується пошук за подібністю. 50 відгуків, найближчих до пошукового терміна у векторному просторі, передаються у Blade-представлення.

```php
// App\Http\Controllers\ReviewController

public function index(Request $request)
{
    $query = $request->query('query');

    $reviews = Review::search($query)
        ->paginate(50)
        ->withQueryString();

    return view('reviews.index', [
        'reviews' => $reviews,
        'query' => $query
    ]);
}
```

Результати запиту «Breakfast» показано на скриншоті в оригінальній статті. Права колонка тепер відображає відстань між вектором пошукового терміна та ембедингом відгуку.

### Пошук пов'язаних відгуків

Демо також показує, як виводити пов'язані відгуки на сторінці деталей відгуку. Для цього в методі `show` контролера `ReviewController` у метод `search` моделі передається вектор поточного відгуку. Після цього вибирається шість найближчих результатів, які передаються у Blade-представлення.

```php
// App\Http\Controllers\ReviewController

public function show(string $id)
{
    $review = Review::findOrFail($id);

    $relatedReviews = Review::search($review->embedding->vector)
        ->take(6)
        ->get();

    return view('reviews.show', compact('review', 'relatedReviews'));
}
```

У статті наведено приклад шести відгуків, пов'язаних із відгуком про корм для собак.
