Laravel Scout: الدليل الشامل لبناء البحث في Laravel من Full-Text Search إلى Semantic Search

لديك متجر أو منصة محتوى مبنية بـ Laravel، ويبحث المستخدم عن كلمة مثل "Laravel queues"، لكن تنفيذ استعلامات LIKE '%query%' على عدة أعمدة بدأ يصبح بطيئًا، والنتائج لا تُرتب بحسب الصلة، وإضافة الفلاتر أو البحث الدلالي أصبحت أكثر تعقيدًا مع نمو البيانات. يمكنك ربط تطبيقك مباشرة بمحرك بحث خارجي وكتابة طبقة مزامنة كاملة بنفسك، لكنك ستحتاج إلى إدارة إنشاء الفهارس وتحديثها وحذف السجلات ومزامنة Eloquent Models مع محرك البحث. هنا يأتي دور Laravel Scout.

Laravel Scout هو الحزمة الرسمية التي توفر طبقة بحث موحدة لتطبيقات Laravel. تضيف Trait بسيطًا إلى Eloquent Model، تحدد البيانات القابلة للبحث، ثم تستخدم API قريبة من أسلوب Laravel المعتاد لتنفيذ البحث سواء عبر قاعدة البيانات مباشرة أو عبر محركات مثل Algolia وMeilisearch وTypesense وTurbopuffer.

والأهم في Laravel 13 أن Scout لم يعد مقتصرًا على البحث النصي التقليدي؛ يدعم التوثيق الرسمي أيضًا Semantic Search وHybrid Search عبر مجموعة من المحركات، بما في ذلك Database Engine عند استخدام PostgreSQL مع pgvector.

يعتمد هذا الدليل على توثيق Laravel Scout 13.x الرسمي فقط، مع الرجوع إلى توثيق Laravel الرسمي للـ Queues وLaravel AI SDK عند الحاجة.

ما هو Laravel Scout؟

Laravel Scout هو طبقة تكامل رسمية بين Eloquent ومحركات البحث. وظيفته الأساسية ليست أن يصبح Search Engine بحد ذاته، بل أن يجعل Model في Laravel قابلًا للفهرسة والبحث باستخدام واجهة موحدة.

عند إضافة Laravel\Scout\Searchable إلى Model، يسجل Scout Model Observer يتابع إنشاء السجلات وتعديلها وحذفها، ثم يزامن التغييرات مع محرك البحث الذي اخترته.

Scout هو Search Abstraction Layer: أنت تتعامل مع API واحدة داخل Laravel، بينما يمكن تغيير المحرك الموجود خلفها حسب احتياجات المشروع.

هذه الطبقة مفيدة جدًا لأنها تفصل منطق التطبيق عن تفاصيل كل Search Provider. يمكن أن تبدأ باستخدام Database Engine دون بنية تحتية إضافية، ثم تنتقل لاحقًا إلى Meilisearch أو Typesense أو Algolia دون إعادة كتابة كل منطق البحث في Controllers.

كيف يعمل Laravel Scout؟

يمكن تبسيط دورة العمل إلى أربع مراحل:

  1. تحدد Model الذي تريد جعله قابلًا للبحث.
  2. تحدد البيانات التي يجب إرسالها إلى Search Engine.
  3. يقوم Scout بمزامنة إنشاء وتحديث وحذف السجلات.
  4. تستخدم Model::search() للحصول على النتائج.
Database / Eloquent Model
        |
        v
Laravel Scout
        |
        +-- Database Engine
        +-- Algolia
        +-- Meilisearch
        +-- Typesense
        +-- Turbopuffer
        +-- Custom Engine
        |
        v
Search Results
        |
        v
Eloquent Models

مع المحركات الخارجية، يحصل Scout على معرفات النتائج من Search Engine ثم يستخدم Eloquent لاسترجاع Models المطابقة من قاعدة البيانات. هذا التفصيل مهم عند استخدام العلاقات أو Global Scopes أو محاولة إضافة شروط Eloquent بعد البحث.

محركات البحث التي يدعمها Laravel Scout

