跳到主要內容

搜尋

📝 此頁面為 Laravel 官方文檔的繁體中文翻譯。查看原始英文版本

搜尋

簡介

幾乎每個應用程式都需要搜尋功能。無論您的使用者是在知識庫中搜尋相關文章、瀏覽產品目錄,還是對文件語料庫提出自然語言問題,Laravel 都提供了內建工具來處理這些場景 — 而且您通常不需要任何外部服務就能實現。

大多數應用程式會發現 Laravel 提供的內建資料庫驅動選項已綽綽有餘 — 外部搜尋服務僅在您需要大規模的拼字容錯、分面篩選或地理搜尋等進階功能時才必要。

全文搜尋

當您需要關鍵字相關性排序 — 即資料庫根據搜尋詞的匹配程度對結果進行評分和排序 — 時,Laravel 的 whereFullText 查詢建構器方法利用了 MariaDB、MySQL 和 PostgreSQL 上的原生全文索引。全文搜尋理解詞語邊界和字根提取,因此搜尋「running」可以匹配包含「run」的記錄。不需要外部服務。

語意向量搜尋

對於基於 AI 的語意搜尋,它根據*含義*而非精確關鍵字來匹配結果,whereVectorSimilarTo 查詢建構器方法使用儲存在帶有 pgvector 擴充功能的 PostgreSQL 中的向量嵌入。例如,搜尋「Napa Valley 最好的酒莊」可以浮現一篇標題為「頂級必訪酒莊」的文章 — 即使詞語完全不重疊。向量搜尋需要帶有 pgvector 擴充功能的 PostgreSQL 和 Laravel AI SDK

重新排序

Laravel 的 AI SDK 提供重新排序功能,使用 AI 模型根據與查詢的語意相關性重新排列任何結果集。重新排序作為快速初始檢索步驟(如全文搜尋)之後的第二階段尤其強大 — 讓您同時獲得速度和語意準確性。

Laravel Scout 搜尋

對於希望使用 Searchable trait 自動保持搜尋索引與 Eloquent 模型同步的應用程式,Laravel Scout 提供了內建的資料庫引擎以及用於 Algolia、Meilisearch 和 Typesense 等第三方服務的驅動程式。

全文搜尋

雖然 LIKE 查詢對於簡單的子字串匹配效果良好,但它們不理解語言。對「running」進行 LIKE 搜尋不會找到包含「run」的記錄,而且結果不會按相關性排序 — 它們只是以資料庫找到的任何順序返回。全文搜尋透過使用理解詞語邊界、字根提取和相關性評分的專用索引來解決這兩個問題,讓資料庫能先返回最相關的結果。

快速全文搜尋內建於 MariaDB、MySQL 和 PostgreSQL 中 — 不需要外部搜尋服務。您只需要為要搜尋的欄位新增全文索引,然後使用 whereFullText 查詢建構器方法來搜尋它們。

[!WARNING] 全文搜尋目前由 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 — 例如,在 MariaDB 和 MySQL 上為 MATCH(...) AGAINST(...),在 PostgreSQL 上為 to_tsvector(...) @@ plainto_tsquery(...)

$articles = Article::whereFullText('body', 'web developer')->get();

使用 MariaDB 和 MySQL 時,結果會自動按相關性評分排序。在 PostgreSQL 上,whereFullText 會篩選匹配的記錄但不會按相關性排序 — 如果您需要在 PostgreSQL 上自動按相關性排序,可以考慮使用 Scout 的資料庫引擎

如果您建立了跨多個欄位的複合全文索引,可以透過向 whereFullText 傳遞相同的欄位陣列來搜尋所有欄位:

$articles = Article::whereFullText(
    ['title', 'body'], 'web developer'
)->get();

orWhereFullText 方法可用於將全文搜尋子句作為「或」條件新增。要了解更多詳細資訊,請參閱查詢建構器文件

語意向量搜尋

全文搜尋依賴關鍵字匹配 — 查詢中的詞語必須以某種形式出現在資料中。語意搜尋採用完全不同的方法:它使用 AI 生成的向量嵌入將文字的*含義*表示為數字陣列,然後找到含義與查詢最相似的結果。例如,搜尋「Napa Valley 最好的酒莊」可以浮現一篇標題為「頂級必訪酒莊」的文章 — 即使詞語完全不重疊。

向量搜尋的基本工作流程是:為每個內容生成一個嵌入向量(數字陣列)並與資料一起儲存,然後在搜尋時為使用者的查詢生成一個嵌入向量,並在向量空間中找到與之最接近的已儲存嵌入向量。

[!NOTE] 向量搜尋需要 Laravel AI SDK,由 PostgreSQL(需要 pgvector 擴充功能)和 MongoDB(需要 Laravel MongoDB 套件)支援。Laravel Cloud 上的所有 Postgres 資料庫都已安裝 pgvector

生成嵌入向量

嵌入向量是一個高維數字陣列(通常有數百或數千個數字),代表一段文字的語意含義。您可以使用 Laravel 的 Stringable 類別上可用的 toEmbeddings 方法為字串生成嵌入向量:

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, ...]]

要了解更多關於設定嵌入提供者、自訂維度和快取的資訊,請參閱 AI SDK 文件

儲存和索引向量

