Create indexes and query vectors

Select a documentation version:

This document shows you how to use stored embeddings to generate indexes and query embeddings. For more information about storing embedding, see Store vector embeddings.

You can create ScaNN, IVF, IVFFlat, and HNSW indexes with AlloyDB.

Before you begin

Before you can start creating indexes, you must complete the following prerequisites.

  • Embedding vectors are added to a table in your AlloyDB database.

  • The vector extension version 0.5.0 or later that is based on pgvector, extended by Google for AlloyDB is installed.

    CREATE EXTENSION IF NOT EXISTS vector;
    
  • To generate ScaNN indexes, install the alloydb_scann extension in addition to the vector extension.

    CREATE EXTENSION IF NOT EXISTS alloydb_scann;
    

Create an index

You can create one of the following index types for tables in your database.

Create a HNSW index

AlloyDB supports creating a graph-based hnsw index available with stock pgvector using the AlloyDB pgvector extension. Using an hnsw index results in a greedy search that moves through the graph constly looking for neighbor closest to the query vector until it finds an optimum result. It provides faster query performance but slower build times when compared to IVF.

For more information about the HNSW algorithm, see Hierarchical navigable small World.

To create an hnsw index, run the following query:

CREATE INDEX INDEX_NAME ON TABLE
  USING hnsw (EMBEDDING_COLUMN DISTANCE_FUNCTION)
  WITH (m = NUMBER_OF_CONNECTIONS, ef_construction = 'CANDIDATE_LIST_SIZE');

Replace the following:

  • INDEX_NAME: the name of the index you want to create—for example, my-hnsw-index.

  • TABLE: the table to add the index to.

  • EMBEDDING_COLUMN: a column that stores vector data.

  • DISTANCE_FUNCTION: the distance function to use with this index. Choose one of the following:

    • L2 distance: vector_l2_ops

    • Inner product: vector_ip_ops

    • Cosine distance: vector_cosine_ops

  • NUMBER_OF_CONNECTIONS: the maximum number of connections per from a node in the graph. You can start with the default value as 16 and experiment with higher values based on the size of your dataset.

  • CANDIDATE_LIST_SIZE: the size of a candidate list maintained during graph construction, which constantly updates the current best candidates for nearest neighbors for a node. Set this value to any value higher than twice of the m value—for example, 64.

To view the indexing progress, use the pg_stat_progress_create_index view:

SELECT * FROM pg_stat_progress_create_index;

The phase column shows the current state of your index creation, and the building graph phase disappears after the index is created.

To tune your index for a target recall and QPS balance, see Tune an hnsw index.

Run a query

After you have stored and indexed embeddings in your database, you can start querying using the pgvector query functionality. You cannot run bulk search queries using the alloydb_scann extension.

To find the nearest semantic neighbors for an embedding vector, you can run the following example query, where you set the same distance function that you used during the index creation.

  SELECT * FROM TABLE
    ORDER BY EMBEDDING_COLUMN DISTANCE_FUNCTION_QUERY ['EMBEDDING']
    LIMIT ROW_COUNT

Replace the following:

  • TABLE: the table containing the embedding to compare the text to.

  • INDEX_NAME: the name of the index you want to use—for example, my-scann-index.

  • EMBEDDING_COLUMN: the column containing the stored embeddings.

  • DISTANCE_FUNCTION_QUERY: the distance function to use with this query. Choose one of the following based on the distance function used while creating the index:

    • L2 distance: <->

    • Inner product: <#>

    • Cosine distance: <=>

  • EMBEDDING: the embedding vector you want to find the nearest stored semantic neighbors of.

  • ROW_COUNT: the number of rows to return.

    Specify 1 if you want only the single best match.

For more information about other query examples, see Querying.

You can use also use the embedding() function to translate the text into a vector. You apply the vector to one of the pgvector nearest-neighbor operators, for example <-> for L2 distance, to find the database rows with the most semantically similar embeddings.

Because embedding() returns a real array, you must explicitly cast the embedding() call to vector in order to use these values with pgvector operators.

  CREATE EXTENSION IF NOT EXISTS google_ml_integration;
  CREATE EXTENSION IF NOT EXISTS vector;

  SELECT * FROM TABLE
    ORDER BY EMBEDDING_COLUMN::vector
    <-> embedding('MODEL_IDVERSION_TAG', 'TEXT')
    LIMIT ROW_COUNT

Replace the following:

  • MODEL_ID: the ID of the model to query.

    If you are using the Vertex AI Model Garden, then specify text-embedding-005 as the model ID. These are the cloud-based models that AlloyDB can use for text embeddings. For more information, see Text embeddings.

  • Optional: VERSION_TAG: the version tag of the model to query. Prepend the tag with @.

    If you are using one of the text-embedding English models with Vertex AI, then specify one of the version tags—for example, text-embedding-005, listed in Model versions.

    Google strongly recommends that you always specify the version tag. If you don't specify the version tag, then AlloyDB always uses the latest model version, which might lead to unexpected results.

  • TEXT: the text to translate into a vector embedding.

What's next