1. 總覽
在本程式碼研究室中,您將瞭解如何使用 Firestore 向量相似度搜尋功能,在應用程式中加入強大的搜尋功能。您將為以 Swift 和 SwiftUI 編寫的記事應用程式實作語意搜尋功能。
學習目標
- 如何安裝 Vector Search with Firestore 擴充功能來計算向量嵌入。
- 如何從 Swift 應用程式呼叫 Firebase Cloud Functions。
- 如何根據登入的使用者預先篩選資料。
您需要準備的項目
- Xcode 15.3
- 程式碼研究室範例程式碼。您會在程式碼研究室的後續步驟中下載這個檔案。
2. 建立及設定 Firebase 專案
如要使用 Firebase Vector Search 擴充功能,您需要 Firebase 專案。在本程式碼研究室的這一部分,您將建立新的 Firebase 專案,並啟用 Cloud Firestore 和 Firebase 驗證等必要服務。
建立 Firebase 專案
- 使用 Google 帳戶登入 Firebase 控制台。
- 按一下按鈕建立新專案,然後輸入專案名稱 (例如
Firestore Vector Search Codelab
)。
- 按一下「繼續」。
- 如果系統提示,請詳閱並接受 Firebase 條款,然後按一下「繼續」。
- (選用) 在 Firebase 控制台中啟用 AI 輔助功能 (稱為「Gemini in Firebase」)。
- 本程式碼研究室不需要 Google Analytics,因此請關閉 Google 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」。然後按一下「開始使用」。
- 您現在位於「驗證」資訊主頁,可以查看已註冊的使用者、設定登入供應商及管理設定。
- 選取「Sign-in method」分頁標籤 (或按這裡直接前往該分頁)。
- 在供應商選項中點選「Anonymous」,將切換鈕切換到「Enable」,然後點選「Save」。
設定 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」。
- 按一下「開始使用」。
- 選取預設 Storage bucket 的位置。
位於US-WEST1
、US-CENTRAL1
和US-EAST1
的 bucket 可享有 Google Cloud Storage 的「永久免費」方案。其他所有位置的值區均適用 Google Cloud Storage 定價和用量。 - 按一下「以測試模式啟動」。請詳閱安全性規則免責事項。
在本程式碼研究室的後續步驟中,您將新增安全性規則來保護資料。請勿在未為 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 應用程式。
- 在「將 Firebase 新增至 Apple 應用程式」畫面中,插入 Xcode 專案中的套件組合 ID (com.google.firebase.codelab.Notes)。
- 你可以視需要輸入應用程式暱稱 (例如「iOS 版記事」)。
- 點選「Register app」前往下一個步驟。
- 下載 GoogleServices-Info.plist 檔案。
- 將 GoogleServices-Info.plist 拖曳至 Xcode 專案的「Notes」資料夾。將檔案拖曳到 Assets.xcassets 檔案下方,就是不錯的做法。
- 選取「視需要複製項目」,確認「新增至目標」已選取「記事」目標,然後按一下「完成」。
- 現在您可以在 Firebase 控制台中點選其餘設定程序:您在本節開頭下載的範例已安裝 Firebase Apple SDK,並設定了初始化作業。按一下「繼續前往控制台」即可完成程序。
執行應用程式
現在可以試用應用程式了!
- 返回 Xcode,在 iOS 模擬器上執行應用程式。在「Run Destinations」下拉式選單中,先選取其中一個 iOS 模擬器。
- 然後點選「Run」按鈕,或按下 ⌘ + R 鍵
- 應用程式在模擬器上成功啟動後,請新增幾則記事。
- 在 Firebase 控制台中,前往 Firestore 資料瀏覽器,即可在應用程式中新增記事時,看到系統建立的新文件。
4. 安裝 Vector Search with Firestore 擴充功能
在本程式碼研究室的這一部分,您將安裝 Vector Search with Firestore 擴充功能,並根據您正在開發的記事應用程式需求進行設定。
開始安裝擴充功能
- 在 Firestore 部分中,按一下「擴充功能」分頁標籤。
- 按一下「探索 Extensions Hub」
。
- 輸入「vector」。
- 按一下「Vector Search with Firestore extension」。
系統會將你帶往擴充功能的詳細資料頁面,你可以在其中進一步瞭解擴充功能、運作方式、所需 Firebase 服務,以及如何設定擴充功能。
- 按一下「在 Firebase 控制台中安裝」。
- 系統會列出所有專案。
- 選取您在本程式碼研究室的第一個步驟中建立的專案。
設定擴充功能
- 查看已啟用的 API 和已建立的資源。
- 啟用必要服務。
- 啟用所有服務後,按一下「下一步」。
- 查看授予這項擴充功能的存取權。
- 設定擴充功能:
- 選取「Vertex AI」做為 LLM
- 集合路徑:notes
- 預設查詢限制:3
- 輸入欄位名稱:text
- 輸出欄位名稱: embedding
- 狀態欄位名稱:* *status*
- 嵌入現有文件:是
- 更新現有文件:是
- Cloud Functions 位置:us-central1
- 按一下「安裝擴充功能」即可完成安裝。
這可能需要幾分鐘的時間,等待安裝完成期間,歡迎前往教學課程的下一節,閱讀有關向量嵌入的背景資訊。
5. 背景
等待安裝完成期間,不妨先瞭解 Firestore 擴充功能的向量搜尋功能運作方式。
什麼是向量、嵌入和向量資料庫?
- 向量是數學物件,代表數量的量值和方向。可用於呈現資料,方便比較和搜尋。
- 嵌入是代表字詞或詞組意義的向量。這類模型是透過訓練類神經網路,並使用大量文字語料庫學習字詞間的關係而建立。
- 向量資料庫是專為儲存及搜尋向量資料而設計的資料庫。這類索引可有效率地執行最鄰近搜尋,也就是找出與指定查詢向量最相似的向量。
向量搜尋如何運作?
向量搜尋的運作方式是比較查詢向量與資料庫中的所有向量。系統會傳回與查詢向量最相似的向量做為搜尋結果。
您可以使用各種距離指標測量兩個向量之間的相似度。最常見的距離指標是餘弦相似度,用來測量兩個向量之間的角度。
6. 試用 Vector Search with Firestore 擴充功能
在您於本程式碼研究室稍早下載的 iOS 應用程式中使用 Vector Search with Firestore extension 之前,可以先在 Firebase 控制台中試用這項擴充功能。
閱讀說明文件
Firebase 擴充功能包含運作方式的說明文件。
- 擴充功能安裝完成後,按一下「開始使用」按鈕。
- 查看「這項擴充功能的運作方式」分頁,瞭解:
- 將文件加入
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 擴充功能的向量搜尋提供實用的小功能,可讓您查詢文件索引,不必連線至應用程式。
- 在 Firebase 控制台的 Firestore 專區中,前往
_firestore-vector-search/index
文件 - 按一下「+ 開始收藏」
- 建立名為
queries
的新子集合 - 建立新文件,並將
query
欄位設為其中一個文件中出現的文字。這項功能最適合語意查詢,例如「如何使用 Swift 對應 Firestore 文件」(前提是您新增的至少一則記事包含討論這個主題的文字)。 - 狀態中可能會顯示錯誤
- 這是因為缺少索引。如要設定缺少的索引設定,請按照這個連結前往專案的 Google Cloud 控制台,然後從清單中選取專案
- 在 Cloud Log Explorer 中,您現在應該會看到「FAILED_PRECONDITION: Missing vector index configuration. 請使用下列 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,這個文件就是最符合查詢條件的文件。
7. 實作語意搜尋
現在終於可以將行動應用程式連結至 Vector Search with Firestore 擴充功能,並實作語意搜尋功能,讓使用者透過自然語言查詢搜尋記事。
連結可呼叫的函式來執行查詢
Vector Search with Firestore 擴充功能包含 Cloud Function,您可以從行動應用程式呼叫該函式,查詢您在本程式碼研究室稍早建立的索引。在這個步驟中,您將在行動應用程式與這個可呼叫函式之間建立連線。Firebase 的 Swift SDK 包含可順暢呼叫遠端函式的 API。
- 返回 Xcode,並確認您位於本程式碼研究室上一個步驟中複製的專案。
- 開啟
NotesRepository.swift
檔案。 - 找出包含
private lazy var vectorSearchQueryCallable: Callable
的行= functions.httpsCallable("")
如要叫用可呼叫的 Cloud Functions,您必須提供要呼叫的函式名稱。
- 前往專案的 Firebase 控制台,然後開啟「建構」部分中的「函式」選單項目。
- 畫面上會顯示擴充功能安裝的函式清單。
- 搜尋名為
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 秒。也就是說,只有在使用者暫停輸入超過 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 控制台會顯示錯誤訊息:「The function was called with an invalid argument」(函式呼叫時使用無效引數)
這表示你傳送的資料格式有誤。
分析錯誤訊息
- 如要找出問題,請前往 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 的可呼叫介面,呼叫 Cloud 函式對資料庫執行語意搜尋。
您現在可以運用在本程式碼研究室中學到的知識,建構強大的應用程式,利用 Cloud Firestore 的語意搜尋功能,為使用者提供更直覺且有效率的搜尋體驗。
如要進一步瞭解 Firestore 的新向量欄位,以及如何計算向量嵌入,請參閱說明文件。