終於登場!?Firestore的全文搜索(向量搜索)

以前只能使用第三方工具,
現在終於可以在Firestore中進行全文搜索了!!

這次介紹的是基於向量搜索的全文搜索實施步驟。

前提

此次介紹的功能是預覽版。
隨著正式發佈,下列代碼可能無法執行。
參考:向量嵌入搜索:https://firebase.google.com/docs/firestore/vector-search

此次使用的Function和Vertex AI需要支付費用。
使用前請先確認。
參考:Vertex AI:https://cloud.google.com/vertex-ai/generative-ai/pricing?hl=zh

大致步驟

關於向量搜索會另寫文章介紹,
這次將使用Google提供的「Vertex AI」來計算用於向量搜索的向量。
此外,向量搜索目前只能使用Python或JavaScript(Node.js),
因此這次使用Google Cloud Function來進行搜索。

因此,為了進行搜索需要準備以下內容。

  • 創建Google Cloud Function
  • 配置Vertex AI
  • 獲取向量
  • 創建索引
  • 在Firestore中實施向量搜索的代碼

詳細步驟如下說明。

創建Google Cloud Function

這次創建的Cloud Functions環境如下。

  • 2nd gen(第二代)
  • https觸發器
  • Python3.12

還配置了以下運行時環境變數。

 名稱:GOOGLE_CLOUD_PROJECT
 值:<專案ID>(注意不是專案名稱)

因為Vertex AI需要與Cloud Run綁定,所以使用了創建GCF時會創建Cloud Run的
2nd gen(第二代)。

配置Google提供的Vertex AI

