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
Atlaschevron-right

Pesquisando sua localização com Atlas Search e operadores geoespaciais

Nic Raboy9 min read • Published Jan 28, 2022 • Updated Feb 03, 2023
AtlasPesquisaJavaScript
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Ao pensar em pesquisa de texto completo, texto e outros dados de string são provavelmente a primeira coisa que vem à mente. Na verdade, se você tem acompanhado meus tutoriais, deve se lembrar Construir um elemento de formulário de preenchimento automático com Atlas Search e JavaScript ou Exibir visualmente os destaques do Atlas Search com JavaScript e HTML, ambos em exemplos de pesquisa de texto em MongoDB Atlas Search.
A capacidade de usar a pesquisa em linguagem natural em dados de texto é provavelmente um dos casos de uso mais populares, mas há cenários em que você pode precisar restringir ainda mais os resultados.
Digamos que você esteja criando um aplicativo de avaliações de restaurantes como o Yelp ou um sistema de reservas de hospedagem e café da manhã como o Airbnb. Claro, você inserirá algum tipo de critério de texto para o que está procurando, mas também há um aspecto de localização. Por exemplo, se você quiser encontrar um lugar para comer um cheeseburger a uma curta distância de sua localização atual, provavelmente não quer que seus resultados de pesquisa contenham entradas de outro país. Este é um exemplo de uma pesquisa geográfica, onde você gostaria de retornar resultados com base em coordenadas de localização.
Neste tutorial, vamos ver como usar o Atlas Search operadorcomposto para pesquisar com base no texto inserido e dentro de uma determinada área geográfica. Para o texto inserido, usaremos o operador depreenchimento automático e, para o componente geoespacial, usaremos o operadorgeoWithin.
Para ter uma ideia do que queremos realizar, dê uma olhada na imagem animada a seguir:
Combinando pesquisas de texto e geoespaciais no Atlas Search
Combinando pesquisas de texto e geoespaciais no Atlas Search
O exemplo acima será semelhante ao que escrevi no tutorialConstruindo um elemento de formulário de preenchimento automático com Atlas Search e JavaScript, com a exceção de que, desta vez, estamos retornando resultados com base em um local fornecido.

Os requisitos

Para este tutorial, usaremos JavaScript e Node.js com o MongoDB Atlas. Existirá um componente de frontend e backend, mas sem muitos requisitos anteriores. Para ter sucesso e acompanhar, você precisará do seguinte:
  • MongoDB Atlas (cluster M0+)
  • Node.js (15.9.0+)
  • O conjunto de dados de amostrasample_airbnb(carregue gratuitamente no Atlas).
As versões acima são apenas as versões que estou usando. Você também pode ter sucesso com uma versão mais antiga do Node.js. Para o MongoDB Atlas, você pode usar um cluster GRÁTIS M0 ou algo mais poderoso.
Usaremos um conjunto de dados de amostra para este exemplo. Você pode saber mais sobre sample_airbnb e outros na documentação.

Criando uma API de pesquisa com Node.js e Express

