חיפוש באמצעות הטמעות וקטוריות

בדף הזה מוסבר איך משתמשים ב-Cloud Firestore כדי לבצע חיפושים וקטוריים של K-nearest neighbor ‏ (KNN) באמצעות השיטות הבאות:

  • אחסון ערכי וקטורים
  • יצירה וניהול של אינדקסים וקטוריים של KNN
  • מבצעים שאילתת K-nearest-neighbor ‏ (KNN) באמצעות אחד ממדדי המרחק של הווקטורים הנתמכים

לפני שמתחילים

לפני שמאחסנים הטמעות ב-Cloud Firestore, צריך ליצור הטמעות וקטורים. ‫Cloud Firestore לא יוצר את ההטמעות. אתם יכולים להשתמש בשירות כמו Agent Platform כדי ליצור ערכי וקטורים, למשל הטמעות טקסט מהנתונים שלכם ב-Cloud Firestore. אחר כך אפשר לאחסן את ההטמעות האלה בחזרה במסמכי Cloud Firestore.

מידע נוסף על הטמעות זמין במאמר מהן הטמעות?

במאמר איך יוצרים הטמעות טקסט מוסבר איך יוצרים הטמעות טקסט באמצעות Agent Platform.

אחסון הטמעות וקטורים

בדוגמאות הבאות מוסבר איך לאחסן הטמעות וקטורים ב-Cloud Firestore.

פעולת כתיבה עם הטמעת וקטורים

בדוגמה הבאה מוסבר איך לאחסן הטמעת וקטורים במסמך Cloud Firestore:

Python
from google.cloud import firestore
from google.cloud.firestore_v1.vector import Vector

firestore_client = firestore.Client()
collection = firestore_client.collection("coffee-beans")
doc = {
    "name": "Kahawa coffee beans",
    "description": "Information about the Kahawa coffee beans.",
    "embedding_field": Vector([0.18332680, 0.24160706, 0.3416704]),
}

collection.add(doc)
Node.js
import {
  Firestore,
  FieldValue,
} from "@google-cloud/firestore";

const db = new Firestore();
const coll = db.collection('coffee-beans');
await coll.add({
  name: "Kahawa coffee beans",
  description: "Information about the Kahawa coffee beans.",
  embedding_field: FieldValue.vector([1.0 , 2.0, 3.0])
});
המשך
import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/firestore"
)

type CoffeeBean struct {
	Name           string             `firestore:"name,omitempty"`
	Description    string             `firestore:"description,omitempty"`
	EmbeddingField firestore.Vector32 `firestore:"embedding_field,omitempty"`
	Color          string             `firestore:"color,omitempty"`
}

func storeVectors(w io.Writer, projectID string) error {
	ctx := context.Background()

	// Create client
	client, err := firestore.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("firestore.NewClient: %w", err)
	}
	defer client.Close()

	// Vector can be represented by Vector32 or Vector64
	doc := CoffeeBean{
		Name:           "Kahawa coffee beans",
		Description:    "Information about the Kahawa coffee beans.",
		EmbeddingField: []float32{1.0, 2.0, 3.0},
		Color:          "red",
	}
	ref := client.Collection("coffee-beans").NewDoc()
	if _, err = ref.Set(ctx, doc); err != nil {
		fmt.Fprintf(w, "failed to upsert: %v", err)
		return err
	}

	return nil
}
Java
import com.google.cloud.firestore.CollectionReference;
import com.google.cloud.firestore.DocumentReference;
import com.google.cloud.firestore.FieldValue;
import com.google.cloud.firestore.VectorQuery;

CollectionReference coll = firestore.collection("coffee-beans");

Map<String, Object> docData = new HashMap<>();
docData.put("name", "Kahawa coffee beans");
docData.put("description", "Information about the Kahawa coffee beans.");
docData.put("embedding_field", FieldValue.vector(new double[] {1.0, 2.0, 3.0}));

ApiFuture<DocumentReference> future = coll.add(docData);
DocumentReference documentReference = future.get();

חישוב הטמעות וקטורים באמצעות פונקציה של Cloud Functions

כדי לחשב ולאחסן הטמעות וקטורים בכל פעם שמסמך מתעדכן או נוצר, אפשר להגדיר פונקציה של Cloud Functions:

Python
@functions_framework.cloud_event
def store_embedding(cloud_event) -> None:
  """Triggers by a change to a Firestore document.
  """
  firestore_payload = firestore.DocumentEventData()
  payload = firestore_payload._pb.ParseFromString(cloud_event.data)

  collection_id, doc_id = from_payload(payload)
  # Call a function to calculate the embedding
  embedding = calculate_embedding(payload)
  # Update the document
  doc = firestore_client.collection(collection_id).document(doc_id)
  doc.set({"embedding_field": embedding}, merge=True)
Node.js
/**
 * A vector embedding will be computed from the
 * value of the `content` field. The vector value
 * will be stored in the `embedding` field. The
 * field names `content` and `embedding` are arbitrary
 * field names chosen for this example.
 */
async function storeEmbedding(event: FirestoreEvent<any>): Promise<void> {
  // Get the previous value of the document's `content` field.
  const previousDocumentSnapshot = event.data.before as QueryDocumentSnapshot;
  const previousContent = previousDocumentSnapshot.get("content");

  // Get the current value of the document's `content` field.
  const currentDocumentSnapshot = event.data.after as QueryDocumentSnapshot;
  const currentContent = currentDocumentSnapshot.get("content");

  // Don't update the embedding if the content field did not change
  if (previousContent === currentContent) {
    return;
  }

  // Call a function to calculate the embedding for the value
  // of the `content` field.
  const embeddingVector = calculateEmbedding(currentContent);

  // Update the `embedding` field on the document.
  await currentDocumentSnapshot.ref.update({
    embedding: embeddingVector,
  });
}
המשך
  // Not yet supported in the Go client library
Java
  // Not yet supported in the Java client library

יצירה וניהול של אינדקסים וקטוריים

כדי לבצע חיפוש של השכן הקרוב ביותר באמצעות הטמעות הווקטורים, צריך ליצור אינדקס תואם. בדוגמאות הבאות מוסבר איך ליצור ולנהל אינדקסים של וקטורים באמצעות Google Cloud CLI והמסוף. אפשר גם לנהל אינדקסים של וקטורים באמצעות Firebase CLI ו-Terraform.

יצירת אינדקס וקטורי

מסוף Google Cloud

כדי ליצור אינדקס חדש באופן ידני ממסוף Google Cloud:

  1. נכנסים לדף Databases במסוף Google Cloud.

    כניסה לדף Databases

  2. בוחרים את מסד הנתונים הרצוי מרשימת מסדי הנתונים.
  3. בתפריט הניווט, לוחצים על Indexes ואז על הכרטיסייה Manual.
  4. לוחצים על Create Index.

    כדי להוסיף לאינדקס שדה וקטורי לחיפושים וקטוריים, בוחרים באפשרות Create vector index.

  5. מזינים מזהה אוסף. מזינים נתיב של שדה וקטורי ואת מספר הממדים של הטמעת הווקטורים. מוסיפים את השמות של שדות נוספים שרוצים להוסיף לאינדקס ומצב אינדקס לכל שדה.

    לוחצים על Save Index.

האינדקס החדש יופיע ברשימת האינדקסים הידניים, והמערכת של Cloud Firestore תתחיל ליצור את האינדקס. כשהאינדקס ייווצר, יופיע סימן וי ירוק ליד האינדקס.

gcloud

לפני שיוצרים אינדקס וקטורי, משדרגים לגרסה העדכנית של Google Cloud CLI:

gcloud components update

כדי ליצור אינדקס וקטורי, משתמשים ב-gcloud firestore indexes composite create:

gcloud firestore indexes composite create \
--collection-group=collection-group \
--query-scope=COLLECTION \
--field-config field-path=vector-field,vector-config='vector-configuration' \
--database=database-id

כאשר:

  • ‫collection-group הוא המזהה של קבוצת האוספים.
  • ‫vector-field הוא שם השדה שמכיל את הטמעת הווקטורים.
  • ‫database-id הוא המזהה של מסד הנתונים.
  • השדה vector-configuration כולל את הווקטור dimension ואת סוג האינדקס. הערך של dimension הוא מספר שלם עד 2048. סוג האינדקס חייב להיות flat. מגדירים את האינדקס בפורמט הבא: {"dimension":"DIMENSION", "flat": "{}"}.

