Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Junte-se a nós no Amazon Web Services re:Invent 2024! Saiba como usar o MongoDB para casos de uso de AI .
Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Produtoschevron-right
MongoDBchevron-right

Aperol Spritze de verão com queries geoespaciais e pesquisa vetorial do MongoDB

Anaiya Raisinghani13 min read • Published Aug 22, 2024 • Updated Aug 22, 2024
IAPythonMongoDB
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
É verão na cidade de Nova York e você sabe o que isso significa: É a estação do spritz! Não há nada (e eu realmente, 110% não significa nada) melhor do que um spritz fresco de Aperol para terminar um dia tão quente e abafado que o metrô era intercambiável com uma sauna.
Embora eu normalmente adore me aventurar pela cidade em busca do que satisfará perfeitamente meu desejo atual, há certos meses em que me recuso a gastar mais tempo do que o necessário me movendo ao ar livre (olá, onda de calor ?!). À noite, durante o verão de Nova York, estamos descansando - descansando em telhados, terraços e calçadas - onde quer que possamos caber. E com o mínimo de movimento, queremos que nosso Aperol borrife o mais próximo possível. Então, vamos usar consultas geoespaciais do MongoDB, aAtlas Vector Searche a API Google Places para encontrar nossos locais de spritz mais próximos no bairro de West Village, na cidade de Nova York, enquanto usamos a pesquisa semântica para nos ajudar a aproveitar ao máximo nossas consultas.
Neste tutorial, usaremos as várias plataformas listadas acima para encontrar todos os locais que vendem Aperol spritzes no bairro de West Village, na cidade de Nova York, aqueles que correspondem à nossa consulta semântica de estar ao ar livre com serviço rápido (precisamos desses spritzes e precisamos deles AGORA!) e o mais próximo do nosso local de partida.
Antes de começarmos o tutorial, Go examinar algumas das plataformas importantes que usaremos em nossa jornada.

O que são queries geoespaciais MongoDB?

As consultas geoespaciais do MongoDB permitem que você pesquise seu banco de dados com base em localizações geográficas! Isso significa que você pode encontrar locais diferentes, como restaurantes, Parques, Museos, etc. com base apenas em suas coordenadas. Neste tutorial, usaremos as queries geoespaciais do MongoDB para pesquisar os locais de locais que atendem aos spritzes do Aperol que obtivemos da API de locais do Google. Para utilizar queries geoespaciais corretamente com MongoDB, precisaremos garantir que nossos pontos de dados sejam carregados no formato GeoJSON. Mais sobre isso abaixo!

O que é o Atlas Vector Search do MongoDB?

O MongoDB Atlas Vector Search é uma maneira de pesquisar em seu banco de dados semanticamente ou por significado. Isso significa que, em vez de pesquisar com base em palavras-chave específicas ou frases de texto exatas, você pode recuperar resultados mesmo que uma palavra esteja escrita incorretamente ou recuperar resultados com base em sinônimos. Isso se integrará perfeitamente ao nosso tutorial, pois podemos pesquisar as avaliações que recuperamos da nossa API do Google Places e ver quais correspondem mais ao que estamos procurando. Vamos lá!

Pré-requisitos

Para ser bem-sucedido com este tutorial, você precisará de:
  1. O IDE de sua escolha — este tutorial usa um bloco de notas doGoogle Colib. Sinta-se à vontade para executar seus comandos diretamente de um bloco de anotações.
  2. Uma conta do MongoDB Atlas Conta do MongoDB Atlas.
  3. Um cluster MongoDB Atlas — a camada grátis funcionará perfeitamente.
  4. Uma conta GCP – crie uma conta e um projeto. Passaremos por isso juntos.
  5. Uma chave de API do Google Cloud Platform.
  6. Uma chave de API OpenAI - é assim que incorporaremos nossas revisões de localização para que possamos usar o MongoDB Atlas Vector Search!
Depois que seu cluster MongoDB Atlas tiver sido provisionado e você tiver todo o resto anotado em um local seguro, você estará pronto para começar. Certifique-se também de ter permitido "Acesso de qualquer lugar" em seu cluster MongoDB, em "Acesso à rede". Isso não é recomendado para produção, mas é usado neste tutorial para facilitar a referência. Sem isso, você não poderá gravar em seu cluster MongoDB.

