This page describes how to use stored embeddings to generate indexes and query
embeddings using ScaNN 
index with AlloyDB for PostgreSQL.
For more information about storing embedding, see Store vector embeddings 
.
AlloyDB alloydb_scann 
, a
PostgreSQL extension developed by Google that implements a highly
efficient nearest-neighbor index powered by the ScaNN
algorithm 
.
The ScaNN 
index is a tree-based quantization index for approximate
nearest neighbor search. It provides lower index building time and smaller
memory footprint as compared to HNSW 
. In addition, it provides faster QPS in
comparison to HNSW 
based on the workload.
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 vectorextension that is based onpgvector, extended by Google for AlloyDB, and thealloydb_scannextension is installed:CREATE EXTENSION IF NOT EXISTS alloydb_scann CASCADE ;
-  If you want to create automatically tuned ScaNN indexes, make sure that the scann.enable_preview_featuresflag is enabled. If you don't want to enable preview features, or for production instances, you can create a ScaNN index with specific parameters instead.
Create an automatically tuned ScaNN index
With the auto index feature, you can simplify index creation to automatically create indexes that are optimized for search performance or balanced index build times and search performance.
When you use the AUTO 
mode, you only need to specify the table name and embedding column along with the distance function that you want to use. You can optimize the index for search performance or balance between index build times and search performance.
There is also an option to use the MANUAL 
mode to create indexes with granular control over other index tuning parameters.
Create a ScaNN index in AUTO mode
Some points to note before creating indexes in AUTO 
mode are as follows:
- AlloyDB can't create a ScaNN index for tables with insufficient data.
- You can't set index creation parameters, such as num_leaves, when you create indexes inAUTOmode.
-  Auto maintenance 
is enabled by default for all indexes created in AUTOmode.
To create an index in AUTO 
mode, enable the feature flag scann.zero_knob_index_creation 
first.
After you enable the flag, run the following command:
   
 CREATE 
  
 INDEX 
  
  INDEX_NAME 
 
  
 ON 
  
  TABLE 
 
  
 \ 
  
 USING 
  
 scann 
  
 ( 
  EMBEDDING_COLUMN 
 
  
  DISTANCE_FUNCTION 
 
 ) 
  
 \ 
  
 WITH 
  
 ( 
 mode 
 = 
 'AUTO' 
 ); 
 
 
Replace the following:
-  INDEX_NAME: the name of the index that you want to create—for example,my-scann-index. The index names are shared across your database. Verify that each index name is unique to each table in your database.
-  TABLE: the table to add the index to.
-  EMBEDDING_COLUMN: the column that storesvectordata.
-  DISTANCE_FUNCTION: the distance function to use with this index. Choose one of the following:-  L2 distance: l2
-  Dot product: dot_product
-  Cosine distance: cosine
 
-  
-  OPTIMIZATION(Optional): By default, a search optimized index is created. Set to one of the following:-  SEARCH_OPTIMIZED(Default): to optimize both vector search recall and vector search latency at a cost of longer index build time.
-  BALANCED: to create an index that balances index build time and search performance.
 
-  
Create a ScaNN 
index in MANUAL mode
 
 If you enabled the scann.enable_preview_features 
flag and you want granular control over the tuning parameters, you can create the index in MANUAL 
mode.
To create a ScaNN 
index in MANUAL 
mode, run the following command:
   
 CREATE 
  
 INDEX 
  
  INDEX_NAME 
 
  
 ON 
  
  TABLE 
 
  
 \ 
  
 USING 
  
 scann 
  
 ( 
  EMBEDDING_COLUMN 
 
  
  DISTANCE_FUNCTION 
 
 ) 
  
 \ 
  
 WITH 
  
 ( 
 mode 
 = 
 ' 
 MANUAL 
 , 
  
 num_leaves 
 = 
  NUM_LEAVES_VALUE 
 
 , 
  
 [ 
 quantizer 
  
 = 
  QUANTIZER 
 
 , 
  
 max_num_levels 
 = 
  MAX_NUM_LEVELS 
 
 ]); 
 
 