בדוגמה הבאה נוצר אינדקס מורכב, שכולל אינדקס וקטורי לשדה vector-field ואינדקס בסדר עולה לשדה color. אתם יכולים להשתמש בסוג הזה של אינדקס כדי לסנן מראש נתונים לפני חיפוש של השכן הקרוב ביותר.

gcloud firestore indexes composite create \
--collection-group=collection-group \
--query-scope=COLLECTION \
--field-config=order=ASCENDING,field-path="color" \
--field-config field-path=vector-field,vector-config='{"dimension":"1024", "flat": "{}"}' \
--database=database-id

הצגת רשימה של כל האינדקסים של הווקטורים

מסוף Google Cloud

  1. נכנסים לדף Databases במסוף Google Cloud.

    כניסה לדף Databases

  2. בוחרים את מסד הנתונים הרצוי מרשימת מסדי הנתונים.
  3. בתפריט הניווט, לוחצים על Indexes ואז על הכרטיסייה Manual.

    בטבלת האינדקסים מופיעים כל האינדקסים של מסד הנתונים. אינדקסים של וקטורים כוללים שדה וקטור עם הסמל .

gcloud

כדי להציג את כל האינדקסים ולאחזר את המזהים שלהם:

gcloud firestore indexes composite list --database=database-id

מחליפים את database-id במזהה של מסד הנתונים.

אתם יכולים להשתמש במזהה האינדקס כדי לראות פרטים נוספים על האינדקס:

gcloud firestore indexes composite describe index-id --database=database-id

כאשר:

  • ‫index-id הוא המזהה של האינדקס שרוצים לתאר.
  • ‫database-id הוא המזהה של מסד הנתונים.

מחיקת אינדקס וקטורי

מסוף Google Cloud

  1. נכנסים לדף Databases במסוף Google Cloud.

    כניסה לדף Databases

  2. בוחרים את מסד הנתונים הרצוי מרשימת מסדי הנתונים.
  3. בתפריט הניווט, לוחצים על Indexes ואז על הכרטיסייה Manual.

  4. ברשימת האינדקסים הידניים, לוחצים על הלחצן More של האינדקס שרוצים למחוק. לוחצים על מחיקה.
  5. מאשרים שרוצים למחוק את האינדקס הזה על ידי לחיצה על Delete Index בהתראה.
gcloud
gcloud firestore indexes composite delete index-id --database=database-id

כאשר:

  • ‫index-id הוא המזהה של האינדקס שרוצים למחוק. משתמשים ב-indexes composite list כדי לאחזר את מזהה האינדקס.
  • ‫database-id הוא המזהה של מסד הנתונים.

יצירת שאילתת השכן הקרוב ביותר

אתם יכולים לבצע חיפוש דמיון כדי למצוא את השכנים הקרובים ביותר של הטמעת וקטורים. כדי לבצע חיפושי דמיון, צריך אינדקסים וקטוריים. אם לא קיים אינדקס, Cloud Firestore מציע אינדקס ליצירה באמצעות gcloud CLI.

בדוגמה הבאה מוצגים 10 השכנים הקרובים ביותר של וקטור השאילתה.

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

# Requires a single-field vector index
vector_query = collection.find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([0.3416704, 0.18332680, 0.24160706]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=5,
)
Node.js
import {
  Firestore,
  FieldValue,
  VectorQuery,
  VectorQuerySnapshot,
} from "@google-cloud/firestore";

// Requires a single-field vector index
const vectorQuery: VectorQuery = coll.findNearest({
  vectorField: 'embedding_field',
  queryVector: [3.0, 1.0, 2.0],
  limit: 10,
  distanceMeasure: 'EUCLIDEAN'
});

const vectorQuerySnapshot: VectorQuerySnapshot = await vectorQuery.get();
המשך
import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/firestore"
)

