Wyszukiwanie z użyciem wektorów dystrybucyjnych wektorowych

Na tej stronie dowiesz się, jak za pomocą usługi Cloud Firestore wykonywać wyszukiwania wektorów z użyciem algorytmu najbliższego sąsiada (KNN) przy użyciu tych technik:

  • Zapisywanie wartości wektorów
  • Tworzenie indeksów wektorów KNN i zarządzanie nimi
  • Wykonaj zapytanie dotyczące najbliższych sąsiadów (KNN) za pomocą jednej z obsługiwanych miar odległości wektorowej.

Przechowywanie wektorów dystrybucyjnych

Z danych Cloud Firestore możesz tworzyć wartości wektorów, np. tekstowe wektory dystrybucyjne, i przechowywać je w dokumentach Cloud Firestore.

Operacja zapisu z wektorem zastępczym

Ten przykład pokazuje, jak zapisać wektor dystrybucyjny w dokumencie 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();

Obliczanie wektorów dystrybucyjnych za pomocą funkcji w Cloud Functions

Aby obliczać i przechowywać wektory zanurzeniowe po każdej aktualizacji lub utworzeniu dokumentu, możesz skonfigurować funkcję 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,
  });
}
Go
  // Not yet supported in the Go client library
Java
  // Not yet supported in the Java client library

Tworzenie indeksów wektorowych i zarządzanie nimi

Zanim wykonasz wyszukiwanie najbliższego sąsiada za pomocą wektorów zanurzonych, musisz utworzyć odpowiedni indeks. Poniższe przykłady pokazują, jak tworzyć indeksy wektorów i nimi zarządzać za pomocą interfejsu Google Cloud CLI. Indeksami wektorowymi można też zarządzać za pomocą wiersza poleceń Firebase i Terraform.

Tworzenie indeksu wektorowego

Zanim utworzysz indeks wektorowy, uaktualnij Google Cloud CLI do najnowszej wersji:

gcloud components update

Aby utworzyć indeks wektorów, użyj 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

gdzie:

  • collection-group to identyfikator grupy kolekcji.
  • vector-field to nazwa pola, które zawiera wektorową reprezentację.
  • database-id to identyfikator bazy danych.
  • vector-configuration obejmuje wektor dimension i typ indeksu. Wartość dimension to liczba całkowita do 2048. Typ indeksu musi być flat. Sformatuj konfigurację indeksu w ten sposób: {"dimension":"DIMENSION", "flat": "{}"}.

W tym przykładzie tworzymy indeks złożony, w tym indeks wektorowy dla pola vector-field i rosnący indeks dla pola color. Tego typu indeksu możesz używać do wstępnego filtrowania danych przed wyszukiwaniem najbliższych sąsiadów.

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

Wyświetlanie listy wszystkich indeksów wektorowych

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

Zastąp database-id identyfikatorem bazy danych.

Usuwanie indeksu wektorowego

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

gdzie:

  • index-id to identyfikator indeksu, który chcesz usunąć. Aby pobrać identyfikator indeksu, użyj polecenia indexes composite list.
  • database-id to identyfikator bazy danych.

Opisz indeks wektorowy

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

gdzie:

  • index-id to identyfikator indeksu, który chcesz opisać. Użyj polecenia indexes composite list, aby pobrać identyfikator indeksu.
  • database-id to identyfikator bazy danych.

Wykonywanie zapytania o najbliższego sąsiada

Możesz przeprowadzić wyszukiwanie podobieństwa, aby znaleźć najbliższych sąsiadów wektora. Wyszukiwanie podobieństwa wymaga indeksów wektorowych. Jeśli indeks nie istnieje, Cloud Firestore sugeruje utworzenie indeksu za pomocą gcloud CLI.

W tym przykładzie znaleziono 10 najbliższych sąsiadów wektora zapytania.

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();

Wektorowe odległości

Zapytania dotyczące najbliższego sąsiada obsługują te opcje odległości wektorowej:

  • EUCLIDEAN: mierzy odległość EUKLIDOWSKĄ między wektorami. Więcej informacji znajdziesz w artykule Euclidean.
  • COSINE: porównuje wektory na podstawie kąta między nimi, co umożliwia pomiar podobieństwa bez uwzględniania wielkości wektorów. Zalecamy użycie DOT_PRODUCT z jednostkowo znormalizowanymi wektorami zamiast odległości cosinusowej, która jest matematycznie równoważna, ale zapewnia lepszą wydajność. Aby dowiedzieć się więcej, zapoznaj się z artykułem Współczynnik cosinusa.
  • DOT_PRODUCT: podobny do COSINE, ale zależy od wielkości wektorów. Więcej informacji znajdziesz w artykule Produkt skalarny.

