Detectando índices no MongoDB
Tags: mongodb, index, atlas, nosql
Published: 2023-08-28 17:46:51

---
title: Detectando índices no MongoDB
published: true
description:Veremos a identificação de índices de uma maneira automatizada utilizando o mongosh e também um script Python, bem como o script para a criação dos índices identificados.
tags: mongodb, index, atlas, nosql
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sy32alx1mtyf1e6ldu7x.png
# Use a ratio of 100:42 for best results.
# published_at: 2023-08-28 20:07 +0000
---
# Detecção de Índices
## Será necessário?
Fala pessoALL, em algumas situações precisaremos identificar índices por algum motivo, seja para uma documentação do ambiente ou até mesmo para a reprodução desses índices em outro cluster, como por exemplo desenvolvimento, homologação ou até mesmo para colocá-los em produção. Ter um script que identifique estes índices é importante em vários sentidos e sempre bom tê-lo à mão.

No meu caso essa necessidade surgiu para efeito de documentação, principalmente para identificar se algumas coleções do banco de dados estavam com índices do tipo TTL, muito utilizados para o expurgo automático de dados.
## Conectando
O cluster utilizado aqui está no MongoDB Atlas e se você não tem ainda um ambiente lá para testes pode criar um cluster totalmente free [aqui](https://www.mongodb.com/cloud/atlas/register).

Vamos utilizar o [mongosh](https://www.mongodb.com/docs/mongodb-shell/) que é uma interface completa para administração do MongoDB, vou demonstrar passo-a-passo e por fim juntar tudo para criar o nosso script de saída. Também é importante ressaltar que os comandos à seguir foram executados na versão 7.0 do MongoDB.
```bash
mongosh "mongodb+srv://<seu_cluster>/myFirstDatabase" --apiVersion 1 --username <seu_usuario>
```
## Listando os databases
Aqui é importante ressaltar que o usuário utilizado para a conexão / execução do script deve ter os privilégios necessários para algumas tarefas, como listar os databases, coleções e por fim os índices. Veja mais sobre as permissões no MongoDB neste [link](https://www.mongodb.com/docs/manual/reference/built-in-roles/).
```javascript
var myDBs = db.adminCommand({ listDatabases: 1 }).databases.map(db => db.name);
myDBs.forEach(myDB => {
    print(myDB);
});
```
## Listando as coleções
Agora com os nomes dos databases de nosso cluster, podemos adicionar mais uma etapa e listar o nome das coleções de cada um deles. Note que não queremos listar as coleções dos databases *admin* e nem *local*, para isso, vamos adicionar uma condição que "exclua" esses databases.
```javascript
var myDBs = db.adminCommand({ listDatabases: 1 }).databases.map(db => db.name);
myDBs.forEach(myDB => {
    if (myDB !== 'admin' && myDB !== 'local') {
        db = db.getSiblingDB(myDB);
        print(`Coleções no banco de dados ${myDB}:`);
        db.getCollectionNames().forEach(collectionName => {
            print(`- ${collectionName}`);
        });
    }
});
```
## Listando os índices
Agora, vamos finalmente listar os índices de cada uma das coleções:
```javascript
var myDBs = db.adminCommand({ listDatabases: 1 }).databases.map(db => db.name);
myDBs.forEach(myDB => {
    if (myDB !== 'admin' && myDB !== 'local') {
        db = db.getSiblingDB(myDB);
        print(`Coleções no banco de dados ${myDB}:`);
        db.getCollectionNames().forEach(collectionName => {
            print(`- ${collectionName}`);
            let indexes = db.getCollection(collectionName).getIndexes();
            indexes.forEach(index => {
                print(`  - ${index.name} (${JSON.stringify(index)})`);
            });
        });
    }
});
```
Note que para cada índice temos seu nome e também as propriedades que compõem cada um deles. Isso é importante não só para a identificação dessas propriedades, mas também para gerar uma saída para a criação de cada um deles.
## Adicionando o createIndex()
Vamos modificar um pouco o script para que ele nos dê o comando para a criação de cada um desses índices:
```javascript
var myDBs = db.adminCommand({ listDatabases: 1 }).databases.map(db => db.name);
myDBs.forEach(myDB => {
    if (myDB !== 'admin' && myDB !== 'local') {
        db = db.getSiblingDB(myDB);
        print(`Coleções no banco de dados ${myDB}:`);
        db.getCollectionNames().forEach(collectionName => {
            print(`- ${collectionName}`);
            let indexes = db.getCollection(collectionName).getIndexes();
            indexes.forEach(index => {
                print(`  - ${index.name} (${JSON.stringify(index)})`);
                print(`    -- create: db.getCollection("${collectionName}").createIndex(${JSON.stringify(index.key)}, ${JSON.stringify(index)});`)
            });
        });
    }
});
```
Podemos também modificar um pouco mais para que o índice *_id* não gere o script para a criação:
```javascript
var myDBs = db.adminCommand({ listDatabases: 1 }).databases.map(db => db.name);
myDBs.forEach(myDB => {
    if (myDB !== 'admin' && myDB !== 'local') {
        db = db.getSiblingDB(myDB);
        print(`Coleções no banco de dados ${myDB}:`);
        db.getCollectionNames().forEach(collectionName => {
            print(`- ${collectionName}`);
            let indexes = db.getCollection(collectionName).getIndexes();
            indexes.forEach(index => {
                if(index.name !== "_id_"){
                    print(`  - ${index.name} (${JSON.stringify(index)})`);
                    print(`    -- create: db.getCollection("${collectionName}").createIndex(${JSON.stringify(index.key)}, ${JSON.stringify(index)});`)
                }
            });
        });
    }
});
```
## Identificando uma propriedade do índice
Ok, chegamos até aqui, mas e se quisermos listar somente os índices com a propriedade TTL (como se deu a origem desse post)?
```javascript
var myDBs = db.adminCommand({ listDatabases: 1 }).databases.map(db => db.name);
myDBs.forEach(myDB => {
    if (myDB !== 'admin' && myDB !== 'local') {
        db = db.getSiblingDB(myDB);
        print(`Coleções no banco de dados ${myDB}:`);
        db.getCollectionNames().forEach(collectionName => {
            print(`- ${collectionName}`);
            let indexes = db.getCollection(collectionName).getIndexes();
            indexes.forEach(index => {
                if(index.name !== "_id_"){
                    if (index.expireAfterSeconds !== undefined) {
                        print(`  - ${index.name} (${JSON.stringify(index)})`);
                        print(`    -- create: db.getCollection("${collectionName}").createIndex(${JSON.stringify(index.key)}, ${JSON.stringify(index)});`)
                    }
                }
            });
        });
    }
});
```
## Juntando tudo
Ufa, quanta coisa! Agora vamos juntar tudo para que nosso script seja realmente funcional em três pontos:
- listar todos os índices (exceto o *_id*) de cada coleção de cada base de dados;
- gerar o comando para a criação de cada um deles;
- e por fim, somente os índices TTL.
### Todos os índices
```javascript
var myDBs = db.adminCommand({ listDatabases: 1 }).databases.map(db => db.name);
myDBs.forEach(myDB => {
    if (myDB !== 'admin' && myDB !== 'local') {
        db = db.getSiblingDB(myDB);
        print(`use ${myDB};`);
        db.getCollectionNames().forEach(collectionName => {
            let indexes = db.getCollection(collectionName).getIndexes();
            indexes.forEach(index => {
                if (index.expireAfterSeconds !== undefined) {
                        printjson(`db.getCollection("${collectionName}").createIndex(${JSON.stringify(index.key)}, ${JSON.stringify(index)});`)
                }
            });
        });
    }
});
```
#### Saída para arquivo
Aqui para facilitar, colocaremos o conteúdo de nosso script em um arquivo chamado *verifyIndex.js*, você pode criar esse arquivo como preferir. Com isso teremos um arquivo de saída chamado *outputIndex.js*.
- crie o arquivo *verifyIndex.js*
```bash
cat << 'index' | tee verifyIndex.js
var myDBs = db.adminCommand({ listDatabases: 1 }).databases.map(db => db.name);
myDBs.forEach(myDB => {
    if (myDB !== 'admin' && myDB !== 'local') {
        db = db.getSiblingDB(myDB);
        print(`use ${myDB};`);
        db.getCollectionNames().forEach(collectionName => {
            let indexes = db.getCollection(collectionName).getIndexes();
            indexes.forEach(index => {
                if(index.name !== "_id_"){
                    printjson(`db.getCollection("${collectionName}").createIndex(${JSON.stringify(index.key)}, ${JSON.stringify(index)});`)
                }
            });
        });
    }
});
index
```
- carregue o arquivo utilizando o *mongosh*. Aqui utilizamos os seguintes parâmetros:
    - --file indicando o arquivo que será carregado
    - --quiet para que o arquivo de saída não contenha nenhum registro sobre a execução do mongosh, como abertura de conexão e etc.
    - e finalmente a saída será escrita no arquivo *outputIndex.js*
```bash
mongosh "mongodb+srv://<user>:<password>@<seu_cluster>/test" --file verifyIndex.js --quiet > outputIndex.js
```
#### Saída para arquivo somente dos índices TTL
```bash
cat << 'index' | tee verifyIndex.js
myDBs.forEach(myDB => {
    if (myDB !== 'admin' && myDB !== 'local') {
        db = db.getSiblingDB(myDB);
        print(`use ${myDB};`);
        db.getCollectionNames().forEach(collectionName => {
            let indexes = db.getCollection(collectionName).getIndexes();
            indexes.forEach(index => {
                if (index.expireAfterSeconds !== undefined) {
                        printjson(`db.getCollection("${collectionName}").createIndex(${JSON.stringify(index.key)}, ${JSON.stringify(index)});`)
                }
            });
        });
    }
});
```
### Conclusão
É isso pessoALL... como sempre destaco em meus posts, existem diversas maneiras de fazer isso, uma delas é inclusive utilizando Python (o que particularmente eu gosto bastante), e prometo compartilhar o script mais a frente!

Espero que tenham gostado e fiquem à vontade para compartilhar!

Um abraço e até mais!