func vectorSearchBasic(w io.Writer, projectID string) error {
	ctx := context.Background()

	// Create client
	client, err := firestore.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("firestore.NewClient: %w", err)
	}
	defer client.Close()

	collection := client.Collection("coffee-beans")

	// Requires a vector index
	// https://firebase.google.com/docs/firestore/vector-search#create_and_manage_vector_indexes
	vectorQuery := collection.FindNearest("embedding_field",
		[]float32{3.0, 1.0, 2.0},
		5,
		// More info: https://firebase.google.com/docs/firestore/vector-search#vector_distances
		firestore.DistanceMeasureEuclidean,
		nil)

	docs, err := vectorQuery.Documents(ctx).GetAll()
	if err != nil {
		fmt.Fprintf(w, "failed to get vector query results: %v", err)
		return err
	}

	for _, doc := range docs {
		fmt.Fprintln(w, doc.Data()["name"])
	}
	return nil
}
Java
import com.google.cloud.firestore.VectorQuery;
import com.google.cloud.firestore.VectorQuerySnapshot;

VectorQuery vectorQuery = coll.findNearest(
        "embedding_field",
        new double[] {3.0, 1.0, 2.0},
        /* limit */ 10,
        VectorQuery.DistanceMeasure.EUCLIDEAN);

ApiFuture<VectorQuerySnapshot> future = vectorQuery.get();
VectorQuerySnapshot vectorQuerySnapshot = future.get();

מרחקים וקטוריים

שאילתות של השכן הקרוב ביותר תומכות באפשרויות הבאות למרחק וקטורי:

  • EUCLIDEAN: מודד את המרחק EUCLIDEAN בין הווקטורים. מידע נוסף זמין במאמר Euclidean.
  • ‫COSINE: השוואה בין וקטורים על סמך הזווית ביניהם, שמאפשרת למדוד דמיון שלא מבוסס על גודל הווקטורים. אנחנו ממליצים להשתמש ב-DOT_PRODUCT עם וקטורים מנורמלים של יחידות במקום במרחק COSINE, שהוא שווה ערך מבחינה מתמטית עם ביצועים טובים יותר. מידע נוסף זמין במאמר דמיון קוסינוס.
  • ‫DOT_PRODUCT: דומה ל-COSINE אבל מושפע מגודל הווקטורים. מידע נוסף על מכפלה סקלרית

בחירת יחידת המידה של המרחק

בהתאם לשאלה אם כל הטמעות הווקטורים שלכם מנורמלות או לא, אתם יכולים לקבוע באיזו מידת מרחק להשתמש כדי למצוא את מידת המרחק. להטמעת וקטורים מנורמלת יש גודל (אורך) של 1.0 בדיוק.

בנוסף, אם אתם יודעים באיזו מידת מרחק המודל אומן, השתמשו בה כדי לחשב את המרחק בין הטמעות הווקטורים.

נתונים מנורמלים

אם יש לכם מערך נתונים שבו כל הטמעות הווקטורים מנורמלות, כל שלושת מדדי המרחק יספקו את אותן תוצאות חיפוש סמנטי. בעצם, למרות שכל מדד מרחק מחזיר ערך שונה, הערכים האלה ממוינים באותו אופן. כשמנרמלים את ההטמעות, DOT_PRODUCT היא בדרך כלל השיטה היעילה ביותר מבחינה חישובית, אבל ברוב המקרים ההבדל זניח. עם זאת, אם האפליקציה רגישה מאוד לביצועים, יכול להיות ש-DOT_PRODUCT יעזור לכם לשפר את הביצועים.

נתונים לא מנורמלים

אם יש לכם מערך נתונים שבו הטמעות הווקטורים לא מנורמלות, אז מבחינה מתמטית לא נכון להשתמש ב-DOT_PRODUCT כמדד מרחק, כי מכפלה סקלרית לא מודדת מרחק. בהתאם לאופן שבו נוצרו ההטמעות ולסוג החיפוש המועדף, מדד המרחק COSINE או EUCLIDEAN מניב תוצאות חיפוש שהן טובות יותר באופן סובייקטיבי ממדדי המרחק האחרים. כדי לקבוע מה הכי מתאים לתרחיש לדוגמה שלכם, יכול להיות שתצטרכו להתנסות ב-COSINE או ב-EUCLIDEAN.

לא בטוחים אם הנתונים מנורמלים או לא מנורמלים