المحركبنية إضافيةالفهرسة الخارجيةSemantic Searchالاستخدام المناسب
Databaseلالانعم مع PostgreSQL وpgvectorمشاريع تريد بحثًا قويًا دون خدمة خارجية
CollectionلالالاTests وPrototypes وبيانات صغيرة جدًا
Algoliaخدمة خارجيةنعمغير مدرج ضمن محركات Scout الداعمة لـ semantic() في التوثيق الحاليبحث مُدار وقابل للتوسع
Meilisearchخادم Meilisearchنعمنعمبحث سريع مع إمكانية الاستضافة الذاتية
Typesenseخادم أو Typesense CloudنعمنعمKeyword وSemantic وGeo وVector Search
Turbopufferخدمة خارجيةنعمنعمFull-Text وSemantic وHybrid Search

اختيار المحرك يجب أن يعتمد على حجم البيانات، الحاجة إلى Ranking وFaceting، تكلفة البنية التحتية، ومتطلبات Semantic Search، وليس فقط على Benchmark واحد.

تثبيت Laravel Scout

ابدأ بتثبيت الحزمة:

composer require laravel/scout

بعدها انشر ملف الإعداد:

php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"

سينشئ ذلك:

config/scout.php

ثم أضف Trait إلى Model:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;

class Article extends Model
{
    use Searchable;
}

إذا أردت أبسط إعداد ممكن دون Search Service خارجي، يمكنك استخدام Database Engine:

SCOUT_DRIVER=database

بعد ذلك تستطيع مباشرة تنفيذ:

$articles = Article::search('Laravel Scout')->get();

تحديد البيانات القابلة للبحث

افتراضيًا، يقوم Scout باستخدام التمثيل الناتج من toArray() للـ Model عند إنشاء سجل داخل Search Index. لكن إرسال كل حقول Model ليس دائمًا قرارًا جيدًا.

يمكنك تحديد الحقول المطلوبة عبر toSearchableArray():

public function toSearchableArray(): array
{
    return [
        'id' => $this->id,
        'title' => $this->title,
        'summary' => $this->summary,
        'body' => $this->body,
        'status' => $this->status,
        'published_at' => $this->published_at?->timestamp,
    ];
}

هذه الخطوة تؤثر في الأداء والخصوصية معًا. لا ترسل إلى Search Index حقولًا لا تحتاج إليها، خصوصًا بيانات المستخدمين أو الحقول الداخلية أو الأسرار.

استخدام محرك مختلف لـ Model محدد

يمكن تغيير المحرك على مستوى Model بدل الالتزام بالمحرك الافتراضي:

use Laravel\Scout\Engines\Engine;
use Laravel\Scout\Scout;

public function searchableUsing(): Engine
{
    return Scout::engine('meilisearch');
}

هذا يسمح لتطبيق واحد باستخدام أكثر من Search Engine إذا كان لديك سبب معماري واضح لذلك.

استخدام Database Engine

يدعم Database Engine حاليًا MySQL وPostgreSQL. وهو يبحث مباشرة داخل جداول قاعدة البيانات باستخدام Full-Text Indexes وعمليات LIKE، لذلك لا يحتاج إلى مزامنة Index خارجي.

SCOUT_DRIVER=database

الميزة الأساسية لهذا المحرك هي البساطة: لا توجد خدمة بحث إضافية، ولا API Key، ولا عملية Import أولية.

تحسين استراتيجية البحث

افتراضيًا يستخدم Database Engine بحثًا من نوع LIKE للحقول القابلة للبحث. لكن يمكنك توجيه Scout لاستخدام Prefix Search أو Full-Text Search:

use Laravel\Scout\Attributes\SearchUsingFullText;
use Laravel\Scout\Attributes\SearchUsingPrefix;

#[SearchUsingPrefix(['id', 'slug'])]
#[SearchUsingFullText(['title', 'body'])]
public function toSearchableArray(): array
{
    return [
        'id' => $this->id,
        'slug' => $this->slug,
        'title' => $this->title,
        'body' => $this->body,
    ];
}

قبل استخدام SearchUsingFullText يجب أن تكون الأعمدة المقابلة لها مزودة فعليًا بـ Full-Text Index داخل قاعدة البيانات.

Semantic Search داخل PostgreSQL

يدعم Database Engine البحث الدلالي والهجين عندما تستخدم PostgreSQL مع امتداد pgvector. تضيف Vector Column وفهرسًا متجهيًا إضافة إلى Full-Text Index:

Schema::ensureVectorExtensionExists();

Schema::table('articles', function (Blueprint $table) {
    $table->vector('embedding', dimensions: 1536)->nullable();
    $table->vectorIndex('embedding');
    $table->fullText(['title', 'body']);
});

