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.
Vamos utilizar o mongosh 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.
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.
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.
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:
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:
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:
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)?
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
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
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
mongosh "mongodb+srv://<user>:<password>@<seu_cluster>/test" --file verifyIndex.js --quiet > outputIndex.js
Saída para arquivo somente dos índices TTL
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!
Top comments (0)