אם אתם לא בטוחים אם הנתונים שלכם מנורמלים ואתם רוצים להשתמש ב-DOT_PRODUCT, מומלץ להשתמש ב-COSINE במקום זאת. COSINE דומה ל-DOT_PRODUCT עם נרמול מובנה. המרחק שנמדד באמצעות COSINE נע בין 0 ל-2. תוצאה שקרובה ל-0 מצביעה על כך שהווקטורים דומים מאוד.

סינון מקדים של מסמכים

כדי לסנן מראש מסמכים לפני שמוצאים את השכנים הקרובים ביותר, אפשר לשלב חיפוש דמיון עם אופרטורים אחרים של שאילתות. יש תמיכה במסננים המורכבים and ו-or. מידע נוסף על מסנני שדות נתמכים זמין במאמר אופרטורים של שאילתות.

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

# Similarity search with pre-filter
# Requires a composite vector index
vector_query = collection.where("color", "==", "red").find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([0.3416704, 0.18332680, 0.24160706]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=5,
)
Node.js
// Similarity search with pre-filter
// Requires composite vector index
const preFilteredVectorQuery: VectorQuery = coll
    .where("color", "==", "red")
    .findNearest({
      vectorField: "embedding_field",
      queryVector: [3.0, 1.0, 2.0],
      limit: 5,
      distanceMeasure: "EUCLIDEAN",
    });

const vectorQueryResults = await preFilteredVectorQuery.get();
המשך
import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/firestore"
)

func vectorSearchPrefilter(w io.Writer, projectID string) error {
	ctx := context.Background()

	// Create client
	client, err := firestore.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("firestore.NewClient: %w", err)
	}
	defer client.Close()

	collection := client.Collection("coffee-beans")

	// Similarity search with pre-filter
	// Requires a composite vector index
	vectorQuery := collection.Where("color", "==", "red").
		FindNearest("embedding_field",
			[]float32{3.0, 1.0, 2.0},
			5,
			// More info: https://firebase.google.com/docs/firestore/vector-search#vector_distances
			firestore.DistanceMeasureEuclidean,
			nil)

	docs, err := vectorQuery.Documents(ctx).GetAll()
	if err != nil {
		fmt.Fprintf(w, "failed to get vector query results: %v", err)
		return err
	}

	for _, doc := range docs {
		fmt.Fprintln(w, doc.Data()["name"])
	}
	return nil
}
Java
import com.google.cloud.firestore.VectorQuery;
import com.google.cloud.firestore.VectorQuerySnapshot;

VectorQuery preFilteredVectorQuery = coll
        .whereEqualTo("color", "red")
        .findNearest(
                "embedding_field",
                new double[] {3.0, 1.0, 2.0},
                /* limit */ 10,
                VectorQuery.DistanceMeasure.EUCLIDEAN);

ApiFuture<VectorQuerySnapshot> future = preFilteredVectorQuery.get();
VectorQuerySnapshot vectorQuerySnapshot = future.get();

אחזור של מרחק הווקטור שחושב

כדי לאחזר את מרחק הווקטור המחושב, מקצים שם למאפיין הפלט distance_result_field בשאילתה FindNearest, כמו בדוגמה הבאה:

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

vector_query = collection.find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([0.3416704, 0.18332680, 0.24160706]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=10,
    distance_result_field="vector_distance",
)

docs = vector_query.stream()

for doc in docs:
    print(f"{doc.id}, Distance: {doc.get('vector_distance')}")
Node.js
const vectorQuery: VectorQuery = coll.findNearest(
    {
      vectorField: 'embedding_field',
      queryVector: [3.0, 1.0, 2.0],
      limit: 10,
      distanceMeasure: 'EUCLIDEAN',
      distanceResultField: 'vector_distance'
    });

const snapshot: VectorQuerySnapshot = await vectorQuery.get();

snapshot.forEach((doc) => {
  console.log(doc.id, ' Distance: ', doc.get('vector_distance'));
});
המשך
import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/firestore"
)