Vamos construir uma API, mas ela terá um único endpoint. O objetivo desta API é permitir que nosso front-end interaja com o MongoDB.
No seu computador, crie um novo diretório para nosso back-end e execute o seguinte na linha de comando:
1npm init -y
2npm install mongodb express cors
Os comandos acima criarão um novo arquivopackage.json e, em seguida, baixarão as dependências do MongoDB e do Express Framework. Como teremos nosso back-end e front-end executando localmente em portas diferentes, a instalação de um pacote de compartilhamento de recursos de origem cruzada (CORS) também é necessária.
Em vez de voltar ao básico com o MongoDB, vamos usar o seguinte código JavaScript padronizado:
1const { MongoClient } = require("mongodb");
2const Express = require("express");
3const Cors = require("cors");
4
5const app = Express();
6
7app.use(Express.json());
8app.use(Express.urlencoded({ extended: true }));
9app.use(Cors());
10
11const client = new MongoClient(
12 process.env["ATLAS_URI"],
13 {
14 useUnifiedTopology: true
15 }
16);
17
18var collection;
19
20app.post("/search", async (request, response, next) => {
21 console.log(JSON.stringify(request.body));
22 try {
23 let result = await collection.aggregate([/* Search Logic Here */]).toArray();
24 response.send(result);
25 } catch (e) {
26 response.status(500).send({ message: e.message });
27 }
28});
29
30app.listen(3000, async () => {
31 try {
32 await client.connect();
33 collection = client.db("sample_airbnb").collection("listingsAndReviews");
34 } catch (e) {
35 console.error(e);
36 }
37});
Adicione o código acima a um arquivo principal.js dentro do seu diretório de projeto. Se você quiser um início rápido para o MongoDB com o Node.js,Lauron Scheefer escreveu uma série de várias partes para que você possa se atualizar.
Há uma coisa a ser observada no código acima:
1const client = new MongoClient(
2 process.env["ATLAS_URI"],
3 {
4 useUnifiedTopology: true
5 }
6);
Minhas informações de conexão do MongoDB Atlas estão sendo armazenadas como uma variável de ambiente no meu computador. Embora as variáveis de ambiente sejam a abordagem mais segura, certifique-se de trocá-las pelo que planeja usar.
Com o código padrão fora do caminho, podemos nos concentrar no que importa para este exemplo: o pipeline de agregação para pesquisa em texto e dados geoespaciais. No entanto, antes de começarmos a escrever os estágios do pipeline, precisamos indexar adequadamente nossos dados para pesquisa.
No MongoDB Atlas, selecione a aba Pesquisar de nível superior após escolher um dos seus clusters. Dentro desta aba, selecione o botão Criar Índice que o levará a um assistente de configuração para criar índices de Atlas Search.
Assistente de Configuração do MongoDB Atlas Search
Assistente de Configuração do MongoDB Atlas Search
Em vez de usar o editor visual para criar um índice, vamos usar o JSON Editor com a seguinte configuração. Forneça sample_airbnb como banco de dados e listingsAndReviews como collection. Você pode copiar e colar a seguinte configuração de índice:
1{
2 "mappings": {
3 "dynamic": false,
4 "fields": {
5 "address": {
6 "fields": {
7 "location": {
8 "type": "geo"
9 }
10 },
11 "type": "document"
12 },
13 "name": [
14 {
15 "foldDiacritics": false,
16 "maxGrams": 7,
17 "minGrams": 3,
18 "tokenization": "edgeGram",
19 "type": "autocomplete"
20 }
21 ]
22 }
23 }
24}
Embora o nome do índice não afete sua funcionalidade, vamos chamá-lo depreenchimento automático e referenciá-lo em nosso aplicativo Node.js. Para detalhar o que o índice acima faz, estamos indexando dois campos nos documentos de nossa coleção. O campoaddress.locationestá sendo indexado como um campo geoespacial, enquanto o campo name está sendo indexado como um campo de texto de preenchimento automático. Nenhum outro campo em nosso documento poderá ser pesquisado com base nesse índice.
Ao final da criação do índice, você deve ter algo parecido com isto:
MongoDB Atlas Search com preenchimento automático Índice geográfico
MongoDB Atlas Search com preenchimento automático Índice geográfico
Então, let's Go back ao nosso código.
Sabemos que nossos resultados de pesquisa devem depender do texto que o usuário fornece e da localização do usuário (como latitude e longitude).
Se quisermos pesquisar apenas com texto, nosso estágio de pipeline de agregação (query) pareceria o seguinte:
1{
2 "$search": {
3 "index": "autocomplete",
4 {
5 "autocomplete": {
6 "query": "apartment",
7 "path": "name",
8 "fuzzy": {
9 "maxEdits": 2,
10 "prefixLength": 3
11 }
12 }
13 }
14 }
15}
O estágio acima diz que queremos usar o autocomplete índice para nossa pesquisa e queremos usar o autocomplete operador . Estamos procurando por " apartment " no name campo e estamos dizendo que estamos permitindo a tolerância a erros de digitação, também conhecida como correspondência difusa.
Se quisermos usar o Atlas Search para o Atlas Search dentro de uma área geográfica, o estágio do pipeline de agregação teria a seguinte aparência:
1{
2 "$search": {
3 "index": "autocomplete",
4 {
5 "geoWithin": {
6 "circle": {
7 "center": {
8 "type": "Point",
9 "coordinates": [-74.0060, 40.7128]
10 },
11 "radius": 10000
12 },
13 "path": "address.location"
14 }
15 }
16 }
17}
O estágio acima diz mais uma vez que estamos usando o índiceautocomplete, mas estamos usando o operador geoWithin . Estamos pesquisando dentro de uma área circular onde o ponto central é especificado por uma latitude e uma longitude. Ao trabalhar com GeoJSON como no código acima, a longitude é o primeiro elemento na arraycoordinates e a latitude é o segundo elemento. Também estamos fornecendo um raio para pesquisar em torno do ponto central.
Acabamos de criar dois possíveis aggregation pipeline stages. O problema é que queremos ser eficientes. Não queremos pesquisar texto usando um estágio e, em seguida, aplicar uma faixa geográfica nos resultados em um estágio diferente. Em vez disso, queremos fazer nossas operaçõesautocomplete e geoWithin em uma única query.
Podemos fazer isso com o operadorcompound.
Para combinar várias operações, podemos alterar nosso código e a lógica do pipeline de agregação para se parecer com o seguinte:
1app.post("/search", async (request, response, next) => {
2 try {
3 let result = await collection.aggregate([
4 {
5 "$search": {
6 "index": "autocomplete",
7 "compound": {
8 "must": [
9 {
10 "autocomplete": {
11 "query": `${request.body.query}`,
12 "path": "name",
13 "fuzzy": {
14 "maxEdits": 2,
15 "prefixLength": 3
16 }
17 }
18 },
19 {
20 "geoWithin": {
21 "circle": {
22 "center": {
23 "type": "Point",
24 "coordinates": [request.body.position.lng, request.body.position.lat]
25 },
26 "radius": 10000
27 },
28 "path": "address.location"
29 }
30 }
31 ]
32 }
33 }
34 }
35 ]).toArray();
36 response.send(result);
37 } catch (e) {
38 response.status(500).send({ message: e.message });
39 }
40});
Observe que estamos incluindo uma matrizmustno operadorcompound. Você pode aprender mais sobre cada um dos termos compostos na documentação, mas o operadormustdefine quais cláusulas devem corresponder para produzir resultados.
Para esclarecer as coisas, estamos dizendo que os resultados devem satisfazer os operadores autocomplete e geoWithin .
Agora, você pode executar o aplicativo Node.js e enviar a seguinte carga útil para o endpoint usando uma solicitação POST:
1{
2 "query": "apartment",
3 "position": {
4 "lng": -74.0060,
5 "lat": 40.7128
6 }
7}
Considerando os dados que estão no conjunto de dadossample_airbnb, devemos obter resultados em torno de Nova York. No entanto, os dados que recebemos provavelmente são mais do que precisamos. Para limitar a resposta, podemos atualizar nosso pipeline de agregação não apenas para pesquisar, mas também para projetar os campos que queremos em nossa resposta.
Modifique o código para que fique parecido com o seguinte:
1app.post("/search", async (request, response, next) => {
2 try {
3 let result = await collection.aggregate([
4 {
5 "$search": {
6 "index": "autocomplete",
7 "compound": {
8 "must": [
9 {
10 "autocomplete": {
11 "query": `${request.body.query}`,
12 "path": "name",
13 "fuzzy": {
14 "maxEdits": 2,
15 "prefixLength": 3
16 }
17 }
18 },
19 {
20 "geoWithin": {
21 "circle": {
22 "center": {
23 "type": "Point",
24 "coordinates": [request.body.position.lng, request.body.position.lat]
25 },
26 "radius": 10000
27 },
28 "path": "address.location"
29 }
30 }
31 ]
32 }
33 }
34 },
35 {
36 "$project": {
37 "name": 1,
38 "address": 1,
39 "score": { "$meta": "searchScore" }
40 }
41 }
42 ]).toArray();
43 response.send(result);
44 } catch (e) {
45 response.status(500).send({ message: e.message });
46 }
47});
Para ser claro sobre o que foi adicionado ao código acima, anote o estágio$project.
1{
2 "$project": {
3 "name": 1,
4 "address": 1,
5 "score": { "$meta": "searchScore" }
6 }
7}
No estágio $projectacima , estamos dizendo que queremos apenas os camposname e address retornados. Também estamos interessados nos dados de pontuação que retornaram de nossa pesquisa. Por padrão, os dados de pontuação não estariam presentes em nossos resultados. Esses dados podem ser úteis para determinar a qualidade da correspondência.
Com a parte de trás fora do caminho, vamos nos concentrar na parte da frente.