ثم تحدد النص الذي يجب تحويله إلى Embedding:

public function toSearchableEmbedding(): string|array
{
    return $this->title.' '.$this->body;
}

إذا كان Scout مسؤولًا عن توليد Embeddings فستحتاج إلى Laravel AI SDK وإعداد مزود Embeddings مناسب.

Collection Engine: مفيد، لكن ليس للإنتاج الكبير

SCOUT_DRIVER=collection

Collection Engine يجلب جميع السجلات المحتملة من قاعدة البيانات ثم يفلترها داخل PHP باستخدام أدوات Laravel. لذلك لا يحتاج إلى فهرسة أو Search Engine خارجي.

لكن التوثيق الرسمي يوصي به فقط للـ Prototypes والاختبارات ومجموعات البيانات الصغيرة جدًا، في حدود بضع مئات من السجلات تقريبًا.

هو أكثر قابلية للنقل من Database Engine لأنه يعمل مع قواعد البيانات العلائقية التي يدعمها Laravel، بما فيها SQLite وSQL Server، لكنه أقل كفاءة بشكل واضح عند نمو البيانات.

إعداد محركات البحث الخارجية

Algolia

composer require algolia/algoliasearch-client-php

بعد ذلك تضبط بيانات الاعتماد المطلوبة داخل إعداد Scout. ومع Algolia يمكن أيضًا تعريف Index Settings مثل Searchable Attributes وFaceting داخل config/scout.php ثم مزامنتها أثناء النشر.

Meilisearch

composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=masterKey

عند استخدام Filters أو Sorting يجب تعريف filterableAttributes وsortableAttributes المناسبة في إعداد Index.

Typesense

composer require typesense/typesense-php
SCOUT_DRIVER=typesense
TYPESENSE_API_KEY=masterKey
TYPESENSE_HOST=localhost

يحتاج Typesense أيضًا إلى Collection Schema يصف أنواع الحقول القابلة للبحث، ويجب الانتباه إلى أنواع البيانات داخل toSearchableArray().

Turbopuffer

SCOUT_DRIVER=turbopuffer
TURBOPUFFER_API_KEY=tpuf_...
TURBOPUFFER_REGION=gcp-us-central1

يدعم Turbopuffer داخل Scout البحث النصي والدلالي والهجين. قيمة TURBOPUFFER_REGION اختيارية، والقيمة الافتراضية الحالية هي gcp-us-central1.

الفهرسة ومزامنة السجلات

هذه العمليات مهمة بصورة أساسية عند استخدام Search Engine خارجي. Database Engine لا يحتاج إلى Import منفصل لأنه يبحث في الجدول نفسه.

استيراد السجلات الموجودة مسبقًا

php artisan scout:import "App\Models\Article"

إذا كان لديك عدد كبير من السجلات ويمكنك الاعتماد على Queues:

php artisan scout:queue-import "App\Models\Article" --chunk=500

حذف جميع سجلات Model من Index

php artisan scout:flush "App\Models\Article"

إضافة سجلات عبر Query

Article::where('status', 'published')->searchable();

يعمل searchable() كعملية Upsert: إذا كان السجل موجودًا يتم تحديثه، وإن لم يكن موجودًا يتم إضافته.

إزالة سجلات من البحث

Article::where('status', 'draft')->unsearchable();

ولإزالة جميع سجلات Model:

Article::removeAllFromSearch();

تعليق المزامنة مؤقتًا

Article::withoutSyncingToSearch(function () {
    // Bulk database operations...
});

هذه الطريقة مفيدة عندما تنفذ تحديثًا ضخمًا ولا تريد إرسال آلاف عمليات Indexing وسيطة غير ضرورية.

فهرسة السجلات وفق شرط

public function shouldBeSearchable(): bool
{
    return $this->status === 'published';
}

انتبه إلى أن shouldBeSearchable() لا يطبق على Database Engine لأن البيانات الأصلية موجودة بالفعل في قاعدة البيانات؛ في هذه الحالة استخدم شروط البحث المناسبة.

استخدام Laravel Queues مع Scout

عند استخدام Engine خارجي، يوصي التوثيق الرسمي بشدة باستخدام Queue حتى لا ينتظر المستخدم عملية مزامنة Search Index أثناء Request.

'queue' => true,

ويمكن تخصيص Connection وQueue:

'queue' => [
    'connection' => 'redis',
    'queue' => 'scout',
],

ثم تشغيل Worker:

php artisan queue:work redis --queue=scout

راجع أيضًا توثيق Laravel Queues الرسمي عند إعداد Workers للإنتاج.

Unique Indexing Jobs

في التطبيقات ذات الكتابة المكثفة قد يتم إرسال أكثر من Job لنفس السجل قبل معالجة السابقة. يوفر Scout إمكانية استخدام Jobs فريدة:

use Laravel\Scout\Jobs\MakeSearchableUniquely;
use Laravel\Scout\Jobs\RemoveFromSearchUniquely;
use Laravel\Scout\Scout;

Scout::makeSearchableUsing(MakeSearchableUniquely::class);
Scout::removeFromSearchUsing(RemoveFromSearchUniquely::class);

تستخدم هذه Jobs آلية Unique Job Locks في Laravel لتقليل العمليات المكررة لنفس Model Record أثناء انتظار Jobs في Queue.

تنفيذ عمليات البحث

API الأساسية بسيطة:

$articles = Article::search('Laravel Scout')->get();

يمكن أيضًا الوصول إلى النتائج الخام التي يعيدها Search Engine قبل تحويلها إلى Eloquent Models:

$results = Article::search('Laravel Scout')->raw();

تحميل العلاقات مع النتائج

use Illuminate\Database\Eloquent\Builder;

$articles = Article::search('Laravel Scout')
    ->query(fn (Builder $query) => $query->with('author'))
    ->get();

مع Third-Party Engines يتم تنفيذ Callback الخاص بـ query() بعد أن يكون Search Engine قد حدد النتائج. لذلك لا تستخدمه كبديل لفلاتر Scout؛ استخدم where() للفلاتر التي يجب أن تؤثر في مجموعة نتائج البحث نفسها.

الفلاتر باستخدام Where Clauses

يمكن دمج البحث النصي مع شروط إضافية:

$articles = Article::search('Laravel')
    ->where('author_id', 10)
    ->get();

ويدعم Scout معاملات مقارنة متعددة:

$products = Product::search('keyboard')
    ->where('status', '=', 'active')
    ->where('price', '>', 50)
    ->where('price', '<=', 300)
    ->get();

كما يدعم:

$orders = Order::search('Laravel')
    ->whereIn('status', ['open', 'paid'])
    ->get();

$orders = Order::search('Laravel')
    ->whereNotIn('status', ['cancelled'])
    ->get();

عند استخدام Meilisearch، يجب إعداد الحقول المستخدمة في where() كـ Filterable Attributes أولًا.

Pagination لنتائج البحث

$articles = Article::search('Laravel')->paginate(15);

يعيد Scout LengthAwarePaginator كما في Eloquent، لذلك يمكنك استخدام Pagination المعتادة في Blade أو API.

وعند استخدام Database Engine، يدعم Scout أيضًا:

$articles = Article::search('Laravel')->simplePaginate(15);

simplePaginate() لا يحتاج إلى حساب العدد الإجمالي الكامل للنتائج، لذلك قد يكون أكثر كفاءة عندما تحتاج فقط إلى Previous وNext.

التعامل مع Soft Deletes

إذا كنت تستخدم SoftDeletes وتحتاج إلى إبقاء السجلات المحذوفة منطقيًا قابلة للبحث، فعّل:

'soft_delete' => true,

عند تفعيل الخيار، لا يزيل Scout السجل من Search Index عند Soft Delete، بل يضيف حالة داخلية باسم __soft_deleted.

بعدها تستطيع استخدام:

$articles = Article::search('Laravel')
    ->withTrashed()
    ->get();

$deletedArticles = Article::search('Laravel')
    ->onlyTrashed()
    ->get();

إعداد Laravel Scout للإنتاج

1. استخدم Queue للمحركات الخارجية

اجعل تحديث Search Index مهمة Background حتى لا يتأثر Response Time للمستخدم.

2. شغّل Queue Workers بصورة دائمة

وجود 'queue' => true دون Worker يعني أن Index لن يتحدث حتى تتم معالجة Jobs.

3. مزامنة إعدادات Index أثناء Deployment

عند استخدام Algolia أو Meilisearch وإدارة Index Settings من config/scout.php، شغّل:

php artisan scout:sync-index-settings

يمكن أن يصبح هذا الأمر جزءًا من Deployment Pipeline.

4. استورد البيانات عند إنشاء Index جديد

php artisan scout:import "App\Models\Article"