將Function的Cloud Run與Vertex AI綁定。
(參考官方文檔:https://cloud.google.com/run/docs/integrate/vertex-ai?authuser=3&hl=zh

這裡也列出了步驟供參考。

  1. 點擊右側「Powered by Cloud Run」下的鏈接,進入Cloud Run
  2. 點擊「整合」標籤
  3. 點擊「添加整合」
  4. 點擊「Vertex AI – 生成 AI」,設置任意名稱,然後點擊「提交」
    ※但名稱需要符合一定規則,否則會報錯。
     如果沒有特別要求,保持默認即可。
  5. 如果要求添加權限等,請進行批准

使用向量搜索的向量計算

這次是以擁有者權限進行的,所以沒有添加權限等操作,
在應用開發時需要為執行Function的帳號分配Vertex AI或Firestore的權限。

以下是使用Vertex AI計算向量並將數據存入Firestore的代碼。
(參考官方文檔:https://firebase.google.com/docs/firestore/vector-search

functions-framework==3.*
google-cloud-firestore
google-cloud-aiplatform
import functions_framework
import os
# firestore
from google.cloud import firestore
from google.cloud.firestore_v1.vector import Vector
# Vertex AI
import vertexai
from vertexai.language_models import TextEmbeddingModel


# 專案名稱(從環境變數中獲取)
MY_PROJECT_ID = os.environ.get("GOOGLE_CLOUD_PROJECT") 

# 從傳遞的字符串中計算向量值
def text_embedding(text: str) -> list:

    # 設置location為各自的地區
    vertexai.init(project=MY_PROJECT_ID, location="asia-northeast1") 

  # 使用最新的向量計算AI「textembedding-gecko@003」
    model = TextEmbeddingModel.from_pretrained("textembedding-gecko@003")
    embeddings = model.get_embeddings([text])
    for embedding in embeddings:
        vector = embedding.values

    return Vector(vector)


# 主處理
# (函數名可隨意命名)
@functions_framework.http
def hello_http(request):

    # 從請求中獲取文章摘要(description)
    request_json = request.get_json(silent=True)
    request_args = request.args

    if request_json and 'description' in request_json:
        description = request_json['description']
    elif request_args and 'description' in request_args:
        description = request_args['description']
    else:
        description = 'World'

    # 初始化Firestore客戶端
    firestore_client = firestore.Client(project=MY_PROJECT_ID)
    # 參考集合(集合名稱可隨意設置(如果集合不存在請提前創建))
    collection = firestore_client.collection("article_collection")

    # 計算embedding
    embedding_vector = text_embedding(description)

    # 準備要添加到Firestore的文檔
    doc = {
        "description": description,
        "embedding_field": embedding_vector
    }
    # 添加文檔
    collection.add(doc)

    return 'OK!'

這次比較簡單,直接在終端執行CLI測試命令來確認操作。

curl -m 70 -X POST https://asia-northeast1-python-tool-001.cloudfunctions.net/vector_chenge \
  -H "Authorization: bearer $(gcloud auth print-identity-token)" \
  -H "Content-Type: application/json" \
  -d '{ "description": "<任意字符串>"}'

如果執行成功,應該會在Firestore中看到如下數據。

為向量搜索創建索引

向量搜索需要創建索引。
這次在控制台執行以下命令創建索引。
(參考官方文檔:https://firebase.google.com/docs/firestore/vector-search

gcloud alpha firestore indexes composite create \
  --collection-group=article_collection \
  --query-scope=COLLECTION \
  --field-config field-path=embedding_field,vector-config='{"dimension":"768", "flat": "{}"}' \
  --database=<數據庫ID>
  • collection-group:創建索引的集合名稱
  • query-scope:這裡不太清楚,但似乎是創建索引的範圍
           可以指定多個集合(集合組)。
  • field-path:存放向量的字段名稱
  • vector-config:設置向量的維度數(這次是768維)
  • database:指定目標數據庫的ID。如果是default,可以不指定

執行後,應該會在Firestore中看到如下索引。

在Firestore中實施向量搜索的代碼

數據準備完成後,進行實際搜索。

這次準備了我的博客文章摘要作為搜索對象數據。
全文展示會很長,這裡省略部分內容。

No.標題
1freezed.dart無法生成時的應對方法 使用freezed設計不可變類時, 在終端執行「…
2什麼是Flutter的pubspec.yaml?介紹其意義和寫法!! YAML是YAML Ain’t Markup Language的簡稱, 用來簡潔地表示數據…
3應用開發中經常聽到的MVVM是什麼? MVVM(Model-View-ViewModel)是, 將應用邏輯和UI(用戶界面)分離, 以提高開發效率和可維護性…
4什麼是Flutter??解釋Flutter的概述 關於「Flutter」,有「在開發移動應用時很方便!」的認識 也許你知道,但為什麼方便,為什麼受歡迎呢?
5什麼是Riverpod?介紹Flutter最主流的狀態管理!! 以前介紹過的「StatefulWidget」也是一種狀態管理功能之一, 當實現具有多個屏幕和功能的應用時,管理…

執行的代碼如下。

import functions_framework
import os
# firestore
from google.cloud import firestore
from google.cloud.firestore_v1.vector import Vector
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
# Vertex AI
import vertexai
from vertexai.language_models import TextEmbeddingModel


# 專案名稱(從環境變數中獲取)
MY_PROJECT_ID = os.environ.get("GOOGLE_CLOUD_PROJECT") 

# 從傳遞的字符串中計算向量值
def text_embedding(text: str) -> list:

    # 設置location為各自的地區
    vertexai.init(project=MY_PROJECT_ID, location="asia-northeast1") 

  # 使用最新的向量計算AI「textembedding-gecko@003」
    model = TextEmbeddingModel.from_pretrained("textembedding-gecko@003")
    embeddings = model.get_embeddings([text])
    for embedding in embeddings:
        vector = embedding.values

    return Vector(vector)


# 主處理
# (函數名可隨意命名)
@functions_framework.http
def hello_http(request):

    # 從請求中獲取文章摘要(description)
    request_json = request.get_json(silent=True)
    request_args = request.args

    if request_json and 'target' in request_json:
        target = request_json['target']
    elif request_args and 'target' in request_args:
        target = request_args['target']
    else:
        target = 'World'

    # 初始化Firestore客戶端
    firestore_client = firestore.Client(project=MY_PROJECT_ID)
    # 參考集合
    collection = firestore_client.collection("article_collection")

    # 計算embedding
    embedding_vector = text_embedding(target)

    # 實施向量搜索
    docs = collection.find_nearest(
        vector_field="embedding_field",
        query_vector=embedding_vector,
        distance_measure=DistanceMeasure.COSINE,
        limit=3
    ).get()

    # 輸出為表格形式(這裡是字符串形式)
    output = "Description \n"
    output += "-" * 50 + "\n"
    
    # 輸出向量搜索獲得的文檔內容
    for doc in docs:
        doc_data = doc.to_dict()
        description = doc_data.get("description", "No description")
        # 將文檔內容添加到字符串
        output += f"{description[:100]} \n"
    
    return output

這次也在終端執行CLI測試命令來確認操作。
嘗試搜索「Riverpodについて」!

curl -m 70 -X POST https://asia-northeast1-python-tool-001.cloudfunctions.net/vector_search \
-H "Authorization: bearer $(gcloud auth print-identity-token)" \
-H "Content-Type: application/json" \
-d '{
  "target": &

quot;Riverpodについて"
}'

執行結果

Description 
--------------------------------------------------
什麼是Riverpod?介紹Flutter最主流的狀態管理!! 以前介紹過的「StatefulWidget」也是一種狀態管理功能之一, 當實現具有多個屏幕和功能的應用時,管理 
什麼是Flutter??解釋Flutter的概述 關於「Flutter」,有「在開發移動應用時很方便!」的認識 也許你知道,但為什麼方便,為什麼受歡迎呢? 
應用開發中經常聽到的MVVM是什麼? MVVM(Model-View-ViewModel)是, 將應用邏輯和UI(用戶界面)分離, 以提高開發效率和可維護性

雖然沒有進行排序,但第一條結果是Riverpod的文章!!
這次的數據無法判斷第二條和第三條是否選得較近。

最後

還需要增加數據量來更好地檢驗搜索精度。
向量數據大小約為768維,每個float為4字節,因此768✕4≒3KB。
文檔限制為1MB,所以感覺有點大。

雖然目前是預覽版,但Firestore可以進行全文搜索確實是個好消息。
期待今後的進展。

标题和URL已复制