1. Visão geral
Neste codelab, você vai aprender a adicionar recursos de pesquisa poderosos 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 da Pesquisa de vetor com o Firestore para calcular embeddings de vetor.
- Como chamar o Cloud Functions do Firebase em um aplicativo Swift.
- Como pré-filtrar dados com base no usuário conectado.
O que é necessário
- Xcode 15.3
- O código de exemplo 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 Pesquisa de vetor do Firebase, você precisa de um projeto do Firebase. Nesta parte do codelab, você criará um novo 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 Firebase.
- No console do Firebase, clique em Adicionar projeto e nomeie o projeto como Laboratório de pesquisa de vetores do Firestore.
- Clique nas opções de criação do projeto. Se for solicitado, aceite os termos do Firebase.
- Na tela do Google Analytics, desmarque a caixa Ativar o Google Analytics para este projeto, porque você não vai usar o Analytics para este app.
- Por fim, clique em Criar projeto.
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 delas, seu projeto do Firebase precisa estar no plano de preços Blaze, ou seja, 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 você se qualifica para receber um crédito de US$300 e uma conta de teste sem custo financeiro 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 ao fluxo de upgrade no console do Firebase para concluir o upgrade.
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 para facilitar o login dos usuários no app.
- 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 necessitam 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. Isso resulta em um processo de integração com pouca fricção. Para saber mais sobre a autenticação anônima e como fazer upgrade para uma conta com nome, consulte as 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 de autenticação, onde pode conferir os usuários registrados, configurar provedores de login e gerenciar as configurações.
- Selecione a guia Método de login ou clique aqui para acessar a guia diretamente.
- Clique em Anônimo nas opções do provedor, ative a chave Ativar e clique em Salvar.
Configurar o Cloud Firestore
Este aplicativo Swift usa o Cloud Firestore para salvar anotações.
Veja como configurar o Cloud Firestore no seu projeto do Firebase:
- No painel esquerdo 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 imagens.
Confira como configurar o Cloud Storage para Firebase no seu projeto do Firebase:
- No painel esquerdo do Console do Firebase, abra Build e selecione Armazenamento.
- Clique em Começar.
- Selecione um local para o bucket do Storage padrão.
Os buckets emUS-WEST1
,US-CENTRAL1
eUS-EAST1
podem aproveitar o nível "Sempre gratuito" do Google Cloud Storage. Os buckets em todos os outros locais seguem os preços e o uso 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 fazer o download do código-fonte de um app simples de anotações e conectá-lo ao projeto do Firebase que você 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 o app possa acessar os serviços do Firebase, você precisa configurá-lo no console do Firebase. É possível conectar vários aplicativos clientes 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 o app para iOS.
- Na tela Adicionar o Firebase ao app da Apple, insira o ID do pacote do projeto do Xcode (com.google.firebase.codelab.Notes).
- Se quiser, insira o apelido do aplicativo (Notes 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 projeto Xcode. Uma boa maneira de fazer isso é colocá-lo abaixo do arquivo Assets.xcassets.
- Selecione Copy items if needed, confira se o destino Notes está selecionado em Add to targets e clique em Finish.
- No console do Firebase, agora você pode clicar no restante do processo de configuração: o exemplo que você fez o download 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 no console.
Execute o aplicativo
Agora é hora de testar o app.
- No Xcode, execute o app no Simulador do iOS. No menu suspenso Destinos de execução, selecione primeiro um dos simuladores do iOS.
- Em seguida, clique no botão Run 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 conferir os novos documentos que são criados à medida que você adiciona novas notas no app.
4. Instalar a pesquisa de vetores com a extensão do Firestore
Nesta parte do codelab, você vai instalar a Pesquisa vetorial com a extensão do 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 "vector".
- Clique em "Pesquisa de vetores com a extensão do Firestore". Isso vai levar você à página de detalhes da extensão, onde você pode ler mais sobre a extensão, como ela funciona, quais serviços do Firebase ela exige e como você pode fazer a configuração.
- Clique em Instalar no console do Firebase.
- Você vai encontrar uma lista com todos os seus projetos.
- 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 de ativar todos os serviços, clique em Próxima.
- Revise o acesso concedido a esta extensão.
- Configure 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: Sim
- Atualizar documentos existentes: Yes
- 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, siga para a próxima seção do tutorial e leia algumas informações básicas sobre embeddings de vetores.
5. Segundo plano
Enquanto você espera a instalação terminar, aqui estão algumas informações básicas sobre como funciona a pesquisa de vetores com a extensão Firestore.
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.
- Os 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.
- Os bancos de dados vetoriais são otimizados para armazenar e pesquisar dados vetoriais. Elas permitem uma pesquisa de vizinho mais próxima eficiente, 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 com 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 cossenos, que mede o ângulo entre dois vetores.
6. Testar a pesquisa de vetores com a extensão do Firestore
Antes de usar a pesquisa vetorial com a extensão do Firestore no app iOS que você fez o download anteriormente neste codelab, teste a extensão no console do Firebase.
Leia a documentação
As Extensões do Firebase incluem uma 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 embeddings para 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, o que é útil se nenhum dos LLMs com suporte da extensão atender aos seus requisitos e você quiser usar um LLM diferente para computar embeddings.
- como calcular embeddings para 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 as inclusões de todos os documentos de anotações que você criou em uma etapa anterior deste codelab. - Para verificar isso, abra um dos documentos de anotações e você verá um campo adicional chamado
embedding
do tipovector<768>
, além de um campostatus
.
Criar um documento de exemplo
Você pode criar um novo documento no console do Firebase para conferir 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
de tipo string e cole algum texto no campo value. É importante que não seja lorem ipsum ou algum outro texto aleatório. Escolha um artigo de notícias, 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>
.
Executar uma consulta
A pesquisa vetorial com a extensão do Firestore tem um recurso útil 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 nova subcoleção chamada
queries
- Crie um documento novo e defina o campo
query
como um texto que aparece em um dos seus documentos. Isso funciona melhor para consultas semânticas, por exemplo, "Como mapear documentos do Firestore com o Swift" (desde que pelo menos uma das observações adicionadas contenha texto que discute esse assunto). - Talvez apareça um erro no status
- Isso ocorre devido a um índice ausente. Para configurar a configuração de índice ausente, acesse o console do Google Cloud para seu projeto neste link e selecione seu projeto na lista
- No Cloud Logging Explorer, você vai encontrar uma mensagem de erro informando "FAILED_PRECONDITION: configuração do índice de vetor ausente. 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 comando a seguir na linha de comando. Se você não tiver a CLI
gcloud
instalada na sua máquina, siga estas instruções para fazer a instalação. A criação do índice leva alguns minutos. É possível verificar o progresso na guia Índices na seção do Firestore do 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 à Pesquisa de vetores com a extensão do Firestore e implementar um recurso de pesquisa semântica que permita que os usuários pesquisem as notas usando consultas em linguagem natural.
Conectar a função chamável para realizar consultas
A extensão da Pesquisa vetorial com o Firestore inclui uma função do Cloud que pode ser chamada no 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 verifique se você está no projeto clonado em uma etapa anterior deste codelab.
- Abra o arquivo
NotesRepository.swift
. - Encontrar a linha que contém
private lazy var vectorSearchQueryCallable: Callable
= functions.httpsCallable("")
Para invocar uma função do Cloud que pode ser chamada, você precisa informar o nome da função que quer chamar.
- Acesse o console do Firebase do seu projeto e abra o item de menu Functions na seção Build.
- Você vai encontrar uma lista de funções que foram instaladas pela extensão.
- Pesquise o arquivo chamado
ext-firestore-vector-search-queryCallable
e copie o nome dele. - Cole o nome no código. Agora, ele vai ficar assim:
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 sua 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 anotações, implemente uma barra de pesquisa na tela da lista de anotações. Quando o usuário digita um termo de pesquisa, é necessário invocar 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 a ela, 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)
}
Este 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 eliminar o ruído da entrada do usuário em 0,8 segundos. Isso significa que semanticSearch
só será chamado quando o usuário pausar a digitação por mais de 0,8 segundos.
O 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")
...
Execute o aplicativo
- Pressione ⌘ + R (ou clique no botão Executar) para iniciar o app no Simulador do iOS.
- Você vai encontrar as mesmas notas que adicionou no app anteriormente neste codelab, além de todas as notas adicionadas pelo console do Firebase.
- Você verá um campo de pesquisa no topo da lista Notes.
- Digite um termo que apareça em um dos documentos adicionados. Novamente, isso funciona melhor para consultas semânticas, como "Como posso chamar APIs assíncronas do Firebase no Swift?", desde que pelo menos uma das notas adicionadas contenha texto que discuta esse tópico.
- Você provavelmente espera ver o resultado da pesquisa, mas a visualização em lista está vazia e o console 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.
Analisar 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 Conferir registros para acessar o Explorador de registros.
- Você vai receber uma mensagem de 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.
Use os tipos de dados corretos
Para descobrir em qual formato a extensão espera que os parâmetros estejam, consulte a documentação da extensão.
- 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. Ela também contém um atributoprefilter
aninhado que será necessário mais tarde.QueryResponse
corresponde à estrutura da resposta da extensão. - Encontre a especificação da função que pode ser chamada 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 anotações
- Uma lista filtrada de anotações vai aparecer.
Pré-filtrar dados do usuário
Antes de começar a dançar para comemorar, há um problema com a versão atual do app: o conjunto de resultados contém dados de todos os usuários.
Para verificar isso, 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 encontrar os documentos que criou na primeira vez.
Se você realizar 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 o 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 inclusões de vetor e userId
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 a criação do índice terminar, 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 e o modificador de tarefa do SwiftUI.
- 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 de vetor, consulte a documentação.