Configurar seu projeto GCP

Nosso primeiro passo é criar um projeto dentro de nossa conta Google Cloud. Isso é para que possamos garantir o uso da API do Google Places para encontrar todos os locais que servem spritzes Aperol no West Village.
É assim que seu projeto ficará depois de criado. Certifique-se de configurar as informações da sua conta de cobrança no lado esquerdo da tela. Você pode configurar um teste gratuito de $300 em créditos, então, se você estiver experimentando este tutorial, sinta-se à vontade para fazer isso e economizar algum dinheiro!
Configuração da conta do Google Cloud
Depois que sua conta estiver configurada, habilitaremos a API do Google place que vamos usar. Você pode fazer isso por meio do mesmo link para configurar seu projeto do Google Cloud.
Esta é a API que queremos usar: API do Google Places.
Pressione o botão Ativar e um pop-up aparecerá com sua chave de API. Armazene-o em algum lugar seguro, pois o usaremos em nosso tutorial! Certifique-se de não perdê-lo ou expô-lo a qualquer lugar.
Com cada solicitação da API de locais, sua chave de API deve ser usada. Você pode encontrar mais informações na documentação.
Uma vez feito isso, podemos começar nosso tutorial.

Importações e configuração da chave API

Agora, vá até o seu bloco de anotações do Google CoLab.
Queremos instalar googlemaps e openai em nosso notebook, pois eles são necessários para nós ao criar este tutorial.
1!pip install googlemaps
2!pip install openai==0.28
Em seguida, defina e execute suas importações:
1import googlemaps
2import getpass
3import openai
Vamos usar a bibliotecagetpass para manter nossas chaves API em segredo.
Configure-o para sua chave de API do Google e sua chave de API OpenAI:
1# google API Key
2google_api_key = getpass.getpass(prompt= "Put in Google API Key here")
3map_client = googlemaps.Client(key=google_api_key)
4# openAI API Key
5openai_api_key = getpass.getpass(prompt= "Put in OpenAI API Key here")

Configuração da função de incorporação do Vector Search

Agora, vamos nos preparar para o sucesso do Vector Search. Primeiro, defina sua chave e, em seguida, estabeleça nossa função de incorporação. Para este tutorial, estamos usando o modelo de incorporação "text-embedding-3-small" da OpenAI. Vamos incorporar as avaliações de nossos locais de spritz para que possamos fazer alguns julgamentos sobre para onde ir!
1# set your key
2openai.api_key = openai_api_key
3
4# embedding model we are using
5EMBEDDING_MODEL = "text-embedding-3-small"
6
7# our embedding function
8def get_embedding(text):
9 response = openai.Embedding.create(input=text, model=EMBEDDING_MODEL)
10 return response['data'][0]['embedding']

Método de pesquisa nas imediações na API do Google place