Preencher uma entrada de formulário com preenchimento automático e JavaScript

Para visualizar o Atlas Search com preenchimento automático , vamos usar jQuery com um pouco de HTML e JavaScript básicos . A biblioteca jQuery será responsável por enviar nossas teclas para a API por meio de uma solicitação HTTP.
Crie outro diretório de projeto que representará o aplicativo de frontend. Dentro desse diretório, crie um arquivoindex.html com a seguinte marcação:
1<!DOCTYPE html>
2<html>
3 <head>
4 <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
5 <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
6 <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
7 </head>
8 <body>
9 <div class="ui-widget">
10 <label for="bnb">Bed and Breakfast [40.7128, -74.0060]:</label><br />
11 <input id="bnb">
12 </div>
13 <script>
14 $(document).ready(function () {
15 // Autocomplete logic here...
16 });
17 </script>
18 </body>
19</html>
A marcação acima é padrão para começar a usar o jQuery — a exceção é o container<div> que tem o campo de entrada. O id do campo de entrada vai ser importante para quando trabalharmos com jQuery.
Então, vamos dar uma olhada na lógica de preenchimento automático do front-end.
1$(document).ready(function () {
2 $("#bnb").autocomplete({
3 source: async function (request, response) {
4 let data = await fetch("http://localhost:3000/search", {
5 "method": "POST",
6 "headers": {
7 "content-type": "application/json"
8 },
9 "body": JSON.stringify({
10 "query": `${request.term}`,
11 "position": {
12 "lat": 40.7128,
13 "lng": -74.0060
14 }
15 })
16 })
17 .then(results => results.json())
18 .then(results => results.map(result => {
19 return { label: result.name, value: result.name, id: result._id };
20 }));
21 response(data);
22 },
23 minLength: 2,
24 select: function (event, ui) {
25 // Further logic here...
26 }
27 });
28});
No código acima, estamos utilizando a funçãoautocomplete para jQuery no elemento de entrada bnb . Osource dos nossos dados a ser mostrado na tela sairá do nosso endpoint da API. Conforme os caracteres são inseridos no campo, uma solicitação POST é feita com a carga útil JSON esperada. Os resultados são então formatados para como o jQuery espera que eles sejam, neste caso, com um campolabel, valuee id dentro de um objeto.
Como queremos um escopo restrito para este exemplo, não analisaremos a lógica de quando um elemento é selecionado nos resultados do autocompletar retornados. No entanto, apenas o fato de termos o camposourcenos permitirá mostrar visualmente os resultados do autocompletar à medida que os digitamos.
Para executar este exemplo, você precisará servir o back-end e o front-end separadamente. Para o back-end, navegue até o diretório do projeto com sua linha de comando e execute o seguinte:
1node main.js
O comando acima deve começar a veicular a API na porta 3000. Para atender ao frontend, você precisará do Python ou de uma ferramenta ou pacote compatível como serve, disponível por meio do NPM.
Se o Python estiver disponível, você poderá executar o seguinte a partir do diretório do projeto de frontend:
1python -m SimpleHTTPServer
O comando acima servirá o front-end na porta 8000.
É claro que o que listei para atender aos seus aplicativos era para desenvolvimento e teste local. Você terá que usar seu bom senso ao implantar seus aplicativos na produção.

Conclusão

Você acabou de ver como usar o operadorcompound do MongoDB Atlas Search para pesquisar com base em texto e em uma área geoespacial, neste caso, um círculo. Por que isso pode ser importante? Imagine precisar procurar um hotel ou restaurante perto de sua localização ou a uma curta distância a seguir, em vez de retornar todas as correspondências possíveis com base em sua entrada de texto. O operadorcompound permite pesquisar resultados somente se eles corresponderem aos termos compostos fornecidos.
Perguntas ou comentários sobre este tutorial? Acesse os MongoDB Community e vamos conversar!

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Tutorial

Simplifique a pesquisa semântica do Atlas com LangChain e MongoDB


Oct 28, 2024 | 4 min read
Artigo

AI Shop: o poder da LangChain, OpenAI e MongoDB Atlas trabalhando juntos


Sep 18, 2024 | 7 min read
Tutorial

Criar um backend de gerenciamento de mídia escalável: integrando Node.js, Armazenamento de blobs Azure e MongoDB


Nov 05, 2024 | 10 min read
Tutorial

Além do básico: aprimorando a API Kotlin Ktor com pesquisa vetorial


Sep 18, 2024 | 9 min read
Sumário