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 產品
您正在建構的應用程式使用了數種適用於 Apple 應用程式的 Firebase 產品:
- Firebase 驗證功能,方便使用者登入您的應用程式。
- Cloud Firestore:可將結構化資料儲存至雲端,並在資料變更時立即收到通知。
- Firebase 安全性規則,用於保護資料庫。
部分產品需要特殊設定,或透過 Firebase 控制台啟用。
啟用 Firebase 驗證的匿名驗證
這個應用程式使用匿名驗證,讓使用者不必建立帳戶即可開始使用應用程式。如此一來,就能簡化新手上路流程。若要進一步瞭解匿名驗證 (以及如何升級至已命名帳戶),請參閱匿名驗證的最佳做法。
- 在 Firebase 控制台的左側面板中,依序點選「Build」>「Authentication」。然後按一下「開始使用」。
- 您目前位於「驗證」資訊主頁,您可以在這裡查看已登入的使用者、設定登入提供者,以及管理設定。
- 選取「登入方式」分頁標籤 (或按這裡直接前往該分頁)。
- 在供應商選項中按一下「匿名」,將開關切換為「啟用」,然後按一下「儲存」。
設定 Cloud Firestore
這個 Swift 應用程式會使用 Cloud Firestore 儲存記事。以下說明如何設定 Cloud Firestore:
- 在 Firebase 控制台的左側面板中,依序點選「Build」>「Firestore Database」。接著,按一下「Create database」(建立資料庫)。
- 選取資料庫位置,並務必選擇可使用 Gemini 的位置 (您可以使用 us-central1)。不過請注意,這個位置在設定後即無法變更。按一下 [Next] (下一步)。
- 選取「以測試模式啟動」選項。請詳閱安全性規則免責事項。透過測試模式,您就能在開發期間自由寫入資料庫。
- 點選「建立」來建立資料庫。
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 應用程式。
- 在「將 Firebase 新增至 Apple 應用程式」畫面中,插入 Xcode 專案 (com.google.firebase.codelab.Notes) 中的軟體包 ID。
- 您可以視需要輸入應用程式暱稱 (iOS 記事)。
- 點按「註冊應用程式」即可前往下一個步驟。
- 下載 GoogleServices-Info.plist 檔案。
- 將 GoogleServices-Info.plist 拖曳到 Xcode 專案的「Notes」資料夾。建議您將檔案拖放到 Assets.xcassets 檔案下方。
- 選取「視需要複製項目」,務必在「Add to 指定目標」中選取「Notes」目標,然後按一下「Finish」。
- 您現在可以在 Firebase 控制台中,完成其餘設定程序:您在本節開頭下載的範例已安裝 Firebase Apple SDK,並完成初始化設定。如要完成這項程序,請按一下「Continue to Console」。
執行應用程式
現在,讓我們來旋轉應用程式吧!
- 返回 Xcode,在 iOS 模擬器上執行應用程式。在「Run Destinations」下拉式選單中,先選取其中一個 iOS 模擬器。
- 然後按一下「Run」按鈕或按下 ⌘ + R 鍵
- 應用程式在模擬器中成功啟動後,請新增幾則附註。
- 在 Firebase 控制台中,前往 Firestore 資料瀏覽器,這樣每當您在應用程式中新增附註時,就能看到系統新建立的文件。
4. 安裝搭配 Firestore 擴充功能的 Vector Search
在程式碼研究室的這個部分,您將安裝搭配 Firestore 擴充功能的 Vector Search 擴充功能,並依據您正在使用的筆記應用程式需求進行設定。
開始安裝擴充功能
- 在仍在「Firestore」部分,按一下「Extensions」分頁標籤。
- 按一下「探索 Extensions Hub」。
- 以「向量」輸入。
- 按一下「Vector Search with Firestore 擴充功能」。
系統會導向擴充功能的詳細資料頁面,方便你進一步瞭解擴充功能、運作方式、需要的 Firebase 服務,以及設定方式。
- 按一下「Install in Firebase Console」。
- 畫面上會列出您的所有專案。
- 選擇您在本程式碼研究室第一個步驟中建立的專案。
設定擴充功能
Firebase Extensions 會使用 Cloud Functions for Firebase,因此你的專案必須採用即付即用 Blaze 方案。您必須先升級專案,才能將 Vector Search 與 Firestore 擴充功能搭配使用。
- 如要繼續操作,請按一下「升級專案」。
- 選取現有的帳單帳戶,或建立新帳戶。按一下「繼續」。
- 設定預算 (例如 $10 美元),按一下「繼續」,然後點選「購買」。
- 查看已啟用的 API 以及已建立的資源。
- 啟用必要服務。
- 啟用 Cloud Storage 後,請為安全性規則選取「測試模式」。
- 確認 Cloud Storage 會使用與 Cloud Firestore 執行個體相同的位置。
- 啟用所有服務後,請點選「Next」(下一步)。
- 請檢查授予這項擴充功能的存取權。
- 設定擴充功能:
- 選取「Vertex AI」做為大型語言模型
- 集合路徑:notes
- 預設查詢限制:3
- 輸入欄位名稱:text
- 輸出欄位名稱: embedding
- 狀態欄位名稱:* *status*
- 嵌入現有文件:是
- 更新現有文件:是
- Cloud 函式位置:us-central1
- 按一下「安裝擴充功能」完成安裝程序。
這可能需要幾分鐘的時間。在等待安裝作業完成的同時,您可以繼續瀏覽教學課程的下一個部分,瞭解向量嵌入功能的背景資訊。
5. 背景
當你等待安裝作業完成時,以下提供一些背景資訊,協助你瞭解 Vector Search 搭配 Firestore 擴充功能的運作方式。
何謂向量、嵌入和向量資料庫?
- 向量是代表數量規模和方向的數學物件。能以更輕鬆的方式比較及搜尋資料。
- 嵌入是代表字詞或詞組意義的向量。演算法是在大型語料庫上訓練類神經網路,並學習字詞之間的關係。
- 向量資料庫是專為儲存及搜尋向量資料而最佳化的資料庫。這種方法可用於有效率地最鄰近搜尋,也就是找出與特定查詢向量最相似的向量的過程。
Vector Search 的運作方式為何?
向量搜尋的運作方式是比較查詢向量與資料庫中的所有向量。與查詢向量最相近的向量會傳回搜尋結果。
您可以運用多種距離指標來評估兩個向量之間的相似度。最常見的距離指標是餘弦相似度,用於測量兩個向量間的角度。
6. 試用搭配 Firestore 擴充功能使用 Vector Search
在您先前在本程式碼研究室下載的 iOS 應用程式中,使用搭配 Firestore 擴充功能的 Vector Search 擴充功能之前,您可以在 Firebase 控制台試用這項擴充功能。
閱讀說明文件
Firebase Extensions 會提供運作方式說明文件。
- 擴充功能安裝完成後,請按一下「Get started」按鈕。
- 查看「這項擴充功能的運作方式」分頁,其中會包含下列內容:
- 如何將文件的嵌入新增至
notes
集合,以計算文件的嵌入。 - 如何呼叫
ext-firestore-vector-search-queryCallable
可呼叫的函式來查詢索引。 - 或將查詢文件新增至
_firestore-vector-search/index/queries
集合,以便查詢索引。 - 也會說明如何設定自訂嵌入函式。如果擴充功能支援的 LLM 都不符合您的需求,而您想使用其他 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 擴充功能的 Vector Search 擴充功能提供少量功能,讓你不必連結應用程式,就能查詢文件索引。
- 在 Firebase 控制台的 Firestore 專區中,前往
_firestore-vector-search/index
文件 - 按一下「+ 開始集合」
- 建立名為「
queries
」的新子集合 - 建立新文件,並將
query
欄位設為出現在其中一張文件中的文字。這種做法最適合使用語意查詢,例如「如何使用 Swift 對應 Firestore 文件」(盡可能提供至少一則包含討論這個主題的文字)。 - 您可能會在狀態中看到錯誤
- 這是因為缺少索引。如要進行缺少的索引設定,請點選這個連結前往 Google Cloud 控制台,然後從清單中選取專案
- 現在 Cloud Log Explorer 應該會顯示「FAILED_PRECONDITION: Missing 向量索引設定」的錯誤訊息,請使用以下 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. 實作語意搜尋
現在終於可以將您的行動應用程式與含有 Firestore 擴充功能的 Vector Search 連結,並導入語意搜尋功能,讓使用者使用自然語言查詢搜尋記事。
連結可呼叫函式以執行查詢
搭配 Firestore 使用 Vector Search 擴充功能包含 Cloud 函式,您可以從行動應用程式呼叫,查詢先前在程式碼研究室中建立的索引。在這個步驟中,您將在行動應用程式和這項可呼叫函式之間建立連線。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 [] } }
連結使用者介面
如要允許使用者搜尋自己的記事,您必須在記事清單畫面實作搜尋列。使用者輸入搜尋字詞時,您必須叫用在上一個步驟中實作的 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 秒。這表示使用者暫停輸入超過 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 的搜尋修飾符和工作修飾詞實作搜尋列。
- 使用 Firestore SDK 的 Callable 介面呼叫 Cloud 函式,在資料庫上執行語意搜尋。
透過在本程式碼研究室中學到的知識,您現在可以建構功能強大的應用程式,運用 Cloud Firestore 的語意搜尋功能,為使用者提供更直覺化且更有效率的搜尋體驗。
如要進一步瞭解 Firestore 的新向量欄位以及如何計算向量嵌入項目,請參閱說明文件。