Comienza a usar la búsqueda híbrida en AlloyDB

1. Introducción

En este codelab, aprenderás a realizar búsquedas híbridas en AlloyDB con la extensión de método de actualización de clasificación (RUM) y el índice de vecino más cercano escalable (ScaNN). Este lab forma parte de una colección de labs dedicados a las funciones de AlloyDB AI. Puedes obtener más información en la página de AlloyDB AI en la documentación.

Requisitos previos

  • Conocimientos básicos sobre la consola de Google Cloud
  • Habilidades básicas de la interfaz de línea de comandos y de Google Shell

Qué aprenderás

Requisitos

  • Una cuenta de Google Cloud y un proyecto de Google Cloud
  • Un navegador web, como Chrome

2. Configuración y requisitos

Configuración del proyecto

Accede a la consola de Google Cloud. Si aún no tienes una cuenta de Gmail o de Google Workspace, debes crear una.

Usar una cuenta personal en lugar de una cuenta laboral o educativa

Crea un proyecto de Google Cloud

  1. En la página del selector de proyectos de la consola de Google Cloud, selecciona o crea un proyecto de Google Cloud.
  2. Asegúrate de que la facturación esté habilitada para tu proyecto de Cloud. Obtén información para verificar si la facturación está habilitada en un proyecto.

Habilitar facturación

Para habilitar la facturación, tienes dos opciones. Puedes usar tu cuenta de facturación personal o canjear créditos siguiendo los pasos que se indican a continuación.

Configura una cuenta de facturación personal

Si configuraste la facturación con créditos de Google Cloud, puedes omitir este paso.

Para configurar una cuenta de facturación personal, ve aquí para habilitar la facturación en la consola de Cloud.

Algunas notas:

  • Completar este lab debería costar menos de USD 3 en recursos de Cloud.
  • Puedes seguir los pasos al final de este lab para borrar recursos y evitar cargos adicionales.
  • Los usuarios nuevos pueden acceder a la prueba gratuita de USD 300.

Inicia Cloud Shell

Si bien Google Cloud y Spanner se pueden operar de manera remota desde tu laptop, en este codelab usarás Google Cloud Shell, un entorno de línea de comandos que se ejecuta en la nube.

Cloud Shell es un entorno de línea de comandos que se ejecuta en Google Cloud y que viene precargado con las herramientas necesarias.

  1. Haz clic en Activar Cloud Shell en la parte superior de la consola de Google Cloud.
  2. Una vez que te conectes a Cloud Shell, verifica tu autenticación:
    gcloud auth list
    
  3. Confirma que tu proyecto esté configurado:
    gcloud config get project
    
  4. Si tu proyecto no está configurado como se esperaba, configúralo:
    export PROJECT_ID=<YOUR_PROJECT_ID>
    gcloud config set project $PROJECT_ID
    

Esta máquina virtual está cargada con todas las herramientas de desarrollo que necesitarás. Ofrece un directorio principal persistente de 5 GB y se ejecuta en Google Cloud, lo que permite mejorar considerablemente el rendimiento de la red y la autenticación. Todo tu trabajo en este codelab se puede hacer en un navegador. No es necesario que instales nada.

3. Antes de comenzar

Habilita la API

Resultado:

Para usar AlloyDB, Compute Engine, servicios de redes y Vertex AI, debes habilitar sus APIs respectivas en tu proyecto de Google Cloud.

Habilita las API

En Cloud Shell, en la terminal, asegúrate de que tu ID del proyecto esté configurado:

gcloud config set project [YOUR-PROJECT-ID]

Configura la variable de entorno PROJECT_ID:

PROJECT_ID=$(gcloud config get-value project)

Habilita todas las APIs necesarias:

gcloud services enable alloydb.googleapis.com \
                       compute.googleapis.com \
                       cloudresourcemanager.googleapis.com \
                       servicenetworking.googleapis.com \
                       aiplatform.googleapis.com

Resultado esperado

student@cloudshell:~ (test-project-001-402417)$ gcloud config set project test-project-001-402417
Updated property [core/project].
student@cloudshell:~ (test-project-001-402417)$ PROJECT_ID=$(gcloud config get-value project)
Your active configuration is: [cloudshell-14650]
student@cloudshell:~ (test-project-001-402417)$ 
student@cloudshell:~ (test-project-001-402417)$ gcloud services enable alloydb.googleapis.com \
                       compute.googleapis.com \
                       cloudresourcemanager.googleapis.com \
                       servicenetworking.googleapis.com \
                       aiplatform.googleapis.com
Operation "operations/acat.p2-4470404856-1f44ebd8-894e-4356-bea7-b84165a57442" finished successfully.

