Adicione abreviações postais dos EUA à sua pesquisa do Atlas em 5 minutos
Avalie esse Tutorial
Há casos em que ajuda ter sinônimos configurados para trabalhar com o índice do Atlas Search. Por exemplo, se a pesquisa em seu aplicativo precisar funcionar com endereços, talvez seja útil configurar uma lista de sinônimos comuns para abreviações postais, para que você possa digitar “blvd” em vez de “boulevard” e ainda encontrar todos os lugares com “boulevard” no endereço.
Este tutorial mostrará como configurar seu índice do Atlas Search para reconhecer abreviaturas postais dos EUA.
Para ser bem-sucedido com este tutorial, você precisará de:
- Python, para usar um script que raspa uma lista de abreviações de sufixo de rua compilada pelo Serviço Postal dos Estados Unidos (USPS). Este tutorial foi escrito usando Python 3.10.15, mas você pode testá-lo em versões anteriores do 3, se quiser.
- Um cluster MongoDB Atlas. Siga o guiaIniciar com Atlas para criar sua conta e um cluster MongoDB. Para este tutorial, você pode usar seu cluster MongoDB Atlas gratuito para sempre! Anote o nome de usuário, a senha e a connection stringdo banco de dados, pois você precisará deles mais tarde.
- Rosetta, se você estiver em um MacOS com chip M1 . Isso permitirá que você execute ferramentas MongoDB como mongoimport e mongosh.
- Uma cópia do mongoimport. Se você tiver o MongoDB instalado em sua estação de trabalho, talvez já tenha o mongoimport instalado. Caso contrário, siga as instruções no site do MongoDB para instalar o mongoimport.
- Vamos usar um conjunto de dados sample_restaurants neste tutorial, pois ele contém dados de endereço. Para obter instruções sobre como carregar dados de exemplo, consulte a documentação. Além disso, você pode ver todos os conjuntos de dados de exemplo disponíveis.
Os exemplos mostrados aqui foram todos escritos em um MacOS, mas devem ser executados em qualquer sistema do tipo unix. Se você estiver executando no Windows, recomendamos executar os comandos de exemplo dentro do subsistema do Windows para Linux.
Para saber mais sobre sinônimos no Atlas Search, sugerimos que você comece consultando nossa documentação. Os sinônimos permitem indexar e pesquisar em sua collection palavras que tenham o mesmo ou quase o mesmo significado ou, no caso de nosso tutorial, você pode pesquisar usando maneiras diferentes de escrever um endereço e ainda obter os resultados esperados. Para configurar e utilizar sinônimos no Atlas Search, você precisará:
- Crie uma collection no mesmo banco de dados da collection que você está indexando contendo os sinônimos. Observe que todos os documentos da coleção de sinônimos devem terum formato específico.
- Faça referência à sua coleção de sinônimos na definição do índice de pesquisa por meio de um mapeamento de sinônimos.
Orientaremos você por essas etapas no tutorial, mas primeiro, vamos começar criando os documentos JSON que formarão nossa collection de sinônimos.
Usaremos a lista de abreviações oficiais de sufixo de rua e uma lista de designadores de unidade secundária do site USPS para criar um JSON document para cada conjunto de sinônimos.
Todos os documentos da coleção de sinônimos devem ter um formato específico queespecifique o tipo de sinônimos: equivalente ou explícito. Os sinônimos explícitos têm um mapeamento unidirecional. Por exemplo, se "boat" for explicitamente mapeado para "sail,", estaremos dizendo que, se alguém pesquisar "boat,", queremos retornar todos os documentos que incluam "sail" e "boat.". No entanto, se pesquisarmos a palavra "sail,", não obteremos nenhum documento que tenha a palavra "boat." No caso de abreviações postais, no entanto, é possível usar todas as abreviações de forma intercambiável, portanto, usaremos o tipo de sinônimo "equivalent" no campo mappingType.
Aqui está um documento de amostra na collection de sinônimos para todas as possíveis abreviações de "avenue ":
1 “Avenue”: 2 3 { 4 5 "mappingType":"equivalent", 6 7 "synonyms":["AVENUE","AV","AVEN","AVENU","AVN","AVNUE","AVE"] 8 9 }
Escrevemos o código de web scraping para você em Python e você pode executá-lo com os seguintes comandos para criar um documento para cada grupo de sinônimos:
1 git clone https://github.com/mongodb-developer/Postal-Abbreviations-Synonyms-Atlas-Search-Tutorial/ 2 3 cd Postal-Abbreviations-Synonyms-Atlas-Search-Tutorial 4 5 python3 main.py
Para ver detalhes do código Python, leia o resto da seção.
1 import json 2 3 import requests 4 5 from bs4 import BeautifulSoup 6 7 import pandas as pd 8 9 import re
Vamos começar com a página Abreviações de sufixo de rua. Queremos criar objetos que representem a URL e a própria página:
1 # Create a URL object 2 3 streetsUrl = 'https://pe.usps.com/text/pub28/28apc_002.htm' 4 5 # Create object page 6 7 headers = { 8 9 "User-Agent": 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Mobile Safari/537.36'} 10 11 streetsPage = requests.get(streetsUrl, headers=headers)
Em seguida, queremos obter as informações da página. Começaremos analisando o HTML e, em seguida, obteremos a tabela por seu ID:
1 # Obtain page's information 2 3 streetsSoup = BeautifulSoup(streetsPage.text, 'html.parser')
1 # Get the table by its id 2 3 streetsTable = streetsSoup.find('table', {'id': 'ep533076'})
Agora que temos a tabela, vamos querer transformá-la em um dataframee formatá-la de uma forma que seja útil para nós:
1 # Transform the table into a list of dataframes 2 3 streetsDf = pd.read_html(str(streetsTable))
Uma coisa a se observar é que, na tabela fornecida no site do USPS, um nome principal geralmente é mapeado para vários nomes comumente usados. Isso significa que precisamos agrupar dinamicamente os nomes comumente usados pelo nome primário correspondente e compilar isso em uma lista:
1 # Group together all "Commonly Used Street Suffix or Abbreviation" entries 2 3 streetsGroup = streetsDf[0].groupby(0)[1].apply(list)
Quando todos os nossos nomes estiverem agrupados, podemos percorrê-los e exportá-los como arquivos JSON individuais.
1 for x in range(streetsGroup.size): 2 3 4 dictionary = { 5 6 "mappingType": "equivalent", 7 8 "synonyms": streetsGroup[x] 9 10 } 11 12 13 # export the JSON into a file 14 15 with open(streetsGroup.index.values[x] + ".json", "w") as outfile: 16 17 json.dump(dictionary, outfile)
Agora, vamos fazer a mesma coisa com a página Designadores de unidade secundária:
Assim como antes, começaremos obtendo a página e transformando-a em um dataframe:
1 # Create a URL object 2 3 unitsUrl = 'https://pe.usps.com/text/pub28/28apc_003.htm' 4 5 6 unitsPage = requests.get(unitsUrl, headers=headers) 7 8 9 # Obtain page's information 10 11 unitsSoup = BeautifulSoup(unitsPage.text, 'html.parser') 12 13 14 # Get the table by its id 15 16 unitsTable = unitsSoup.find('table', {'id': 'ep538257'}) 17 18 19 # Transform the table into a list of dataframes 20 21 unitsDf = pd.read_html(str(unitsTable))
Se examinarmos a tabela mais de perto, podemos ver que um dos valores está em branco. Embora faça sentido que o USPS inclua isso na tabela, não é algo que queremos em nossa lista de sinônimos. Para resolver isso, removeremos todas as linhas com valores em branco:
1 unitsDf[0] = unitsDf[0].dropna()
Em seguida, pegaremos nosso novo dataframe e o transformaremos em uma lista:
1 # Create a 2D list that we will use for our synonyms 2 3 unitsList = unitsDf[0][[0, 2]].values.tolist()
Você deve ter notado que alguns dos valores na tabela têm asteriscos. Vamos nos livrar rapidamente deles para que não sejam incluídos em nossos mapeamentos de sinônimos:
1 # Remove all non-alphanumeric characters 2 3 unitsList = [[re.sub("[^ \w]"," ",x).strip().lower() for x in y] for y in unitsList]
Agora podemos percorre-los e exportá-los como arquivos JSON individuais, como fizemos antes. A única coisa a observar é que queremos restringir o intervalo no qual estamos iterando para incluir apenas os dados relevantes que queremos:
1 # Restrict the range to only retrieve the results we want 2 3 for x in range(1, len(unitsList) - 1): 4 5 6 dictionary = { 7 8 "mappingType": "equivalent", 9 10 "synonyms": unitsList[x] 11 12 } 13 14 15 # export the JSON into a file 16 17 with open(unitsList[x][0] + ".json", "w") as outfile: 18 19 json.dump(dictionary, outfile)
Agora que criamos os documentos JSON para abreviações, vamos carregá-los todos em uma coleção no banco de dados sample_restaurants. Se você ainda não criou um cluster MongoDB, agora é um bom momento para fazer isso e carregar os dados de amostra.
O primeiro passo é conectar-se ao seu Atlas cluster. Usaremos o mongosh para fazer isso. Se você não tiver o mongosh instalado, siga as instruções.
Para se conectar ao Atlas cluster, você precisará de uma connection string. Escolha a opção "Connect with the MongoDB Shell " e siga as instruções. Observe que você precisará se conectar com um usuário do bancode dados que tenha permissões para modificar o banco de dados, pois criaremos uma coleção no banco de dados sample_restaurant. O comando que você precisa inserir no terminal será parecido com:
1 mongosh "mongodb+srv://cluster0.XXXXX.mongodb.net/sample_restaurant" --apiVersion 1 --username <USERNAME>
Quando solicitado para a senha, digite a senha do usuário do banco de dados.
Criamos nossos documentos JSON sinônimos já no formato correto, mas vamos garantir que, se decidirmos adicionar mais documentos a essa collection, eles também terão o formato correto. Para fazer isso, criaremos uma collection de sinônimos com um validador que usa $jsonSchema. Os comandos abaixo criarão uma collection com o nome "postal_synonyms " no banco de dados sample_restaurants e garantirão que somente documentos com formato correto sejam inseridos na collection.
1 use('sample_restaurants') 2 3 db.createCollection("postal_synonyms", { validator: { $jsonSchema: { "bsonType": "object", "required": ["mappingType", "synonyms"], "properties": { "mappingType": { "type": "string", "enum": ["equivalent", "explicit"], "description": "must be a either equivalent or explicit" }, "synonyms": { "bsonType": "array", "items": { "type": "string" }, "description": "must be an Array with each item a string and is required" }, "input": { "type": "array", "items": { "type": "string" }, "description": "must be an Array and is each item is a string" } }, "anyOf": [{ "not": { "properties": { "mappingType": { "enum": ["explicit"] } }, "required": ["mappingType"] } }, { "required": ["input"] }] } } })
Usaremos o mongoimport para importar todos os arquivos JSON que criamos.
Você precisará de uma connection string para seu Atlas cluster usar no comando mongoimport. Se você ainda não tiver o mongoimport instalado, use as instruções na documentação do MongoDB.
No terminal, navegue até a pasta onde todos os arquivos JSON para sinônimos de abreviação postal foram criados.
1 cat *.json | mongoimport --uri 'mongodb+srv://<USERNAME>:<PASSWORD>@cluster0.pwh9dzy.mongodb.net/sample_restaurants?retryWrites=true&w=majority' --collection='postal_synonyms'
Dê uma olhada nas coleções de sinônimos que você acabou de criar no Atlas. Você deve ver cerca de 229 documentos lá.
Agora que criamos a collection de sinônimos em nosso banco de dados sample_restaurants, vamos usá-la.
Vamos começar criando um índice de pesquisa. Navegue até a guia Pesquisar em seu cluster Atlas e clique no botão “CREATE INDEX”.
Como o Visual Index Builder ainda não oferece suporte a mapeamentos de sinônimos, escolheremos o JSON Editor e clicaremos em Next:
No Editor JSON, escolha a coleção de restaurantes no banco de dados sample_restaurants e insira o seguinte na definição de índice. Aqui, o nome da coleção de origem refere-se ao nome da coleção com todos os sinônimos de abreviatura postal, que chamamos de "postal_synonyms."
1 { 2 3 "mappings": { 4 5 "dynamic": true 6 7 }, 8 9 "synonyms": [ 10 11 { 12 13 "analyzer": "lucene.standard", 14 15 "name": "synonym_mapping", 16 17 "source": { 18 19 "collection": "postal_synonyms" 20 21 } 22 23 } 24 25 ] 26 27 }
Estamos indexando a collection de restaurantes e criando um mapeamento de sinônimos com o nome "synonym_mapping " que faz referência à collection de sinônimos "postal_synonyms. "
Clique em Avançar e, em seguida, em Criar índice de pesquisa, e aguarde a criação do índice de pesquisa.
Quando o índice estiver ativo, estamos prontos para testá-lo.
Agora que temos um índice de pesquisa ativo, estamos prontos para testar se nossos sinônimos estão funcionando. Vamos até o pipeline de agregação na guia Coleções para testar diferentes chamadas para $search. Você também pode usar Compass, a interface do usuário do MongoDB, se preferir.
Escolha $search na lista de estágios do pipeline. A UI nos fornece um espaço reservado útil para os argumentos do comando $search.
Vamos procurar todos os restaurantes localizados em um bulevard. Vamos pesquisar no campo “address.street”, então os argumentos para o estágio $search ficarão assim:
1 { 2 3 index: 'default', 4 5 text: { 6 7 query: 'boulevard', 8 9 path: 'address.street' 10 11 } 12 13 }
Vamos adicionar um estágio $count após o estágio $search para ver quantos restaurantes com um endereço que contenha "boulevard " encontramos: Como esperado, encontramos muitos restaurantes com a palavra “boulevard” no endereço. Mas e se não quisermos que os usuários digitem "boulevard " na barra de pesquisa? O que aconteceria se colocássemos "blvd, ", por exemplo?
1 { 2 3 index: 'default', 4 5 text: { 6 7 query: blvd, 8 9 path: 'address.street' 10 11 } 12 13 }
Parece que nos encontrou restaurantes com endereços que têm “blvd” neles. E os endereços com "boulevard, "? Esses não foram detectados pela busca.
E se não tivéssemos certeza de como se escreve "boulevard " e apenas procurarmos "boul "? O site do USPS nos informa que é uma abreviação aceitável para Boulevard, mas nosso $search não encontra nada. É aqui que entram os nossos sinônimos! Precisamos adicionar uma opção sinônimos ao operador de texto no comando $search e fazer referência ao nome do mapeamento de sinônimos:
1 { 2 3 index: 'default', 4 5 text: { 6 7 query: 'blvd', 8 9 path: 'address.street', 10 11 synonyms:'synonym_mapping' 12 13 } 14 15 }
E aqui está! Encontrámos todos os restaurantes em bulevards, independentemente de como o endereço foi abreviado, tudo graças aos nossos sinônimos.
O Synonyms é apenas um dos muitos recursos que o Atlas Search oferece para oferecer a você toda a funcionalidade de pesquisa necessária em seu aplicativo. Todas essas funcionalidades estão disponíveis agora no MongoDB Atlas. Acabamos de mostrar como adicionar suporte para abreviações postais comuns ao seu índice do Atlas Search — o que você pode fazer com o Atlas Search a seguir? Experimente agora em seu cluster MongoDB Atlas gratuito para sempre e acesse os fóruns da comunidade se tiver alguma dúvida!