Introdução à pesquisa híbrida no AlloyDB

1. Introdução

Neste codelab, você vai aprender a fazer pesquisas híbridas no AlloyDB usando a extensão do método de atualização de classificação (RUM, na sigla em inglês) e o índice de vizinho mais próximo escalonável (ScaNN, na sigla em inglês). Este laboratório faz parte de uma coleção dedicada aos recursos de IA do AlloyDB. Leia mais na página da IA do AlloyDB na documentação.

Pré-requisitos

  • Conhecimentos básicos sobre o Google Cloud e o console
  • Habilidades básicas na interface de linha de comando e no Google Shell

O que você vai aprender

O que é necessário

  • Uma conta e um projeto do Google Cloud
  • Um navegador da Web, como o Chrome

2. Configuração e requisitos

Configuração do projeto

Faça login no Console do Google Cloud. Crie uma conta do Gmail ou do Google Workspace, se ainda não tiver uma.

Use uma conta pessoal em vez de uma conta escolar ou de trabalho.

Criar um projeto do Google Cloud

  1. No console do Google Cloud, na página do seletor de projetos, selecione ou crie um projeto na nuvem do Google Cloud.
  2. Verifique se o faturamento está ativado para seu projeto do Cloud. Saiba como verificar se o faturamento está ativado em um projeto.

Ativar faturamento

Para ativar o faturamento, você tem duas opções. Você pode usar sua conta de faturamento pessoal ou resgatar créditos seguindo estas etapas.

Configurar uma conta de faturamento pessoal

Se você configurou o faturamento usando créditos do Google Cloud, pule esta etapa.

Para configurar uma conta de faturamento pessoal, acesse este link e ative o faturamento no console do Cloud.

Algumas observações:

  • A conclusão deste laboratório custa menos de US $3 em recursos do Cloud.
  • Siga as etapas no final deste laboratório para excluir recursos e evitar mais cobranças.
  • Novos usuários podem aproveitar o teste sem custos financeiros de US$300.

Inicie o Cloud Shell

Embora o Google Cloud e o Spanner possam ser operados remotamente do seu laptop, neste codelab usaremos o Google Cloud Shell, um ambiente de linha de comando executado no Cloud.

O Cloud Shell é um ambiente de linha de comando executado no Google Cloud que vem pré-carregado com as ferramentas necessárias.

  1. Clique em Ativar o Cloud Shell na parte de cima do console do Google Cloud.
  2. Depois de se conectar ao Cloud Shell, verifique sua autenticação:
    gcloud auth list
    
  3. Confirme se o projeto está configurado:
    gcloud config get project
    
  4. Se o projeto não estiver definido como esperado, faça o seguinte:
    export PROJECT_ID=<YOUR_PROJECT_ID>
    gcloud config set project $PROJECT_ID
    

Essa máquina virtual contém todas as ferramentas de desenvolvimento necessárias. Ela oferece um diretório principal persistente de 5 GB, além de ser executada no Google Cloud. Isso aprimora o desempenho e a autenticação da rede. Neste codelab, todo o trabalho pode ser feito com um navegador. Você não precisa instalar nada.

3. Antes de começar

Ativar API

Saída:

Para usar o AlloyDB, o Compute Engine, os serviços de rede e a Vertex AI, é necessário ativar as APIs respectivas no seu projeto do Google Cloud.

Como ativar as APIs

No terminal do Cloud Shell, verifique se o ID do projeto está configurado:

gcloud config set project [YOUR-PROJECT-ID]

Defina a variável de ambiente PROJECT_ID:

PROJECT_ID=$(gcloud config get-value project)

Ative todas as APIs necessárias:

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.

Apresentação das APIs

  • A API AlloyDB (alloydb.googleapis.com) permite criar, gerenciar e escalonar clusters do AlloyDB para PostgreSQL. Ele oferece um serviço de banco de dados totalmente gerenciado e compatível com PostgreSQL, projetado para cargas de trabalho empresariais transacionais e analíticas exigentes.
  • A API Compute Engine (compute.googleapis.com) permite criar e gerenciar máquinas virtuais (VMs), discos permanentes e configurações de rede. Ela fornece a base principal de infraestrutura como serviço (IaaS) necessária para executar suas cargas de trabalho e hospedar a infraestrutura subjacente de muitos serviços gerenciados.
  • A API Resource Manager (cloudresourcemanager.googleapis.com) permite gerenciar de forma programática os metadados e a configuração do seu projeto do Google Cloud. Ele permite organizar recursos, processar políticas de gerenciamento de identidade e acesso (IAM) e validar permissões em toda a hierarquia do projeto.
  • A API Service Networking (servicenetworking.googleapis.com) permite automatizar a configuração da conectividade particular entre sua rede de nuvem privada virtual (VPC) e os serviços gerenciados do Google. Ele é especificamente necessário para estabelecer o acesso a IP particular para serviços como o AlloyDB, para que eles possam se comunicar com segurança com seus outros recursos.
  • A API Vertex AI (aiplatform.googleapis.com) permite que seus aplicativos criem, implantem e escalonem modelos de machine learning. Ela oferece a interface unificada para todos os serviços de IA do Google Cloud, incluindo acesso a modelos de IA generativa (como o Gemini) e treinamento de modelos personalizados.