func vectorSearchDistanceResultField(w io.Writer, projectID string) error {
	ctx := context.Background()

	client, err := firestore.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("firestore.NewClient: %w", err)
	}
	defer client.Close()

	collection := client.Collection("coffee-beans")

	// Requires a vector index
	// https://firebase.google.com/docs/firestore/vector-search#create_and_manage_vector_indexes
	vectorQuery := collection.FindNearest("embedding_field",
		[]float32{3.0, 1.0, 2.0},
		10,
		firestore.DistanceMeasureEuclidean,
		&firestore.FindNearestOptions{
			DistanceResultField: "vector_distance",
		})

	docs, err := vectorQuery.Documents(ctx).GetAll()
	if err != nil {
		fmt.Fprintf(w, "failed to get vector query results: %v", err)
		return err
	}

	for _, doc := range docs {
		fmt.Fprintf(w, "%v, Distance: %v\n", doc.Data()["name"], doc.Data()["vector_distance"])
	}
	return nil
}
Java
import com.google.cloud.firestore.VectorQuery;
import com.google.cloud.firestore.VectorQueryOptions;
import com.google.cloud.firestore.VectorQuerySnapshot;

VectorQuery vectorQuery = coll.findNearest(
        "embedding_field",
        new double[] {3.0, 1.0, 2.0},
        /* limit */ 10,
        VectorQuery.DistanceMeasure.EUCLIDEAN,
        VectorQueryOptions.newBuilder().setDistanceResultField("vector_distance").build());

ApiFuture<VectorQuerySnapshot> future = vectorQuery.get();
VectorQuerySnapshot vectorQuerySnapshot = future.get();

for (DocumentSnapshot document : vectorQuerySnapshot.getDocuments()) {
    System.out.println(document.getId() + " Distance: " + document.get("vector_distance"));
}

אם אתם רוצים להשתמש במסכת שדות כדי להחזיר קבוצת משנה של שדות מסמך יחד עם distanceResultField, אתם צריכים לכלול גם את הערך של distanceResultField במסכת השדות, כמו בדוגמה הבאה:

Python
vector_query = collection.select(["color", "vector_distance"]).find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([0.3416704, 0.18332680, 0.24160706]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=10,
    distance_result_field="vector_distance",
)
Node.js
const vectorQuery: VectorQuery = coll
    .select('name', 'description', 'vector_distance')
    .findNearest({
      vectorField: 'embedding_field',
      queryVector: [3.0, 1.0, 2.0],
      limit: 10,
      distanceMeasure: 'EUCLIDEAN',
      distanceResultField: 'vector_distance'
    });
המשך
import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/firestore"
)

func vectorSearchDistanceResultFieldMasked(w io.Writer, projectID string) error {
	ctx := context.Background()

	client, err := firestore.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("firestore.NewClient: %w", err)
	}
	defer client.Close()

	collection := client.Collection("coffee-beans")

	// Requires a vector index
	// https://firebase.google.com/docs/firestore/vector-search#create_and_manage_vector_indexes
	vectorQuery := collection.Select("color", "vector_distance").
		FindNearest("embedding_field",
			[]float32{3.0, 1.0, 2.0},
			10,
			firestore.DistanceMeasureEuclidean,
			&firestore.FindNearestOptions{
				DistanceResultField: "vector_distance",
			})

	docs, err := vectorQuery.Documents(ctx).GetAll()
	if err != nil {
		fmt.Fprintf(w, "failed to get vector query results: %v", err)
		return err
	}

	for _, doc := range docs {
		fmt.Fprintf(w, "%v, Distance: %v\n", doc.Data()["color"], doc.Data()["vector_distance"])
	}
	return nil
}
Java
import com.google.cloud.firestore.VectorQuery;
import com.google.cloud.firestore.VectorQueryOptions;
import com.google.cloud.firestore.VectorQuerySnapshot;

VectorQuery vectorQuery = coll
        .select("name", "description", "vector_distance")
        .findNearest(
          "embedding_field",
          new double[] {3.0, 1.0, 2.0},
          /* limit */ 10,
          VectorQuery.DistanceMeasure.EUCLIDEAN,
          VectorQueryOptions.newBuilder()
            .setDistanceResultField("vector_distance")
            .build());

ApiFuture<VectorQuerySnapshot> future = vectorQuery.get();
VectorQuerySnapshot vectorQuerySnapshot = future.get();

