ค้นหาด้วยการฝังเวกเตอร์

หน้านี้จะแสดงวิธีใช้ Cloud Firestore เพื่อทำการค้นหาเวกเตอร์เพื่อนบ้านใกล้เคียงที่สุด (KNN) โดยใช้เทคนิคต่อไปนี้

  • จัดเก็บค่าเวกเตอร์
  • สร้างและจัดการดัชนีเวกเตอร์ KNN
  • ทำการค้นหาแบบเพื่อนบ้านที่ใกล้ที่สุด K คน (KNN) โดยใช้การวัดระยะทางเวกเตอร์ที่รองรับ

จัดเก็บการฝังเวกเตอร์

คุณสามารถสร้างค่าเวกเตอร์ เช่น การฝังข้อความจากข้อมูล Cloud Firestore และจัดเก็บไว้ในเอกสาร 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([1.0, 2.0, 3.0]),
}

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])
});
Go
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 Function

หากต้องการคำนวณและจัดเก็บการฝังเวกเตอร์ทุกครั้งที่มีการสร้างหรืออัปเดตเอกสาร ให้ตั้งค่า Cloud Function ดังนี้

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,
  });
}
Go
  // 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 CLI เป็นเวอร์ชันล่าสุดโดยทำดังนี้

gcloud components update

หากต้องการสร้างดัชนีเวกเตอร์ ให้ใช้ gcloud firestore indexes composite create

gcloud
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

where:

  • collection-group คือรหัสของกลุ่มคอลเล็กชัน
  • vector-field คือชื่อของช่องที่มีการฝังเวกเตอร์
  • database-id คือรหัสของฐานข้อมูล
  • vector-configuration ประกอบด้วยเวกเตอร์ dimension และประเภทดัชนี dimension เป็นจํานวนเต็มไม่เกิน 2048 ประเภทดัชนีต้องเป็น flat จัดรูปแบบการกําหนดค่าดัชนีดังนี้ {"dimension":"DIMENSION", "flat": "{}"}

ตัวอย่างต่อไปนี้สร้างดัชนีผสม ซึ่งประกอบด้วยดัชนีเวกเตอร์สําหรับฟิลด์ vector-field และดัชนีตามลําดับจากน้อยไปมากสําหรับฟิลด์ color คุณสามารถใช้ดัชนีประเภทนี้เพื่อกรองข้อมูลล่วงหน้าก่อนการค้นหาเพื่อนบ้านที่ใกล้ที่สุด

gcloud
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

แสดงรายการดัชนีเวกเตอร์ทั้งหมด

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

แทนที่ database-id ด้วยรหัสของฐานข้อมูล

ลบดัชนีเวกเตอร์

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

where:

  • index-id คือรหัสของดัชนีที่จะลบ ใช้ indexes composite list เพื่อเรียกข้อมูลรหัสดัชนี
  • database-id คือรหัสของฐานข้อมูล

อธิบายดัชนีเวกเตอร์

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

where:

  • index-id คือรหัสของดัชนีที่จะอธิบาย ใช้ or indexes composite list เพื่อเรียกข้อมูลรหัสดัชนี
  • database-id คือรหัสของฐานข้อมูล

ทำการค้นหาเพื่อนบ้านที่ใกล้ที่สุด

คุณทำการค้นหาแบบความคล้ายคลึงเพื่อหาองค์ประกอบใกล้เคียงที่สุดของ embeddings เวกเตอร์ได้ การค้นหาความคล้ายคลึงต้องใช้ดัชนีเวกเตอร์ หากไม่มีดัชนี 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([3.0, 1.0, 2.0]),
    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();
Go
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 อย่างแน่นอน

นอกจากนี้ หากคุณทราบว่าโมเดลได้รับการฝึกด้วยวิธีการวัดระยะทางใด ให้ใช้วิธีการวัดระยะทางนั้นเพื่อคํานวณระยะห่างระหว่างการฝังเวกเตอร์

ข้อมูลมาตรฐาน

หากคุณมีชุดข้อมูลที่มีการทำให้เป็นมาตรฐานของเวกเตอร์การฝังทั้งหมด การวัดระยะทางทั้ง 3 รายการจะให้ผลการค้นหาเชิงความหมายเดียวกัน สรุปคือ แม้ว่าการวัดระยะทางแต่ละรายการจะแสดงผลค่าที่แตกต่างกัน แต่ค่าเหล่านั้นจะจัดเรียงในลักษณะเดียวกัน เมื่อมีการทำให้ค่าเชิงซ้อนเป็นมาตรฐาน 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([3.0, 1.0, 2.0]),
    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();
Go
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([3.0, 1.0, 2.0]),
    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'));
});
Go
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([3.0, 1.0, 2.0]),
    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'
    });
Go
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 distance จะจำกัดเกณฑ์ไว้ที่เอกสารที่มีระยะทางมากกว่าหรือเท่ากับเกณฑ์ที่ระบุ ระยะทางผลคูณเวกเตอร์จะเพิ่มขึ้นเมื่อเวกเตอร์มีความคล้ายคลึงกันมากขึ้น

ตัวอย่างต่อไปนี้แสดงวิธีระบุเกณฑ์ระยะทางเพื่อแสดงเอกสารที่อยู่ใกล้ที่สุดไม่เกิน 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([3.0, 1.0, 2.0]),
    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);
});
Go
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());
}

ข้อจำกัด

โปรดทราบว่าการฝังเวกเตอร์มีข้อจํากัดต่อไปนี้เมื่อใช้งาน

  • มิติข้อมูลการฝังสูงสุดที่รองรับคือ 2048 หากต้องการจัดเก็บดัชนีขนาดใหญ่ขึ้น ให้ใช้การลดมิติข้อมูล
  • จำนวนเอกสารสูงสุดที่จะแสดงจากการค้นหาเพื่อนบ้านที่ใกล้ที่สุดคือ 1,000 รายการ
  • การค้นหาเวกเตอร์ไม่รองรับเครื่องมือรับภาพรวมแบบเรียลไทม์
  • เฉพาะไลบรารีของไคลเอ็นต์ Python, Node.js, Go และ Java เท่านั้นที่รองรับการค้นหาเวกเตอร์

ขั้นตอนถัดไป