1. 總覽
在本程式碼研究室中,您將瞭解如何使用 Firestore 向量相似度搜尋,將強大的搜尋功能新增至應用程式。您將為以 Swift 和 SwiftUI 編寫的記事應用程式,實作語意搜尋功能。
學習目標
- 如何安裝Firestore 擴充功能的 Vector Search,以便計算向量嵌入。
- 如何從 Swift 應用程式呼叫 Firebase Cloud Functions。
- 如何根據已登入的使用者預先篩選資料。
您需要準備的項目
- Xcode 15.3
- 程式碼研究室的範例程式碼。您會在程式碼研究室的後續步驟中下載此檔案。
2. 建立及設定 Firebase 專案
如要使用 Firebase Vector Search 擴充功能,您需要建立 Firebase 專案。在本程式碼研究室的這個部分,您將建立新的 Firebase 專案,並啟用所需服務,例如 Cloud Firestore 和 Firebase 驗證。
建立 Firebase 專案
- 登入 Firebase
- 在 Firebase 控制台中,按一下「新增專案」,然後將專案命名為「Firestore Vector Search Lab」
- 點選專案建立選項。當系統顯示提示時,請接受 Firebase 條款。
- 在 Google Analytics 畫面中,取消勾選「啟用這項專案的 Google Analytics」方塊,因為這個應用程式不會用到 Analytics。
- 最後,按一下「建立專案」。
如要進一步瞭解 Firebase 專案,請參閱「瞭解 Firebase 專案」一文。
升級 Firebase 定價方案
如要使用 Firebase Extensions 及其基礎雲端服務,您的 Firebase 專案必須採用即付即用 (Blaze) 方案,也就是說,必須連結至 Cloud Billing 帳戶。
- 您必須提供付款方式 (例如信用卡),才能建立 Cloud Billing 帳戶。
- 如果您是 Firebase 和 Google Cloud 的新手,請確認您是否符合 $300 美元的抵免額和免費試用 Cloud Billing 帳戶的資格。
- 如果您是為了參加活動而進行這個程式碼研究室,請向活動主辦單位詢問是否有任何 Cloud 抵用金。
如要將專案升級至 Blaze 方案,請按照下列步驟操作:
- 在 Firebase 控制台中,選取「升級方案」。
- 選取 Blaze 方案。按照畫面上的指示將 Cloud Billing 帳戶連結至專案。
如需升級時需要建立 Cloud Billing 帳戶,您可能需要返回 Firebase 控制台的升級流程。
在控制台中啟用及設定 Firebase 產品
您建構的應用程式會使用幾項 Firebase 產品,這些產品都可用於 Apple 應用程式:
- Firebase 驗證:可讓使用者輕鬆登入您的應用程式。
- Cloud Firestore:在雲端儲存結構化資料,並在資料變更時立即收到通知。
- Firebase 安全性規則:可保護資料庫的安全。
部分產品需要特殊設定或透過 Firebase 控制台啟用。
為 Firebase 驗證啟用匿名驗證機制
這個應用程式會使用匿名驗證功能,讓使用者不必先建立帳戶,就能開始使用應用程式。這樣新手上路流程就能順暢無礙。如要進一步瞭解匿名驗證 (以及如何升級為命名帳戶),請參閱「匿名驗證最佳做法」。
- 在 Firebase 控制台的左側面板中,依序點選「Build」>「Authentication」。然後按一下「開始使用」。
- 您現在進入「驗證」資訊主頁。您可以在這裡查看已登入的使用者、設定登入供應商及管理設定。
- 選取「登入方式」分頁標籤 (或按這裡直接前往該分頁)。
- 在供應商選項中點選「匿名」,將切換鈕切換為「啟用」,然後點選「儲存」。
設定 Cloud Firestore
這個 Swift 應用程式會使用 Cloud Firestore 儲存筆記。
以下說明如何在 Firebase 專案中設定 Cloud Firestore:
- 在 Firebase 主控台的左側面板中展開「Build」,然後選取「Firestore database」。
- 按一下 [Create database] (建立資料庫)。
- 將「資料庫 ID」設為
(default)
。 - 選取資料庫位置,然後點選「下一步」。
如果是實際的應用程式,建議您選擇靠近使用者的位置。 - 按一下「以測試模式啟動」。請詳閱安全性規則免責事項。
在本程式碼研究室的後續部分,您將新增安全性規則來保護資料。請勿在未新增資料庫安全性規則的情況下公開發行或發布應用程式。 - 按一下「建立」。
設定 Cloud Storage for Firebase
網頁應用程式會使用 Cloud Storage for Firebase 儲存、上傳及分享圖片。
以下說明如何在 Firebase 專案中設定 Cloud Storage for Firebase:
- 在 Firebase 主控台的左側面板中,展開「Build」,然後選取「Storage」。
- 按一下「開始使用」。
- 選取預設儲存體值區的位置。
US-WEST1
、US-CENTRAL1
和US-EAST1
中的值區可利用 Google Cloud Storage 的「永遠免費」方案。其他所有位置的值區都會按照 Google Cloud Storage 的定價和用量收費。 - 按一下「以測試模式啟動」。請詳閱安全性規則免責事項。
在本程式碼研究室的後續部分,您將新增安全性規則來保護資料。請勿發布或公開應用程式,否則請為儲存空間值區新增安全規則。 - 按一下「建立」。
3. 連結行動應用程式
在本節的程式碼研究室中,您將下載簡易記事應用程式的原始碼,並將其連結至剛建立的 Firebase 專案。
下載範例應用程式
- 前往 https://github.com/FirebaseExtended/codelab-firestore-vectorsearch-ios,然後將存放區複製到本機電腦
- 在 Xcode 中開啟 Notes.xcodeproj 專案
將應用程式連結至 Firebase 專案
您必須在 Firebase 控制台設定應用程式,應用程式才能存取 Firebase 服務。您可以將多個用戶端應用程式連結至同一個 Firebase 專案。舉例來說,假設您建立了 Android 或網頁應用程式,就必須將這些應用程式連結至同一個 Firebase 專案。
如要進一步瞭解 Firebase 專案,請參閱「瞭解 Firebase 專案」一文。
- 在 Firebase 控制台中,前往 Firebase 專案的總覽頁面。
- 按一下 iOS+ 圖示,即可新增 iOS 應用程式。
- 在「Add Firebase to your Apple app」畫面中,插入 Xcode 專案中的套件 ID (com.google.firebase.codelab.Notes)。
- 如有需要,可以輸入應用程式暱稱 (iOS 記事)。
- 點選「註冊應用程式」即可前往下一個步驟。
- 下載 GoogleServices-Info.plist 檔案。
- 將 GoogleServices-Info.plist 拖曳至 Xcode 專案的 Notes 資料夾。將其放在 Assets.xcassets 檔案下方,這是最理想的做法。
- 選取「視需要複製項目」,確認「新增至指定目標」中已選取「附註」指定目標,然後按一下「完成」。
- 您現在可以在 Firebase 主控台中點選完成其餘設定程序:您在本節開頭下載的範例已安裝 Firebase Apple SDK,並完成初始化設定。按一下「Continue to console」即可完成程序。
執行應用程式
現在可以試用應用程式了!
- 回到 Xcode,在 iOS 模擬器上執行應用程式。在「Run Destinations」下拉式選單中,先選取其中一個 iOS 模擬器。
- 接著,點選「Run」按鈕,或按下 ⌘ + R 鍵。
- 應用程式順利在模擬器上啟動後,請加入幾則附註。
- 在 Firebase 控制台中前往 Firestore 資料瀏覽器,即可在應用程式中新增筆記時,查看建立的新文件。
4. 安裝 Vector Search with Firestore 擴充功能
在本程式碼研究室的這個部分,您將安裝 Vector Search with Firestore 擴充功能,並根據您正在使用的筆記應用程式需求進行設定。
開始安裝擴充功能
- 在 Firestore 部分,點選「Extensions」分頁。
- 按一下「探索 Extensions Hub」。
- 輸入「向量」。
- 按一下「含有 Firestore 擴充功能的 Vector Search」。 系統會將您帶往擴充功能的詳細資料頁面,您可以在其中進一步瞭解擴充功能、運作方式、所需的 Firebase 服務,以及如何設定擴充功能。
- 按一下「在 Firebase 控制台中安裝」。
- 系統會列出所有專案。
- 選擇您在本程式碼研究室的第一個步驟中建立的專案。
設定擴充功能
- 請檢查已啟用的 API 和已建立的資源。
- 啟用必要服務。
- 所有服務都已啟用後,請點選「下一步」。
- 審查授予這項擴充功能的存取權。
- 設定擴充功能:
- 選取 Vertex AI 做為 LLM
- 收集路徑:notes
- 預設查詢限制:3
- 輸入欄位名稱:text
- 輸出欄位名稱: 嵌入
- 狀態欄位名稱:* *status*
- 嵌入現有文件:是
- 更新現有文件:是
- Cloud 函式位置:us-central1
- 按一下「Install extension」即可完成安裝。
這可能需要幾分鐘的時間。在等待安裝作業完成的同時,您可以繼續閱讀教學課程的下一節,瞭解向量嵌入的相關背景資訊。
5. 背景
在等待安裝完成的同時,請參閱以下背景資訊,瞭解 Vector Search with Firestore 擴充功能的運作方式。
向量、嵌入和向量資料庫分別是什麼?
- 向量是數學物件,用來表示數量的大小和方向。可用於以更容易比較和搜尋的方式呈現資料。
- 嵌入是代表字詞或詞組意義的向量。這類模型是透過大量文字資料訓練類神經網路,並學習字詞之間的關係而建立。
- 向量資料庫是專為儲存及搜尋向量資料而最佳化的資料庫。這類向量可進行最鄰近搜尋,也就是找出與指定查詢向量最相似的向量。
向量搜尋如何運作?
向量搜尋會將查詢向量與資料庫中的所有向量進行比較。系統會傳回與查詢向量最相似的向量,做為搜尋結果。
您可以運用多種距離指標,測量兩個向量之間的相似度。最常見的距離指標是餘弦相似度,用於測量兩個向量之間的角度。
6. 試用 Vector Search 與 Firestore 擴充功能
在本程式碼研究室中先前往下載 iOS 應用程式,然後在 Vector Search with Firestore 擴充功能中試用 Firebase 控制台。
閱讀說明文件
Firebase 擴充功能包含說明文件,說明這些功能的運作方式。
- 擴充功能安裝完成後,按一下「Get started」按鈕。
- 請參閱「這項擴充功能的運作方式」分頁,說明:
- 如何透過將文件加入
notes
集合來計算文件的嵌入資料 - 如何透過呼叫
ext-firestore-vector-search-queryCallable
可呼叫函式查詢索引。 - 或如何透過在
_firestore-vector-search/index/queries
集合中新增查詢文件來查詢索引。 - 也會說明如何設定自訂嵌入函式。如果擴充功能支援的 LLM 都不符合您的需求,而且您想使用其他大型語言模型計算嵌入,這種做法就非常實用。
- 如何透過將文件加入
- 按一下「Cloud Firestore 資訊主頁」連結,前往 Firestore 執行個體
- 前往
_firestore-vector-search/index
文件。這應該會顯示擴充功能已完成計算所有筆記文件的嵌入資料。您可以在本程式碼研究室的先前步驟中建立這些筆記文件。 - 如要進行驗證,請開啟任一記事文件,當中會顯示名為
embedding
且類型為vector<768>
的額外欄位,以及status
欄位。
建立範例文件
您可以在 Firebase 主控台中建立新文件,查看擴充功能的運作情形。
- 還是在 Firestore 資料瀏覽器中前往
notes
集合,然後按一下中間欄中的「+ 新增文件」。 - 按一下「自動產生 ID」,產生新的不重複文件 ID。
- 新增一個名為
text
的字串型別欄位,然後將一些文字貼到「value」欄位中。請注意,這不是 Lorem Ipsum 或其他隨機文字。例如選擇新聞文章。 - 按一下「儲存」。
- 請注意,擴充功能如何新增狀態欄位,指出擴充功能目前正在處理資料。
- 過一小段時間後,您應該會看到新的欄位
embedding
,其值為vector<768>
。
執行查詢
向量搜尋與 Firestore 擴充功能提供一個實用的功能,讓您不必連結應用程式,即可查詢文件索引。
- 在 Firebase 主控台的「Firestore」專區中,前往
_firestore-vector-search/index
文件 - 按一下「+ 開始收集」
- 建立名為「
queries
」的新子集合 - 建立新文件,並將
query
欄位設為其中一個文件中的文字。這類查詢最適合用於語意查詢,例如「如何使用 Swift 對應 Firestore 文件」(前提是您新增的至少一則筆記包含討論此主題的文字)。 - 您可能會在狀態 中看到錯誤訊息
- 這是因為缺少索引。如要建立缺少的索引設定,請點選這個連結前往專案的 Google Cloud 控制台,然後從清單中選取所需專案
- 在 Cloud Logging Explorer 中,您現在應該會看到「FAILED_PRECONDITION: Missing vector index configuration」(「FAILED_PRECONDITION:向量索引設定遺漏」) 的錯誤訊息。請使用下列 gcloud 指令建立必要索引:..."
- 錯誤訊息中也包含
gcloud
指令,您必須執行這項指令才能設定缺少的索引。 - 在指令列中執行下列指令。如果您尚未在電腦上安裝
gcloud
CLI,請按照這裡的操作說明安裝。 建立索引需要幾分鐘的時間。您可以在 Firebase 控制台的「Firestore」部分,透過「索引」分頁查看進度。gcloud alpha firestore indexes composite create --project=INSERT-YOUR=PROJECT-ID-HERE --collection-group=notes --query-scope=COLLECTION --field-config=vector-config='{"dimension":"768","flat": "{}"}',field-path=embedding
- 索引設定完成後,您可以建立新的查詢文件。
- 您現在應該會在結果欄位中看到相符的文件 ID 清單
- 複製其中一個 ID,然後返回
notes
集合。 - 使用 ⌘+F 鍵搜尋您複製的文件 ID,這份文件就是最符合查詢的文件 ID。
7. 實作語意搜尋
最後,您可以將行動應用程式連結至 Vector Search with Firestore 擴充功能,並實作語意搜尋功能,讓使用者能透過自然語言查詢搜尋筆記。
連結可呼叫的函式,以便執行查詢
Vector Search with Firestore 擴充功能包含 Cloud Functions,您可以透過行動應用程式呼叫該函式,查詢先前在本程式碼研究室中建立的索引。在這個步驟中,您將在行動應用程式和這個可呼叫的函式之間建立連線。Firebase 的 Swift SDK 包含 API,可讓您順暢地呼叫遠端函式。
- 返回 Xcode,並確認您在本程式碼研究室中先前步驟複製的專案。
- 開啟
NotesRepository.swift
檔案。 - 找出包含
private lazy var vectorSearchQueryCallable: Callable
的那一行= functions.httpsCallable("")
如要叫用可呼叫的 Cloud 函式,您必須提供要呼叫的函式名稱。
- 前往專案的 Firebase 主控台,然後在「Build」部分開啟「Functions」選單項目。
- 您會看到擴充功能已安裝的函式清單。
- 搜尋名為
ext-firestore-vector-search-queryCallable
的變數,然後複製其名稱。 - 將名稱貼到程式碼中。目前應顯示以下內容:
private lazy var vectorSearchQueryCallable: Callable<String, String> = functions.httpsCallable("ext-firestore-vector-search-queryCallable")
呼叫查詢函式
- 找出
performQuery
方法 - 透過呼叫以下方法呼叫可叫用的函式
let result = try await vectorSearchQueryCallable(searchTerm)
由於這是遠端呼叫,因此可能會失敗。
- 新增一些基本錯誤處理機制,擷取任何錯誤並記錄到 Xcode 的控制台。
private func performQuery(searchTerm: String) async -> [String] { do { let result = try await vectorSearchQueryCallable(searchTerm) return [result] } catch { print(error.localizedDescription) return [] } }
連結 UI
如要讓使用者搜尋筆記,您可以在筆記清單畫面中加入搜尋列。使用者輸入搜尋字詞時,您需要叫用先前步驟中實作的 performQuery
方法。由於 SwiftUI 提供 searchable
和 task
檢視器修飾符,因此只需要幾行程式碼即可完成這項操作。
- 首先,開啟
NotesListScreen.swift
- 如要在清單檢視畫面中加入搜尋框,請在
.navigationTitle("Notes")
行正上方新增.searchable(text: $searchTerm, prompt: "Search")
檢視畫面修飾符 - 接著,請在下方新增以下程式碼,以便叫用搜尋函式:
.task(id: searchTerm, debounce: .milliseconds(800)) {
await notesRepository.semanticSearch(searchTerm: searchTerm)
}
這段程式碼會以非同步方式呼叫 semanticSearch
方法。提供 800 毫秒的逾時時間,可指示工作修飾符以 0.8 秒的時間來debounce 使用者輸入內容。也就是說,只有在使用者暫停輸入超過 0.8 秒時,系統才會呼叫 semanticSearch
。
程式碼現在應如下所示:
...
List(repository.notes) { note in
NavigationLink(value: note) {
NoteRowView(note: note)
}
.swipeActions {
Button(role: .destructive, action: { deleteNote(note: note) }) {
Label("Delete", systemImage: "trash")
}
}
}
.searchable(text: $searchTerm, prompt: "Search")
.task(id: searchTerm, debounce: .milliseconds(800)) {
await notesRepository.semanticSearch(searchTerm: searchTerm)
}
.navigationTitle("Notes")
...
執行應用程式
- 按下 ⌘ + R (或點選「Run」按鈕),在 iOS 模擬器上啟動應用程式
- 您應該會看到先前在本程式碼研究室中新增的應用程式附註,以及透過 Firebase 主控台新增的任何附註
- 你應該會在「筆記」清單頂端看到搜尋欄位
- 輸入你新增的其中一個文件中出現的字詞。再次提醒,這項功能最適合用於語意查詢,例如「如何從 Swift 呼叫非同步 Firebase API」(至少有一個你新增的附註包含討論這個主題的文字)。
- 您可能會看到搜尋結果,但清單檢視為空白,且 Xcode 主控台會顯示錯誤訊息:「使用無效引數呼叫函式」
這表示你傳送的資料格式錯誤。
分析錯誤訊息
- 如要瞭解問題所在,請前往 Firebase 控制台
- 前往「函式」區段
- 找到
ext-firestore-vector-search-queryCallable
函式,按一下三個垂直圓點圖示開啟溢位選單 - 選取「查看記錄檔」,前往記錄檔探索工具
- 您應該會看到錯誤訊息
Unhandled error ZodError: [
{
"code": "invalid_type",
"expected": "object",
"received": "string",
"path": [],
"message": "Expected object, received string"
}
]
這表示你傳送的資料格式錯誤。
使用正確的資料類型
如要瞭解擴充功能預期的參數格式,請參閱擴充功能的說明文件。
- 前往 Firebase 控制台的「擴充功能」專區
- 依序按一下「管理」->
- 在「這項擴充功能的運作方式」一節中,您會看到輸入和輸出參數的規格。
- 返回 Xcode,然後前往
NotesRepository.swift
- 在檔案開頭處加入下列程式碼:
private struct QueryRequest: Codable { var query: String var limit: Int? var prefilters: [QueryFilter]? } private struct QueryFilter: Codable { var field: String var `operator`: String var value: String } private struct QueryResponse: Codable { var ids: [String] }
QueryRequest
會根據擴充功能說明文件,比對擴充功能預期的輸入參數結構。它還包含您稍後會用到的巢狀prefilter
屬性。QueryResponse
會比對擴充功能回應的結構。 - 尋找可呼叫函式規格,並更新輸入和輸出類型
private lazy var vectorSearchQueryCallable: Callable<QueryRequest, QueryResponse> = functions.httpsCallable("ext-firestore-vector-search-queryCallable")
- 更新
performQuery
中可呼叫函式的叫用private func performQuery(searchTerm: String) async -> [String] { do { let queryRequest = QueryRequest(query: searchTerm, limit: 2) let result = try await vectorSearchQueryCallable(queryRequest) print(result.ids) return result.ids } catch { print(error.localizedDescription) return [] } }
再次執行應用程式
- 再次執行應用程式
- 輸入含有某個筆記內含字詞的搜尋查詢
- 您現在應該會看到篩選後的筆記清單
預先篩選使用者資料
在您跳舞慶祝之前,請注意目前的應用程式版本有問題:結果集包含所有使用者的資料。
您可以在不同的模擬器上執行應用程式並新增更多文件,藉此驗證這項情況。新的文件只會顯示在該模擬器中,如果您在其他模擬器中再次執行應用程式,您只會看到第一次建立的文件。
執行搜尋時,您會發現對 vectorSearchQueryCallable
的呼叫會傳回可能屬於其他使用者的文件 ID。為避免這種情況,我們需要使用預先篩選器。
在 performQuery
中,按照下列方式更新程式碼:
let prefilters: [QueryFilter] = if let uid = user?.uid {
[QueryFilter(field: "userId", operator: "==", value: uid)]
}
else {
[]
}
let queryRequest = QueryRequest(query: searchTerm,
limit: 2,
prefilters: prefilters)
這會根據已登入使用者的 ID 預先篩選資料。如您所料,這需要更新 Firestore 索引。
在指令列中執行下列指令,定義新的 Firestore 索引,其中包含 userId
和 embedding
欄位中的向量嵌入。
gcloud alpha firestore indexes composite create --project=INSERT-YOUR-PROJECT-ID-HERE --collection-group=notes --query-scope=COLLECTION --field-config=order=ASCENDING,field-path=userId --field-config=vector-config='{"dimension":"768","flat": "{}"}',field-path=embedding
索引建構完成後,請再次執行應用程式,確認應用程式是否正常運作
8. 恭喜!
恭喜您順利完成本程式碼研究室!
在本程式碼研究室中,您已瞭解如何:
- 設定已啟用語意搜尋功能的 Cloud Firestore 資料庫。
- 建立簡單的 SwiftUI 應用程式,與資料庫互動。
- 使用 SwiftUI 的可搜尋檢視畫面修飾符和工作修飾符,實作搜尋列。
- 呼叫 Cloud 函式,使用 Firestore SDK 的 Callable 介面,在資料庫上執行語義搜尋。
有了本程式碼研究室的知識,您現在可以建構功能強大的應用程式,運用 Cloud Firestore 的語意搜尋功能,為使用者提供更直覺且有效率的搜尋體驗。
如要進一步瞭解 Firestore 的新向量欄位,以及如何計算向量嵌入資料,請參閱說明文件。