Exportando dados filtrados em mais de uma collection
Tags: mongodb, mongoshell, mongotools
Published: 2021-08-30 10:46:51

Imagine a seguinte situação: 
>A equipe estratégica da montadora deseja criar um relatório contendo todas as vendas feitas no mês de agosto de 2020 tanto pela concessionária quanto direto pela fábrica. 
>O sistema da montadora divide essas vendas em duas collections do mongo.
>O relatório é gerado por uma ferramenta consolidada de mercado que aceita a importação dos dados por arquivos .csv ou .json

A primeira solução que vem à cabeça é utilizar o mongoexport com o parâmetro --query para gerar os dados solicitados. No entanto, o mongoexport possui a limitação de só exportar dados de uma única collection por vez. 
Fazendo um teste simples de exportar todos os dados de um database sem definir uma collection com o mongoexport obtemos o seguinte resultado:

```sh
#$> mongoexport mongodb://localhost:27017 --db examples #comando executado
2021-08-26T08:39:03.607-0300    must specify a collection
2021-08-26T08:39:03.615-0300    try 'mongoexport --help' for more information
```

##Usando o [mongosh](https://docs.mongodb.com/mongodb-shell/)
O mongosh é a ferramenta básica para interagir com qualquer mongodb. Utilizando ele é possível criar consultas, procedures, indexes e gerenciar o database de maneira geral. Outro ponto interessante é que possível importar scripts .js e executá-los dentro do mongosh. É possível importar o arquivo de duas formas:

1. Importar diretamente dentro do mongosh
```shell
mongosh mongodb://localhost:27017
> load('export_data_multiple_databases/obter_vendas.js')
```

2. Importar no momento da execução do mongosh
```shell
mongosh mongodb://localhost:27017/examples ~/export_data_multiple_databases/obter_vendas.js 
```

No primeiro caso os resultados gerados são mostrados diretamente no console do mongosh (contexto do mongosh), tornando a tarefa exportar os dados complexa para o cenário apresentado.

*Exemplo de execução*
```shell
Current Mongosh Log ID: 612918a2e3f888785924e0e9
Connecting to:          mongodb://localhost:27017/examples?directConnection=true&serverSelectionTimeoutMS=2000
Using MongoDB:          5.0.0
Using Mongosh Beta:     0.14.0

For mongosh info see: https://docs.mongodb.com/mongodb-shell/

------
   The server generated these startup warnings when booting:
   2021-08-27T10:16:56.220+00:00: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem
   2021-08-27T10:16:56.525+00:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted
   2021-08-27T10:16:56.526+00:00: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. We suggest setting it to 'never'
------
> load('~/export_data_multiple_databases/obter_vendas.js')
[{
  fabricante: 'Lexus',
  modelo: 'LS',
  ano: 2004,
  datavenda: ISODate("2021-08-03T00:00:00.000Z"),
  preco: '$106375.36',
  collectionName: 'vendas_concessionaria'
},
{
  fabricante: 'GMC',
...
```

Já no segundo caso é possível exportar diretamente para um arquivo externo, pois o comando devolve o resultado no contexto do console e não no contexto do mongosh.

> Importante ressaltar que o script importado fica disponível somente naquela sessão.

##Exportando para um arquivo
A forma mais simples de exportar para um arquivo é justamente usando o segundo comando de importação de scripts.

```shell
mongosh mongodb://localhost:27017/examples ~/export_data_multiple_databases/obter_vendas.js > arquivo_resultado.json
```

##Mongosh [--eval](https://docs.mongodb.com/mongodb-shell/reference/options/#std-option-mongosh.--eval)
O parâmetro **--eval** executa um script js inline usando o contexto do mongosh, porém sem carregar o ambiente do mongosh no console. O resultado é da execução é enviado para o console conforme exemplo abaixo:

```shell
mongosh mongodb://localhost:27017/examples --eval="load('export_data_multiple_databases/obter_massa_dados.js');"
```
Como resultado é enviado diretamente para o console (fora do contexto do mongosh), exportar os dados para um arquivo .json é simples

```shell
mongosh mongodb://localhost:27017/examples --eval="load('export_data_multiple_databases/obter_massa_dados.js');" > arquivo_resultado.json
```
A vantagem em relação à exportação padrão é que com o **--eval** é possível adicionar outras instruções além do que está no script.

##Eliminando o cabeçalho com o parâmetro [--quiet](https://docs.mongodb.com/mongodb-shell/reference/options/#std-option-mongosh.--quiet)
Examinando o arquivo exportado, nota-se que os cabeçalhos de inicialização do mongosh também foram exportados. 
```json
Current Mongosh Log ID:	6129153e30afffaa9fb76fe4
Connecting to:		mongodb://localhost:27017/examples?directConnection=true&serverSelectionTimeoutMS=2000
Using MongoDB:		5.0.0
Using Mongosh Beta:	0.14.0

For mongosh info see: [1mhttps://docs.mongodb.com/mongodb-shell/]

------
   The server generated these startup warnings when booting:
   2021-08-27T10:16:56.220+00:00: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem
   2021-08-27T10:16:56.525+00:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted
   2021-08-27T10:16:56.526+00:00: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. We suggest setting it to 'never'
------

Loading file: ~\export_data_multiple_databases\obter_vendas.js
[{
  fabricante: 'Hyundai',
  modelo: 'Genesis',
  ano: 2009,
...
```

Para resolver este problema basta adicionar o parâmetro *--quiet* ao comando de exportação.

Através do método de importação pela linha de comando:
```shell
mongosh mongodb://localhost:27017/examples --quiet ~/export_data_multiple_databases/obter_massa_dados.js > arquivo_resultado.json
```

Usando o --eval:
```shell
mongosh mongodb://localhost:27017/examples --quiet --eval="load('export_data_multiple_databases/obter_massa_dados.js');" > arquivo_resultado.json
```
Ao examinar novamente o arquivo, nota-se que o cabeçalho sumiu e com isso temos um arquivo .json válido e somente com os dados solicitados.

## Impressões e Conclusão
A ideia deste post surgiu de uma tarefa que solicitava a exportação de dados oriundos de um schema específico para csv. Tal schema existia em diversas collections com documentos que possuiam campos diferentes entre si. O único ponto em comum dessas collections era justamente um subdocumento com o schema em questão.
Como não sou um profundo conhecedor de mongodb e não encontrei uma forma fácil na documentação e muito menos no stackoverflow e afins, resolvi ser criativo. Me vali da capacidade do mongodb de executar scripts .js e de conhecimentos básicos de shell.

A solução é um tanto verbosa, porém resolveu meu problema de forma simples e eficiente. Imagino que os especialistas em mongodb vão me contar nos comentários:
*- basta rodar essa linha de comando que você obtém o mesmo resultado*
Mas até lá, vou seguir usando a mesma abordagem para obter esses dados sempre que forem solicitados.

No github (pasta export_data_multiple_databases) deixei o passo a passo de como reproduzir esse post.
{%github https://github.com/julianopoveda/mongoblogposts/ no-readme%}

Quero deixar um agradecimento especial para o [Caio Lucena](https://www.instagram.com/lucenacaio/)  pelas revisões dos posts publicados até agora. 

## Referências
https://docs.mongodb.com/mongodb-shell/
[Solução do Erro: Como importar data em formato string como tipo date](https://stackoverflow.com/a/33107939)