Replace the following:
-  INDEX_NAME: the name of the index you want to create—for example,my-scann-index. The index names are shared across your database. Verify that each index name is unique to each table in your database.
-  TABLE: the table to add the index to.
-  EMBEDDING_COLUMN: the column that storesvectordata.
-  DISTANCE_FUNCTION: the distance function to use with this index. Choose one of the following:-  L2 distance: l2
-  Dot product: dot_product
-  Cosine distance: cosine
 
-  
-  NUM_LEAVES_VALUE: the number of partitions to apply to this index. Set to any value between 1 to 1048576.
-  QUANTIZER: the type of quantizer to use. Available options are as follows:-  SQ8: provides a balance of query performance with minimal recall loss, typically less than 1-2%. This is the default value.
-  AH: consider this for potentially better query performance when the columnar engine is enabled and your index and table data are populated into the columnar engine, subject to its configured size. Note thatAHis up to 4x compressed when compared withSQ8. For more information, see Best practices for tuning ScaNN .
-  FLAT: provides the highest recall of 99% or higher at the cost of search performance.
 
-  
-  MAX_NUM_LEVELS: the maximum number of levels of the K-means clustering tree. Set to1(default) for two-level tree-based quantization, and set to2for three-level tree-based quantization.
You can add other index creation or query runtime parameters to tune your index. For more information, see Tune a ScaNN 
index 
.
Change modes for existing indexes
If you created a ScaNN index using the AUTO 
mode and you want to tune the index manually, then you must change the mode to MANUAL 
.
To change mode to MANUAL 
, follow these steps:
-  Update the index to set the mode to MANUAL:ALTER INDEX INDEX_NAME SET ( mode = 'MANUAL' , num_leaves = NUM_LEAVES_VALUE );Replace the following: -  INDEX_NAME: the name of the index you want to create—for example,my-scann-index. The index names are shared across your database. Verify that each index name is unique to each table in your database.
-  NUM_LEAVES_VALUE: the number of partitions to apply to this index. Set to any value between 1 to 1048576.
 You can add other index creation or query runtime parameters to tune your index. For more information, see Tune a ScaNNindex .
-  
-  Rebuild your index to apply the parameters: REINDEX INDEX CONCURRENTLY INDEX_NAME ;
To change mode to AUTO 
, complete the following steps:
-  Update the index to set the mode to AUTO:ALTER INDEX INDEX_NAME SET ( mode = 'AUTO' );
-  Rebuild your index to apply the parameters: REINDEX INDEX CONCURRENTLY INDEX_NAME ;
Create a ScaNN 
index with specific parameters
 
 If your application has specific requirements for recall and index build
times, then you can manually create the index. You can create a two-level or
three-level tree index based on your workload. For more information about tuning
parameters, see Tune a ScaNN 
index 
.
Two-level tree index
To apply a two-level tree index using the ScaNN algorithm to a column containing stored vector embeddings, run the following DDL query:
  CREATE 
  
 INDEX 
  
  INDEX_NAME 
 
  
 ON 
  
  TABLE 
 
  
 USING 
  
 scann 
  
 ( 
  EMBEDDING_COLUMN 
 
  
  DISTANCE_FUNCTION 
 
 ) 
  
 WITH 
  
 ( 
 num_leaves 
 = 
  NUM_LEAVES_VALUE 
 
 , 
  
 quantizer 
  
 = 
  QUANTIZER 
 
 ); 
 
 
Replace the following:
-  INDEX_NAME: the name of the index you want to create—for example,my-scann-index. The index names are shared across your database. Ensure that each index name is unique to each table in your database.
-  TABLE: the table to add the index to.
-  EMBEDDING_COLUMN: a column that storesvectordata.
-  DISTANCE_FUNCTION: the distance function to use with this index. Choose one of the following:-  L2 distance: l2
-  Dot product: dot_product
-  Cosine distance: cosine
 