Ao usar a Pesquisa nas proximidades em nossa API do Google Places, somos obrigados a configurar três parâmetros: localização, raio e palavra-chave. Para nossa localização, podemos encontrar nossas coordenadas iniciais (bem no meio do West Village) clicando com o botão direito no Google Maps e copiando as coordenadas para nossa área de transferência. Foi assim que obtive as coordenadas mostradas abaixo: Como encontrar nossas coordenadas
Para o nosso raio, temos que ter em metros. Como não sou muito experiente com medidores, vamos escrever uma pequena função para nos ajudar a fazer essa conversão.
1# for Google Maps API we need to use a radius in meters. Let's first change our miles to meters
2def miles_to_meters(miles):
3 return miles * 1609.344
Nossa palavra-chave será apenas o que esperamos encontrar na API do Google Places: Aperol spritzes!
1middle_of_west_village = (40.73490473393682, -74.00521094160642)
2search_radius = miles_to_meters(0.4) # West Village is small so just do less than half a mile.
3spritz_finder = 'aperol spritz'
Podemos então fazer nossa chamada API usando o métodoplaces_nearby.
1# making the API call using our places_nearby method and our parameters
2response = map_client.places_nearby(
3 location=middle_of_west_village,
4 radius=search_radius,
5 keyword=spritz_finder
6)
Antes de Go e imprimirmos nossas localizações, vamos pensar em nosso objetivo final. Queremos realizar algumas coisas antes de inserir nossos documentos em nosso MongoDB Atlas cluster. Queremos:
  1. Obtenha informações detalhadas sobre nossas localizações, então precisamos fazer outra chamada de API para obter nosso place_id, o local name, nosso formatted_address,geometry para nossas coordenadas, alguns reviews (somente até cinco) e o local rating. Você pode encontrar mais campos para retornar (se seu coração desejar!) da documentação do Nearby Search.
  2. Incorpore nossas avaliações para cada local usando nossa função de incorporação. Queremos ter a certeza de que temos um campo para estes, para que nossos vetores sejam armazenados em uma array dentro do nosso cluster. Estamos optando por incorporar aqui apenas para facilitar as coisas para nós a longo prazo. Vamos também unir as cinco revisões em uma string para facilitar um pouco as coisas na incorporação.
  3. Pense em como nossas coordenadas estão configuradas, enquanto criamos um dicionário com todas as informações importantes que queremos retratar.As queries geoespaciais do MongoDB exigemobjetos GeoJSON. Isto significa que precisamos garantir que tenhamos o formato adequado, caso contrário, não poderemos usar nossos operadores de queries geoespaciais posteriormente. Também precisamos ter em mente que a longitude e a latitude são armazenadas em uma array aninhada abaixo geometry e location dentro da API do Google place. Então, desatualmente, não podemos acessá-lo a partir do nível superior. Precisamos fazer alguma tipoia primeiro. Aqui está um exemplo da saída que copiei da documentação deles mostrando onde a latitude e a longitude estão aninhadas:
1{
2"html_attributions": [],
3"results":
4 [
5 {
6 "business_status": "OPERATIONAL",
7 "geometry":
8 {
9 "location": { "lat": -33.8587323, "lng": 151.2100055 },
10 "viewport":
11 {
12 "northeast":
13 { "lat": -33.85739847010727, "lng": 151.2112436298927 },
14 "southwest":
15 { "lat": -33.86009812989271, "lng": 151.2085439701072 },
16 },
Com tudo isso em mente, vamos começar!
1# find information we want: use the Nearby Places documentation to figure out which fields you want
2spritz_locations = []
3for location in response.get('results', []):
4 location_detail = map_client.place(
5 place_id=location['place_id'], fields=['name', 'formatted_address', 'geometry', 'reviews', 'rating']
6 )
7
8
9 # these are the specific details we want to be saved as fields in our documents
10 details = location_detail.get('result', {})
11
12
13 # we want to embed the five reviews so lets extract and join together
14 location_reviews = details.get('reviews', [])
15 store_reviews = [review['text'] for review in location_reviews[:5]]
16 joined_reviews = " ".join(store_reviews)
17
18
19 # generate embedding on your reviews
20 embedding_reviews = get_embedding(joined_reviews)
21
22
23 # we know that the longitude and latitude is nested inside Geometry and Location.
24 # so let's grab it using .get and then format it how we want.
25 geometry = details.get('geometry', {})
26 location = geometry.get('location', {})
27
28
29 # both are nested under location so open it up
30 longitude = location.get('lng')
31 latitude = location.get('lat')
32
33
34 location_info = {
35 'name': details.get('name'),
36 'address': details.get('formatted_address'),
37
38
39 # MongoDB geospatial queries require GeoJSON formatting
40 'location': {
41 'type': 'Point',
42 'coordinates': [longitude, latitude]
43 },
44 'rating': details.get('rating'),
45 'reviews': store_reviews,
46 'embedding': embedding_reviews
47 }
48 spritz_locations.append(location_info)
Vamos imprimir nossa produção e ver quais são nossos locais de spritz no bairro de West Village! Vamos também verificar se temos um campo de incorporação recém-desenvolvido com nossas avaliações incorporadas:
1# print our spritz information
2for location in spritz_locations:
3 print(f"Name: {location['name']}, Address: {location['address']}, Coordinates: {location['location']}, Rating: {location['rating']}, Reviews: {location['reviews']}, Embedding: {location['embedding']}")
Nossa saída adequada
Então, se eu rolar no meu caderno, posso ver que existem incorporações, mas vou provar que elas estão lá quando inserirmos nossos dados no MongoDB Atlas, pois é um pouco difícil capturá-los em uma única imagem.
Vamos inseri-los usando a bibliotecapymongo.

Inserir documentos no cluster do MongoDB Atlas

Primeiro, vamos instalar pymongo.
1# install pymongo
2!pip install pymongo
Agora, configure nossa conexão MongoDB. Para fazer isso, certifique-se de ter sua string de conexão.
Lembre-se de que você pode nomear seu banco de dados e coleção como quiser, pois ele não será criado até que escrevamos nossos dados. Estou nomeando meu banco de dados "spritz_summer " e minha coleção de "spritz_locations_WV ". Execute o bloco de código abaixo para inserir seus documentos em seu cluster:
1from pymongo import MongoClient
2
3# set up your MongoDB connection
4connection_string = getpass.getpass(prompt= "Enter connection string WITH USER + PASS here")
5client = MongoClient(connection_string)
6
7# name your database and collection anything you want since it will be created when you enter your data
8database = client['spritz_summer']
9collection = database['spritz_locations_WV']
10
11# insert our spritz locations
12collection.insert_many(spritz_locations)
Go em frente e verifique se tudo foi escrito corretamente no MongoDB Atlas: Nossos documentos no MongoDB Atlas
Certifique-se de verificar novamente se o campo de incorporação existe e se é uma matriz de 1536e certifique-se de que suas coordenadas estejam configuradas corretamente da mesma forma que as minhas na imagem.

O que vem primeiro, Atlas Search vetorial ou queries geoespaciais?

Ótima pergunta! Como ambos - se estivermos olhando para eles simplesmente de um operador de pipeline de agregação - precisam ser o primeiro estágio em seus pipelines, em vez de fazer um pipeline, podemos fazer uma pequena brecha e criar dois. Mas como vamos decidir qual fazer primeiro?
Quando uso o Google Maps para descobrir aonde ir, normalmente procuro primeiro o que estou desejando e depois vejo a que distância está de onde estou atualmente. Então, vamos manter essa mentalidade e começar com o MongoDB Atlas Vector Search. Mas, eu entendo que, intuitivamente, alguns de vocês podem preferir pesquisar em todos os locais próximos e, em seguida, pesquisar semanticamente (consultas geoespaciais primeiro e depois pesquisa vetorial), então vamos destacar esse método também abaixo.
Temos alguns passos aqui. Nosso primeiro passo é criar um índice de pesquisa do Atlas Vector. Faça isso dentro do MongoDB Atlas seguindo a documentação do Vector Atlas Search.
Lembre-se de que seu índice não é executado em seu script. Ele vive no seu cluster. Você saberá que ele está pronto para Go quando ele ficar verde e for ativado.
1# create a Vector Search Index so we can use it
2{
3"fields": [
4 {
5 "numDimensions": 1536,
6 "path": "embedding",
7 "similarity": "cosine",
8 "type": "vector"
9 }
10]
11}
Depois de ativado, vamos à pesquisa vetorial!
Então. Digamos que acabei de jantar com meus melhores amigos em nosso restaurante favorito no West Village, Balaboosta. A comida estava ótima, é uma noite de verão, estamos com vontade de borrifar depois do jantar do lado de fora e preferimos nos sentar rapidamente. Vamos ver se conseguimos encontrar um lugar!
Nossa primeira etapa na construção de nosso pipeline é incorporar nossa query. Não podemos comparar texto com vetores; temos que comparar vetores com vetores. Podemos fazer isso com apenas algumas linhas, pois estamos usando o mesmo modelo de incorporação com o qual incorporamos nossas avaliações:
1# You have to embed your queries just the same way you embedded your documents.
2# my query
3query_description = "outdoor seating quick service"
4
5# we need to embed the query as well, since our documents are embedded
6query_vector = get_embedding(query_description)
Agora, vamos criar nosso pipeline de agregação. Como usaremos um $geoNear Atlas Search em nosso pipeline em seguida, queremos manter os IDs encontrados nesse pipeline de agregação para não pesquisarmos tudo — pesquisamos apenas o tamanho da amostra. Por enquanto, certifique-se de $vectorSearch que seu esteja no topo!
1spritz_near_me_vector = [
2 {
3 '$vectorSearch': {
4 'index': 'vector_index',
5 'path': 'embedding',
6 'queryVector': query_vector,
7 'numCandidates': 15,
8 'limit': 5
9 }
10 },
11 {
12 "$project": {
13 "_id": 1, # we want to keep this in place so we can search again using GeoNear
14 "name": 1,
15 "rating": 1,
16 "reviews": 1
17 #"address": 1,
18 #"location": 1,
19 #"embedding": 1
20 }
21 }
22]
Vamos imprimir nossos resultados e ver o que acontece com nossa consulta de “outdoor seating quick service”:
1spritz_near_me_vector_results = list(collection.aggregate(spritz_near_me_vector))
2for result in spritz_near_me_vector_results:
3 print(result)
Saída da impressão do nosso pipeline de agregação $vectorSearch
Temos cinco opções fantásticos! Se Go e ler as avaliações, veremos que elas estão alinhadas com o que estamos procurando. Aqui está um exemplo: Uma avaliação de exemplo alinhando com o que procuramos
Go salvar os IDs do nosso pipeline acima em uma linha simples para que possamos especificar que queremos usar apenas o operador$geoNear nestes cinco:
1# now, we want to take the _ids from our above pipeline so we can use it to geo search
2spritz_near_me_ids = [result['_id'] for result in spritz_near_me_vector_results]
3print(spritz_near_me_ids)
Agora que eles estão salvos, podemos construir nosso pipeline$geoNeare ver qual dessas opções está mais próxima de nós a partir de nosso ponto de partida, Balaboosta, para que possamos ir até lá.

Consultas geoespaciais no MongoDB

Para descobrir as coordenadas do Balaboosta, cliquei com o botão direito do mouse no Google Maps e salvei as coordenadas e, em seguida, certifiquei-me de que tinha a longitude e a latitude na ordem correta.
Primeiro, crie uma dsphere 2em nosso campo de localização, para que possamos colocar um índice dsphere2 na nossa collection:
1collection.create_index( { "location" : "2dsphere" } )
Aqui está o pipeline, com nossa query especificando que queremos usar apenas os IDs dos locais que encontramos acima:
1# use the $geoNear operator to return documents that are at least 100 meters and at most 1000 meters from our specified GeoJSON point.
2spritz_near_me_geo = [
3 {
4 "$geoNear": {
5 "near": {
6 "type": "Point",
7 "coordinates": [-74.0059456749148, 40.73781277366724]
8 },
9 # here we are saying that we only want to use the sample size from above
10 "query": {"_id": {"$in": spritz_near_me_ids}},
11 "minDistance": 100,
12 "maxDistance": 1000,
13 "spherical": True,
14 "distanceField": "dist.calculated"
15 }
16 },
17 {
18 "$project": {
19 "_id": 0,
20 "name": 1,
21 "address": 1,
22 "rating": 1,
23 "dist.calculated": 1,
24 #"location": 1,
25 #"embedding": 1
26 }
27 },
28 {
29 "$limit": 3
30 },
31 {
32 "$sort": {
33 "dist.calculated": 1
34 }
35 }
36]
Vamos imprimir e ver no que dá!
1spritz_near_me_geo_results = collection.aggregate(spritz_near_me_geo)
2for result in spritz_near_me_geo_results:
3 print(result)
Saída após pesquisar à distância de nossos cinco tamanhos de amostra
Parece que o restaurante para o qual estamos indo é Pastis, já que é apenas 182.83 metros (0).1 milhas) de distância. É hora de um Aperol spritzao ar livre!
Para aqueles que preferem mudar as coisas e executar primeiro as consultas geoespaciais e depois incorporar a pesquisa vetorial, aqui está o pipeline:
1# create a 2dsphere index on our location field
2collection.create_index({"location": "2dsphere"})
3
4# our $geoNear pipeline
5spritz_near_me_geo = [
6 {
7 "$geoNear": {
8 "near": {
9 "type": "Point",
10 "coordinates": [-74.0059456749148, 40.73781277366724]
11 },
12 "minDistance": 100,
13 "maxDistance": 1000,
14 "spherical": True,
15 "distanceField": "dist.calculated"
16 }
17 },
18 {
19 "$project": {
20 "_id": 1,
21 "dist.calculated": 1
22 }
23 }
24]
25
26# list of ID's and distances so we can use them as our sample size
27places_ids = list(collection.aggregate(spritz_near_me_geo))
28distances = {result['_id']: result['dist']['calculated'] for result in places_ids} # have to create a new dictionary to keep our distances
29spritz_near_me_ids = [result['_id'] for result in places_ids]
30# print(spritz_near_me_ids)
Primeiro, crie nosso pipeline$geoNear e garanta que você esteja salvando seu places_ids e o distances para que possamos carregá-los por meio de nosso pipeline de pesquisa vetorial.
Também precisamos reconstruir nosso índice do MongoDB Atlas Vector Search com um caminho "_id " incluído:
1# our vector search index that was created inside of MongoDB Atlas
2vector_search_index = {
3 "fields": [
4 {
5 "numDimensions": 1536,
6 "path": "embedding",
7 "similarity": "cosine",
8 "type": "vector"
9 },
10 {
11 "type": "filter",
12 "path": "_id"
13 }
14 ]
15}
Quando estiver ativo e pronto, podemos criar nosso pipeline de pesquisa vetorial:
1# vector search pipeline
2spritz_near_me_vector = [
3 {
4 '$vectorSearch': {
5 'index': 'vector_index',
6 'path': 'embedding',
7 'queryVector': query_vector,
8 'numCandidates': 15,
9 'limit': 3,
10 'filter': {"_id": {'$in': spritz_near_me_ids}}
11 }
12 },
13 {
14 "$project": {
15 "_id": 1, # we want to keep this in place
16 "name": 1,
17 "rating": 1,
18 "dist.calculated": 1
19 #"reviews": 1
20 # "address": 1,
21 # "location": 1,
22 # "embedding": 1
23 }
24 }
25]
26
27spritz_near_me_vector_results = collection.aggregate(spritz_near_me_vector)
28for result in spritz_near_me_vector_results:
29 result['dist.calculated'] = distances.get(result['_id'])
30 print(result)
Execute-o e você verá alguns resultados bem semelhantes aos de antes! Deixe um comentário abaixo, informando quais locais apareceram para você como sua saída — estes são os meus: Saída da execução geoespacial primeiro e depois da pesquisa vetorial
Como você pode ver, são os mesmos resultados, mas em uma ordem ligeiramente diferente, pois não são mais ordenados por distância.

Conclusão

Neste tutorial, abordamos como usar aAtlas Vector Searche a API do Google Places para encontrar nossos locais de spritz mais próximos no bairro de West Village, na cidade de Nova York, com pesquisa semântica e, em seguida, usamos consultas geoespaciais do MongoDB para descobrir quais locais estavam mais próximos de nós a partir de um ponto de partida específico.
Para obter mais informações sobre queries geoespaciais do MongoDB, visite a documentação localizada acima e, se tiver alguma dúvida ou quiser compartilhar seu trabalho, Junte-se a nós na Comunidade de desenvolvedores do MongoDB.
Principais comentários nos fóruns
Avatar do Comentarista do Fórum
joao_schaabjoão schaab2 meses atrás

Graças a este artigo, ele é muito útil. Existe uma maneira de retornar documentos classificados por pontuação e distância sem fazer isso na camada do aplicação ?

Veja mais nos fóruns

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Início rápido

Folha de referências do MongoDB


Sep 29, 2023 | 5 min read
Artigo

Orquestração do MongoDB com o Spring e Atlas Kubernetes Operator


Jun 12, 2024 | 13 min read
Artigo

Java vs Kotlin: Different Syntax, Same Possibilities


Nov 25, 2024 | 5 min read
exemplo de código

Gestão de revistas


Sep 11, 2024 | 0 min read
Sumário