Presentamos las APIs

  • La API de AlloyDB (alloydb.googleapis.com) te permite crear, administrar y escalar clústeres de AlloyDB para PostgreSQL. Proporciona un servicio de base de datos completamente administrado y compatible con PostgreSQL diseñado para cargas de trabajo empresariales transaccionales y analíticas exigentes.
  • La API de Compute Engine (compute.googleapis.com) te permite crear y administrar máquinas virtuales (VM), discos persistentes y parámetros de configuración de red. Proporciona la base de infraestructura como servicio (IaaS) principal necesaria para ejecutar tus cargas de trabajo y alojar la infraestructura subyacente de muchos servicios administrados.
  • La API de Resource Manager (cloudresourcemanager.googleapis.com) te permite administrar de forma programática los metadatos y la configuración de tu proyecto de Google Cloud. Te permite organizar recursos, controlar políticas de Identity and Access Management (IAM) y validar permisos en toda la jerarquía del proyecto.
  • La API de Service Networking (servicenetworking.googleapis.com) te permite automatizar la configuración de la conectividad privada entre tu red de nube privada virtual (VPC) y los servicios administrados de Google. Se requiere específicamente para establecer el acceso a la IP privada para servicios como AlloyDB, de modo que puedan comunicarse de forma segura con tus otros recursos.
  • La API de Vertex AI (aiplatform.googleapis.com) permite que tus aplicaciones compilen, implementen y escalen modelos de aprendizaje automático. Proporciona la interfaz unificada para todos los servicios de IA de Google Cloud, incluido el acceso a modelos de IA generativa (como Gemini) y el entrenamiento de modelos personalizados.

Opcionalmente, puedes configurar tu región predeterminada para usar los modelos de incorporación de Vertex AI. Obtén más información sobre las ubicaciones disponibles para Vertex AI. En el ejemplo, usamos la región us-central1.

gcloud config set compute/region us-central1

4. Implementa AlloyDB

Antes de crear un clúster de AlloyDB, necesitamos un rango de IP privada disponible en nuestra VPC para que lo utilice la instancia futura de AlloyDB. Si no lo tenemos, debemos crearlo, asignarlo para que lo usen los servicios internos de Google y, luego, podremos crear el clúster y la instancia.

Crea un rango de IP privada

Debemos establecer la configuración del acceso privado a servicios en nuestra VPC para AlloyDB. Aquí, suponemos que tenemos la red de VPC “predeterminada” en el proyecto y que se utilizará para todas las acciones.

Crea el rango de IP privada:

gcloud compute addresses create psa-range \
    --global \
    --purpose=VPC_PEERING \
    --prefix-length=24 \
    --description="VPC private service access" \
    --network=default

Crea una conexión privada con el rango de IP asignado:

gcloud services vpc-peerings connect \
    --service=servicenetworking.googleapis.com \
    --ranges=psa-range \
    --network=default

Resultado esperado en la consola:

student@cloudshell:~ (test-project-402417)$ gcloud compute addresses create psa-range \
    --global \
    --purpose=VPC_PEERING \
    --prefix-length=24 \
    --description="VPC private service access" \
    --network=default
