1. Visão geral
Neste codelab, você vai aprender a adicionar recursos de pesquisa avançados ao seu app usando a pesquisa de similaridade de vetores do Firestore. Você vai implementar um recurso de pesquisa semântica para um app de anotações escrito em Swift e SwiftUI.
O que você vai aprender
- Como instalar a extensão de pesquisa vetorial com o Firestore para calcular embeddings de vetor.
- Como chamar o Firebase Cloud Functions de um aplicativo Swift.
- Como pré-filtrar dados com base no usuário conectado.
O que é necessário
- Xcode 15.3
- O exemplo de código do codelab. Você vai fazer o download dele em uma etapa posterior do codelab.
2. Criar e configurar um projeto do Firebase
Para usar a extensão Firebase Vector Search, você precisa de um projeto do Firebase. Nesta parte do codelab, você vai criar um projeto do Firebase e ativar os serviços necessários, como o Cloud Firestore e o Firebase Authentication.
Criar um projeto do Firebase
- Faça login no console do Firebase usando sua Conta do Google.
- Clique no botão para criar um projeto e insira um nome (por exemplo,
Firestore Vector Search Codelab
).
- Clique em Continuar.
- Se solicitado, leia e aceite os Termos do Firebase e clique em Continuar.
- (Opcional) Ative a assistência de IA no console do Firebase (chamada de "Gemini no Firebase").
- Neste codelab, você não precisa do Google Analytics. Portanto, desative a opção do Google Analytics.
- Clique em Criar projeto, aguarde o provisionamento e clique em Continuar.
Para saber mais sobre os projetos do Firebase, consulte Noções básicas sobre projetos do Firebase.
Fazer upgrade do plano de preços do Firebase
Para usar as Extensões do Firebase e os serviços de nuvem subjacentes, seu projeto do Firebase precisa estar no plano de preços de pagamento por uso (Blaze), o que significa que ele está vinculado a uma conta do Cloud Billing.
- Uma conta do Cloud Billing exige uma forma de pagamento, como cartão de crédito.
- Se você ainda não conhece o Firebase e o Google Cloud, confira se tem qualificação para receber um crédito de US$300 e uma conta de teste sem custos financeiros do Cloud Billing.
- Se você estiver fazendo este codelab como parte de um evento, pergunte ao organizador se há créditos do Cloud disponíveis.
Para fazer upgrade do seu projeto para o plano Blaze, siga estas etapas:
- No console do Firebase, selecione Fazer upgrade do seu plano.
- Selecione o plano Blaze. Siga as instruções na tela para vincular uma conta do Cloud Billing ao seu projeto.
Se você precisou criar uma conta do Cloud Billing como parte desse upgrade, talvez seja necessário voltar para o fluxo de upgrade no console do Firebase para concluir o processo.
Ativar e configurar produtos do Firebase no console
O app que você está criando usa vários produtos do Firebase disponíveis para apps da Apple:
- Firebase Authentication: usado para permitir que os usuários acessem seu app facilmente.
- Cloud Firestore: é usado para salvar dados estruturados na nuvem e receber notificações instantâneas quando os dados são alterados.
- Regras de segurança do Firebase para proteger seu banco de dados.
Alguns desses produtos precisam de configuração especial ou precisam ser ativados usando o Console do Firebase.
Ativar a autenticação anônima para o Firebase Authentication
Esse aplicativo usa a autenticação anônima para permitir que os usuários comecem a usar o app sem precisar criar uma conta primeiro. Isso resulta em um processo de integração sem dificuldades. Para saber mais sobre a autenticação anônima e como fazer upgrade para uma conta nomeada, consulte Práticas recomendadas para autenticação anônima.
- No painel à esquerda do console do Firebase, clique em Build > Authentication. Em seguida, clique em Começar.
- Agora você está no painel "Autenticação", onde é possível conferir os usuários inscritos, configurar provedores de login e gerenciar configurações.
- Selecione a guia Método de login ou clique aqui para acessar a guia diretamente.
- Clique em Anônimo nas opções de provedor, ative a chave para Ativar e clique em Salvar.
Configurar o Cloud Firestore
Este aplicativo Swift usa o Cloud Firestore para salvar notas.
Veja como configurar o Cloud Firestore no seu projeto do Firebase:
- No painel à esquerda do console do Firebase, expanda Build e selecione Banco de dados do Firestore.
- Clique em Criar banco de dados.
- Deixe o ID do banco de dados definido como
(default)
. - Selecione um local para o banco de dados e clique em Próxima.
No caso de apps reais, escolha um local próximo aos usuários. - Clique em Iniciar no modo de teste. Leia o aviso sobre as regras de segurança.
Mais adiante neste codelab, você vai adicionar regras de segurança para proteger seus dados. Não distribua ou exponha um aplicativo publicamente sem adicionar regras de segurança ao seu banco de dados. - Clique em Criar.
Configurar o Cloud Storage para Firebase
O app da Web usa o Cloud Storage para Firebase para armazenar, fazer upload e compartilhar fotos.
Veja como configurar o Cloud Storage para Firebase no seu projeto do Firebase:
- No painel à esquerda do console do Firebase, expanda Build e selecione Storage.
- Clique em Começar.
- Selecione um local para seu bucket de armazenamento padrão.
Os buckets emUS-WEST1
,US-CENTRAL1
eUS-EAST1
podem aproveitar o nível"Sempre sem custo financeiro" do Google Cloud Storage. Os buckets em todos os outros locais seguem os preços e usos do Google Cloud Storage. - Clique em Iniciar no modo de teste. Leia o aviso sobre as regras de segurança.
Mais adiante neste codelab, você vai adicionar regras de segurança para proteger seus dados. Não distribua ou exponha um aplicativo publicamente sem adicionar regras de segurança ao bucket do Storage. - Clique em Criar.
3. Conectar o app para dispositivos móveis
Nesta seção do codelab, você vai baixar o código-fonte de um app simples para anotações e conectá-lo ao projeto do Firebase que acabou de criar.
Baixar o aplicativo de exemplo
- Acesse https://github.com/FirebaseExtended/codelab-firestore-vectorsearch-ios e clone o repositório na sua máquina local.
- Abra o projeto Notes.xcodeproj no Xcode.
Conectar o app ao seu projeto do Firebase
Para que seu app possa acessar os serviços do Firebase, é necessário configurá-lo no console do Firebase. É possível conectar vários aplicativos cliente ao mesmo projeto do Firebase. Por exemplo, se você criar um app Android ou da Web, conecte-os ao mesmo projeto do Firebase.
Para saber mais sobre os projetos do Firebase, consulte Noções básicas sobre projetos do Firebase.
- No Console do Firebase, acesse a página de visão geral do seu projeto.
- Clique no ícone iOS+ para adicionar seu app iOS.
- Na tela Adicionar o Firebase ao app da Apple, insira o ID do pacote do projeto Xcode (com.google.firebase.codelab.Notes).
- Se quiser, você pode inserir um apelido para o app (Notas para iOS).
- Clique em "Registrar app" para avançar para a próxima etapa.
- Faça o download do arquivo GoogleServices-Info.plist.
- Arraste GoogleServices-Info.plist para a pasta Notes do seu projeto Xcode. Uma boa maneira de fazer isso é soltar o arquivo abaixo de Assets.xcassets.
- Selecione Copiar itens, se necessário, verifique se o destino Notas está selecionado em Adicionar a destinos e clique em Concluir.
- No console do Firebase, clique no restante do processo de configuração: o exemplo que você baixou no início desta seção já tem o SDK do Firebase para Apple instalado e a inicialização configurada. Para concluir o processo, clique em Continuar para o console.
Executar o app
Agora é hora de testar o app.
- De volta ao Xcode, execute o app no simulador do iOS. No menu suspenso Executar destinos, primeiro selecione um dos simuladores do iOS.
- Em seguida, clique no botão Executar ou pressione ⌘ + R.
- Depois que o app for iniciado no simulador, adicione algumas observações.
- No console do Firebase, navegue até o navegador de dados do Firestore para ver novos documentos sendo criados à medida que você adiciona novas notas no app.
4. Instalar a extensão da pesquisa de vetores com o Firestore
Nesta parte do codelab, você vai instalar a extensão Vector Search com Firestore e configurá-la de acordo com os requisitos do app de anotações em que está trabalhando.
Iniciar a instalação da extensão
- Ainda na seção do Firestore, clique na guia Extensões.
- Clique em Conheça o Extensions Hub.
- Digite "vetor".
- Clique em "Pesquisa de vetores com a extensão do Firestore".
Isso vai levar você à página de detalhes da extensão, onde é possível ler mais sobre ela, como ela funciona, quais serviços do Firebase são necessários e como configurá-la.
- Clique em Instalar no console do Firebase.
- Uma lista de todos os seus projetos vai aparecer.
- Escolha o projeto que você criou na primeira etapa deste codelab.
Configurar a extensão
- Analise as APIs ativadas e os recursos criados.
- Ative os serviços necessários.
- Depois que todos os serviços estiverem ativados, clique em Próxima.
- Verifique o acesso garantido a esta extensão.
- Configurar a extensão:
- Selecione Vertex AI como o LLM.
- Caminho da coleção: notes
- Limite de consulta padrão: 3
- Nome do campo de entrada: text
- Nome do campo de saída : embedding
- Nome do campo de status: *status*
- Incorporar documentos atuais: Sim
- Atualizar documentos atuais: Sim
- Local da função do Cloud: us-central1
- Clique em Instalar extensão para concluir a instalação.
Isso pode levar alguns minutos. Enquanto aguarda a conclusão da instalação, avance para a próxima seção do tutorial e leia algumas informações básicas sobre embeddings de vetores.
5. Segundo plano
Enquanto você aguarda a conclusão da instalação, confira algumas informações básicas sobre como a extensão de pesquisa vetorial com o Firestore funciona.
O que são vetores, embeddings e bancos de dados de vetores?
- Os vetores são objetos matemáticos que representam a magnitude e a direção de uma quantidade. Eles podem ser usados para representar dados de uma forma que facilite a comparação e a pesquisa.
- Embeddings são vetores que representam o significado de uma palavra ou frase. Eles são criados treinando uma rede neural em um grande corpus de texto e aprendendo as relações entre as palavras.
- Bancos de dados vetoriais são otimizados para armazenar e pesquisar dados vetoriais. Eles permitem uma pesquisa eficiente de vizinho mais próximo, que é o processo de encontrar os vetores mais semelhantes a um determinado vetor de consulta.
Como funciona a pesquisa vetorial?
A pesquisa vetorial funciona comparando o vetor de consulta a todos os vetores no banco de dados. Os vetores mais semelhantes ao vetor de consulta são retornados como resultados da pesquisa.
A semelhança entre dois vetores pode ser medida usando várias métricas de distância. A métrica de distância mais comum é a similaridade de cosseno, que mede o ângulo entre dois vetores.
6. Testar a extensão de pesquisa de vetores com o Firestore
Antes de usar a Extensão de pesquisa vetorial com o Firestore no app iOS que você baixou no início deste codelab, teste a extensão no console do Firebase.
Leia a documentação
As Extensões do Firebase incluem documentação sobre como elas funcionam.
- Quando a instalação da extensão terminar, clique no botão Começar.
- Confira a guia "Como a extensão funciona", que explica:
- como calcular incorporações de documentos adicionando-os à coleção
notes
; - como consultar o índice chamando a função chamável
ext-firestore-vector-search-queryCallable
; - ou como consultar o índice adicionando um documento de consulta à coleção
_firestore-vector-search/index/queries
. - Ele também explica como configurar uma função de incorporação personalizada. Isso é útil se nenhum dos LLMs compatíveis com a extensão atender aos seus requisitos e você quiser usar um LLM diferente para calcular incorporações.
- como calcular incorporações de documentos adicionando-os à coleção
- Clique no link Painel do Cloud Firestore para acessar sua instância do Firestore.
- Navegue até o documento
_firestore-vector-search/index
. Ele vai mostrar que a extensão terminou de calcular os embeddings de todos os documentos de notas que você criou em uma etapa anterior deste codelab. - Para verificar isso, abra um dos documentos de notas. Você vai encontrar um campo adicional chamado
embedding
do tipovector<768>
, além de um campostatus
.
Criar um documento de amostra
Crie um documento no console do Firebase para ver a extensão em ação.
- Ainda no navegador de dados do Firestore, navegue até a coleção
notes
e clique em + Adicionar documento na coluna do meio. - Clique em ID automático para gerar um novo ID de documento exclusivo.
- Adicione um campo chamado
text
do tipo string e cole algum texto no campo value. É importante que não seja lorem ipsum ou algum outro texto aleatório. Escolha uma notícia, por exemplo. - Clique em Salvar.
- Observe como a extensão adiciona um campo de status para indicar que está processando dados.
- Depois de um breve momento, um novo campo
embedding
vai aparecer com o valorvector<768>
.
Fazer uma consulta
A extensão Pesquisa Vetorial com Firestore tem um recurso interessante que permite consultar o índice de documentos sem precisar conectar um app.
- Na seção do Firestore do console do Firebase, acesse o documento
_firestore-vector-search/index
. - Clique em + Iniciar coleção
- Crie uma subcoleção chamada
queries
. - Crie um documento e defina o campo
query
como um texto que aparece em um dos seus documentos. Isso funciona melhor para consultas semânticas, como "Como posso mapear documentos do Firestore com Swift", desde que pelo menos uma das anotações adicionadas contenha texto sobre esse assunto. - É possível que um erro apareça no status
- Isso ocorre devido a um índice ausente. Para configurar a configuração de índice ausente, acesse o console do Google Cloud do seu projeto seguindo este link e selecione seu projeto na lista
- No explorador de registros do Cloud, agora você vai ver uma mensagem de erro dizendo "FAILED_PRECONDITION: Missing vector index configuration. Crie o índice necessário com o seguinte comando gcloud: ..."
- A mensagem de erro também contém um comando
gcloud
que você precisa executar para configurar o índice ausente. - Execute o seguinte comando na linha de comando. Se você não tiver a CLI
gcloud
instalada na sua máquina, siga as instruções aqui para fazer a instalação. A criação do índice leva alguns minutos. Você pode verificar o progresso na guia Índices da seção do Firestore no console do Firebase.gcloud alpha firestore indexes composite create --project=INSERT-YOUR=PROJECT-ID-HERE --collection-group=notes --query-scope=COLLECTION --field-config=vector-config='{"dimension":"768","flat": "{}"}',field-path=embedding
- Depois que o índice for configurado, você poderá criar um novo documento de consulta.
- Agora você vai ver uma lista de IDs de documentos correspondentes no campo de resultados
- Copie um desses IDs e volte para a coleção
notes
. - Use ⌘+F para pesquisar o ID do documento que você copiou. Esse é o documento que melhor corresponde à sua consulta.
7. Implementar a pesquisa semântica
Agora é hora de conectar seu app para dispositivos móveis à extensão Pesquisa vetorial com Firestore e implementar um recurso de pesquisa semântica que permita aos usuários pesquisar as anotações usando consultas em linguagem natural.
Conectar a função chamável para realizar consultas
A extensão de pesquisa vetorial com o Firestore inclui uma função do Cloud que pode ser chamada do seu app para dispositivos móveis para consultar o índice criado anteriormente neste codelab. Nesta etapa, você vai estabelecer uma conexão entre o app para dispositivos móveis e essa função chamável. O SDK do Swift do Firebase inclui APIs que facilitam a chamada de funções remotas.
- Volte ao Xcode e confira se você está no projeto clonado em uma etapa anterior deste codelab.
- Abra o arquivo
NotesRepository.swift
. - Encontre a linha que contém
private lazy var vectorSearchQueryCallable: Callable
= functions.httpsCallable("")
Para invocar uma função do Cloud chamável, você precisa fornecer o nome da função que quer chamar.
- Acesse o console do Firebase para seu projeto e abra o item de menu Functions na seção Build.
- Você vai ver uma lista de funções instaladas pela extensão.
- Pesquise o arquivo chamado
ext-firestore-vector-search-queryCallable
e copie o nome dele. - Cole o nome no seu código. Agora ele vai mostrar
private lazy var vectorSearchQueryCallable: Callable<String, String> = functions.httpsCallable("ext-firestore-vector-search-queryCallable")
Chamar a função de consulta
- Encontre o método
performQuery
- Chame a função chamável invocando
let result = try await vectorSearchQueryCallable(searchTerm)
Como essa é uma chamada remota, ela pode falhar.
- Adicione um tratamento de erros básico para detectar e registrar erros no console do Xcode.
private func performQuery(searchTerm: String) async -> [String] { do { let result = try await vectorSearchQueryCallable(searchTerm) return [result] } catch { print(error.localizedDescription) return [] } }
Conectar a interface
Para permitir que os usuários pesquisem as observações, implemente uma barra de pesquisa na tela da lista de observações. Quando o usuário digitar um termo de pesquisa, invoque o método performQuery
implementado na etapa anterior. Graças aos modificadores de visualização searchable
e task
fornecidos pelo SwiftUI, isso requer apenas algumas linhas de código.
- Primeiro, abra
NotesListScreen.swift
. - Para adicionar uma caixa de pesquisa à visualização em lista, adicione o modificador de visualização
.searchable(text: $searchTerm, prompt: "Search")
logo acima da linha.navigationTitle("Notes")
. - Em seguida, invoque a função de pesquisa adicionando o seguinte código logo abaixo:
.task(id: searchTerm, debounce: .milliseconds(800)) {
await notesRepository.semanticSearch(searchTerm: searchTerm)
}
Esse snippet de código chama o método semanticSearch
de forma assíncrona. Ao fornecer um tempo limite de 800 milissegundos, você instrui o modificador de tarefa a fazer o debounce da entrada do usuário em 0,8 segundo. Isso significa que semanticSearch
só será chamado quando o usuário pausar a digitação por mais de 0,8 segundo.
Seu código vai ficar assim:
...
List(repository.notes) { note in
NavigationLink(value: note) {
NoteRowView(note: note)
}
.swipeActions {
Button(role: .destructive, action: { deleteNote(note: note) }) {
Label("Delete", systemImage: "trash")
}
}
}
.searchable(text: $searchTerm, prompt: "Search")
.task(id: searchTerm, debounce: .milliseconds(800)) {
await notesRepository.semanticSearch(searchTerm: searchTerm)
}
.navigationTitle("Notes")
...
Executar o app
- Pressione ⌘ + R (ou clique no botão "Executar") para iniciar o app no simulador do iOS.
- Você vai encontrar as mesmas observações que adicionou no app no início deste codelab, além de todas as observações adicionadas pelo console do Firebase.
- Um campo de pesquisa vai aparecer na parte de cima da lista Notas.
- Digite um termo que apareça em um dos documentos adicionados. Isso funciona melhor para consultas semânticas, como "Como posso chamar APIs assíncronas do Firebase em Swift?", desde que pelo menos uma das anotações adicionadas contenha texto sobre esse assunto.
- Você provavelmente espera ver o resultado da pesquisa, mas, em vez disso, a visualização em lista fica vazia, e o console do Xcode mostra uma mensagem de erro: "A função foi chamada com um argumento inválido".
Isso significa que você enviou os dados no formato errado.
Analise a mensagem de erro
- Para descobrir o que está errado, acesse o console do Firebase.
- Acesse a seção Funções.
- Encontre a função
ext-firestore-vector-search-queryCallable
e abra o menu flutuante clicando nos três pontos verticais. - Selecione Ver registros para acessar o Explorador de registros.
- Você vai receber um erro.
Unhandled error ZodError: [
{
"code": "invalid_type",
"expected": "object",
"received": "string",
"path": [],
"message": "Expected object, received string"
}
]
Isso significa que você enviou os dados no formato errado.
Usar os tipos de dados corretos
Para saber em qual formato a extensão espera que os parâmetros estejam, consulte a documentação dela.
- Acesse a seção Extensões no console do Firebase.
- Clique em Gerenciar ->
.
- Na seção Como essa extensão funciona, você encontra uma especificação dos parâmetros de entrada e saída.
- Volte para o Xcode e navegue até
NotesRepository.swift
. - Adicione o seguinte código no início do arquivo:
private struct QueryRequest: Codable { var query: String var limit: Int? var prefilters: [QueryFilter]? } private struct QueryFilter: Codable { var field: String var `operator`: String var value: String } private struct QueryResponse: Codable { var ids: [String] }
QueryRequest
corresponde à estrutura do parâmetro de entrada esperado pela extensão, de acordo com a documentação dela. Ele também contém um atributoprefilter
aninhado que você vai precisar mais tarde.QueryResponse
corresponde à estrutura da resposta da extensão. - Encontre a especificação da função invocável e atualize os tipos de entrada e saída.
private lazy var vectorSearchQueryCallable: Callable<QueryRequest, QueryResponse> = functions.httpsCallable("ext-firestore-vector-search-queryCallable")
- Atualize a invocação da função chamável em
performQuery
.private func performQuery(searchTerm: String) async -> [String] { do { let queryRequest = QueryRequest(query: searchTerm, limit: 2) let result = try await vectorSearchQueryCallable(queryRequest) print(result.ids) return result.ids } catch { print(error.localizedDescription) return [] } }
Executar o app novamente
- Executar o app novamente
- Digite uma consulta de pesquisa que contenha termos incluídos em uma das suas observações
- Agora você vai ver uma lista filtrada de notas.
Pré-filtrar dados do usuário
Antes de começar a dançar para comemorar, saiba que há um problema com a versão atual do app: o conjunto de resultados contém dados de todos os usuários.
Para verificar, execute o app em um simulador diferente e adicione mais documentos. Os novos documentos só vão aparecer nesse simulador. Se você executar o app novamente no outro simulador, só vai ver os documentos criados na primeira vez.
Se você fizer uma pesquisa, vai notar que a chamada para vectorSearchQueryCallable
retorna IDs de documentos que podem pertencer ao outro usuário. Para evitar isso, precisamos usar um pré-filtro.
Em performQuery
, atualize seu código da seguinte maneira:
let prefilters: [QueryFilter] = if let uid = user?.uid {
[QueryFilter(field: "userId", operator: "==", value: uid)]
}
else {
[]
}
let queryRequest = QueryRequest(query: searchTerm,
limit: 2,
prefilters: prefilters)
Isso vai pré-filtrar os dados com base no ID do usuário conectado. Como esperado, isso exige que o índice do Firestore seja atualizado.
Execute o comando a seguir na linha de comando para definir um novo índice do Firestore que inclua as incorporações de userId
e de vetores no campo embedding
.
gcloud alpha firestore indexes composite create --project=INSERT-YOUR-PROJECT-ID-HERE --collection-group=notes --query-scope=COLLECTION --field-config=order=ASCENDING,field-path=userId --field-config=vector-config='{"dimension":"768","flat": "{}"}',field-path=embedding
Quando o índice terminar de ser criado, execute o app novamente para verificar se ele funciona como esperado.
8. Parabéns
Parabéns por concluir este codelab.
Neste codelab, você aprendeu a:
- Configure um banco de dados do Cloud Firestore com a pesquisa semântica ativada.
- Crie um app SwiftUI simples para interagir com o banco de dados.
- Implemente uma barra de pesquisa usando o modificador de visualização pesquisável do SwiftUI e o modificador de tarefa.
- Chame uma Função do Cloud para realizar uma pesquisa semântica no banco de dados usando a interface chamável do SDK do Firestore.
Com o conhecimento adquirido neste codelab, agora você pode criar aplicativos poderosos que aproveitam os recursos de pesquisa semântica do Cloud Firestore para oferecer aos usuários uma experiência de pesquisa mais intuitiva e eficiente.
Para saber mais sobre o novo campo de vetor do Firestore e como calcular embeddings vetoriais, consulte a documentação.