Se quiser, configure a região padrão para usar os modelos de embedding da Vertex AI. Leia mais sobre os locais disponíveis para a Vertex AI. No exemplo, usamos a região "us-central1".

gcloud config set compute/region us-central1

4. Implantar o AlloyDB

Antes de criar um cluster do AlloyDB, é necessário ter um intervalo de IP privado disponível na VPC para ser usado pela instância futura do AlloyDB. Se não tivermos, precisamos criar e atribuir para uso por serviços internos do Google. Depois disso, será possível criar o cluster e a instância.

Criar um intervalo de IP privado

É preciso configurar o acesso a serviços particulares na VPC para o AlloyDB. Vamos supor que o projeto tem uma rede VPC "padrão" a ser usada para todas as ações.

Crie o intervalo de IP privado:

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

Crie uma conexão privada com o intervalo de IP alocado:

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

Saída esperada do console:

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)$

Criar cluster do AlloyDB

Nesta seção, vamos criar um cluster do AlloyDB na região us-central1.

Defina a senha do usuário postgres. Você pode definir sua própria senha ou usar uma função aleatória para gerar uma.

export PGPASSWORD=`openssl rand -hex 12`

Saída esperada do console:

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

Anote a senha do PostgreSQL para uso futuro.

echo $PGPASSWORD

Você vai precisar dessa senha no futuro para se conectar à instância como o usuário postgres. Recomendamos anotar ou copiar em algum lugar para usar depois.

Saída esperada do console:

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

Criar cluster do AlloyDB

Defina a região e o nome do cluster do AlloyDB. Vamos usar a região us-central1 e alloydb-hybrid-search como nome do cluster:

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

Execute o comando para criar o cluster:

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

Saída esperada do console:

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.                                                                                                                                                                                                                                                           

Crie uma instância principal do AlloyDB para o cluster na mesma sessão do Cloud Shell. Se você se desconectar, será necessário definir as variáveis de ambiente de região e nome do cluster novamente.

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

Saída esperada do console:

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. Conectar-se ao AlloyDB

O AlloyDB é implantado usando uma conexão somente privada. Por isso, precisamos de uma VM com o cliente PostgreSQL instalado para trabalhar com o banco de dados.

Implantar a VM do GCE

Crie uma VM do GCE na mesma região e VPC que o cluster do AlloyDB.

No Cloud Shell, execute:

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

Saída esperada do console:

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

Instalar o cliente Postgres

Instale o software do cliente PostgreSQL na VM implantada.

Conecte-se à VM:

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

Saída esperada do console:

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:~$ 

Instale o comando em execução do software na VM:

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

Saída esperada do console:

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) ...

Conectar-se à instância

Conecte-se à instância principal pela VM com psql.

Na mesma guia do Cloud Shell com a sessão SSH aberta na VM instance-1.

Use o valor da senha do AlloyDB (PGPASSWORD) e o ID do cluster do AlloyDB para se conectar ao AlloyDB pela VM do 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"

Saída esperada do console:

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=>

Feche a sessão psql:

exit

6. Preparar banco de dados

Precisamos criar um banco de dados, ativar a integração da Vertex AI, criar objetos de banco de dados e importar os dados.

Conceder as permissões necessárias ao AlloyDB

Adicione permissões da Vertex AI ao agente de serviço do AlloyDB.

Abra outra guia do Cloud Shell pelo sinal "+" na parte superior.

abc505ac4d41f24e.png

Na nova guia do Cloud Shell, execute:

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"

Saída esperada do console:

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
 

Feche a guia pelo comando de execução "sair" na guia:

exit

Criar banco de dados

Crie um banco de dados chamado quickstart.

Na sessão da VM do GCE, execute:

Crie o banco de dados:

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

Saída esperada do console:

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

Ativar a integração da Vertex AI

Ative a integração da Vertex AI e as extensões pgvector no banco de dados.

Na VM do GCE, execute:

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"

Saída esperada do console:

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 dados

Faça o download dos dados preparados e importe-os para o novo banco de dados.

Na VM do GCE, execute:

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"