for (DocumentSnapshot document : vectorQuerySnapshot.getDocuments()) {
    System.out.println(document.getId() + " Distance: " + document.get("vector_distance"));
}

הגדרת סף מרחק

אתם יכולים לציין מידת דמיון מינימלית כדי לקבל רק מסמכים שעומדים בה. ההתנהגות של שדה הסף תלויה במדד המרחק שבוחרים:

  • המרחקים EUCLIDEAN ו-COSINE מגבילים את הסף למסמכים שבהם המרחק קטן מהסף שצוין או שווה לו. המרחקים האלה קטנים ככל שהווקטורים דומים יותר.
  • ‫DOT_PRODUCT מגבילה את הסף למסמכים שבהם המרחק גדול מהסף שצוין או שווה לו. המרחקים של המכפלה הסקלרית גדלים ככל שהווקטורים דומים יותר.

בדוגמה הבאה מוסבר איך לציין סף מרחק כדי להחזיר עד 10 מסמכים קרובים ביותר שנמצאים במרחק של 4.5 יחידות לכל היותר, באמצעות מדד המרחק EUCLIDEAN:

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

vector_query = collection.find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([0.3416704, 0.18332680, 0.24160706]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=10,
    distance_threshold=4.5,
)

docs = vector_query.stream()

for doc in docs:
    print(f"{doc.id}")
Node.js
const vectorQuery: VectorQuery = coll.findNearest({
  vectorField: 'embedding_field',
  queryVector: [3.0, 1.0, 2.0],
  limit: 10,
  distanceMeasure: 'EUCLIDEAN',
  distanceThreshold: 4.5
});

const snapshot: VectorQuerySnapshot = await vectorQuery.get();

snapshot.forEach((doc) => {
  console.log(doc.id);
});
המשך
import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/firestore"
)

func vectorSearchDistanceThreshold(w io.Writer, projectID string) error {
	ctx := context.Background()

	client, err := firestore.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("firestore.NewClient: %w", err)
	}
	defer client.Close()

	collection := client.Collection("coffee-beans")

	// Requires a vector index
	// https://firebase.google.com/docs/firestore/vector-search#create_and_manage_vector_indexes
	vectorQuery := collection.FindNearest("embedding_field",
		[]float32{3.0, 1.0, 2.0},
		10,
		firestore.DistanceMeasureEuclidean,
		&firestore.FindNearestOptions{
			DistanceThreshold: firestore.Ptr[float64](4.5),
		})

	docs, err := vectorQuery.Documents(ctx).GetAll()
	if err != nil {
		fmt.Fprintf(w, "failed to get vector query results: %v", err)
		return err
	}

	for _, doc := range docs {
		fmt.Fprintln(w, doc.Data()["name"])
	}
	return nil
}
Java
import com.google.cloud.firestore.VectorQuery;
import com.google.cloud.firestore.VectorQueryOptions;
import com.google.cloud.firestore.VectorQuerySnapshot;

VectorQuery vectorQuery = coll.findNearest(
        "embedding_field",
        new double[] {3.0, 1.0, 2.0},
        /* limit */ 10,
        VectorQuery.DistanceMeasure.EUCLIDEAN,
        VectorQueryOptions.newBuilder()
          .setDistanceThreshold(4.5)
          .build());

ApiFuture<VectorQuerySnapshot> future = vectorQuery.get();
VectorQuerySnapshot vectorQuerySnapshot = future.get();

for (DocumentSnapshot document : vectorQuerySnapshot.getDocuments()) {
    System.out.println(document.getId());
}

מגבלות

כשעובדים עם הטמעות וקטורים, חשוב לשים לב למגבלות הבאות:

  • הגודל המקסימלי של מאפיין ההטמעה הוא 2,048. כדי לאחסן אינדקסים גדולים יותר, משתמשים בהפחתת ממדים.
  • מספר המסמכים המקסימלי שאפשר להחזיר משאילתת השכן הקרוב ביותר הוא 1,000 (מגבלה של מהדורת Standard בלבד).
  • חיפוש וקטורי לא תומך במאזינים לתמונות מצב בזמן אמת.
  • רק ספריות הלקוח של Python,‏ Node.js,‏ Go ו-Java תומכות בחיפוש וקטורי.

המאמרים הבאים