للمجموعات الكبيرة:

php artisan scout:queue-import "App\Models\Article" --chunk=500

5. افصل إعدادات البيئات

استخدم Credentials وIndexes مستقلة للتطوير والاختبار والإنتاج حتى لا يكتب Developer Machine داخل Production Index عن طريق الخطأ.

الأمان والخصوصية في Laravel Scout

لا تفهرس بيانات أكثر مما تحتاج

عند استخدام محرك خارجي، كل قيمة ترجعها من toSearchableArray() قد تنتقل إلى مزود البحث. لذلك لا ترسل Password Hashes أو Tokens أو معلومات داخلية أو بيانات شخصية لا تحتاج إليها عملية البحث.

احتفظ بمفاتيح Search Engine خارج Git

MEILISEARCH_KEY=...
TYPESENSE_API_KEY=...
TURBOPUFFER_API_KEY=...

يجب أن توجد الأسرار في Environment Variables أو Secret Manager، لا داخل Repository.

لا تعتبر Search Index طبقة Authorization

وجود Record داخل Search Index لا يعني أن المستخدم الحالي مخول لرؤيته. طبّق Permissions المناسبة في التطبيق، واستخدم فلاتر مرتبطة بالمستخدم أو المؤسسة عندما تكون البيانات متعددة المستأجرين.

انتبه إلى SCOUT_IDENTIFY مع Algolia

يوفر Scout خيار SCOUT_IDENTIFY=true عند استخدام Algolia لربط عمليات البحث بالمستخدم. وفق التوثيق الرسمي، يؤدي ذلك إلى إرسال IP Address ومعرف المستخدم المصادق عليه إلى Algolia. لذلك يجب أن يكون تفعيله متوافقًا مع سياسة الخصوصية لديك.

تحسين أداء Laravel Scout

قلل حجم Search Document

كلما كان toSearchableArray() أكثر دقة، انخفض حجم البيانات التي يتم إرسالها وتخزينها ومعالجتها.

استخدم Database Full-Text بدل LIKE عند الحاجة

إذا كان Database Engine كافيًا لمشروعك، فإضافة Full-Text Index للحقول المناسبة قد تكون أكثر كفاءة من البحث عبر %term%.

لا تستخدم Collection Engine لآلاف السجلات

Collection Engine يجلب البيانات ثم يبحث داخل PHP؛ هذه راحة للتطوير وليست استراتيجية Scale.

استخدم Queued Indexing

لا تجعل المستخدم ينتظر API Call إلى Search Provider بعد كل Update إذا لم تكن هناك حاجة لذلك.

استخدم Unique Jobs في Write-Heavy Applications

إذا كان Model يتغير عدة مرات بسرعة، فقد تقلل Unique Indexing Jobs من عمليات Indexing المتكررة.

اختر الحقول القابلة للفلترة والترتيب بعناية

بعض المحركات تحتاج إلى تعريف Filterable وSortable Fields مسبقًا. لا تحول كل حقل إلى Facet أو Filter لمجرد إمكانية ذلك.

قيّم الحاجة إلى Semantic Search

Embeddings تضيف تكلفة حسابية وتخزينية. استخدمها عندما تضيف قيمة فعلية لتجربة البحث.

مثال عملي: بناء بحث لمقالات تقنية

سننشئ Model للمقالات بحيث تظهر المقالات المنشورة فقط في Search Index الخارجي.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;

class Article extends Model
{
    use Searchable;

    public function toSearchableArray(): array
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'summary' => $this->summary,
            'body' => $this->body,
            'category_id' => $this->category_id,
            'published_at' => $this->published_at?->timestamp,
        ];
    }

    public function shouldBeSearchable(): bool
    {
        return $this->status === 'published';
    }
}

ثم Controller للبحث:

<?php

namespace App\Http\Controllers;

use App\Models\Article;
use Illuminate\Http\Request;

class ArticleSearchController extends Controller
{
    public function __invoke(Request $request)
    {
        $query = (string) $request->string('q');

        $articles = Article::search($query)
            ->when(
                $request->filled('category'),
                fn ($search) => $search->where(
                    'category_id',
                    $request->integer('category')
                )
            )
            ->paginate(15);

        return response()->json($articles);
    }
}

وعند إضافة Scout إلى مشروع قائم:

php artisan scout:import "App\Models\Article"