Created [https://www.googleapis.com/compute/v1/projects/test-project-402417/global/addresses/psa-range].

student@cloudshell:~ (test-project-402417)$ gcloud services vpc-peerings connect \
    --service=servicenetworking.googleapis.com \
    --ranges=psa-range \
    --network=default
Operation "operations/pssn.p24-4470404856-595e209f-19b7-4669-8a71-cbd45de8ba66" finished successfully.

student@cloudshell:~ (test-project-402417)$

Crea un clúster de AlloyDB

En esta sección, crearemos un clúster de AlloyDB en la región us-central1.

Define la contraseña para el usuario de postgres. Puedes definir tu propia contraseña o usar una función aleatoria para generar una.

export PGPASSWORD=`openssl rand -hex 12`

Resultado esperado en la consola:

student@cloudshell:~ (test-project-402417)$ export PGPASSWORD=`openssl rand -hex 12`

Toma nota de la contraseña de PostgreSQL para utilizarla más adelante.

echo $PGPASSWORD

Necesitarás esa contraseña en el futuro para conectarte a la instancia como usuario de postgres. Te sugiero que lo anotes o lo copies en algún lugar para poder usarlo más adelante.

Resultado esperado en la consola:

student@cloudshell:~ (test-project-402417)$ echo $PGPASSWORD
bbefbfde7601985b0dee5723

Crea un clúster de AlloyDB

Define la región y el nombre del clúster de AlloyDB. Usaremos la región us-central1 y alloydb-hybrid-search como nombre del clúster:

export REGION=us-central1
export ADBCLUSTER=alloydb-hybrid-search

Ejecuta el comando para crear el clúster:

gcloud alloydb clusters create $ADBCLUSTER \
    --password=$PGPASSWORD \
    --network=default \
    --region=$REGION

Resultado esperado en la consola:

export REGION=us-central1
export ADBCLUSTER=alloydb-hybrid-search
gcloud alloydb clusters create $ADBCLUSTER \
    --password=$PGPASSWORD \
    --network=default \
    --region=$REGION 
Operation ID: operation-1697655441138-6080235852277-9e7f04f5-2012fce4
Creating cluster...done.                                                                                                                                                                                                                                                           

Crea una instancia principal de AlloyDB para nuestro clúster en la misma sesión de Cloud Shell. Si te desconectas, deberás volver a definir las variables de entorno de la región y el nombre del clúster.

gcloud alloydb instances create $ADBCLUSTER-pr \
    --instance-type=PRIMARY \
    --cpu-count=2 \
    --region=$REGION \
    --cluster=$ADBCLUSTER

Resultado esperado en la consola:

student@cloudshell:~ (alloydb-hybrid-search)$ gcloud alloydb instances create $ADBCLUSTER-pr \
    --instance-type=PRIMARY \
    --cpu-count=2 \
    --region=$REGION \
    --availability-type ZONAL \
    --cluster=$ADBCLUSTER
Operation ID: operation-1697659203545-6080315c6e8ee-391805db-25852721
Creating instance...done.                                                                                                                                                                                                                                                     

5. Conectarte a AlloyDB

AlloyDB se implementa con una conexión solo privada, por lo que necesitamos una VM con el cliente de PostgreSQL instalado para trabajar con la base de datos.

Implementa la VM de GCE

Crea una VM de GCE en la misma región y VPC que el clúster de AlloyDB.

En Cloud Shell, ejecuta el siguiente comando:

export ZONE=us-central1-a
gcloud compute instances create instance-1 \
    --zone=$ZONE \
    --create-disk=auto-delete=yes,boot=yes,image=projects/debian-cloud/global/images/$(gcloud compute images list --filter="family=debian-12 AND family!=debian-12-arm64" --format="value(name)") \
    --scopes=https://www.googleapis.com/auth/cloud-platform

Resultado esperado en la consola:

student@cloudshell:~ (alloydb-hybrid-search)$ export ZONE=us-central1-a
student@cloudshell:~ (talloydb-hybrid-search)$ export ZONE=us-central1-a
gcloud compute instances create instance-1 \
    --zone=$ZONE \
    --create-disk=auto-delete=yes,boot=yes,image=projects/debian-cloud/global/images/$(gcloud compute images list --filter="family=debian-12 AND family!=debian-12-arm64" --format="value(name)") \
    --scopes=https://www.googleapis.com/auth/cloud-platform

Created [https://www.googleapis.com/compute/v1/projects/test-project-402417/zones/us-central1-a/instances/instance-1].
NAME: instance-1
ZONE: us-central1-a
MACHINE_TYPE: n1-standard-1
PREEMPTIBLE: 
INTERNAL_IP: 10.128.0.2
EXTERNAL_IP: 34.71.192.233
STATUS: RUNNING

Instala el cliente de Postgres

Instala el software del cliente de PostgreSQL en la VM implementada

Conéctate a la VM:

gcloud compute ssh instance-1 --zone=us-central1-a

Resultado esperado en la consola:

student@cloudshell:~ (alloydb-hybrid-search)$ gcloud compute ssh instance-1 --zone=us-central1-a
Updating project ssh metadata...working..Updated [https://www.googleapis.com/compute/v1/projects/alloydb-hybrid-search].                                                                                                                                                         
Updating project ssh metadata...done.                                                                                                                                                                                                                                              
Waiting for SSH key to propagate.
Warning: Permanently added 'compute.5110295539541121102' (ECDSA) to the list of known hosts.
Linux instance-1.us-central1-a.c.gleb-test-short-001-418811.internal 6.1.0-18-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.76-1 (2024-02-01) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
student@instance-1:~$ 

Instala el comando de ejecución de software dentro de la VM:

sudo apt-get update
sudo apt-get install --yes postgresql-client

Resultado esperado en la consola:

student@instance-1:~$ sudo apt-get update
sudo apt-get install --yes postgresql-client
Get:1 https://packages.cloud.google.com/apt google-compute-engine-bullseye-stable InRelease [5146 B]
Get:2 https://packages.cloud.google.com/apt cloud-sdk-bullseye InRelease [6406 B]   
Hit:3 https://deb.debian.org/debian bullseye InRelease  
Get:4 https://deb.debian.org/debian-security bullseye-security InRelease [48.4 kB]
Get:5 https://packages.cloud.google.com/apt google-compute-engine-bullseye-stable/main amd64 Packages [1930 B]
Get:6 https://deb.debian.org/debian bullseye-updates InRelease [44.1 kB]
Get:7 https://deb.debian.org/debian bullseye-backports InRelease [49.0 kB]
...redacted...
update-alternatives: using /usr/share/postgresql/13/man/man1/psql.1.gz to provide /usr/share/man/man1/psql.1.gz (psql.1.gz) in auto mode
Setting up postgresql-client (13+225) ...
Processing triggers for man-db (2.9.4-2) ...
Processing triggers for libc-bin (2.31-13+deb11u7) ...

Conéctate a la instancia

Conéctate a la instancia principal desde la VM usando psql.

En la misma pestaña de Cloud Shell con la sesión SSH abierta en tu VM instance-1

Usa el valor de la contraseña de AlloyDB (PGPASSWORD) que se indicó y el ID del clúster de AlloyDB para conectarte a AlloyDB desde la VM de GCE:

export PGPASSWORD=<Noted password>
export PROJECT_ID=$(gcloud config get-value project)
export REGION=us-central1
export ADBCLUSTER=alloydb-hybrid-search
export INSTANCE_IP=$(gcloud alloydb instances describe $ADBCLUSTER-pr --cluster=$ADBCLUSTER --region=$REGION --format="value(ipAddress)")
psql "host=$INSTANCE_IP user=postgres sslmode=require"

Resultado esperado en la consola:

student@instance-1:~$ export PGPASSWORD=CQhOi5OygD4ps6ty
student@instance-1:~$ ADBCLUSTER=alloydb-aip-01
student@instance-1:~$ REGION=us-central1
student@instance-1:~$ INSTANCE_IP=$(gcloud alloydb instances describe $ADBCLUSTER-pr --cluster=$ADBCLUSTER --region=$REGION --format="value(ipAddress)")
gleb@instance-1:~$ psql "host=$INSTANCE_IP user=postgres sslmode=require"
psql (15.6 (Debian 15.6-0+deb12u1), server 15.5)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, compression: off)
Type "help" for help.

postgres=>

Cierra la sesión de psql:

exit

6. Prepara la base de datos

Debemos crear una base de datos, habilitar la integración de Vertex AI, crear objetos de base de datos y, luego, importar los datos.

Otorga los permisos necesarios a AlloyDB

Agrega permisos de Vertex AI al agente de servicio de AlloyDB.

Abre otra pestaña de Cloud Shell con el signo "+" en la parte superior.

abc505ac4d41f24e.png

En la nueva pestaña de Cloud Shell, ejecuta lo siguiente:

PROJECT_ID=$(gcloud config get-value project)
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" \
  --role="roles/aiplatform.user"

Resultado esperado en la consola:

student@cloudshell:~ (test-project-001-402417)$ PROJECT_ID=$(gcloud config get-value project)
Your active configuration is: [cloudshell-11039]
student@cloudshell:~ (test-project-001-402417)$ gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" \
  --role="roles/aiplatform.user"
Updated IAM policy for project [test-project-001-402417].
bindings:
- members:
  - serviceAccount:service-4470404856@gcp-sa-alloydb.iam.gserviceaccount.com
  role: roles/aiplatform.user
- members:
...
etag: BwYIEbe_Z3U=
version: 1
 

Cierra la pestaña con el comando de ejecución “exit” en la pestaña:

exit

Crea la base de datos

Crea una base de datos llamada quickstart.

En la sesión de la VM de GCE, ejecuta lo siguiente:

Crea la base de datos:

psql "host=$INSTANCE_IP user=postgres" -c "CREATE DATABASE quickstart_db"

Resultado esperado en la consola:

student@instance-1:~$ psql "host=$INSTANCE_IP user=postgres" -c "CREATE DATABASE quickstart_db"
CREATE DATABASE
student@instance-1:~$  

Habilita la integración de Vertex AI

Habilita la integración en Vertex AI y las extensiones pgvector en la base de datos.

En la VM de GCE, ejecuta lo siguiente:

psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE"
psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "CREATE EXTENSION IF NOT EXISTS vector"

Resultado esperado en la consola:

student@instance-1:~$ psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE"
psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "CREATE EXTENSION IF NOT EXISTS vector"
CREATE EXTENSION
CREATE EXTENSION
student@instance-1:~$ 

Importar datos

Descarga los datos preparados y, luego, impórtalos a la nueva base de datos.

En la VM de GCE, ejecuta lo siguiente:

gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_demo_schema.sql |psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db"
gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_products.csv |psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "\copy cymbal_products from stdin csv header"
gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_inventory.csv |psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "\copy cymbal_inventory from stdin csv header"
gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_stores.csv |psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "\copy cymbal_stores from stdin csv header"

Resultado esperado en la consola:

student@instance-1:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_demo_schema.sql |psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db"
SET
SET
SET
SET
SET
 set_config 
------------
 
(1 row)
SET
SET
SET
SET
SET
SET
CREATE TABLE
ALTER TABLE
CREATE TABLE
ALTER TABLE
CREATE TABLE
ALTER TABLE
CREATE TABLE
ALTER TABLE
CREATE SEQUENCE
ALTER TABLE
ALTER SEQUENCE
ALTER TABLE
ALTER TABLE
ALTER TABLE
student@instance-1:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_products.csv |psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "\copy cymbal_products from stdin csv header"
COPY 941
student@instance-1:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_inventory.csv |psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "\copy cymbal_inventory from stdin csv header"
COPY 263861
student@instance-1:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_stores.csv |psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db" -c "\copy cymbal_stores from stdin csv header"
COPY 4654
student@instance-1:~$

7. Genera embeddings de vectores

Después de importar los datos, tenemos las siguientes tablas: cymbal_products, que almacena información sobre los productos; cymbal_inventory, que hace un seguimiento del stock de artículos en cada tienda; y cymbal_stores, que es una lista de tiendas. Para realizar una búsqueda semántica en nuestros productos, debemos generar embeddings de vectores de las descripciones de los productos con la función initialize_embeddings. Usaremos la integración de Vertex AI para calcular los datos de vectores según las descripciones de nuestros productos y agregarlos a la tabla. Puedes obtener más información sobre la tecnología utilizada en la documentación.

Para usar la integración, conéctate a la base de datos con psql desde tu VM usando la IP de la instancia de AlloyDB y la contraseña de postgres:

psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db"

Verifica la versión de la extensión google_ml_integration.

SELECT extversion FROM pg_extension WHERE extname = 'google_ml_integration';

La versión debe ser 1.5.2 o superior. Este es un ejemplo del resultado:

quickstart_db=> SELECT extversion FROM pg_extension WHERE extname = 'google_ml_integration';
 extversion 
------------
 1.5.2
(1 row)

La versión predeterminada debe ser la 1.5.2 o una posterior, pero si tu instancia muestra una versión anterior, es probable que deba actualizarse. Verifica si se inhabilitó el mantenimiento para la instancia.

Usaremos la generación de incorporaciones por lotes para mejorar la eficiencia. Puedes obtener más información sobre las diferentes opciones y técnicas de generación de incorporaciones en la guía. Para usar la función de embedding por lotes, debemos habilitar goole_ml_integration.enable_faster_embedding_generation.

show google_ml_integration.enable_faster_embedding_generation;

Si la marca está en la posición correcta, el resultado esperado se verá de la siguiente manera:

quickstart_db=> show google_ml_integration.enable_faster_embedding_generation;                          
 google_ml_integration.enable_faster_embedding_generation 
----------------------------------------------------------
 on
(1 row)

Pero si se muestra como “desactivado”, debemos actualizar la instancia. Puedes hacerlo con la consola web o el comando de gcloud, como se describe en la documentación. Aquí te muestro cómo hacerlo con el comando de gcloud:

export PROJECT_ID=$(gcloud config get-value project)
export REGION=us-central1
export ADBCLUSTER=alloydb-hybrid-search
gcloud beta alloydb instances update $ADBCLUSTER-pr \
   --database-flags google_ml_integration.enable_faster_embedding_generation=on \
   --region=$REGION \
   --cluster=$ADBCLUSTER \
   --project=$PROJECT_ID \
   --update-mode=FORCE_APPLY

Puede tardar unos minutos, pero, finalmente, el valor de la marca debería cambiar a "activado". Después, puedes continuar con los siguientes pasos.

psql "host=$INSTANCE_IP user=postgres dbname=quickstart_db"

En la sesión de psql conectada a la base de datos, crea una columna nueva para almacenar las incorporaciones en cymbal_products.

ALTER TABLE cymbal_products ADD COLUMN product_embedding vector(768);

Resultado esperado en la consola:

quickstart_db=> ALTER TABLE cymbal_products ADD COLUMN product_embedding vector(768);
ALTER TABLE
quickstart_db=> 

Por último, también queremos que los embeddings se actualicen a medida que se modifican los valores de las columnas, por lo que incluimos el argumento incremental_refresh_mode en la llamada a función. Esto genera una sobrecarga en nuestra base de datos, pero es una compensación que hacemos para mantener automáticamente las incorporaciones sincronizadas con el contenido. Si deseas actualizar manualmente las incorporaciones, puedes encontrar las instrucciones en la documentación.

Ahora, para unir todo y generar embeddings, usamos la función initialize_embeddings y pasamos batch_size de 50 como sugerencia de lote y establecemos incremental_refresh_mode en transactional.

CALL ai.initialize_embeddings(
    model_id => 'text-embedding-005',
    table_name => 'cymbal_products',
    content_column => 'product_description',
    embedding_column => 'product_embedding',
    batch_size => 50,
    incremental_refresh_mode => 'transactional'
);

Ahora, si insertamos una fila nueva en la tabla con el valor NULL para la columna product_embedding

INSERT INTO "cymbal_products" ("uniq_id", "crawl_timestamp", "product_url", "product_name", "product_description", "list_price", "sale_price", "brand", "item_number", "gtin", "package_size", "category", "postal_code", "available", "product_embedding") VALUES ('fd604542e04b470f9e6348e640cff794', NOW(), 'https://example.com/new_product', 'New Cymbal Product', 'This is a new cymbal product description.', 199.99, 149.99, 'Example Brand', 'EB123', '1234567890', 'Single', 'Cymbals', '12345', TRUE, NULL);

Ahora, cuando consultemos la fila que acabamos de insertar, veremos que la columna product_embedding se actualiza automáticamente.

SELECT uniq_id, (product_embedding::real[])[1:5] as product_embedding  FROM cymbal_products WHERE uniq_id='fd604542e04b470f9e6348e640cff794';

El resultado debe verse de la siguiente manera:

quickstart_db=> SELECT uniq_id,(product_embedding::real[])[1:5] as product_embedding  FROM cymbal_products WHERE uniq_id='fd604542e04b470f9e6348e640cff794';
             uniq_id              |                      product_embedding                       
----------------------------------+---------------------------------------------------------------
 fd604542e04b470f9e6348e640cff794 | {0.015003494,-0.005349732,-0.059790313,-0.0087091,-0.0271452}
(1 row)

Time: 3.295 ms

8. Crear índice vectorial

Para mejorar el rendimiento de la búsqueda de vectores, agregaremos un índice ScaNN.

Crea un índice de ScaNN

Para compilar el índice de SCANN, debemos habilitar una extensión más. La extensión alloydb_scann proporciona una interfaz para trabajar con el índice de vectores de tipo ANN usando el algoritmo ScaNN de Google.

CREATE EXTENSION IF NOT EXISTS alloydb_scann;

Resultado esperado:

quickstart_db=> CREATE EXTENSION IF NOT EXISTS alloydb_scann;
CREATE EXTENSION
Time: 27.468 ms
quickstart_db=> 

El índice se puede crear en modo MANUAL o AUTO. El modo MANUAL está habilitado de forma predeterminada y puedes crear un índice y mantenerlo como cualquier otro índice. Sin embargo, si habilitas el modo AUTO, podrás crear el índice que no requiere mantenimiento de tu parte. Puedes leer en detalle sobre todas las opciones en la documentación. En nuestro caso, no tenemos suficientes filas para crear el índice en el modo AUTO, por lo que lo crearemos como MANUAL y, luego, incluiremos parámetros de ajuste. Puedes obtener información sobre el ajuste de los parámetros del índice en la documentación.

Debemos habilitar la marca scann.enable_preview_features para poder modificar los parámetros de ajuste. En Cloud Shell

export PROJECT_ID=$(gcloud config get-value project)
export REGION=us-central1
export ADBCLUSTER=alloydb-hybrid-search
gcloud beta alloydb instances update $ADBCLUSTER-pr \
   --database-flags scann.enable_preview_features=on \
   --region=$REGION \
   --cluster=$ADBCLUSTER \
   --project=$PROJECT_ID \
   --update-mode=FORCE_APPLY

Puede tardar unos minutos, pero, finalmente, el valor de la marca debería cambiar a "activado". Una vez que se haya establecido la marca, podemos volver a nuestra sesión de psql en la VM y crear el índice con parámetros de ajuste.

CREATE INDEX cymbal_products_embeddings_scann ON cymbal_products
  USING scann (product_embedding cosine)
  WITH (mode='MANUAL', num_leaves=31, max_num_levels = 2);

Resultado esperado:

quickstart_db=> CREATE INDEX cymbal_products_embeddings_scann ON cymbal_products
  USING scann (product_embedding cosine)
  WITH (num_leaves=31, max_num_levels = 2);
CREATE INDEX
quickstart_db=>

Inspecciona el uso del índice

Ahora podemos ejecutar la consulta de búsqueda vectorial en el modo EXPLAIN y verificar si se está usando el índice.

EXPLAIN (analyze) 
WITH trees as (
SELECT
        cp.product_name,
        left(cp.product_description,80) as description,
        cp.sale_price,
        cs.zip_code,
        cp.uniq_id as product_id
FROM
        cymbal_products cp
JOIN cymbal_inventory ci on
        ci.uniq_id=cp.uniq_id
JOIN cymbal_stores cs on
        cs.store_id=ci.store_id
        AND ci.inventory>0
        AND cs.store_id = 1583
ORDER BY
        (cp.product_embedding <=> embedding('text-embedding-005','What kind of fruit trees grow well here?')::vector) ASC
LIMIT 1)
SELECT json_agg(trees) FROM trees;

Resultado esperado (oculto para mayor claridad):

...
Aggregate (cost=16.59..16.60 rows=1 width=32) (actual time=2.875..2.877 rows=1 loops=1)
-> Subquery Scan on trees (cost=8.42..16.59 rows=1 width=142) (actual time=2.860..2.862 rows=1 loops=1)
-> Limit (cost=8.42..16.58 rows=1 width=158) (actual time=2.855..2.856 rows=1 loops=1)
-> Nested Loop (cost=8.42..6489.19 rows=794 width=158) (actual time=2.854..2.855 rows=1 loops=1)
-> Nested Loop (cost=8.13..6466.99 rows=794 width=938) (actual time=2.742..2.743 rows=1 loops=1)
-> Index Scan using cymbal_products_embeddings_scann on cymbal_products cp (cost=7.71..111.99 rows=876 width=934) (actual time=2.724..2.724 rows=1 loops=1)
Order By: (embedding <=> '[0.008864171,0.03693164,-0.024245683,-0.00355923,0.0055611245,0.015985578,...<redacted>...5685,-0.03914233,-0.018452475,0.00826032,-0.07372604]'::vector)
...

En el resultado, se puede ver claramente que la consulta usó "Index Scan using cymbal_products_embeddings_scann on cymbal_products".

9. Índice de búsqueda en el texto completo

AlloyDB admite todos los tipos de índices para la búsqueda de texto completo que admite PostgreSQL nativo. La elección del índice depende del equilibrio entre la velocidad de búsqueda, el tiempo de compilación del índice, la velocidad de actualización y las funcionalidades de búsqueda específicas que se requieren, como la búsqueda de frases o la clasificación por relevancia.

En nuestro ejemplo, usaremos la extensión de RUM para realizar operaciones de búsqueda en el texto completo con mayor rendimiento. El RUM mejora los índices GIN estándar almacenando información posicional directamente en el índice, lo que te permite realizar búsquedas de frases y clasificaciones de relevancia más rápidas sin acceder a los datos de la tabla.

Puedes usar AlloyDB Studio o seguir usando el cliente de psql para habilitar la extensión de RUM.

Crea un índice de RUM

CREATE EXTENSION IF NOT EXISTS rum;

Para buscar en las descripciones de los productos dentro de la tabla cymbal_products, debemos crear una columna que almacene la descripción del producto como tsvector. Esta columna almacena automáticamente el texto procesado y mejora el rendimiento de las consultas.

ALTER TABLE cymbal_products
ADD COLUMN product_search_vector tsvector
GENERATED ALWAYS AS (to_tsvector('english', product_description)) STORED;

Ahora podemos crear un nuevo índice de RUM para la columna product_search_vector.

CREATE INDEX cymbal_products_rum
ON cymbal_products
USING rum (product_search_vector rum_tsvector_ops);

Para consultar la tabla con el índice, ejecuta la siguiente consulta que busca coincidencias de "cerezo". El operador <=> calcula la puntuación de relevancia, o distancia, entre el documento y la búsqueda directamente desde el índice.

SELECT product_name, product_description
FROM cymbal_products
WHERE product_search_vector @@ to_tsquery('english', 'cherry <-> tree')
ORDER BY product_search_vector <=> to_tsquery('english', 'cherry <-> tree');

10. Realiza una búsqueda híbrida

La función google_vector_utils.hybrid_search() te permite combinar resultados de varios tipos de búsqueda, como la búsqueda de vectores y la búsqueda en el texto completo. La función fusiona los resultados clasificados de cada componente de búsqueda en una sola lista unificada con el algoritmo de fusión de clasificación recíproca (RRF). Este enfoque proporciona resultados más relevantes que un solo tipo de búsqueda.

La función hybrid_search() construye y ejecuta de forma dinámica una sola consulta en SQL. Crea una expresión de tabla común (CTE) para cada componente de búsqueda que definas. Luego, la función une los resultados de todos los CTE y calcula una puntuación final de RR para cada documento y, así, genera una lista unificada y clasificada.

Para usar la función, debemos activar enable_preview_ai_functions en la instancia principal. Ejecuta el siguiente comando en Cloud Shell:

export PROJECT_ID=$(gcloud config get-value project)
export REGION=us-central1
export ADBCLUSTER=alloydb-hybrid-search
gcloud beta alloydb instances update $ADBCLUSTER-pr \
   --database-flags google_ml_integration.enable_preview_ai_functions=on \
   --region=$REGION \
   --cluster=$ADBCLUSTER \
   --project=$PROJECT_ID \
   --update-mode=FORCE_APPLY

La siguiente consulta combina nuestra pregunta de búsqueda de vectores anterior con nuestra pregunta de búsqueda de texto completo. Esta es una consulta de búsqueda híbrida muy simple. Puedes probar algo más complejo, como usar "árboles que crecen más que una casa" en el componente de búsqueda de vectores y "California" en el componente de FTS.

SELECT score, id, p.product_name
FROM ai.hybrid_search(
  search_inputs => ARRAY[
      '{
        "data_type": "vector",
        "table_name": "cymbal_products",
        "key_column": "uniq_id",
        "vec_column": "product_embedding",
        "distance_operator": "public.<=>",
        "limit": 5,
        "query_vector": "ai.embedding(''text-embedding-005'', ''cherry'')::vector"
      }'::JSONB,
      '{
        "data_type": "text",
        "table_name": "cymbal_products",
        "key_column": "uniq_id",
        "text_column": "product_search_vector",
        "limit": 5,
        "ranking_function": "<=>",
        "query_text_input": "tree"
      }'::JSONB
  ]
) JOIN cymbal_products p ON id = p.uniq_id;

