Como importar dados no MongoDB com o mongoimport
Avalie esse Tutorial
Não importa o que você esteja criando com o MongoDB, em algum momento você vai querer importar alguns dados. Quer se trate da maioria dos seus dados, ou apenas alguns dados de referência que você deseja integrar com seu conjunto de dados principal, você acabrá com um monte de arquivos JSON ou CSV que precisa importar para uma coleção. Felizmente, o MongoDB fornece uma ferramenta chamada mongoimport que é projetada para essa tarefa. Este guia explicará como usar efetivamente o mongoimport para inserir seus dados no MongoDB database.
Também fornecemos documentação de referência do MongoImport, se você estiver procurando algo abrangente ou apenas precisar procurar uma opção de linha de comando.
Este guia pressupõe que você esteja razoavelmente confortável com a linha de comando. A maior parte do guia será apenas sobre a execução de comandos, mas no final mostrarei como redirecionar dados por meio de algumas ferramentas de linha de comando, como
jq
.Se você não tem muita experiência com linha de comando (também às vezes chamada de terminal, shell ou bash), acompanhe alguns dos exemplos. É uma ótima maneira de começar.
Os exemplos mostrados foram todos escritos no MacOS, mas provavelmente podem ser executados em qualquer sistema do tipo unix. Se você estiver executando no Windows, recomendamos executar os comandos de exemplo dentro do Windows Subsystem for Linux.
Você precisará de um MongoDB database temporário para testar esses comandos. Se você nunca fez isso antes, recomendamos que se cadastre em uma conta gratuita do MongoDB Atlas e nós tomaremos conta do cluster para você!
E, claro, você precisará de uma cópia de
mongoimport
. Se você tiver o MongoDB instalado em sua estação de trabalho, talvez já tenha o mongoimport
instalado. Caso contrário, siga estas instruções no site do MongoDB para instalá-lo.Criei um repositório do GitHub de dados de amostra, contendo uma extração do conjunto de dados New York Citibike em diferentes formatos que devem ser úteis para experimentar os comandos neste guia.
O
mongoimport
é uma poderosa ferramenta de linha de comando para importar dados de arquivos JSON, CSV e TSV para coleções do MongoDB. Ele é muito rápido e multisegmentado, então muitas vezes será mais rápido do que qualquer script personalizado que você criar com a mesma finalidade.O mongoimport
pode ser combinado com algumas outras ferramentas de linha de comando, como jq
para manipulação de JSON, csvkit
para manipulação de CSV ou até curl
para baixar dinamicamente arquivos de dados de servidores na Internet. Assim como muitas ferramentas de linha de comando, as opções são infinitas!De várias maneiras, ter seus dados de origem em arquivos JSON é melhor do que CSV (e TSV). JSON é um formato de dados hierárquico, como documentos MongoDB, e também é explícito sobre os tipos de dados que codifica. Por outro lado, pode ser difícil lidar com dados JSON de origem - em muitos casos, eles não estão na estrutura que você gostaria, ou eles têm dados numéricos codificados como strings, ou talvez os formatos de data não estejam em um formato que
mongoimport
aceita.Os dados CSV (e TSV) são tabulares e cada linha será importada para o MongoDB como um documento separado. Isso significa que esses formatos não permitem dados hierárquicos, diferentemente de um documento do MongoDB. Ao importar dados CSV para o MongoDB, o
mongoimport
tentará fazer escolhas coerentes ao identificar o tipo de um campo específico, como int32
ou string
. Esse comportamento pode ser substituído pelo uso de alguns sinalizadores, e você pode especificar tipos, se desejar. Além disso, o mongoimport
fornece alguns recursos para analisar datas e outros tipos em diferentes formatos.Em muitos casos, a escolha do formato de dados de origem não depende de você, mas sim da organização que gera os dados e os fornece a você. Recomendamos que, se os dados de origem estiverem no formato CSV, você não tente convertê-los em JSON primeiro, a menos que planeje reestruturá-los.
Esta seção pressupõe que você esteja se conectando a uma configuração relativamente simples, com um banco de dados de autenticação padrão e alguma autenticação configurada. (Você deve sempre criar alguns usuários para autenticação!)
Se você não fornecer nenhum detalhe de conexão ao mongoimport, ele tentará se conectar ao MongoDB em sua máquina local, na porta 27017 (que é o padrão do MongoDB). Isso é o mesmo que fornecer
--host=localhost:27017
.Há várias opções que permitem que você forneça informações de conexão separadas para o mongoimport, mas recomendamos que você use a opção
--uri
. Se você estiver usando o Atlas, poderá obter o URI de conexão apropriado na interface do Atlas, clicando no botão "Conectar" do cluster e selecionando "Conectar seu aplicativo" (o Atlas está sendo desenvolvido continuamente, então estas instruções podem estar um pouco desatualizadas). Defina o URI como o valor da sua opção --uri
e substitua o nome de usuário e a senha pelos valores apropriados:1 mongoimport --uri 'mongodb+srv://MYUSERNAME:SECRETPASSWORD@mycluster-ABCDE.azure.mongodb.net/test?retryWrites=true&w=majority'
Esteja ciente de que, neste formulário, o nome de usuário e a senha devem ser codificados em URL. Se você não quiser lidar com isso, forneça o nome de usuário e a senha usando as opções
--username
e --password
:1 mongoimport --uri 'mongodb+srv://mycluster-ABCDE.azure.mongodb.net/test?retryWrites=true&w=majority' \ 2 --username='MYUSERNAME' \ 3 --password='SECRETPASSWORD'
Se você omitir uma senha do URI e não fornecer uma opção
--password
, então o mongoimport
solicitará uma senha na linha de comando. Em todos esses casos, o uso de aspas simples ao redor dos valores, como fizemos, evitará problemas posteriores!Se você não estiver se conectando a um banco de dados do Atlas, precisará gerar seu próprio URI. Se você estiver se conectando a um único servidor (ou seja, não tiver um conjunto de replicações), seu URI terá a seguinte aparência:
mongodb://your.server.host.name:port/
. Se você estiver executando um conjunto de réplicas (e deveria!), então você tem mais de um nome de host para se conectar e não sabe com antecedência qual é o principal. Nesse caso, seu URI consistirá em uma série de servidores em seu cluster (você não precisa fornecer todos os servidores do cluster, desde que um deles esteja disponível), e o mongoimport descobrirá e se conectará ao primário automaticamente. Um URI replicaset tem esta aparência: mongodb://username:password@host1:port,host2:port/?replicaSet=replicasetname
.Todas as informações sobre os formatos de URI compatíveis estão disponíveis na nossa documentação de referência.
Também há muitas outras opções disponíveis e elas estão documentadas na documentação de referência do mongoimport.
Depois de determinar o URI, a diversão começa. No resto deste guia, vou deixar essas bandeiras de fora. Você precisará adicioná-las ao experimentar as várias outras opções.
A maneira mais simples de importar um único arquivo no MongoDB é usar a opção
--file
para especificar um arquivo. Na nossa opinião, a melhor situação é quando você tem um diretório cheio de arquivos JSON que precisam ser importados. O ideal é o seguinte: 1. cada arquivo JSON conter 1 (um) documento que você deseja importar para o MongoDB; 2. estar na estrutura correta e 3. cada um dos valores ser do tipo correto. Use essa opção quando desejar importar um único arquivo como um único documento em uma coleção do MongoDB.Você encontrará dados nesse formato no diretório "file_per_document" no repositório de dados de amostra do GitHub. Cada documento terá a seguinte aparência:
1 { 2 "tripduration": 602, 3 "starttime": "2019-12-01 00:00:05.5640", 4 "stoptime": "2019-12-01 00:10:07.8180", 5 "start station id": 3382, 6 "start station name": "Carroll St & Smith St", 7 "start station latitude": 40.680611, 8 "start station longitude": -73.99475825, 9 "end station id": 3304, 10 "end station name": "6 Ave & 9 St", 11 "end station latitude": 40.668127, 12 "end station longitude": -73.98377641, 13 "bikeid": 41932, 14 "usertype": "Subscriber", 15 "birth year": 1970, 16 "gender": "male" 17 }
1 mongoimport --collection='mycollectionname' --file='file_per_document/ride_00001.json'
O comando acima importará todo o arquivo json para uma coleção
mycollectionname
. Você não precisa criar a coleção antes.Se você usar o MongoDB Compass ou outra ferramenta para se conectar à coleção que acabou de criar, verá que o MongoDB também gerou um valor
_id
em cada documento para você. Isso ocorre porque o MongoDB exige que cada documento tenha um _id
exclusivo, mas você não forneceu um. Falarei mais sobre isso em breve.O Mongoimport importará apenas um arquivo por vez com a opção
--file
, mas você pode contornar isso enviando vários documentos JSON para o mongoimport de outra ferramenta, como cat
. Isso é mais rápido do que importar um arquivo de cada vez, executando o mongoimport a partir de um loop, pois o próprio mongoimport é multithread para uploads mais rápidos de vários documentos. Com um diretório cheio de arquivos JSON, em que cada arquivo JSON deve ser importado como um documento MongoDB separado, é possível importá-lo por cd
-ing diretório que contém os arquivos JSON e executando:1 cat *.json | mongoimport --collection='mycollectionname'
Assim como antes, o MongoDB cria um novo
_id
para cada documento inserido na coleção do MongoDB, porque eles não estão contidos nos dados de origem.Às vezes, você terá vários documentos contidos em uma array JSON em um único documento, parecido com o seguinte:
1 [ 2 { title: "Document 1", data: "document 1 value"}, 3 { title: "Document 2", data: "document 2 value"} 4 ]
Você pode importar dados nesse formato usando a opção
--file
, usando a opção --jsonArray
:1 mongoimport --collection='from_array_file' --file='one_big_list.json' --jsonArray
Se você esquecer de adicionar a opção --jsonArray,
mongoimport
falhará com o erro "não é possível decodificar o array em um documento." Isso ocorre porque documentos são equivalentes a objetos JSON, não arrays. Você pode armazenar um array como um _value_ em um documento, mas um documento não pode ser um array.Se você importar alguns dados JSON do repositório de dados de amostra do GitHub e visualizar o esquema da coleção no Compass, poderá notar alguns problemas:
- Os valores de
starttime
estoptime
devem ser tipos "data", não "string". - O MongoDB é compatível com pontos geográficos, mas não reconhece as latitudes e longitudes das estações de início e parada como pontos geográficos.
Isso decorre de uma diferença fundamental entre documentos MongoDB e documentos JSON. Embora os documentos do MongoDB muitas vezes pareçam dados JSON, eles não são. O MongoDB armazena dados como BSON. O BSON tem múltiplas vantagens sobre o JSON. É mais compacto, mais rápido de percorrer e suporta mais tipos que o JSON. Entre esses tipos estão datas, tipos GeoJSON, dados binários e números decimais. Todos os tipos estão listados na documentação do MongoDB
Se você quiser que o MongoDB reconheça os campos que estão sendo importados do JSON como BSON types, esses campos deverão ser manipulados para que sigam uma estrutura que chamamos de JSON. Isso significa que o campo a seguir:
1 "starttime": "2019-12-01 00:00:05.5640"
deve ser fornecido ao MongoDB como:
1 "starttime": { 2 "$date": "2019-12-01T00:00:05.5640Z" 3 }
para que seja reconhecido como um tipo de data. Perceba que o formato da string de data mudou um pouco, em que o "T" separa a data e a hora, e o Z no final indica o fuso horário UTC.
Da mesma forma, a latitude e a longitude devem ser convertidas em um tipo de ponto GeoJSON se você quiser aproveitar a capacidade do MongoDB de pesquisar dados de localização. Os dois valores:
1 "start station latitude": 40.680611, 2 "start station longitude": -73.99475825,
1 "start station location": { 2 "type": "Point", 3 "coordinates": [ -73.99475825, 40.680611 ] 4 }
Observação: o par de valores é longitude e depois latitude (as pessoas às vezes confundem).
Depois de ter dados geoespaciais em sua coleção, você pode usar as queries geoespaciais do MongoDB para pesquisar dados por localização.
Ao importar dados para uma coleção que já contém documentos, seu valor
_id
é importante. Se os documentos recebidos não contiverem valores _id
, novos valores serão criados e atribuídos aos novos documentos à medida que eles forem adicionados à coleção. Se os documentos recebidos contiverem valores _id
, eles serão verificados em relação aos documentos existentes na coleção. O valor _id
deve ser exclusivo dentro de uma coleção. Por padrão, se o documento recebido tiver um valor _id
que já exista na coleção, o documento será rejeitado e um erro será registrado. Este modo (o padrão) é chamado de "modo de inserção". No entanto, existem outros modos que se comportam de maneira diferente quando um documento correspondente é importado usando mongoimport
.Se você receber periodicamente novos arquivos de dados, poderá usar
mongoimport
para atualizar com eficiência os dados em sua coleção. Se seus dados de entrada forem fornecidos com um identificador estável, use esse campo como o campo _id
e forneça a opção --mode=upsert
. Este modo inserirá um novo documento se o valor _id
não estiver atualmente presente na coleção. Se o valor _id
já existir em um documento, esse documento será substituído pelos novos dados do documento.Se estiver fazendo upserting de registros que não têm IDs estáveis, é possível especificar alguns campos a serem usados para fazer a correspondência com os documento da coleção, com a opção
--upsertFields
. Se estiver usando mais de um nome de campo, separe esses valores com uma vírgula:1 --upsertFields=name,address,height
Se você receber arquivo de dados que ampliam seus documentos existentes adicionando novos campos ou atualizando determinados campos, você pode usar
mongoimport
com o "modo de mesclagem". Se seus dados de entrada forem fornecidos com um identificador estável, use esse campo como o campo _id
e forneça a opção --mode=merge
. Este modo irá inserir um novo documento se o valor de _id
não estiver presente na coeção. Se o valor de _id
já existir em um documento, esse documento será substituído pelos novos dados do documento.Você também pode usar a opção
--upsertFields
aqui e ao fazer upserts, para corresponder aos documentos que deseja atualizar.Se você tiver arquivos CSV (ou arquivos TSV - eles são conceitualmente iguais) para importar, use a opção
--type=csv
ou --type=tsv
para informar o mongoimport
qual formato esperar. Também é importante saber se seu arquivo CSV tem uma linha de cabeçalho (cuja primeira linha não contém dados) ou se contém o nome de cada coluna. Se você tiver uma linha de cabeçalho, deverá usar a opção --headerline
para informar o mongoimport
de que a primeira linha não deve ser importada como um documento.Com dados CSV, talvez seja necessário fazer algum trabalho extra para anotar os dados e fazer com que eles sejam importados corretamente. Os principais problemas são:
- Os dados CSV são "uniformes", então não há uma boa maneira de incorporar subdocumentos em uma linha de um arquivo CSV. Por isso, talvez seja melhor reestruturar os dados para corresponderem à estrutura que você deseja ter em seus documentos MongoDB.
- Os dados CSV não incluem informações de tipo.
O primeiro problema é provavelmente um problema maior. Você tem duas opções. Uma é escrever um script para reestruturar os dados antes de usar
mongoimport
para importar os dados. Outra abordagem poderia ser importar os dados para o MongoDB e, em seguida, executar um pipeline de agregação para transformar os dados na estrutura necessária.Essas duas abordagens estão fora do escopo desta postagem do blog. Se você quiser ver mais explicações sobre isso, vá para MongoDB Community.
O fato de os arquivos CSV não especificarem o tipo de dados em cada campo pode ser resolvido especificando-se os tipos de campo ao chamar
mongoimport
.Se você não tiver uma linha de cabeçalho, deverá informar o
mongoimport
o nome de cada uma das suas colunas, para que mongoimport
saiba como chamar cada um dos campos em cada um dos documentos a serem importados. Há dois métodos para fazer isso: você pode listar os nomes dos campos na linha de comando com a opção --fields
ou colocar os nomes dos campos em um arquivo e apontar para ele com a opção --fieldFile
.1 mongoimport \ 2 --collection='fields_option' \ 3 --file=without_header_row.csv \ 4 --type=csv \ 5 --fields="tripduration","starttime","stoptime","start station id","start station name","start station latitude","start station longitude","end station id","end station name","end station latitude","end station longitude","bikeid","usertype","birth year","gender"
Essa é uma linha bem longa! Quando há muitas colunas, é melhor gerenciar os nomes dos campos em um arquivo de campo.
Um arquivo de campo é uma lista de nomes de colunas, com um nome por linha. Então, o equivalente do valor
--fields
da chamada acima fica assim:1 tripduration 2 starttime 3 stoptime 4 start station id 5 start station name 6 start station latitude 7 start station longitude 8 end station id 9 end station name 10 end station latitude 11 end station longitude 12 bikeid 13 usertype 14 birth year 15 gender
Se você colocar esse conteúdo em um arquivo chamado 'field_file.txt' e, em seguida, executar o seguinte comando, ele usará esses nomes de coluna como nomes de campo no MongoDB:
1 mongoimport \ 2 --collection='fieldfile_option' \ 3 --file=without_header_row.csv \ 4 --type=csv \ 5 --fieldFile=field_file.txt
Se você abrir o Compass e observar o esquema de "fields_option" ou "fieldfile_option", verá que
mongoimport
converteu automaticamente os tipos de números inteiros em int32
e manteve os valores de latitude e longitude como double
, o que é um tipo real (ou número de ponto flutuante). Entretanto, em alguns casos, o MongoDB pode tomar uma decisão errada. Na captura de tela acima, dá para ver que os campos "starttime" e "stoptime" foram importados como strings. O ideal seria que eles tivessem sido importados como um tipo de data BSON, o que é mais eficiente para armazenamento e filtragem.Nesse caso, você desejará especificar o tipo de algumas ou de todas as colunas.
Para informar
mongoimport
de que você deseja especificar o tipo de alguns ou de todos os seus campos, você deve usar a opção --columnsHaveTypes
. Além de usar a opção --columnsHaveTypes
, você precisará especificar os tipos de seus campos. Se estiver usando a opção --fields
, você poderá adicionar informações de tipo a esse valor, mas é altamente recomendável adicionar dados de tipo ao arquivo de campo. Assim ficará mais fácil para ler e manter, e é isso que demonstraremos aqui.Criei um arquivo chamado
field_file_with_types.txt
e inseri o seguinte:1 tripduration.auto() 2 starttime.date(2006-01-02 15:04:05) 3 stoptime.date(2006-01-02 15:04:05) 4 start station id.auto() 5 start station name.auto() 6 start station latitude.auto() 7 start station longitude.auto() 8 end station id.auto() 9 end station name.auto() 10 end station latitude.auto() 11 end station longitude.auto() 12 bikeid.auto() 13 usertype.auto() 14 birth year.auto() 15 gender.auto()
Como
mongoimport
já fez a coisa certa com a maioria dos campos, eu os configurei para auto()
- o tipo de informação vem após um período (.
). Os dois campos de tempo, starttime
e stoptime
, estavam sendo importados incorretamente como strings, portanto, nesses casos, especifiquei que eles devem ser tratados como um tipo date
. Muitos dos tipos recebem argumentos dentro dos parênteses. No caso do tipo date
, ele espera que o argumento seja uma data formatada da mesma forma que você espera que os valores da coluna sejam formatados. Consulte a documentação de referência para obter mais detalhes.Agora, os dados podem ser importados com a seguinte chamada para
mongoimport
:1 mongoimport --collection='with_types' \ 2 --file=without_header_row.csv \ 3 --type=csv \ 4 --columnsHaveTypes \ 5 --fieldFile=field_file_with_types.txt
Espero que agora você tenha uma boa ideia de como usar o
mongoimport
e de como ele é flexível! No entanto, não abordei quase todas as opções que podem ser fornecidas ao mongoimport
, apenas as mais importantes. Outras que considero úteis com frequência são:Opção | Descrição |
---|---|
--ignoreBlanks | Ignorar campos ou colunas com valores vazios. |
--drop | Descartar a coleção antes de importar os novos documento. Isso é especialmente útil durante o desenvolvimento, mas você perderá dados se usar sem querer. |
--stopOnError | Outra opção útil durante o desenvolvimento, faz com que mongoimport pare imediatamente quando ocorrer um erro. |
E tem muitos mais! Confira a documentação de referência do mongoimport para saber todos os detalhes.
Um dos principais benefícios dos programas de linha de comando é que eles são projetados para funcionar com outros programas de linha de comando para fornecer mais potência. Há alguns programas de linha de comando que eu particularmente recomendo que você dê uma olhada:
jq
, que é uma ferramenta de manipulação JSON; e csvkit
, uma ferramenta semelhante para trabalhar com arquivos CSV.JQ é um processador de dados JSON. Ele incorpora uma poderosa linguagem de filtragem e script para filtrar, manipular e até mesmo gerar dados JSON. Este guia não abrange um tutorial completo sobre como usar JQ, mas para dar uma breve amostra:
Se você criar um script JQ chamado
fix_dates.jq
contendo o seguinte:1 .starttime |= { "$date": (. | sub(" "; "T") + "Z") } 2 | .stoptime |= { "$date": (. | sub(" "; "T") + "Z") }
Agora você pode fazer pipe nos dados JSON de amostra com este script para modificar os campos
starttime
e stoptime
para que sejam importados para o MongoDB como tipos Date
:1 echo ' 2 { 3 "tripduration": 602, 4 "starttime": "2019-12-01 00:00:05.5640", 5 "stoptime": "2019-12-01 00:10:07.8180" 6 }' \ 7 | jq -f fix_dates.jq 8 { 9 "tripduration": 602, 10 "starttime": { 11 "$date": "2019-12-01T00:00:05.5640Z" 12 }, 13 "stoptime": { 14 "$date": "2019-12-01T00:10:07.8180Z" 15 } 16 }
Isso pode ser usado em um pipe de vários estágios, em que os dados são encaminhados para
mongoimport
via jq
.A ferramenta
jq
pode ser um pouco trabalhosa de entender no início, mas quando você começa a entender como a linguagem funciona, ela é muito poderosa e rápida. Forneci um exemplo de script JQ mais complexo no repositório de dados de amostra do GitHub, chamado json_fixes.jq
. Confira mais ideias e a documentação completa no site do JQ.Da mesma forma que o
jq
é uma ferramenta para filtrar e manipular dados JSON, o csvkit
é uma coleção de ferramentas para filtrar e manipular dados CSV. Algumas das ferramentas, embora úteis por si só, provavelmente não serão úteis quando combinadas com o mongoimport
. Ferramentas como csvgrep
, que filtra linhas de arquivo CSV com base em expressões, e csvcut
, que pode remover colunas inteiras da entrada CSV, são ferramentas úteis para segmentar e dividir seus dados antes de fornecê-los a mongoimport
.Confira a documentação do csvkit para obter mais informações sobre como usar essa coleção de ferramentas.
Você conhece outras ferramentas que funcionariam bem com
mongoimport
? Tem um ótimo exemplo de uso do awk
para lidar com dados tabulares antes de importá-los para o MongoDB? Informe-nos nos fóruns da comunidade!É um erro comum escrever código personalizado para importar dados para o MongoDB. Esperamos ter demonstrado como o
mongoimport
é uma ferramenta poderosa para importar dados para o MongoDB de forma rápida e eficiente. Combinado com outras ferramentas simples de linha de comando, é uma maneira rápida e flexível de importar seus dados para o MongoDB.