跳轉至內容

搜尋

簡介

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

大多數應用程式會發現,Laravel 提供的內建資料庫驅動選項已經足夠滿足需求——只有當您需要容錯糾錯、分面過濾或大規模地理搜尋等功能時,才需要外部搜尋服務。

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

對於透過含義而非精確關鍵詞匹配結果的 AI 驅動語義搜尋,whereVectorSimilarTo 查詢構建器方法使用了儲存在帶有 pgvector 擴充套件的 PostgreSQL 中的向量嵌入。例如,搜尋“Napa Valley 最好的酒莊”可以呈現出一篇題為“值得一遊的頂級葡萄園”的文章——儘管它們沒有任何單詞重疊。向量搜尋需要帶有 pgvector 擴充套件的 PostgreSQL 和 Laravel AI SDK

重排序 (Reranking)

Laravel 的 AI SDK 提供了重排序功能,該功能使用 AI 模型根據語義相關性對任何結果集進行重新排序。在執行全文搜尋等快速初始檢索步驟後,作為第二階段使用,重排序尤為強大——為您提供速度和語義準確性雙重保證。

Laravel Scout 搜尋

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

雖然 LIKE 查詢對於簡單的子字串匹配效果良好,但它們無法理解語言。對“running”進行 LIKE 搜尋無法找到包含“run”的記錄,且結果不會按相關性排序——它們僅僅按照資料庫發現它們的順序返回。全文搜尋透過使用理解詞邊界、詞幹提取和相關性評分的專用索引解決了這兩個問題,使資料庫能夠首先返回最相關的結果。

MariaDB、MySQL 和 PostgreSQL 內建了快速全文搜尋功能——無需外部搜尋服務。您只需將全文索引新增到要搜尋的列中,然後使用 whereFullText 查詢構建器方法進行搜尋即可。

全文搜尋目前由 MariaDB、MySQL 和 PostgreSQL 支援。

新增全文索引

要使用全文搜尋,首先向要搜尋的列新增全文索引。您可以將索引新增到單個列,或傳遞一個列陣列來建立可同時搜尋多個欄位的複合索引。

1Schema::create('articles', function (Blueprint $table) {
2 $table->id();
3 $table->string('title');
4 $table->text('body');
5 $table->timestamps();
6 
7 $table->fullText(['title', 'body']);
8});

在 PostgreSQL 上,您可以為索引指定語言配置,這可以控制詞幹提取的方式。

1$table->fullText('body')->language('english');

有關建立索引的更多資訊,請參閱遷移文件

執行全文查詢

索引就緒後,使用 whereFullText 查詢構建器方法進行搜尋。Laravel 將為您的資料庫驅動生成合適的 SQL——例如,MariaDB 和 MySQL 上的 MATCH(...) AGAINST(...),以及 PostgreSQL 上的 to_tsvector(...) @@ plainto_tsquery(...)

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

使用 MariaDB 和 MySQL 時,結果會自動按相關性得分排序。在 PostgreSQL 上,whereFullText 會過濾匹配的記錄,但不會按相關性對其進行排序——如果您需要 PostgreSQL 上的自動相關性排序,請考慮使用 Scout 的資料庫引擎,它會自動為您處理此問題。

如果您建立了跨多個列的複合全文索引,則可以透過將相同的列陣列傳遞給 whereFullText 來對所有列進行搜尋。

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

orWhereFullText 方法可用於新增“或”條件的全文搜尋子句。有關完整詳細資訊,請參閱查詢構建器文件

全文搜尋依賴於關鍵詞匹配——查詢中的單詞必須(以某種形式)出現在資料中。語義搜尋採取了根本不同的方法:它使用 AI 生成的向量嵌入將文字的含義表示為數字陣列,然後查詢含義與查詢最相似的結果。例如,搜尋“Napa Valley 最好的酒莊”可以呈現出一篇題為“值得一遊的頂級葡萄園”的文章——儘管它們在單詞上完全沒有重疊。

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

向量搜尋需要帶有 pgvector 擴充套件的 PostgreSQL 資料庫和 Laravel AI SDK。所有 Laravel Cloud 無伺服器 Postgres 資料庫都已經包含了 pgvector

生成嵌入 (Embeddings)

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

1use Illuminate\Support\Str;
2 
3$embedding = Str::of('Napa Valley has great wine.')->toEmbeddings();

要同時為多個輸入生成嵌入——這比一次生成一個更有效率,因為它只需要對嵌入提供程式進行一次 API 呼叫——請使用 Embeddings 類。

1use Laravel\Ai\Embeddings;
2 
3$response = Embeddings::for([
4 'Napa Valley has great wine.',
5 'Laravel is a PHP framework.',
6])->generate();
7 
8$response->embeddings; // [[0.123, 0.456, ...], [0.789, 0.012, ...]]

有關配置嵌入提供程式、自定義維度和快取的更多詳細資訊,請參閱 AI SDK 文件

儲存與索引向量

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

1Schema::ensureVectorExtensionExists();
2 
3Schema::create('documents', function (Blueprint $table) {
4 $table->id();
5 $table->string('title');
6 $table->text('content');
7 $table->vector('embedding', dimensions: 1536)->index();
8 $table->timestamps();
9});

Schema::ensureVectorExtensionExists 方法會在建立表之前確保您的 PostgreSQL 資料庫上啟用了 pgvector 擴充套件。

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

1protected function casts(): array
2{
3 return [
4 'embedding' => 'array',
5 ];
6}