-  
-  NUM_LEAVES_VALUE: the number of partitions to apply to this index. Set to any value between 1 to 1048576. For more information about how to decide this value, see Tune aScaNNindex .
-  QUANTIZER: the type of quantizer to use. Available options are as follows:-  SQ8: provides a balance of query performance with minimal recall loss, typically less than 1-2%. This is the default value.
-  AH: consider this for potentially better query performance when the columnar engine is enabled and your index and table data are populated into the columnar engine, subject to its configured size. Note thatAHis up to 4x compressed when compared withSQ8. For more information, see Best practices for tuning ScaNN .
-  FLAT: provides the highest recall of 99% or higher at the cost of search performance.
 
-  
Three-level tree index
To create a three-level tree index using the ScaNN algorithm to a column containing stored vector embeddings, run the following DDL query:
  CREATE 
  
 INDEX 
  
  INDEX_NAME 
 
  
 ON 
  
  TABLE 
 
  
 USING 
  
 scann 
  
 ( 
  EMBEDDING_COLUMN 
 
  
  DISTANCE_FUNCTION 
 
 ) 
  
 WITH 
  
 ( 
 num_leaves 
 = 
  NUM_LEAVES_VALUE 
 
 , 
  
 max_num_levels 
  
 = 
  
 2 
 ); 
 
 
After you create the index, you can run nearest-neighbor search queries that make use of the index by following the instructions in Make a nearest-neighbor query with given text .
The index parameters must be set to strike a right balance between QPS and
recall. For more information about tuning the ScaNN 
index, see Tune a ScaNN 
index 
.
To create this index on an embedding column that uses the real[] 
data type
instead of vector 
, cast the column into the vector 
data type:
  CREATE 
  
 INDEX 
  
  INDEX_NAME 
 
  
 ON 
  
  TABLE 
 
  
 USING 
  
 scann 
  
 ( 
 CAST 
 ( 
  EMBEDDING_COLUMN 
 
  
 AS 
  
 vector 
 ( 
  DIMENSIONS 
 
 )) 
  
  DISTANCE_FUNCTION 
 
 ) 
  
 WITH 
  
 ( 
 num_leaves 
 = 
  NUM_LEAVES_VALUE 
 
 , 
  
 max_num_levels 
  
 = 
  
  MAX_NUM_LEVELS 
 
 ); 
 
 
Replace  DIMENSIONS 
 
with the dimensional width of the
embedding column. For more information about how to find the dimensions,
see the vector_dims 
function in Vector
functions 
.
To achieve a consistent search experience, enable auto-maintenance when you create a ScaNN index. For more information, see Maintain vector indexes . This feature is available in Preview .
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. After the index building phase is complete, the row for the index isn't visible.
To tune your index for an average recall and QPS balance, see Tune a ScaNN 
index 
.
Build indexes in parallel
To build your index faster, AlloyDB might automatically spawn multiple parallel workers, depending on your dataset and the type of index that you choose.
The parallel index build is often triggered if you're creating a 3-level ScaNN index or if your dataset exceeds 100M rows.
Though AlloyDB automatically optimizes the number of parallel workers, you can tune the parallel workers using the  max_parallel_maintenance_workers 
 
,  max_parallel_workers 
 
, and the  min_parallel_table_scan_size 
 
PostgreSQL query planning parameters.
Run a query
After you store and index the embeddings in your database, you can start
querying your data. 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 1if you want only the single best match.
You can also use the  embedding() 
 
function to translate the
text into a vector. Since embedding() 
returns a real 
array, you must explicitly cast the embedding() 
call to vector 
before applying it to one of the
nearest-neighbor operators (e.g., <-> 
for L2 distance). These operators can then use the ScaNN index to find the database rows with the most semantically similar embeddings.