Wybierz jednostkę miary odległości

W zależności od tego, czy wszystkie wektory zastępcze są znormalizowane, możesz określić, która miara odległości ma być używana do znajdowania odległości. Normalizowany wektor embeddingu ma wielkość (długość) dokładnie 1,0.

Jeśli wiesz, z którego rodzaju miarą odległości był trenowany Twój model, możesz użyć tej miary do obliczenia odległości między wektorami osadzania.

Normalizowane dane

Jeśli masz zbiór danych, w którym wszystkie wektory osadzone są znormalizowane, wszystkie 3 wskaźniki odległości dają te same wyniki wyszukiwania semantycznego. Chociaż każda miara odległości zwraca inną wartość, w podstawie są one sortowane w taki sam sposób. Gdy wstępnie znormalizowane embeddingi są znormalizowane, DOT_PRODUCT jest zwykle najbardziej wydajnym rozwiązaniem pod względem obliczeń, ale w większości przypadków różnica jest znikoma. Jeśli jednak Twoja aplikacja jest bardzo wrażliwa na wydajność, DOT_PRODUCT może pomóc w jej optymalizacji.

Dane nieunormowane

Jeśli masz zbiór danych, w którym wektory dystrybucyjne nie są znormalizowane, użycie DOT_PRODUCT jako miary odległości nie jest matematycznie poprawne, ponieważ iloczyn skalarny nie mierzy odległości. W zależności od tego, jak zostały wygenerowane wektory i który typ wyszukiwania jest preferowany, wyniki wyszukiwania z użyciem odległości COSINE lub EUCLIDEAN są subiektywnie lepsze od wyników z użyciem innych miar odległości. Aby określić, która z nich najlepiej sprawdzi się w Twoim przypadku, konieczne może być przeprowadzenie eksperymentu z użyciem COSINE lub EUCLIDEAN.

Nie wiesz, czy dane są znormalizowane

Jeśli nie masz pewności, czy Twoje dane są znormalizowane, i chcesz użyć funkcji DOT_PRODUCT, zalecamy użycie funkcji COSINE. COSINE to funkcja DOT_PRODUCT z wbudowaną normalizacją. Odległość zmierzona za pomocą COSINE ma zakres od 0 do 2. Wynik zbliżony do 0 wskazuje, że wektory są bardzo podobne.

Filtrowanie wstępne dokumentów

Aby wstępnie odfiltrować dokumenty przed znalezieniem najbliższych sąsiadów, możesz połączyć wyszukiwanie podobieństwa z innymi operatorami zapytań. Obsługiwane są filtry złożone and i or. Więcej informacji o obsługiwanych filtrach pól znajdziesz w artykule Operatory zapytań.

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();

Pobieranie obliczonej odległości wektorowej

Aby pobrać obliczoną odległość wektorową, przypisz do zapytania FindNearest nazwę właściwości wyjściowej distance_result_field, jak pokazano w tym przykładzie:

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"));
}

Jeśli chcesz użyć maski pola, aby zwrócić podzbiór pól dokumentu wraz z polem distanceResultField, musisz też uwzględnić w niej wartość pola distanceResultField, jak w tym przykładzie:

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"));
}

Określanie progu odległości

Możesz określić próg podobieństwa, który zwraca tylko dokumenty w danym zakresie. Zachowanie pola progu zależy od wybranej miary odległości:

  • Odległości EUCLIDEANCOSINE ograniczają próg do dokumentów, w których odległość jest mniejsza lub równa określonemu progowi. Te miary odległości maleją, gdy wektory są do siebie coraz bardziej podobne.
  • DOT_PRODUCT odległość ogranicza próg do dokumentów, w których odległość jest większa lub równa określonemu progowi. Odległości w formie iloczynu skalarnego rosną, gdy wektory stają się bardziej podobne.

Ten przykład pokazuje, jak określić próg odległości, aby zwrócić maksymalnie 10 najbliższych dokumentów, które znajdują się w odległości maksymalnie 4,5 jednostek za pomocą miary odległości 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());
}

Ograniczenia

Podczas pracy z wektorami dystrybucyjnymi należy pamiętać o tych ograniczeniach:

  • Maksymalna obsługiwana wymiarość umieszczania to 2048. Aby przechowywać większe indeksy, użyj redukcji wymiarów.
  • Maksymalna liczba dokumentów zwracanych przez zapytanie o najbliższego sąsiada to 1000.
  • Wyszukiwanie wektorów nie obsługuje odbiorwników zrzutów w czasie rzeczywistym.
  • Wyszukiwanie wektorów obsługują tylko biblioteki klienta Python, Node.js, Go i Java.

Co dalej?