بعد ذلك، أي عملية save() أو create() على Model القابل للبحث ستؤدي إلى مزامنة Index تلقائيًا، ومع Queue سيتم تنفيذ هذه المزامنة في الخلفية.

Laravel Scout مقارنة بالبدائل

Scout مقابل Eloquent LIKE

استعلام LIKE البسيط قد يكون كافيًا لمشروع صغير. Scout يصبح أكثر فائدة عندما تريد API موحدة، Full-Text Strategies، Engine خارجيًا، Ranking، Filtering، Pagination أو Semantic Search.

Scout مقابل استخدام SDK لمحرك البحث مباشرة

استخدام SDK مباشرة يمنحك الوصول إلى كل ميزة خاصة بالمزود، لكنه يربط التطبيق بتفاصيله. Scout يقلل هذا الاقتران ويهتم بمزامنة Models ويقدم API متجانسة.

ومع ذلك لا يمنعك Scout من النزول إلى مستوى Engine نفسه؛ يوفر التوثيق طرقًا لتخصيص Search Engine Query عندما تحتاج ميزة خاصة بالمزود.

Scout ليس بديلًا عن Elasticsearch بحد ذاته

Scout ليس Search Server. هو Integration Layer. إذا كنت تحتاج Engine غير مدعوم افتراضيًا، يستطيع Laravel Scout دعم Custom Engines عبر تنفيذ Contract الخاص بمحركات Scout.

أخطاء شائعة عند استخدام Laravel Scout

الخطأ 1: إضافة Searchable Trait ثم نسيان استيراد البيانات القديمة

السجلات الجديدة ستتم مزامنتها، لكن البيانات الموجودة قبل تثبيت Scout تحتاج إلى scout:import عند استخدام Third-Party Engine.

الخطأ 2: تفعيل Queue دون تشغيل Worker

النتيجة: قاعدة البيانات تتحدث لكن Search Index يبقى قديمًا.

الخطأ 3: إرسال كل Model إلى Search Provider

الاعتماد على toArray() دون مراجعة قد يؤدي إلى تخزين حقول غير مطلوبة أو حساسة.

الخطأ 4: استخدام Collection Engine على Dataset كبيرة

هذا المحرك مصمم أساسًا للبيانات الصغيرة جدًا والاختبارات.

الخطأ 5: استخدام where مع Meilisearch دون Filterable Attributes

يجب تعريف الحقول المطلوبة في Index Settings ثم مزامنة الإعداد.

الخطأ 6: استخدام query() كفلتر بعد Third-Party Search

عند المحركات الخارجية، Search Engine يكون قد حدد النتائج أصلًا. استخدم Scout Where Clauses للفلاتر.

الخطأ 7: الاعتماد على Global Scopes مع Pagination

Search Engines لا تعرف Global Scopes الخاصة بـ Eloquent. يجب إعادة تطبيق القيود المناسبة في Search Query.

الخطأ 8: تغيير Index Settings ونسيان مزامنتها

php artisan scout:sync-index-settings

الخطأ 9: إضافة Semantic Search دون استراتيجية Embeddings

البحث الدلالي يحتاج إلى Vector Data صحيحة وإعداد Engine مناسب؛ استدعاء semantic() وحده ليس إعدادًا كاملًا.

أفضل الممارسات مع Laravel Scout

  • ابدأ بـ Database Engine إذا كانت متطلبات البحث بسيطة ولا تحتاج بنية إضافية.
  • استخدم Third-Party Engine عندما تحتاج إمكانات Ranking أو Search UX أو Scale تتجاوز قاعدة البيانات.
  • خصص toSearchableArray() ولا ترسل كل البيانات افتراضيًا.
  • اجعل Queueing جزءًا من تصميم الإنتاج للمحركات الخارجية.
  • راقب Failed Jobs لأن فشل Indexing قد يسبب اختلافًا بين قاعدة البيانات ونتائج البحث.
  • اجعل scout:sync-index-settings جزءًا من Deployment عند إدارة Settings من التطبيق.
  • استخدم scout:queue-import للبيانات الكبيرة.
  • استخدم Unique Jobs عندما يكون Model عرضة لتحديثات متكررة بسرعة.
  • لا تعتمد على Search Index لتنفيذ Authorization.
  • استخدم Filters داخل Scout بدل محاولة فلترة النتائج بعد استرجاعها.
  • اختبر Relevance وليس Response Time فقط.
  • استخدم Full-Text Indexes الصحيحة عند Database Engine.
  • استخدم Semantic Search للمحتوى الذي يستفيد فعلًا من فهم المعنى.
  • افصل Credentials وIndexes بين Development وStaging وProduction.
  • خطط لإعادة بناء Index بالكامل إذا تغير Schema بصورة جوهرية.