Resultado esperado

"score","id","product_name"
"0.00819672631147241","d536e9e823296a2eba198e52dd23e712","Cherry Tree"
"0.015873015873015872","23e41a71d63d8bbc9bdfa1d118cfddc5","Apple Tree"
"0.00819672631147241","dc789a2f87b142e94e6e325689482af9","Oak Tree"
"0.008064521129029258","f5c70d62ccf3118d73863bf3b17edcbe","Cypress Tree"
"0.008064521129029258","b70c44b1a38c0a2329fa583c9109a80f","Peach Tree"

En los resultados, encontrarás id, que es el key_column especificado, y score, que es el valor final calculado por el RRF. La fusión de clasificación recíproca (RRF) es un algoritmo basado en la clasificación que combina varias listas clasificadas de resultados de la búsqueda en una sola lista clasificada asignando una puntuación a cada documento. Esta puntuación se basa en la clasificación recíproca del RRF en todas las listas que contribuyen, y los documentos con una clasificación más alta reciben una mayor contribución. Si usas include_json_output => true en el parámetro, se mostrará una columna detail_json que contiene un desglose del cálculo de la puntuación para cada componente.

Si bien la búsqueda de texto completo es la mejor opción para encontrar términos específicos o coincidencias exactas, la búsqueda de vectores se destaca por encontrar sinónimos y la intención, incluso cuando las palabras no coinciden. Al combinar estos dos métodos, la búsqueda híbrida garantiza que los usuarios obtengan un conjunto sólido de resultados que sean precisos literalmente y relevantes semánticamente.