要儲存向量嵌入,請在遷移中定義一個 vector 欄位,指定與嵌入提供者輸出匹配的維度數(例如,OpenAI 的 text-embedding-3-small 模型為 1536)。您還應該在欄位上呼叫 index 來建立 HNSW(分層可導航小型世界)索引,這可以大幅加速大型資料集上的相似度搜尋:

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 方法在建立資料表之前確保 PostgreSQL 資料庫上已啟用 pgvector 擴充功能。

在您的 Eloquent 模型上,將向量欄位轉換為 array,以便 Laravel 自動處理 PHP 陣列與資料庫向量格式之間的轉換:

protected function casts(): array
{
    return [
        'embedding' => 'array',
    ];
}

要了解更多關於向量欄位和索引的資訊,請參閱遷移文件

相似度查詢

儲存內容的嵌入向量後,您可以使用 whereVectorSimilarTo 方法搜尋相似的記錄。此方法使用餘弦相似度將給定的嵌入向量與已儲存的向量進行比較,過濾掉低於 minSimilarity 閾值的結果,並自動按相關性排序結果 — 最相似的記錄排在最前面。閾值應為 0.01.0 之間的值,其中 1.0 表示向量完全相同:

$documents = Document::query()
    ->whereVectorSimilarTo('embedding', $queryEmbedding, minSimilarity: 0.4)
    ->limit(10)
    ->get();

作為便利功能,當給定純文字字串而非嵌入向量陣列時,Laravel 將使用您設定的嵌入提供者自動為您生成嵌入向量。這意味著您可以直接傳遞使用者的搜尋查詢,而無需先手動將其轉換為嵌入向量:

$documents = Document::query()
    ->whereVectorSimilarTo('embedding', 'best wineries in Napa Valley')
    ->limit(10)
    ->get();

要對向量查詢進行更底層的控制,whereVectorDistanceLessThanselectVectorDistanceorderByVectorDistance 方法也可用。這些方法讓您可以直接使用距離值而非相似度評分、在結果中選擇計算的距離作為欄位,或手動控制排序。要了解更多詳細資訊,請參閱查詢建構器文件AI SDK 文件

重新排序結果

重新排序是一種技術,AI 模型根據每個結果與給定查詢的語意相關性重新排列結果集。與需要預先計算和儲存嵌入向量的向量搜尋不同,重新排序適用於任何文字集合 — 它接受原始內容和查詢作為輸入,並返回按相關性排序的項目。

重新排序作為快速初始檢索步驟之後的第二階段尤其強大。例如,您可以使用全文搜尋快速將數千條記錄縮減到前 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 宏,它接受一個欄位名稱(或閉包)和一個查詢,使得重新排序 Eloquent 結果變得很容易:

$articles = Article::all()
    ->rerank('body', 'Laravel tutorials');

要了解更多關於設定重新排序提供者和可用選項的資訊,請參閱 AI SDK 文件

Laravel Scout

上述搜尋技術都是您在程式碼中直接呼叫的查詢建構器方法。Laravel Scout 採用了不同的方法:它提供了一個 Searchable trait,您將其新增到 Eloquent 模型中,Scout 會在記錄建立、更新和刪除時自動保持搜尋索引同步。當您希望模型始終可搜尋而無需手動管理索引更新時,這特別方便。

資料庫引擎

Scout 的內建資料庫引擎對您現有的資料庫執行全文和 LIKE 搜尋 — 不需要外部服務或額外的基礎設施。只需將 Searchable trait 新增到模型中,並定義一個 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,
        ];
    }
}

[!WARNING] 在指定欄位應使用全文查詢約束之前,請確保該欄位已分配全文索引

新增 trait 後,您可以使用 Scout 的 search 方法來搜尋模型。Scout 的資料庫引擎將自動按相關性排序結果,即使在 PostgreSQL 上也是如此:

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

當您的搜尋需求適中且您希望享受 Scout 自動索引同步的便利而無需部署外部服務時,資料庫引擎是一個很好的選擇。它能很好地處理最常見的搜尋使用場景,包括篩選、分頁和軟刪除記錄處理。要了解更多詳細資訊,請參閱 Scout 文件

第三方引擎

Scout 還支援第三方搜尋引擎,如 AlgoliaMeilisearchTypesense。這些專用搜尋服務提供進階功能,如拼字容錯、分面篩選、地理搜尋和自訂排序規則 — 這些功能在非常大規模或需要高度精緻的即打即搜體驗時變得很重要。

由於 Scout 在所有驅動程式之間提供統一的 API,以後從資料庫引擎切換到第三方引擎只需要最少的程式碼變更。您可以從資料庫引擎開始,僅在應用程式的需求超出資料庫所能提供的範圍時才遷移到第三方服務。

要了解更多關於設定第三方引擎的資訊,請參閱 Scout 文件

[!NOTE] 許多應用程式永遠不需要外部搜尋引擎。本頁面描述的內建技術涵蓋了絕大多數使用場景。

結合技術

本頁面描述的搜尋技術並非互斥 — 結合它們通常能產生最佳結果。以下是兩個常見模式,展示這些工具如何协同工作。

全文檢索 + 重新排序

使用全文搜尋快速將大型資料集縮減到候選集,然後套用重新排序按語意相關性排序這些候選者。這讓您獲得資料庫原生全文搜尋的速度和 AI 驅動的相關性評分的準確性:

$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();