الأسئلة الشائعة حول Laravel Scout

هل Laravel Scout هو محرك بحث؟

لا. هو طبقة تكامل رسمية بين Laravel/Eloquent ومحركات البحث أو قاعدة البيانات.

هل أحتاج Algolia أو Meilisearch لاستخدام Scout؟

لا. يوفر Scout Database Engine وCollection Engine دون خدمة بحث خارجية.

ما قواعد البيانات التي يدعمها Database Engine؟

التوثيق الحالي يحدد MySQL وPostgreSQL لـ Database Engine.

هل يحتاج Database Engine إلى scout:import؟

لا، لأنه يبحث مباشرة داخل جداول قاعدة البيانات ولا يحتفظ بـ Search Index منفصل.

متى أستخدم Collection Engine؟

للاختبارات والـ Prototypes ومجموعات البيانات الصغيرة جدًا. لا ينصح به للبيانات الكبيرة.

هل يدعم Scout Semantic Search؟

نعم. Laravel Scout 13.x يدعم Semantic Search عبر Database Engine وMeilisearch وTypesense وTurbopuffer، وفق متطلبات كل Engine.

هل يدعم Hybrid Search؟

نعم. يمكنك استخدام hybrid() للجمع بين Text وSemantic Search مع التحكم بأوزان كل منهما.

هل Database Semantic Search يعمل على MySQL؟

وفق التوثيق الحالي، Semantic وHybrid Search للـ Database Engine يتطلبان PostgreSQL مع امتداد pgvector.

هل يجب استخدام Queues؟

ليست مطلوبة للـ Database وCollection Engines، لكن التوثيق يوصي بقوة باستخدام Queue مع المحركات الخارجية لتحسين زمن استجابة واجهة التطبيق.

ماذا يحدث عند تحديث Model؟

عند استخدام Searchable Trait يراقب Scout تغيرات Model ويزامنها تلقائيًا مع Index الخاص بالمحرك المستخدم.

كيف أعيد بناء Index؟

يمكنك إزالة السجلات ثم إعادة استيرادها:

php artisan scout:flush "App\Models\Article"
php artisan scout:import "App\Models\Article"

هل يمكن استخدام Engine مختلف لكل Model؟

نعم، باستخدام searchableUsing().

هل يمكن البحث في السجلات المحذوفة Soft Delete؟

نعم، عند تفعيل soft_delete ثم استخدام withTrashed() أو onlyTrashed().

هل Scout مناسب للبحث متعدد المستأجرين Multi-Tenant؟

يمكن استخدامه، لكن يجب تصميم Filters وIndex Data بحيث تضمن عدم إعادة نتائج Tenant آخر، مع إبقاء Authorization داخل التطبيق.

الخلاصة

Laravel Scout يحل واحدة من أكثر المشكلات تكرارًا في التطبيقات التي تنمو تدريجيًا: كيف تضيف بحثًا متقدمًا دون تحويل التطبيق إلى مجموعة من Integrations المتفرقة مع Search Providers.

يمكنك البدء بأبسط صورة عبر Database Engine والاستفادة من Full-Text Search وPrefix Strategies دون أي خدمة إضافية، ثم الانتقال إلى Meilisearch أو Typesense أو Algolia أو Turbopuffer عندما تحتاج إمكانات أوسع.

وفي Laravel 13 توسع دور Scout بصورة مهمة مع دعم Semantic وHybrid Search، بما في ذلك PostgreSQL وpgvector في Database Engine. هذا يجعل Scout طبقة موحدة ليس فقط للبحث بالكلمات، بل أيضًا لبناء تجارب بحث تفهم معنى Query عند الحاجة.

لكن جودة البحث لا تأتي من تثبيت الحزمة وحدها. النجاح يعتمد على اختيار Engine المناسب، تصميم toSearchableArray() بعناية، إعداد الفهارس والفلاتر، استخدام Queues بصورة صحيحة، ومراقبة التزامن بين قاعدة البيانات وSearch Index.

المراجع الرسمية

هذا المقال جزء من سلسلة حزم Laravel الرسمية، والتصنيف الخاص بالسلسلة هو laravel-packages.