11. Limpia el entorno

Cuando termines el lab, destruye las instancias y el clúster de AlloyDB.

Borra el clúster de AlloyDB y todas las instancias

Si usaste la versión de prueba de AlloyDB No borres el clúster de prueba si planeas probar otros labs y recursos con él. No podrás crear otro clúster de prueba en el mismo proyecto.

El clúster se destruye con la opción force que también borra todas las instancias que pertenecen al clúster.

En Cloud Shell, define el proyecto y las variables de entorno si te desconectaste y se perdieron todos los parámetros de configuración anteriores:

gcloud config set project <your project id>
export REGION=us-central1
export ADBCLUSTER=alloydb-hybrid-search
export PROJECT_ID=$(gcloud config get-value project)

Borra el clúster:

gcloud alloydb clusters delete $ADBCLUSTER --region=$REGION --force

Resultado esperado en la consola:

student@cloudshell:~ (test-project-001-402417)$ gcloud alloydb clusters delete $ADBCLUSTER --region=$REGION --force
All of the cluster data will be lost when the cluster is deleted.

Do you want to continue (Y/n)?  Y

Operation ID: operation-1697820178429-6082890a0b570-4a72f7e4-4c5df36f
Deleting cluster...done.   

Borra las copias de seguridad de AlloyDB