Saída esperada do console:

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. Gerar embeddings de vetor

Depois de importar os dados, temos as seguintes tabelas: cymbal_products, que armazena informações sobre produtos, cymbal_inventory, que rastreia o estoque de itens em cada loja, e cymbal_stores, que é uma lista de lojas. Para realizar uma pesquisa semântica nos nossos produtos, precisamos gerar embeddings de vetores das descrições dos produtos com a função initialize_embeddings. Vamos usar a integração da Vertex AI para calcular dados de vetor com base nas descrições dos produtos e adicioná-los à tabela. Leia mais sobre a tecnologia usada na documentação.

Para usar a integração, conecte-se ao banco de dados usando psql na VM com o IP da instância do AlloyDB e a senha do postgres:

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

Verifique a versão da extensão google_ml_integration.

SELECT extversion FROM pg_extension WHERE extname = 'google_ml_integration';

A versão precisa ser 1.5.2 ou mais recente. Confira um exemplo da saída:

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

A versão padrão precisa ser 1.5.2 ou mais recente, mas se a instância mostrar uma versão mais antiga, ela provavelmente precisará ser atualizada. Verifique se a manutenção foi desativada para a instância.

Vamos usar a geração de embeddings em lote para melhorar a eficiência. Leia mais sobre as diferentes opções e técnicas de geração de incorporações no guia. Para usar o embedding em lote, é necessário ativar o goole_ml_integration.enable_faster_embedding_generation

show google_ml_integration.enable_faster_embedding_generation;

Se a flag estiver na posição correta, a saída esperada será assim:

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

Mas, se aparecer "desativado", será necessário atualizar a instância. Faça isso usando o console da Web ou o comando gcloud, conforme descrito na documentação. Aqui mostro como fazer isso usando o comando 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

Pode levar alguns minutos, mas o valor da flag será alterado para "ativado". Depois disso, siga para as próximas etapas.

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

Na sessão psql conectada ao banco de dados, crie uma nova coluna para armazenar embeddings em cymbal_products

ALTER TABLE cymbal_products ADD COLUMN product_embedding vector(768);

Saída esperada do console:

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

Por fim, também queremos que os embeddings sejam atualizados à medida que os valores das colunas são alterados. Para isso, incluímos o argumento incremental_refresh_mode na chamada de função. Isso introduz uma sobrecarga no nosso banco de dados, mas é uma troca que fazemos para manter automaticamente os embeddings sincronizados com o conteúdo. Se você quiser atualizar manualmente os embeddings, consulte as instruções na documentação.

Agora, juntando tudo e gerando embeddings, usamos a função initialize_embeddings e transmitimos batch_size de 50 como a dica de lote e definimos incremental_refresh_mode como 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'
);

Agora, se inserirmos uma nova linha na tabela com o valor NULL para a coluna 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);

Agora, quando consultarmos a linha que acabamos de inserir, vamos ver que a coluna product_embedding é atualizada automaticamente.

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

A saída será semelhante a esta:

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. Criar índice vetorial

Para melhorar o desempenho da pesquisa vetorial, vamos adicionar um índice ScaNN.

Criar índice do ScaNN

Para criar o índice SCANN, precisamos ativar mais uma extensão. A extensão alloydb_scann oferece uma interface para trabalhar com o índice vetorial do tipo ANN usando o algoritmo ScaNN do Google.

CREATE EXTENSION IF NOT EXISTS alloydb_scann;

Saída esperada:

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

O índice pode ser criado no modo MANUAL ou AUTO. O modo MANUAL é ativado por padrão, e você pode criar e manter um índice como qualquer outro. Mas se você ativar o modo AUTO, poderá criar o índice sem precisar fazer nenhuma manutenção. Leia em detalhes sobre todas as opções na documentação. No nosso caso, não temos linhas suficientes para criar o índice no modo AUTO. Por isso, vamos criá-lo como MANUAL e incluir parâmetros de ajuste. Leia sobre o ajuste de parâmetros de índice na documentação.

Precisamos ativar a flag scann.enable_preview_features para modificar os parâmetros de ajuste. No 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

Pode levar alguns minutos, mas o valor da flag será alterado para "ativado". Depois que a flag for definida, podemos voltar à nossa sessão psql na VM e criar o índice com 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);

Saída esperada:

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=>

Inspecionar o uso do índice

Agora podemos executar a consulta de pesquisa vetorial no modo EXPLAIN e verificar se o índice está sendo usado.

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;

Saída esperada (editada para fins de esclarecimento):

...
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)
...

Na saída, é possível ver claramente que a consulta estava usando "Index Scan using cymbal_products_embeddings_scann on cymbal_products".

