3 Funcionalidades do MongoDB subutilizadas
Mark Smith6 min read • Published Feb 03, 2022 • Updated Sep 11, 2024
Avalie esse Artigo
Como consultor de desenvolvedores do MongoDB, tenho algumas conversas com desenvolvedores. Muitos desses desenvolvedores nunca usaram o MongoDB e, portanto, a conversa geralmente é em torno do tipo de dados para os quais o MongoDB é particularmente bom. (Spoiler: quase todos eles! O MongoDB é um banco de dados de uso geral que por acaso está centralizado em documentos em vez de tabelas.)
Mas há muitos desenvolvedores que já usam o MongoDB todos os dias e, nessas situações, meu trabalho é garantir que eles saibam como usar o MongoDB de forma eficaz. Certifico-me, em primeiro lugar, de que esses desenvolvedores saibam sobre a estrutura de aggregationdo MongoDB, que é, em minha visão, a funcionalidade mais poderosa do MongoDB. É relativamente subutilizado. Se você não estiver usando o Aggregation Framework em seus projetos, ou seu projeto é muito simples ou você provavelmente poderia estar fazendo as coisas de forma mais eficiente adicionando alguns pipelines de agregação.
Mas este artigo não é sobre a Estrutura de agregação! Este artigo trata de três outros recursos do MongoDB que devem ser mais conhecidos: TTL, Coleções limitadase Change Streams.
Uma das grandes coisas sobre o MongoDB é que é muito fácil armazenar dados nele, sem ter que passar por etapas complexas para mapear seus dados para o modelo esperado pelas expectativas de esquema do seu banco de dados.
Por isso, é muito comum usar o MongoDB como cache e como banco de dados para armazenar itens como informações de sessão, dados de autenticação para serviços de terceiros e outras coisas que são relativamente curtas.
Um idioma comum é armazenar uma data de validade no documento e, ao recuperar o documento, comparar a data de validade com a hora atual e usá-la apenas se ainda for válida. Em alguns casos, como acontece com os tokens de acesso OAuth, se o token tiver expirado, um novo poderá ser obtido do provedor OAuth e o documento poderá ser atualizado.
1 coll.insert_one( 2 { 3 "name": "Professor Bagura", 4 # This document will disappear before 2022: 5 "expires_at": datetime.fromisoformat("2021-12-31 23:59:59"), 6 } 7 ) 8 9 # Retrieve a valid document by filtering on docs where `expires_at` is in the future: 10 if (doc := coll.find_one({"expires_at": {"$gt": datetime.now()}})) is None: 11 # If no valid documents exist, create one (and probably store it): 12 doc = create_document() 13 14 # Code to use the retrieved or created document goes here. 15 print(doc)
Outra expressão comum também envolve armazenar uma data de expiração no documento e, em seguida, executar o código periodicamente que exclui ou atualiza documentos expirados, dependendo do que é correto para o caso de uso.
1 while True: 2 # Delete all documents where `expires_at` is in the past: 3 coll.delete_many({"expires_at": {"$lt": datetime.now()}}) 4 time.sleep(60)
Uma forma alternativa de gerenciar dados que tenham uma expiração, seja absoluta ou relativa ao tempo em que o documento está armazenado, é usar um índice TTL.
Os índices TTL são índices especiais de campo único que o MongoDB pode usar para remover automaticamente documentos de uma coleção após um determinado período de tempo ou em um horário específico. Os índices TTL são o motivo pelo qual gostaria de pensar no MongoDB como uma plataforma para criar aplicativos de dados, não apenas um banco de dados. Se você aplicar um índice TTL ao campo de expiração de seus documentos, o MongoDB removerá automaticamente o documento para você! Isso significa que você não precisa escrever seu próprio código para remover documentos expirados e não precisa se lembrar de sempre filtrar os documentos com base no fato de a expiração ser anterior ao horário atual. Você também não precisa calcular o tempo de expiração absoluto se tudo que você tem é o número de segundos que um documento permanece válido!
Deixe-me mostrar como isso funciona. O código abaixo demonstra como criar um índice no campo
created_at
. Como expiresAfterSeconds
está definido como 3600 (que é uma hora), todos os documentos na coleção com created_at
definido como uma data serão excluídos uma hora após esse ponto no tempo.1 coll = db.get_collection("ttl_collection") 2 3 # Creates a new index on the `created_at`. 4 # The document will be deleted when current time reaches one hour (3600 seconds) 5 # after the date stored in `created_at`: 6 coll.create_index([("expires_at", 1)], expireAfterSeconds=3600) 7 8 coll.insert_one( 9 { 10 "name": "Professor Bagura", 11 "created_at": datetime.now(), # Document will disappear after one hour. 12 } 13 )
Outro idioma comum é definir explicitamente o tempo de expiração, quando o documento deve ser excluído. Isso é feito definindo
expireAfterSeconds
como 0:1 coll = db.get_collection("expiry_collection") 2 3 # Creates a new index on the `expires_at`. 4 # The document will be deleted when 5 # the current time reaches the date stored in `expires_at`: 6 coll.create_index([("expires_at", 1)], expireAfterSeconds=0) 7 8 coll.insert_one( 9 { 10 "name": "Professor Bagura", 11 # This document will disappear before 2022: 12 "expires_at": datetime.fromisoformat("2021-12-31 23:59:59"), 13 } 14 )
Lembre-se de que o processo em segundo plano que remove documentos expirados é executado apenas a cada 60 segundos e em um cluster sob carga pesada, talvez com menos frequência do que isso. Portanto, se você estiver trabalhando com documentos com períodos de expiração muito curtos, esse recurso provavelmente não é para você. Uma alternativa é continuar a filtrar pela expiração em seu código, para se beneficiar do controle mais preciso da validade do documento, mas permitir que o serviço de expiração TTL mantenha a collection ao longo do tempo, removendo documentos que obviamente expiraram.
Se você estiver trabalhando com dados que têm uma vida útil, os índices TTL são um ótimo recurso para manter os documentos em uma collection.
As coleções limitadas são um recurso interessante do MongoDB, útil se você deseja armazenar com eficiência um buffer de documentos em anel.
Uma capped collection tem um tamanho máximo em bytes e, opcionalmente, um número máximo de documentos. (O menor dos dois valores é usado a qualquer momento, portanto, se você quiser atingir o número máximo de documentos, certifique-se de definir o tamanho de bytes grande o suficiente para lidar com o número de documentos que deseja armazenar.) Os documentos são armazenados em ordem de inserção, sem a necessidade de um índice específico para manter essa ordem, e portanto pode lidar com uma taxa de transferência mais alta do que uma collection indexada. Quando a coleção atingir o byte definido
size
ou o númeromax
de documentos, os documentos mais antigos da coleção serão limpos.Coleções limitadas podem ser úteis para armazenar em buffer operações recentes (operações em nível de aplicativo - o oplog do MongoDB é um tipo diferente de coisa), e elas podem ser consultadas quando ocorre um estado de erro, a fim de ter um registro das operações recentes que levam ao estado do erro.
Ou, se você apenas deseja armazenar com eficiência um número fixo de documentos na ordem de inserção, as coleções limitadas são o Go.
As coleções limitadas são criadas com o métodocreateCollection, definindo os parâmetros
capped
, size
e, opcionalmente, os parâmetrosmax
:1 # Create acollection with a large size value that will store a max of 3 docs: 2 coll = db.create_collection("capped", capped=True, size=1000000, max=3) 3 4 # Insert 3 docs: 5 coll.insert_many([{"name": "Chico"}, {"name": "Harpo"}, {"name": "Groucho"}]) 6 7 # Insert a fourth doc! This will evict the oldest document to make space (Zeppo): 8 coll.insert_one({"name": "Zeppo"}) 9 10 # Print out the docs in the collection: 11 for doc in coll.find(): 12 print(doc) 13 14 # {'_id': ObjectId('600e8fcf36b07f77b6bc8ecf'), 'name': 'Harpo'} 15 # {'_id': ObjectId('600e8fcf36b07f77b6bc8ed0'), 'name': 'Groucho'} 16 # {'_id': ObjectId('600e8fcf36b07f77b6bc8ed1'), 'name': 'Zeppo'}
Se quiser ter uma ideia aproximada do tamanho dos seus documentos bson em bytes, para calcular o valor de
size
, você pode usar o métodobsonSize do driver no mongo
shell , em um documento construído no código, ou você pode use MongoDB 4.4 novo operador de agregação bsonSize do , em documentos já armazenados no MongoDB.Observe que, além da maior eficiência gerada pelas capped collections, também há algumas limitações. Não é possível excluir explicitamente um documento de uma capped collection, embora os documentos sejam eventualmente substituídos por documentos recém-inseridos. As atualizações em uma capped collection também não podem alterar o tamanho de um documento. Você não pode fragmentar uma coleta limitada. Existem algumas outras limitações na substituição e atualização de documentos e transações. Leia a documentação para obter mais detalhes.
É importante notar que esse padrão é semelhante ao Bucket Pattern, que permite armazenar um número limitado de itens em uma matriz e cria automaticamente um novo documento para armazenar valores subsequentes quando esse limite for atingido.
E, finalmente, a maior funcionalidade menos conhecida de todas! Oschange streams são uma transmissão ao vivo de alterações no seu banco de dados. O método
watch
, implementado na maioria dos drivers do MongoDB, transmite as alterações feitas em uma coleção, em um banco de dadosou até mesmo emtodo o conjunto de réplicas ou cluster do MongoDBpara seu aplicativo em tempo real. Sempre me surpreendo ao ver que poucas pessoas não ouviram falar dele, já que é uma das primeiras funcionalidades do MongoDB que realmente me emocionou. Talvez seja apenas sorte eu ter me deparado com isso antes.Em Python, se eu quisesse imprimir todas as alterações em uma collection conforme elas são feitas, o código seria mais ou menos assim:
1 with my_database.my_collection.watch() as stream: 2 for change in stream: 3 print(change)
Nesse caso,
watch
retorna um iterador que bloqueia até que uma alteração seja feita na coleção, momento em que produzirá um documento BSON descrevendo a alteração que foi feita.Você também pode filtrar os tipos de eventos que serão enviados para o fluxo de alterações. Portanto, se você estiver interessado apenas em inserções ou exclusões, esses serão os únicos eventos que você receberá.
Usei change streams (que é o que o método
watch
retorna) para implementar um aplicativo de bate-papo, em que as alterações em uma coleção que representava uma conversa eram transmitidas para o navegador usando WebSockets.Mas, fundamentalmente, os change streams permitem que você implemente o equivalente a um gatilho de banco de dados (trigger), mas em sua linguagem de programação favorita, usando todas as bibliotecas que preferir, em execução nos servidores especificados. É uma funcionalidade superpotente e que deveria ser mais conhecida.
Se você ainda não usa o Aggregation Framework, confira a documentação sobre isso. Isso vai explodir sua mente (no bom sentido)!
Mais documentação sobre os tópicos discutidos aqui:
Se tiver dúvidas, acesse o site da nossa comunidade de desenvolvedores, no qual os engenheiros e a comunidade do MongoDB ajudarão você a desenvolver sua próxima grande ideia com o MongoDB.