Borra todas las copias de seguridad de AlloyDB del clúster:

for i in $(gcloud alloydb backups list --filter="CLUSTER_NAME: projects/$PROJECT_ID/locations/$REGION/clusters/$ADBCLUSTER" --format="value(name)" --sort-by=~createTime) ; do gcloud alloydb backups delete $(basename $i) --region $REGION --quiet; done

Resultado esperado en la consola:

student@cloudshell:~ (test-project-001-402417)$ for i in $(gcloud alloydb backups list --filter="CLUSTER_NAME: projects/$PROJECT_ID/locations/$REGION/clusters/$ADBCLUSTER" --format="value(name)" --sort-by=~createTime) ; do gcloud alloydb backups delete $(basename $i) --region $REGION --quiet; done
Operation ID: operation-1697826266108-60829fb7b5258-7f99dc0b-99f3c35f
Deleting backup...done.                                                                                                                                                                                                                                                            

Ahora podemos destruir nuestra VM

Borra la VM de GCE

En Cloud Shell, ejecuta el siguiente comando:

export GCEVM=instance-1
export ZONE=us-central1-a
gcloud compute instances delete $GCEVM \
    --zone=$ZONE \
    --quiet

Resultado esperado en la consola:

student@cloudshell:~ (test-project-001-402417)$ export GCEVM=instance-1
export ZONE=us-central1-a
gcloud compute instances delete $GCEVM \
    --zone=$ZONE \
    --quiet
Deleted

12. Felicitaciones

Felicitaciones por completar el codelab.

Temas abordados