有關向量列和索引的更多詳細資訊,請參閱 遷移文件

相似度查詢

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

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

為方便起見,當提供的是純字串而不是嵌入陣列時,Laravel 將自動使用您配置的嵌入提供程式為您生成嵌入。這意味著您可以直接傳遞使用者的搜尋查詢,而無需先手動將其轉換為嵌入。

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

對於向量查詢的底層控制,還可以使用 whereVectorDistanceLessThanselectVectorDistanceorderByVectorDistance 方法。這些方法讓您可以直接處理距離值而不是相似度得分,將計算出的距離作為結果中的一列進行選擇,或者手動控制排序。有關完整詳細資訊,請參閱 查詢構建器文件AI SDK 文件

重排序結果

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

重排序作為快速初始檢索步驟後的第二階段尤為強大。例如,您可以使用全文搜尋快速將數千條記錄縮小到前 50 個候選者,然後使用重排序將最相關的結果放在頂部。這種“先檢索後重排序”的模式為您提供了速度和語義準確性。

您可以使用 Reranking 類對字串陣列進行重排序。

1use Laravel\Ai\Reranking;
2 
3$response = Reranking::of([
4 'Django is a Python web framework.',
5 'Laravel is a PHP web application framework.',
6 'React is a JavaScript library for building user interfaces.',
7])->rerank('PHP frameworks');
8 
9$response->first()->document; // "Laravel is a PHP web application framework."

Laravel 集合還提供了一個 rerank 宏,它接受欄位名稱(或閉包)和查詢,從而可以輕鬆對 Eloquent 結果進行重排序。

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

有關配置重排序提供程式和可用選項的完整詳細資訊,請參閱 AI SDK 文件

Laravel Scout

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

資料庫引擎

Scout 的內建資料庫引擎會對現有資料庫執行全文搜尋和 LIKE 搜尋——無需外部服務或額外的基礎設施。只需將 Searchable 特性新增到您的模型中,並定義一個返回您希望可搜尋列的 toSearchableArray 方法即可。

您可以使用 PHP 屬性來控制每一列的搜尋策略。SearchUsingFullText 將使用資料庫的全文索引,SearchUsingPrefix 將僅匹配字串的開頭 (example%),而任何沒有屬性的列都將使用預設的 LIKE 策略(兩邊都有萬用字元:%example%)。

1<?php
2 
3namespace App\Models;
4 
5use Illuminate\Database\Eloquent\Model;
6use Laravel\Scout\Attributes\SearchUsingFullText;
7use Laravel\Scout\Attributes\SearchUsingPrefix;
8use Laravel\Scout\Searchable;
9 
10class Article extends Model
11{
12 use Searchable;
13 
14 #[SearchUsingPrefix(['id'])]
15 #[SearchUsingFullText(['title', 'body'])]
16 public function toSearchableArray(): array
17 {
18 return [
19 'id' => $this->id,
20 'title' => $this->title,
21 'body' => $this->body,
22 ];
23 }
24}

在指定列應使用全文查詢約束之前,請確保該列已分配了全文索引

新增該特性後,您可以使用 Scout 的 search 方法搜尋您的模型。即使在 PostgreSQL 上,Scout 的資料庫引擎也會自動按相關性對結果進行排序。

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

當您的搜尋需求適中,且希望獲得 Scout 自動索引同步的便利性而又不想部署外部服務時,資料庫引擎是一個不錯的選擇。它很好地處理了最常見的搜尋用例,包括過濾、分頁和軟刪除記錄處理。有關完整詳細資訊,請參閱 Scout 文件

第三方引擎

Scout 還支援第三方搜尋引擎,如 AlgoliaMeilisearchTypesense。這些專用搜索服務提供高階功能,如容錯糾錯、分面過濾、地理搜尋和自定義排名規則——這些功能在超大規模或當您需要高度最佳化的“輸入即搜尋”體驗時變得非常重要。

由於 Scout 在其所有驅動程式中提供統一的 API,因此以後從資料庫引擎切換到第三方引擎所需的程式碼更改極少。您可以從資料庫引擎開始,僅在應用程式的需求超過資料庫所能提供的能力時才遷移到第三方服務。

有關配置第三方引擎的完整詳細資訊,請參閱 Scout 文件

許多應用程式根本不需要外部搜尋引擎。本頁介紹的內建技術涵蓋了絕大多數用例。

組合技術

本頁介紹的搜尋技術並非互斥——組合使用它們通常會產生最佳結果。以下是展示這些工具如何協同工作的兩種常見模式。

全文檢索 + 重排序

使用全文搜尋快速將大型資料集縮小到候選集,然後應用重排序按語義相關性對這些候選者進行排序。這為您提供了資料庫原生全文搜尋的速度以及 AI 驅動的相關性評分的準確性。

1$articles = Article::query()
2 ->whereFullText('body', $request->input('query'))
3 ->limit(50)
4 ->get()
5 ->rerank('body', $request->input('query'), limit: 10);

向量搜尋 + 傳統過濾器

將向量相似度與標準的 where 子句相結合,將語義搜尋範圍限制在記錄的子集中。當您需要基於含義的搜尋,但需要按所有權、類別或任何其他屬性限制結果時,這非常有用。

1$documents = Document::query()
2 ->where('team_id', $user->team_id)
3 ->whereVectorSimilarTo('embedding', $request->input('query'))
4 ->limit(10)
5 ->get();