9. Índice de pesquisa de texto completo

O AlloyDB é compatível com todos os tipos de índice para pesquisa de texto completo que o PostgreSQL nativo oferece. A escolha do índice depende do equilíbrio entre velocidade de pesquisa, tempo de criação do índice, velocidade de atualização e as funcionalidades de pesquisa específicas necessárias, como pesquisa de frases ou classificação de relevância.

No nosso exemplo, vamos usar a extensão RUM para operações de pesquisa de texto completo mais eficientes. O RUM melhora os índices GIN padrão armazenando informações de posição diretamente no índice, permitindo que você faça pesquisas de frases e classificação de relevância mais rápidas sem acessar os dados da tabela.

Você pode usar o AlloyDB Studio ou continuar usando o cliente psql para ativar a extensão rum.

Criar índice do RUM

CREATE EXTENSION IF NOT EXISTS rum;

Para pesquisar descrições de produtos na tabela cymbal_products, precisamos criar uma coluna que armazene a descrição do produto como tsvector. Essa coluna armazena automaticamente o texto processado e melhora o desempenho da consulta.

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

Agora podemos criar um novo índice de RUM para a coluna product_search_vector.

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

Para consultar a tabela usando o índice, execute a consulta a seguir, que procura correspondências de "cherry tree". O operador <=> calcula a pontuação de relevância ou a distância entre o documento e a consulta diretamente do í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. Fazer uma pesquisa híbrida

Com a função google_vector_utils.hybrid_search(), você pode combinar resultados de vários tipos de pesquisa, como pesquisa vetorial e de texto completo. A função funde os resultados classificados de cada componente de pesquisa em uma única lista unificada usando o algoritmo de Fusão de Classificação Recíproca (RRF, na sigla em inglês). Essa abordagem oferece resultados mais relevantes do que um único tipo de pesquisa.

A função hybrid_search() cria e executa dinamicamente uma única consulta SQL. Ele cria uma expressão de tabela comum (CTE) para cada componente de pesquisa definido. Em seguida, a função une os resultados de todas as CTEs e calcula uma pontuação final de RRF para cada documento, produzindo uma lista unificada e classificada.

Para usar a função, ative o enable_preview_ai_functions na instância principal. Execute o comando a seguir no 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

A consulta a seguir combina nossa pergunta anterior de pesquisa de vetor com nossa pergunta de pesquisa de texto completo. Essa é uma consulta de pesquisa híbrida muito simples. Você pode tentar algo mais complexo, como usar "árvores que crescem mais do que uma casa" no componente de pesquisa vetorial e "Califórnia" no componente de pesquisa de texto completo.

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"

Nos resultados, você vai encontrar id, que é o key_column especificado, e score, que é o valor final calculado pela RRF. A fusão de classificação recíproca (RRF, na sigla em inglês) é um algoritmo baseado em classificação que combina várias listas de resultados da pesquisa em uma única lista classificada, atribuindo uma pontuação a cada documento. Essa pontuação é baseada na classificação recíproca do RRF em todas as listas contribuintes, e os documentos com classificação mais alta recebem uma contribuição maior. Usando o include_json_output => true no parâmetro, uma coluna detail_json será retornada com um detalhamento do cálculo da pontuação de cada componente.

A pesquisa de texto completo é melhor para encontrar termos específicos ou correspondências exatas, enquanto a pesquisa vetorial é ótima para encontrar sinônimos e intenções, mesmo quando as palavras não correspondem. Ao combinar esses dois métodos, a pesquisa híbrida garante que os usuários recebam um conjunto robusto de resultados que são literalmente precisos e semanticamente relevantes.

11. Limpar o ambiente

Destrua as instâncias e o cluster do AlloyDB quando terminar o laboratório.

Excluir o cluster do AlloyDB e todas as instâncias

Se você usou a versão de teste do AlloyDB. Não exclua o cluster de teste se você planeja testar outros laboratórios e recursos usando esse cluster. Não será possível criar outro cluster de teste no mesmo projeto.

O cluster é destruído com a opção "force" que também exclui todas as instâncias pertencentes.

No Cloud Shell, defina o projeto e as variáveis de ambiente se tiver ocorrido uma desconexão e todas as configurações anteriores forem perdidas:

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

Exclua o cluster:

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

Saída esperada do console:

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.   

Excluir backups do AlloyDB

Exclua todos os backups do AlloyDB do cluster:

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

Saída esperada do console:

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.                                                                                                                                                                                                                                                            

Agora você pode destruir a VM.

Excluir a VM do GCE

No Cloud Shell, execute:

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

Saída esperada do console:

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. Parabéns

Parabéns por concluir o codelab.

O que vimos